数据结构杂例之堆

文章目录

  • 引入
    • 什么是堆
    • 堆的定义
    • 堆的分类
  • 堆的操作(小根堆为例)
    • 堆的存储和遍历
    • 堆的维护
      • 元素的下沉
      • 元素的上浮
    • 堆的基本操作
      • 插入一个数
      • 删除根节点(最大值/最小值)
      • 修改任意结点元素

引入

什么是堆

堆(英语:heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。

堆的定义

去做风吧,去做不被定义的风,我们堆拒绝“标签”,不被定义

  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树

堆的分类

大根堆:

						  	9
                           /  \
                          6    8
                         /\    /\
                        3  2  1  4 		

很明显,任何一个子节点都不小于它的父节点,所以根节点最大

小根堆:

							1
                           / \
                          2   8
                         /\   /\
                        3  7 8  9 

任何一个子节点都不大于它的父节点,所以根节点最小

堆的操作(小根堆为例)

堆的存储和遍历

堆用数组存储,顺序是二叉树的层序遍历

为什么呢?
拿上边的小根堆举例:

							1
                           / \
                          2   8
                         /\   /\
                        3  7 8  9 

对应数组:
在这里插入图片描述
我们发现这样存储,会有:

节点下标/2 = 父节点下标
父节点下标 * 2 = 左孩子
父节点下标 * 2 + 1 = 右孩子

那么我们可以根据下标作为索引,找到任何一个节点的孩子和父亲(前提是存在)。
那我们怎么判断有的结点是不是有孩子或者父节点呢?

  • 很简单,判断父节点就是判断是不是根节点,因为只有根节点没有父节点。

  • 孩子呢?我们加入一个size变量,标记当前数组的元素个数和作为存入堆操作的指针下标,岂不妙哉?

即:

insertEnd(int x){
	heap[++size] = x; 
}

上边操作只是将数据插入最后,并不是真正插入,因为我们需要时刻维护这堆的特殊性质。

堆的维护

元素的下沉

元素什么时候会下沉?仅当一个节点的子节点都大于等于它时,才会下沉:

while(heap[x] >= heap[x * 2] && x * 2 <= n) || (heap[x] >= heap[2 * x + 1] && x * 2 + 1 <= n){}

那接下来就判断是哪个结点不符合这个性质,然后对两个节点进行交换,并且让指针移到新坐标

void down(int x)	//x为下标
{
   while(heap[x] >= heap[x * 2] && x * 2 <= n) || (heap[x] >= heap[2 * x + 1] && x * 2 + 1 <= n){
   //只要找到有一个点小于它,那么就下沉,直无法下沉为止。
        if(heap[x * 2] <= heap[x * 2 + 1])//两个子节点,往更小的地方下沉。
        {
            swap(heap[x], heap[x * 2]);
            x *= 2;//需要改变下标。
        }
        else
        {
            swap(heap[x], heap[x * 2 + 1]);
            x = 2 * x + 1;
        }
    }
}

元素的上浮

和下沉比起来思想一致,代码很少,因为每个节点只有一个父节点(除了根节点)

void up(int x)
{
    while(heap[x] < heap[x / 2] && x > 1)
    //注意这个x>1,我们说过 x == 1 就成为根节点了,此时上浮无意义
    {
        swap(heap[x],heap[x / 2]);
        x /= 2;
    }
}

ok主要操作就这点,其他的操作,都是基于上边的两个操作。

堆的基本操作

插入一个数

void insert(int x){
	heap[++size] = x;
	up(x);		//在最后了,只能向上爬
}

删除根节点(最大值/最小值)

void Delete_root(){
	heap[1] = heap[size--];
	//size-- 更新边界
	down(1);
}

删除任意结点也是这个办法,只不过比根节点多加一个

	up(x);

因为根节点不需要去 up(1);

修改任意结点元素

void modify(int idx, int x){
	heap[idx] = x;
	down(idx);
	up(idx);
}

你可能感兴趣的:(数据结构,数据结构)