八大排序算法 之 堆排序(二叉樹排序)

例如數組 a={19,3,60,7,1,15,33,24,45,32,79,85};

排序思想:

1,堆排序也是選擇排序的一種,根據堆得特性,每次把最大或最小值(本次以最大值爲例)拿出來,按序排列;

2,堆排序是對普通選擇排序的一種優化:如果是一個穩定堆,每次在選擇最大值時,只用沿着二叉樹其中一個分叉去交換即可,其他分叉符合堆得特性(因是排好的穩定堆),可以看作是穩定的,不用重排交換,省去了絕大多數的比較交換步驟,數組的數越多,分支越多,該算法的優勢就越明顯;

3,第一步,將數組初始化爲穩定堆,穩定堆的特性:二叉樹之父節點總比其左右孩子節點大!初始化穩定堆有很多方法,可以從堆頂向堆底方向初始化,也可以從堆底向堆頂方向排列初始化,也可以通過小三角遞歸調等完成初始化,本例爲理解方便,選用從堆底向堆頂方向初始化,即,每次從小三角里找到最大值放在父節點,從最後一個小三角向前循環,第一遍,找到所有數的最大值,第二遍循環找到次最大值放在第二層節點上,依次類推,完成穩定堆得初始化;

4,初始化完穩定堆之後,將選出的最大值與最後一個數字交換放在數組的最後固定下來(以後循環不再用到此數字),此時除了頂部三角不穩定,下面都是穩定的,根據穩定堆得特性(父節點總是大於其左右孩子節點),從頂部往底部尋找交換,只沿着變動的那個分叉交換下去(只用單線一次循環即可),其他分叉不用動,交換完畢後,次最大數又被移到頂部,此時的堆仍然是一個穩定堆,再將頂部的最大值交換的數組的後面固定下來,重複這個步驟,依次類推,即可完成;

排序趟數:

1,初始化堆所用趟數:不確定,最大趟數是二叉樹的層數,最小一趟

2,初始化堆後所用趟數:length-2次

排序原理:

穩定堆特性,二叉樹排序,具體見上述排序思想

參考圖片:



具體代碼實現如下:

 //主方法

<span style="font-size:14px;">	public static void heapSort(int[] array){
		
		int length=array.length;
		initHeap(array, length);//初始化穩定堆
		System.out.println("初始化堆後:" + Arrays.toString(array));
		switchData(array,0,length-1);//交換第一個元素和本輪最後一個元素
		//二叉樹排序
		for (int length2 = length - 1; length2 > 1 ; length2--) {//循環length - 2次
			int index=0;
			while(2 * index + 1 < length2){//只要有左孩子節點就可能產生交換,進入循環
				if (2 * index + 2 < length2) {//左右孩子都有
					int max = index;
					if (array[max] < array[2 * index + 1]) {
						max = 2 * index + 1;
					}
					if (array[max] < array[2 * index + 2]) {
						max = 2 * index + 2;
					}
					if (max != index) {
						switchData(array, index, max);
						index = max;
					}else {
						break;//max==index,表示節點最大,下面的堆都是穩定的,不用繼續循環
					}
					
				}
				if (2 * index + 1 < length2 && 2 * index + 2 >= length2) {//只有左孩子,沒有右孩子
					if (array[index] < array[2 * index + 1]) {
						switchData(array, index, 2 * index + 1);
					}else {
						break;//2 * index + 1==index,表示節點最大,下面的堆都是穩定的,不用繼續循環
					}
				}
			}
			switchData(array, 0, length2 - 1);//交換第一個元素和本輪最後一個元素
		}
	}</span>
<span style="font-size:14px;">//初始化堆;
	public static void initHeap(int[] array,int length){
		
		int high=length-1;
		boolean isChange=false;
		for(int k=(high-1)/2;k >= 0;k--){
			//找到最後一個父節點
			int left=2*k+1;
			int right=left+1;
			//判斷是否有節點
			if(left<=high){
				int max=left;
				if(right<=high){
					if(array[left]<array[right]){
						max=right;
					}
				}
				//將最大值跟父節點交換
				if(array[max] > array[k]){
					isChange=true;
					switchData(array,max,k);
				}
			}
		}
		if(isChange){//如果一次就能完成穩定堆的初始化,則不再需要遞歸調用,以達到優化算法的目的
			initHeap(array,length); 
			System.out.println("遞歸:"+Arrays.toString(array));
		}
		
	}</span>
//交換數組中兩個數的方法,因爲要多次用到,封裝成一個方法;
	public static void switchData(int[] array,int index1 ,int index2){
		array[index1] ^= array[index2];
		array[index2] ^= array[index1];
		array[index1] ^= array[index2];
	}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章