06.C++ 堆排(堆的 heapInsert 和heapify 过程)

225 阅读3分钟

一、堆的概念

一个堆必定是一个完全二叉树,

这又说到了什么是二叉树,它分为 完全二叉树和不完全二叉树两种。

  • 大根堆:任何一个节点店,都是以他为父节点的树的最大值
  • 小根堆:任何一个节点店,都是以他为父节点的树的最小值
  • 规律: 某个下标是 i 的节点,它的 左孩子:2*i+1 右孩子:2*i+2 父节点:(i-1)/2 向下取整;

image.png

image.png

二、堆的 heapInsert 过程

-----------------heapInsert往上的过程-----------------

  • 新进加来的数,现在停在了index位置,请依次往上移动
  • 移动到0位置,或者干不掉自己的父亲了,停
       
	int heapInsert(int arr[],int index){
		// 进来的 [index]   他父亲[(index-1)/2]
		// 但是当index==0 的时候,这个条件就不会成立了,因为你永远不会比你你自己大 0 的父节点 0 
		// 当 index位小于父亲的时候,条件也不成立,使以说这个条件是关键。 
		while(arr[index]>arr[(index-1)/2]){
			swap(arr[index],arr[(index-1)/2]);
			index= (index-1)/2;
		}
	};

三、堆的 heapify 过程

-----------------heapify 往下-----------------

  • 从index位置,往下看,不断的下沉
  • 停:较大的那个孩子都没有index位置的数大;或者是index已经没有孩子了
		
	int heapify(int arr[],int index,int heapSize){
		int left= index*2+1;
		//heapSize 表示队列的长度大小 
		while(left <= heapSize){ // 如果有左孩子,此时可能有右孩子,也可能没有 
			// 把较大孩子的下表,给largest 
			int largest = left+1 < heapSize && arr[left+1] > arr[left] ? left+1 : left;
			largest = arr[largest] > arr[index] ? largest : index;
			if(largest == index){
				break;
			}
			swap(arr[largest],arr[index]);
			index= largest;
			left= index* 2+ 1;
		}
			
	};

image.png

四、现在我有一个堆整挺好的,突然某个index的数变了,不知道它是变大了还是变小了。

** 这时候怎么复原呢???**

执行一次 heapInsert() ,再执行一次heapify(),顺序调用就完了,其实他们只发生一个!

** 通过这个方式堆一定能重新整好 ** ** 接下来可以讲堆排序了 **

五、堆排序

堆排思路分析

  1. 无序的数组进来,共N个元素
  2. 每进来一个数,执行一次 heapInsert 上浮,变成大根堆,此时heapSize = N-1
  3. 0位上的数 和 N-1 上的数 交换,然和 heapSize-1
  4. 此时再执行一次heapify 下沉过程,又变成了大根堆,
  5. 然后不断去重复 3 4过程,直到heapSize>0。

image.png

C++具体代码:

#include<bits/stdc++.h> 
using namespace std;
	// -----------------heapInsert 往上—————————— 
		// 新进加来的数,现在停在了index位置,请依次往上移动
		// 移动到0位置,或者干不掉自己的父亲了,停 
	int heapInsert(int arr[],int index){
		// 进来的 [index]   他父亲[(index-1)/2]
		// 但是当index==0 的时候,这个条件就不会成立了,因为你永远不会比你你自己大 0 的父节点 0 
		// 当 index位小于父亲的时候,条件也不成立,使以说这个条件是关键。 
		while(arr[index]>arr[(index-1)/2]){
			swap(arr[index],arr[(index-1)/2]);
			index= (index-1)/2;
		}
	}; 
	
	// -----------------heapify 往下——————————
		// 从index位置,往下看,不断的下沉 
		// 停:较大的那个孩子都没有index位置的数大;或者是index已经没有孩子了 		
	int heapify(int arr[],int index,int heapSize){
		int left= index*2+1;
		//heapSize 表示队列的长度大小 
		while(left <= heapSize){ // 如果有左孩子,此时可能有右孩子,也可能没有 
			// 把较大孩子的下表,给largest 
			int largest = left+1 < heapSize && arr[left+1] > arr[left] ? left+1 : left;
			largest = arr[largest] > arr[index] ? largest : index;
			if(largest == index){
				break;
			}
			swap(arr[largest],arr[index]);
			index= largest;
			left= index* 2+ 1;
		}
			
	};
	// -----------------堆排——————————
	int heapSort(int arr[],int length){
		
		//1.给我一个数组,建立大根堆
		if(length< 2){  //为啥 arr[]== null || sizeof(arr)< 2 报错 
			return 0; 
		}
		for(int i=0; i<length;i++){  //O(N)
			heapInsert(arr,i); //O(logN)
		}
		//2.heapSize一定是数组大小 
		int heapSize= length-1;
		//3.0位置和heapSize(堆上最后一个数)交换,然后 heapSize-1
		swap(arr[0],arr[heapSize]);
		heapSize--;     //全局最大值换到了N-1位,而且我还把它断开了 
		//复杂度 O(N*logN)
		while(heapSize>0 ){ //O(N)
			heapify(arr,0,heapSize);  //O(logN)
			swap(arr[0],arr[heapSize]); //O(1)
			heapSize--;
			
		} 
	};	
	int main()
	{
		int arr[6]={8,25,43,65,3,7};
		heapSort(arr,6);
		for(int m=0; m<=5;m++){
			cout<<arr[m]<<endl;
		}
		cout<<"hhhhh"<<endl;
		return 0;
	}