JAVA代碼實現堆排序

堆排序

最近學習了堆排序算法。堆排序是一種選擇排序,是不穩定的排序,其最壞、平均、最優時間複雜度都爲O(nlogn)。堆排序邏輯上是利用完全二叉樹對數進行移動,實際是對數組進行操作。

堆排序算法的基本思路:

  • 根據升序或降序將數組邏輯上轉換成大頂堆或小頂堆;
  • 將根節點的數和最後的數交換位置,“沉”在數組的最後,然後繼續調整成大頂堆/小頂堆;
  • 反覆執行步驟2,直至結束。

看了上述步驟之後可能會有這兩個問題:1.什麼是大頂堆和小頂堆?2.爲什麼對數組操作,會跟二叉樹有關係?

  • 首先來解釋大頂堆和小頂堆的問題:
    大頂堆:對一顆二叉樹而言,如果每個節點的值都大於或等於其左右子節點的值,則稱爲大頂堆;
    小頂堆:對一顆二叉樹而言,如果每個節點的值都小於或等於其左右子節點的值,則稱爲小頂堆;
    注意:沒有要求節點的左右子節點值的大小關係
  • 爲什麼對數組操作,會跟二叉樹有關係?
    堆排序實際上是在對數組進行操作,但是其邏輯思想是利用完全二叉樹來移動數據。首先將一個無序數組按順序排列成一棵完全二叉樹,例如數組arr:{8,6,7,4,3,2,5,1,9},排列成一棵完全二叉樹:
    在這裏插入圖片描述
  • 然後從最後的非葉子節點開始,將這棵完全二叉樹調整成大頂堆;

在這裏插入圖片描述

  • 再將根節點和最後的節點互換位置,將最大的數放到了數組的最後:
    在這裏插入圖片描述
  • 將9放到數組末端之後,將不再參與下一輪排序。重新將剩餘的數調整爲大頂堆。依次類推,直至結束。
    在這裏插入圖片描述
    其代碼實現如下:
package com.tree.heapSort;

import java.util.Arrays;

/*
 * 堆排序
 * 將數組從小到大排序爲例:
 * 說明:邏輯上是將數組按順序排列成一個完全二叉樹進行操作,實際上是對數組的操作
 * 1.構建大頂堆
 * 2.將最開始的節點和末尾節點互換位置,將最大元素“沉”到最後,也就是放在數組的最末尾,然後繼續反覆調整+交換
 */
public class HeapSortDemo2 {

	public static void main(String[] args) {
		// 生成一個無序的數組
		int[] arr = new int[10];
		int num = 0;
		int count = 0;
		boolean flag = false;
		while (count < arr.length) {
			num = (int) (Math.random() * 100);
			for (int i = 0; i < arr.length; i++) {
				if (arr[i] == num) {
					flag = true;
					break;
				}
			}
			if (!flag) {
				arr[count++] = num;
			} else {
				flag = false;
			}
		}
		System.out.println("原數組爲:" + Arrays.toString(arr));

		// 堆排序
		heapSort(arr);

	}

	/**
	 * 堆排序
	 * 
	 * @param arr
	 */
	public static void heapSort(int[] arr) {
		// 1.將數組轉換成大頂堆
		// 從最後的非葉子節點開始自下向上調整
		for (int i = arr.length / 2 - 1; i >= 0; i--) {
			adjustHeap(arr, i, arr.length);
		}
		System.out.println("數組轉換成大頂堆後:" + Arrays.toString(arr));

		// 2.將頂端的元素與末尾元素置換位置,然後再繼續調整成大頂堆
		int temp = 0;
		int count = 0;
		for (int j = arr.length - 1; j > 0; j--) {
			temp = arr[j];
			arr[j] = arr[0];
			arr[0] = temp;
			// 交換後較小的元素又被放在了根節點,需要重新調整成大頂堆
			adjustHeap(arr, 0, j);
			System.out.println("第" + (++count) + "輪堆排序後:" + Arrays.toString(arr));
		}
		System.out.println("堆排序最終結果:" + Arrays.toString(arr));
	}

	/**
	 * 將數組調整爲大頂堆
	 * 
	 * @param arr    帶調整的數組
	 * @param i      每次需要調整子樹的父節點下標
	 * @param length 每次需要調整的元素個數,遞減
	 */
	public static void adjustHeap(int[] arr, int i, int length) {
		// 將待調整的元素臨時保存
		int temp = arr[i];
		// 開始調整
		// 從下標i開始,比較其左右子節點的值是否大於arr[i],如果大於,就和arr[i]互換位置,構建局部大頂堆
		for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
			// k表示下標i元素的左子節點,k+1表示其右子節點,首先判將k移動到較大值的位置
			if (k + 1 < length && arr[k] < arr[k + 1]) {// 說明右子節點大於左子節點
				k++;// 移動到右子節點
			}
			// 判斷下標k的值是否大於待調整元素,如果大於,就arr[k]=arr[i]
			if (arr[k] > temp) {
				arr[i] = arr[k];
				// 賦值完畢後,要將i移動到k的位置,下一輪比較該位置和其左右子節點的大小
				i = k;
			} else {
				break;
			}
		}
		// 循環結束時,說明已經將最初下標爲i的父節點的樹調整爲大頂堆,並且此時i的位置已經移動
		// 需要將temp填在arr[i]的位置
		arr[i] = temp;
	}

}

如有錯誤之處,還望指出,定會及時改正。

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