欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的堆排序詳解

 更新時(shí)間:2023年08月28日 10:01:52   作者:小小角色熊  
這篇文章主要介紹了Java中的堆排序詳解,堆排序的重點(diǎn),在于排序的方式,堆排序,就是以堆的形式去排序,毫無疑問,了解堆很重要,文中提供了圖解與部分代碼,需要的朋友可以參考下

1:堆

毫無疑問,排序兩個(gè)字沒必要去死磕,這里的重點(diǎn),在于排序的方式,堆排序,就是以堆的形式去排序,毫無疑問,了解堆很重要。

那么,什么是堆呢?

這里,必須引入一個(gè)完全二叉樹的概念,然后過渡到堆的概念。

上圖,就是一個(gè)完全二叉樹,其特點(diǎn)在于:

從作為第一層的根開始,除了最后一層之外,第N層的元素個(gè)數(shù)都必須是2的N次方;

第一層2個(gè)元素,第二層4個(gè),第三層8個(gè),以此類推。

而最后一行的元素,都要緊貼在左邊,換句話說,每一行的元素都從最左邊開始安放,兩個(gè)元素之間不能有空閑,具備了這兩個(gè)特點(diǎn)的樹,就是一棵完全二叉樹。

那么,完全二叉樹與堆有什么關(guān)系呢?

我們假設(shè)有一棵完全二叉樹,在滿足作為完全二叉樹的基礎(chǔ)上,對(duì)于任意一個(gè)擁有父節(jié)點(diǎn)的子節(jié)點(diǎn),其數(shù)值均不小于父節(jié)點(diǎn)的值;

這樣層層遞推,就是根節(jié)點(diǎn)的值最小,這樣的樹,稱為小根堆。

同理,又有一棵完全二叉樹,對(duì)于任意一個(gè)子節(jié)點(diǎn)來說,均不大于其父節(jié)點(diǎn)的值,如此遞推,就是根節(jié)點(diǎn)的值是最大的,這樣的數(shù),稱為大根堆。

如上圖,左邊就是大根堆;右邊則是小根堆,這里必須要注意一點(diǎn),只要求子節(jié)點(diǎn)與父節(jié)點(diǎn)的關(guān)系,兩個(gè)節(jié)點(diǎn)的大小關(guān)系與其左右位置沒有任何關(guān)系。

明確下大根堆,小根堆的概念,繼續(xù)說堆排序。

現(xiàn)在對(duì)于堆排序來說,我們先要做的是,把待排序的一堆無序的數(shù),整理成一個(gè)大根堆,或者小根堆,下面討論以大根堆為例子。

給定一個(gè)列表array=[16,7,3,20,17,8],對(duì)其進(jìn)行堆排序(使用大根堆)。

接下來內(nèi)容是轉(zhuǎn)載部分,自己繪圖功底太差:其中綠色部分為自己的注解。

步驟一

構(gòu)造初始堆。將給定無序序列構(gòu)造成一個(gè)大頂堆(一般升序采用大頂堆,降序采用小頂堆)。

假設(shè)給定無序序列結(jié)構(gòu)如下

此時(shí)我們從最后一個(gè)非葉子結(jié)點(diǎn)開始(葉結(jié)點(diǎn)自然不用調(diào)整,第一個(gè)非葉子結(jié)點(diǎn) arr.length/2-1=5/2-1=1,也就是下面的6結(jié)點(diǎn)),從左至右,從下至上進(jìn)行調(diào)整。

此處必須注意,我們把6和9比較交換之后,必須考量9這個(gè)節(jié)點(diǎn)對(duì)于其子節(jié)點(diǎn)會(huì)不會(huì)產(chǎn)生任何影響?

因?yàn)槠涫侨~子節(jié)點(diǎn),所以不加考慮;但是,一定要熟練這種思維,寫代碼的時(shí)候就比較容易理解為什么會(huì)出現(xiàn)一次非常重要的交換了。

找到第二個(gè)非葉節(jié)點(diǎn)4,由于[4,9,8]中9元素最大,4和9交換。

