倍道而行 :堆(heap)

概念


 先来两个概念(别头疼):

普通队列:先进先出,后进后出

优先队列出队顺序和入队顺序无关;和优先级相关。就好比:我们的电脑操作系统会按照各个进程的优先级来安排CPU执行哪一个进程。

堆(heap)被称为一种优先队列,但是堆的本质不是队列。取出顺序就是每次取最大的呗。

下图就是一个堆:

本质就是一个完全二叉树。完全二叉树的定义看图就可得出:父节点都大于等于子节点。且是从上大下,从左至右的排序。

堆的实现


看图:

仔细看每个数的下标。数组是从1开始的。父节点和子节点的关系也可以很容易得出。

eg:41是28和16的父节点,下标分别为2、4、5,满足图中的公式。

如题:有10个数,请用代码将之组成一个堆?

算法思想:每次往里面插入一个数,我们只需要比较插入的数和其父节点数的大小就行。

代码:

k指的是插入的序号。建议大家这样理解:
假设现在已经构建好了有10个数的堆,我再往里面插入一个数为102,即第11个数。那么k=11.然后与其父节点(k/2=5),即arr[5]进行比较。若大于父节点就交换位置,并让k/2=2,继续进行下去,至到和根结点进行比较。
void
shiftUp(int k,int arr[]){ while (k>1 && arr[k/2]<arr[k]) { swap(arr[k/2], arr[k]); k /=2; } }

 取堆里面的数:每次取都是取第一个位置的数,也就是最大的数。


 如题:现在构建好了一个堆,那么我取走第一个数后,整个堆就需要进行调整,那该如何?

目前思想:(以本遍博客上面的例子为例,读者可以对照着看) 

本题的:k代表开始的位置为1,j为子节点的位置(eg:指的是62这个根的左右两个子节点)

  • 1.取走62,我就让15代替62的位置。即,用最末尾的数字放到根节点上。
  • 2.比较用15和62的两个左右节点进行比较,如果大就不做处理,否则交换位置并且,让k=j.
  • 继续走第二步
void shiftDown(int k,int arr[],int count ){
    while (2*k< count) { //确认有左子节点
        int j = 2*k;
        if (j+1<count && arr[j+1] > arr[j]) { //如果有右节点
            j+=1; 
        }
        if (arr[k]>arr[j]) { 如果,比较发现字节点没有父节点大,就退出
            break;
        }
        swap(arr[k], arr[j]);//否则就交换位置
        k = j;//然后让k=j,继续进行比较。
    }
}

结尾:     


 堆排序算法还是很简单的。

建立堆,然后一个一个的取出,那么这就是一个从大到小的一个有序的序列。

但行好事,莫问前程。
原文地址:https://www.cnblogs.com/yuhui-snail/p/8581107.html