堆类似树的结构,但是可以用数组进行表示,可以用有效节省空间和提高效率。

大顶堆 / 小顶堆

堆的每个节点可以有两个子节点,如果父节点都比子节点大 / 小,且每个节点在拥有右节点的情况下,左节点不为空,则称为大 / 小顶堆。为了描述方便,一下说明和结构实现都以大顶堆为例。

所以堆的顶节点肯定是最大的节点,但子节点间的关系就无法确认,例如无法确认最小的节点在左子树还是右子树。

堆的这个属性往往用于存储最关心的前 N 个数据,并且不在意 N 个数据之间的具体顺序。例如在处理前 N 个最大元素的问题,可以先对所有元素进行排序,在选取前 N 个。然而求算元素的具体顺序就增加了不必要的判断次数,因为我们只需要前 N 个,具体顺序对结果没有影响。

堆的一些属性

更具上问提到的堆的要求,可以得到以下堆的属性:

  • 堆的高度为 h = floor(log(2, n)) + 1,n 为节点数学数量。
  • index 为 i 的节点,parentIndex = floor((i - 1) / 2)
  • index 为 i 的节点,leftIndex = 2 * i + 1rightIndex = 2 * i + 2

增删节点

增加

为了不破坏堆的特性,增加节点从末尾开始增加。假设新增为节点 c ,父节点为 a,同级的左节点为 b。根据堆的属性,c 和 b 无需比较,c 和 a 进行比较,如果 c 大于 a 则可直接和 a 换位置,因为 a 是必定大雨 b 的。所以替换位置后的 c 不会破坏 a,b,c 三个节点的堆属性,然后继续向上。维护以 c 新位置作为子节点的堆直至顶点。

删除

删除任意索引节点,删除要比增加多一次判断操作,假设删除节点为 a,左子节点为 b,右子节点为 c,则先取尾节点和 a 替换。删除后,a,b,c 节点堆属性可能以破坏,进行维护。因为顶节点事堆属性里三个节点最大的,需要先的到左右节点中较大的那个节点,然后再进行替换判断。假设 b 是较大的,则和 a 替换,替换后,以 a 节点新位置位顶结点的堆属性可能破坏了,再进判断,直至尾结点。

堆节点的增删操作有相似的规律,对于修改的节点要么一直上升,要么一直下移。

实现代码

#include <iostream>
#include <math.h>
#include "HeapDemo.h"

#define MAX_LOOP_COUNT 1000
#define DEFAULT_VALUE  -1

void Swap(int &a, int &b)
{
    int temp = a;
    a = b;
    b = temp;
}

enum HeapTopType
{
    Max,
    Min,
};

class Heap_Demo {
private:
    int* array;
    int curSize;
    int totalSize;
    HeapTopType type;

    int GetNewIndex()
    {
    	return curSize < totalSize - 1 ? curSize : totalSize - 1;
		}
 
    void InitSize(int s)
    {
        curSize = 0;
        totalSize = s + 1;
        array = new int[totalSize];
        for (int i = 0; i < totalSize; ++i)
        {    
            array[totalSize - 1] = DEFAULT_VALUE;
        }
    }

public:
    Heap_Demo(int * ary, int s)
    {
      	type = Max;
        InitSize(s);

        for (int i = 0; i < s; ++i)
        {
            Add(ary[i]);
        }
    }

    Heap_Demo(int s)
    {
    		type = Max;

    		InitSize(s);
		}


    void Add(int num)
    {
        int curIndex = GetNewIndex();
        array[curIndex] = num;
        ++curSize;

        int count = 0;
        while (curIndex > 0)
        {
            ++count;
            if (count > MAX_LOOP_COUNT)
            {
                std::cout << "Error: deap looping" << std::endl;
                return;
            }

            int parentIndex = int(floor(((curIndex - 1) / 2)));
            if (parentIndex < 0) break;

            if (type == Max)
            {
                if (array[parentIndex] > array[curIndex]) break;
            }
            else if (type == Min)
            {
                if (array[parentIndex] < array[curIndex]) break;
            }

            Swap(array[parentIndex], array[curIndex]);
            curIndex = parentIndex;
        }
		}

    void Delete(int value)
    {
        for (int i = 0; i < curSize; ++i)
        {
            if (array[i] == value)
            {
                DeleteAt(i);
                return;
            }
        }
    }
      
    void DeleteAt(int index)
    {
        if (index < 0 || index > curSize - 1)
        {
            std::cout << "Error, please remove index from " << 0 << " to " << curSize - 1;
            return;
        }

        int removeIndex = index;
        std::cout << "Removing index at " << index << " value : " << array[index] << "\n";

        Swap(array[removeIndex], array[curSize - 1]);
        --curSize;

        int curIndex = removeIndex;
        while (curIndex < curSize - 1 && curIndex >= 0)
        {
            int leftChild = 2 * curIndex + 1;
            int rightChild = 2 * curIndex + 2;
            int switchIndex = -1;
            if (leftChild < curSize - 1)
            {
                switchIndex = leftChild;

                if (rightChild < curSize - 1)
                {
                    if ((type == Max && array[rightChild] > array[leftChild])
                        || (type == Min && array[rightChild] < array[leftChild]))
                    {
                        switchIndex = rightChild;
                    }
                }

                if (switchIndex != -1)
                {
                    if ((type == Max && array[switchIndex] > array[curIndex])
                        || (type == Min && array[switchIndex] < array[curIndex]))
                    {
                        Swap(array[switchIndex], array[curIndex]);
                    }
                }
            }

            curIndex = switchIndex;
        }
		}
      
    void Display()
    {
        for (int i = 0; i < curSize; ++i)
        {
            std::cout << array[i] << "\t";
        }

        std::cout << "|\t";

        for (int i = curSize; i < totalSize - 1; ++i)
        {
            std::cout << array[i] << "\t";
        }

        std::cout << "||\t";
        std::cout << array[totalSize - 1] << "\t";

        std::cout << "\n";
    }
      
    int GetTop()
    {
        if (curSize > 0)
        {
            return array[0];
        }

        return DEFAULT_VALUE;
    }
 
    int PopTop()
    {
        int top = GetTop();
        DeleteAt(0);
        return top;
    }
      
    ~Heap_Demo()
    {
      delete[] array;
      array = NULL;
    }
}

Comments