在真正代碼的實(shí)現(xiàn)中,這時(shí)候4和9交換過后,必須考慮9所在的這個(gè)節(jié)點(diǎn)位置,因?yàn)槠渖系闹底兞?,必須判斷?duì)其的兩個(gè)子節(jié)點(diǎn)是否造成了影響

這么說不合適,實(shí)際上就是判斷其作為根節(jié)點(diǎn)的那棵子樹,是否還滿足大根堆的原則,每一次交換,都必須要循環(huán)把子樹部分判別清楚。

這時(shí),交換導(dǎo)致了子根[4,5,6]結(jié)構(gòu)混亂,繼續(xù)調(diào)整,[4,5,6]中6最大,交換4和6。

牢記上面說的規(guī)則,每次交換都要把改變了的那個(gè)節(jié)點(diǎn)所在的樹重新判定一下,這里就用上了,4和9交換了,變動(dòng)了的那棵子樹就必須重新調(diào)整,一直調(diào)整到符合大根堆的規(guī)則為截。

此時(shí),我們就將一個(gè)無序序列構(gòu)造成了一個(gè)大頂堆。

步驟二

  • 將堆頂元素與末尾元素進(jìn)行交換,使末尾元素最大。然后繼續(xù)調(diào)整堆,再將堆頂元素與末尾元素交換,得到第二大元素。

如此反復(fù)進(jìn)行交換、重建、交換。

將堆頂元素9和末尾元素4進(jìn)行交換

這里,必須說明一下,所謂的交換,實(shí)際上就是把最大值從樹里面拿掉了,剩下參與到排序的樹,其實(shí)只有總結(jié)點(diǎn)的個(gè)數(shù)減去拿掉的節(jié)點(diǎn)個(gè)數(shù)了。所以圖中用的是虛線。

  • 重新調(diào)整結(jié)構(gòu),使其繼續(xù)滿足堆定義

  • 再將堆頂元素8與末尾元素5進(jìn)行交換,得到第二大元素8.

后續(xù)過程,繼續(xù)進(jìn)行調(diào)整,交換,如此反復(fù)進(jìn)行,最終使得整個(gè)序列有序

下面,附上我的代碼,也是從文末鏈接中模仿過來的,但是親自敲過一遍,印象深刻。

