二叉堆/堆排序概念理解以及Java語言實現(學習筆記)

二叉堆/堆排序概念理解以及Java語言實現(學習筆記)

1.基礎概念介紹

(1)滿二叉樹:每一層都達到了最大節點數,高度爲h的滿二叉樹節點數爲pow(2,h)-1 ( pow(x,y)代表x^y )

如圖所示:
在這裏插入圖片描述

(2)完全二叉樹:高度爲h的二叉樹每一個節點都和高度爲h的滿二叉樹節點編號一一對應,那麼它就是滿二叉樹。

如圖所示:

在這裏插入圖片描述

	[1] 高度爲h的完全二叉樹節點數取值範圍[pow(2,h-1),pow(2,h)-1],可見完全二叉樹的高度是O( log(n) )
	
	[2] 滿二叉樹是特殊的完全二叉樹

	[3]根節點編號記爲1,對於節點i,它的左孩子編號爲2i,右孩子編號爲2i+1,對於節點i,它的父節點爲floor(i/2) (floor表示向下取整)。完全二叉樹適合用順序存儲結構來表示

(4)二叉堆概念:

[1] 首先是一顆完全二叉樹

[2] 小根堆:任何一個節點小於等於它的左孩子且小於等於他的右孩子

[3] 大根堆:任何一個節點大於等於它的左孩子且大於等於他的右孩子

舉個例子(大根堆):
在這裏插入圖片描述

2.堆排序

[1] 堆排序屬於選擇排序,第i趟在n-i+1個元素中選擇最大(值),作爲子序列的第i個元素。

[2]和AVL樹、紅黑樹、B樹和B+樹不同,他們是用於查找的,但堆是用於排序的。

2.1 建立大根堆(在已有序列基礎上建立)

設序列長度爲n,則最後一個父節點編號爲floor(n/2) (floor表示向下取整)

	[1]  i=floor(n/2),查看節點i和它的孩子,如果節點i小於兩個(也可能是一個)孩子的最大值,則把節點i和最大孩子交換
	
	[2] 令i減少1,查看節點i和它的孩子,如果節點i小於兩個(也可能是一個)孩子的最大值,則把節點i和最大孩子交換,以交換後的孩子爲根節點的樹可能不再滿足堆的定義,如果不滿足需要對它進行調整(下調)(多級的調整)
	
	[3] 重複[2],直到i=1

如圖:

在這裏插入圖片描述

空間複雜度:O(1)

時間複雜度:O(n)

Java實現:

實際上Java可以比較任何對象的大小,我的上篇博客有講到(https://blog.csdn.net/weixin_42111859/article/details/104132520),這裏用int數組(ArrayList更合適,可以動態添加刪除,更加靈活)舉個例子:

    // 從0編號,i左孩子爲2i+1,右孩子爲2i+2,父節點爲(i+1)/2-1
    private static void buildMaxHeap(int[] array) {
        for (int i = array.length / 2 - 1; i >= 0; i--) {
            adjustDown(i, array);
        }
    }

    // 使得i爲頂點的子樹成爲堆
    private static void adjustDown(int i, int[] array) {
        for (int j = i; j < array.length; ) {
            // k爲較大的孩子
            int k;
            // 有兩個孩子
            if (array.length >= 2 * j + 3) {
                k = array[2 * j + 1] > array[2 * j + 2] ? 2 * j + 1 : 2 * j + 2;
            }
            // 只有左孩子
            else if (array.length >= 2 * j + 2) {
                k = 2 * j + 1;

            }
            // 沒有孩子
            else {
                break;
            }
            // 父節點更大,不必交換
            if (array[j] >= array[k]) {
                break;
            }
            // 交換
            int temp = array[j];
            array[j] = array[k];
            array[k] = temp;
            j = k;
        }
    }

    @Test
    public void testBuildMaxHeap() {
        int[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        System.out.println(Arrays.toString(a));
        buildMaxHeap(a);
        System.out.println(Arrays.toString(a));
    }

控制檯輸出結果:

在這裏插入圖片描述
代碼和上面的例子是對應的

2.2 堆排序

第i趟,先取堆頂元素(堆頂一定是最大值),然後把堆底賦給堆頂,對n-i個元素下調(建立大根堆)

這也是不斷刪除最大元素的過程

時間複雜度:O( n*log(n) )

空間複雜度:O(1) 下面代碼中用一個數組存儲結果了,實際上可以輸出或者用迭代器返回結果,只需要常數個輔助單元

如果只求前m大的值,時間複雜度爲 O( m*log(n) )

java代碼:

public static int[] heapSort(int[] array) {
        buildMaxHeap(array);
        int[] rlt = new int[array.length];
        int index = 0;
        for (int i = array.length - 1; i >= 0; i--) {
            // 保存堆頂元素
            rlt[index++] = array[0];
            // 堆底給堆頂
            array[0] = array[i];
            // 構建大根堆
            adjustDown(0, array, i);
        }
        return rlt;

    }

    @Test
    public void testHeapSort() {
        int[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        System.out.println(Arrays.toString(a));
        System.out.println(Arrays.toString(heapSort(a)));
    }

運行結果爲:

在這裏插入圖片描述
2.3 堆的插入
堆是可以插入元素的,尾接新元素,如果比父元素更大就交換,然後重複這個步驟(上調)

舉一個例子(不寫代碼了):
在這裏插入圖片描述

轉載請註明出處,謝謝合作

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