堆以及堆排序

堆的概念

堆是用数组实现的二叉树,它没有使用父指针或者子指针。利用数组的下标,就可以得到节点的父节点与子节点在数组中的位置。下标为i的节点,在不越界的情况下,它的左孩子坐标为2i+1,右孩子坐标为2i+2,父节点为(i-1)/2。

堆分为两种,大根堆与小根堆。在大根堆中,父节点的值比每一个子节点的值都要大,任何一棵子树的最大值都是这棵子树的头部。在小根堆中,父节点的值比每一个父节点的值都要大,任何一棵子树的最小值都是这棵子树的头部。

堆的操作

  • 插入过程:

    以大根堆为例,在已有的堆中插入一个新的元素,需要经过如下的调整:将插入的节点与父节点的值进行比对,如果父节点的值小于插入值,两者进行交换,直到新插入的值小于它的父节点的值。

    void heapinsert(vector<int> &arr, int index)
    {
    	while (arr[index] > arr[(index - 1) / 2]) //与父节点的值进行对比
    	{
    		swap(arr[index], arr[(index - 1) / 2]);
    		index = (index - 1) / 2;
    	}
    }
    
  • 调整过程:

    在堆中某个元素发生改变,使数组不再形成一个大根堆,需要将该节点向下调整,与子节点中的最大值进行交换。

    void heapify(vector<int> &arr, int index, int heapsize) //heapsize为堆的大小
    {
    	int left = index * 2 + 1;
    	while (left < heapsize)
    	{
    		int largest;
    		if (left + 1 < heapsize)
    			largest = arr[left] > arr[left + 1] ? left : left + 1; 
    		else
    			largest = left; 
    		largest = arr[index] > arr[largest] ? index : largest; //节点与其子节点中的最大值
    		if (largest == index) //最大值为该节点,不调整
    			break;
    		swap(arr[index], arr[largest]);
    		index = largest;
    		left = index * 2 + 1;
    	}
    }
    
  • 堆的弹出:

    与栈一样,堆也有弹出操作,具体的做法如下:

    1. 将堆顶元素与堆中的最后一个元素进行交换。
    2. 对堆顶位置进行heapify操作。
    3. 堆的大小减少1。

堆排序

堆排序是利用堆这种数据结构而设计的一种排序算法,它的时间复杂度为O(NlogN)。它的算法步骤如下:

  1. 利用heapinsert操作将一个无序数组调整为一个大根堆。
  2. 将堆顶元素与末尾元素进行交换,使末尾元素最大,heapsize减1。利用heapify对堆进行调整,使其重新满足堆的定义。然后继续调整堆,再将堆顶元素与末尾元素交换,不断进行重建。
void heapsort(vector<int> &arr)
{
	if (arr.size() < 2)
		return;
	for (int i = 0; i < arr.size(); i++) //构建大根堆
		heapinsert(arr, i);
	int size = arr.size();
	while (size > 0) //将最大值调整到末尾
	{
		swap(arr[0], arr[--size]);
        heapify(arr, 0, size);
	}
}

posted @ 2019-06-10 09:10  番茄起司汤  阅读(158)  评论(0编辑  收藏  举报