public class HeapSort {
	public static void main(String[] args) {
		int[] array = new int[] { 2, 1, 4, 3, 6, 5, 8, 7 };
		// 接下來就是排序的主體邏輯
		sort(array);
		System.out.println(Arrays.toString(array));
	}
	/**
	 * 
	 * @description 本方法只有一個(gè)參數(shù),那就是待排序的array
	 * @author
	 * @param
	 * @return
	 * @time 2018年3月9日 下午2:24:45
	 */
	public static void sort(int[] array) {
		// 按照完全二叉樹的特點(diǎn),從最后一個(gè)非葉子節(jié)點(diǎn)開始,對(duì)于整棵樹進(jìn)行大根堆的調(diào)整
		// 也就是說,是按照自下而上,每一層都是自右向左來進(jìn)行調(diào)整的
		// 注意,這里元素的索引是從0開始的
		// 另一件需要注意的事情,這里的建堆,是用堆調(diào)整的方式來做的
		// 堆調(diào)整的邏輯在建堆和后續(xù)排序過程中復(fù)用的
		for (int i = array.length / 2 - 1; i >= 0; i--) {
			adjustHeap(array, i, array.length);
		}
		// 上述邏輯,建堆結(jié)束
		// 下面,開始排序邏輯
		for (int j = array.length - 1; j > 0; j--) {
			// 元素交換
			// 說是交換,其實(shí)質(zhì)就是把大頂堆的根元素,放到數(shù)組的最后;換句話說,就是每一次的堆調(diào)整之后,都會(huì)有一個(gè)元素到達(dá)自己的最終位置
			swap(array, 0, j);
			// 元素交換之后,毫無疑問,最后一個(gè)元素?zé)o需再考慮排序問題了。
			// 接下來我們需要排序的,就是已經(jīng)去掉了部分元素的堆了,這也是為什么此方法放在循環(huán)里的原因
			// 而這里,實(shí)質(zhì)上是自上而下,自左向右進(jìn)行調(diào)整的
			adjustHeap(array, 0, j);
		}
	}
	/**
	 * 
	 * @description 這里,是整個(gè)堆排序最關(guān)鍵的地方,正是因?yàn)榘堰@個(gè)方法抽取出來,才更好理解了堆排序的精髓,會(huì)盡可能仔細(xì)講解
	 * @author
	 * @param
	 * @return
	 * @time 2018年3月9日 下午2:54:38
	 */
	public static void adjustHeap(int[] array, int i, int length) {
		// 先把當(dāng)前元素取出來,因?yàn)楫?dāng)前元素可能要一直移動(dòng)
		int temp = array[i];
		// 可以參照sort中的調(diào)用邏輯,在堆建成,且完成第一次交換之后,實(shí)質(zhì)上i=0;也就是說,是從根所在的最小子樹開始調(diào)整的
		// 接下來的講解,都是按照i的初始值為0來講述的
		// 這一段很好理解,如果i=0;則k=1;k+1=2
		// 實(shí)質(zhì)上,就是根節(jié)點(diǎn)和其左右子節(jié)點(diǎn)記性比較,讓k指向這個(gè)不超過三個(gè)節(jié)點(diǎn)的子樹中最大的值
		// 這里,必須要說下為什么k值是跳躍性的。
		// 首先,舉個(gè)例子,如果a[0] > a[1]&&a[0]>a[2],說明0,1,2這棵樹不需要調(diào)整,那么,下一步該到哪個(gè)節(jié)點(diǎn)了呢?肯定是a[1]所在的子樹了,
		// 也就是說,是以本節(jié)點(diǎn)的左子節(jié)點(diǎn)為根的那棵小的子樹
		// 而如果a[0}<a[2]呢,那就調(diào)整a[0]和a[2]的位置,然后繼續(xù)調(diào)整以a[2]為根節(jié)點(diǎn)的那棵子樹,而且肯定是從左子樹開始調(diào)整的
		// 所以,這里面的用意就在于,自上而下,自左向右一點(diǎn)點(diǎn)調(diào)整整棵樹的部分,直到每一顆小子樹都滿足大根堆的規(guī)律為止
		for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
			// 讓k先指向子節(jié)點(diǎn)中最大的節(jié)點(diǎn)
			if (k + 1 < length && array[k] < array[k + 1]) {
				k++;
			}
			// 如果發(fā)現(xiàn)子節(jié)點(diǎn)更大,則進(jìn)行值的交換
			if (array[k] > temp) {
				swap(array, i, k);
				// 下面就是非常關(guān)鍵的一步了
				// 如果子節(jié)點(diǎn)更換了,那么,以子節(jié)點(diǎn)為根的子樹會(huì)不會(huì)受到影響呢?
				// 所以,循環(huán)對(duì)子節(jié)點(diǎn)所在的樹繼續(xù)進(jìn)行判斷
				i = k;
				// 如果不用交換,那么,就直接終止循環(huán)了
			} else {
				break;
			}
		}
	}
	/**
	 * 交換元素
	 * 
	 * @param arr
	 * @param a
	 *            元素的下標(biāo)
	 * @param b
	 *            元素的下標(biāo)
	 */
	public static void swap(int[] arr, int a, int b) {
		int temp = arr[a];
		arr[a] = arr[b];
		arr[b] = temp;
	}
}

小頂推

/**
 * @Author: 白雄雄
 * @Date: 2019/9/10 13:48
 */
public class SmailHeap {
    public static void main(String[] args) {
        int[] array = new int[]{1,3,2,7,4,0,5,10};
        heapSort(array);
        System.out.println(Arrays.toString(array));
    }
    private static void heapSort(int[] array) {
        for (int i = array.length / 2 - 1; i >= 0; i--) {
            adjustHeap(array,i,array.length);
        }
        for (int j = array.length - 1; j >= 0; j--) {
            int temp = array[0];
            array[0] = array[j];
            array[j] = temp;
            adjustHeap(array,0,j);
        }
    }
    private static void adjustHeap(int[] array, int i, int len) {
        int temp = array[i];
        for (int k = i * 2 + 1; i < len; k = 2 * k + 1) {
            if (k + 1 < len && array[k] > array[k + 1]) {
                k++;
            }
            if (k<len && array[k] < temp) {
                array[i] = array[k];
                i = k;
            } else {
                break;
            }
        }
        array[i] = temp;
    }
}

到此這篇關(guān)于Java中的堆排序詳解的文章就介紹到這了,更多相關(guān)Java堆排序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java9遷移注意問題總結(jié)

    java9遷移注意問題總結(jié)

    本篇文章給大家詳細(xì)整理了java9遷移注意的問題,希望我們整理的內(nèi)容能夠幫助到大家。
    2018-02-02
  • Java實(shí)現(xiàn)年獸大作戰(zhàn)游戲詳解

    Java實(shí)現(xiàn)年獸大作戰(zhàn)游戲詳解

    春節(jié)要到了,看慣了前端各種小游戲,確實(shí)做得很好,很精致。本文將為大家介紹一款java版本的年獸大作戰(zhàn)游戲,感興趣的小伙伴可以試一試
    2022-01-01
  • mybatis-plus 表名添加前綴的實(shí)現(xiàn)方法

    mybatis-plus 表名添加前綴的實(shí)現(xiàn)方法

    這篇文章主要介紹了mybatis-plus 表名添加前綴的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • SpringMVC 實(shí)現(xiàn)用戶登錄實(shí)例代碼

    SpringMVC 實(shí)現(xiàn)用戶登錄實(shí)例代碼

    這篇文章主要介紹了SpringMVC 實(shí)現(xiàn)用戶登錄實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Java?ObjectMapper的使用和使用過程中遇到的問題

    Java?ObjectMapper的使用和使用過程中遇到的問題

    在Java開發(fā)中,ObjectMapper是Jackson庫(kù)的核心類,用于將Java對(duì)象序列化為JSON字符串,或者將JSON字符串反序列化為Java對(duì)象,這篇文章主要介紹了Java?ObjectMapper的使用和使用過程中遇到的問題,需要的朋友可以參考下
    2024-07-07
  • Java基于JDBC連接數(shù)據(jù)庫(kù)及顯示數(shù)據(jù)操作示例

    Java基于JDBC連接數(shù)據(jù)庫(kù)及顯示數(shù)據(jù)操作示例

    這篇文章主要介紹了Java基于JDBC連接數(shù)據(jù)庫(kù)及顯示數(shù)據(jù)操作,結(jié)合實(shí)例形式分析了Java使用jdbc進(jìn)行mysql數(shù)據(jù)庫(kù)連接與數(shù)據(jù)讀取、顯示等相關(guān)操作技巧,需要的朋友可以參考下
    2018-06-06
  • SpringBoot如何上傳圖片

    SpringBoot如何上傳圖片

    這篇文章主要介紹了SpringBoot如何上傳圖片,幫助大家更好的理解和學(xué)習(xí)springboot框架,感興趣的朋友可以了解下
    2020-09-09
  • 深入了解HttpClient的ResponseHandler接口

    深入了解HttpClient的ResponseHandler接口

    這篇文章主要為大家介紹了深入了解HttpClient的ResponseHandler接口,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • Java實(shí)現(xiàn)飛機(jī)小游戲

    Java實(shí)現(xiàn)飛機(jī)小游戲

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)飛機(jī)小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • Java Calendar日歷與Date日期的相互轉(zhuǎn)換詳解

    Java Calendar日歷與Date日期的相互轉(zhuǎn)換詳解

    這篇文章主要介紹了Java Calendar日歷與Date日期的相互轉(zhuǎn)換詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08

最新評(píng)論