久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

378,數(shù)據(jù)結(jié)構(gòu)-7,堆

 數(shù)據(jù)結(jié)構(gòu)和算法 2023-06-10 發(fā)布于上海

Make magic out of simple things.

在平凡中創(chuàng)造奇跡,。

基礎(chǔ)知識

通常情況下我們把堆看成是一棵完全二叉樹,。堆一般分為兩種,,一種是最大堆,一種是最小堆,。最大堆要求根節(jié)點的值即大于左子樹的值,,又大于右子樹的值。也就是說最大堆根節(jié)點的值是堆中最大的,。最小堆根節(jié)點的值是堆中最小的,,前面我們講堆排序的時候介紹過堆,實際上他就是數(shù)組結(jié)構(gòu),,但因為數(shù)組中間有關(guān)聯(lián),,所以我們可以把它當(dāng)做一棵完全二叉樹來看,下面我們再來看一下堆的數(shù)據(jù)結(jié)構(gòu)是什么樣的,。

結(jié)點中的數(shù)字是數(shù)組元素的下標(biāo),,不是數(shù)組元素的值。所以如果我們知道父節(jié)點的下標(biāo)我們就可以知道他的兩個子節(jié)點(如果有子節(jié)點),,如果知道子節(jié)點的下標(biāo)也一定能找到父節(jié)點的下標(biāo),,他們的關(guān)系是:

父節(jié)點的下標(biāo)=(子節(jié)點下標(biāo)-1)>>1;

左子節(jié)點下標(biāo)=父節(jié)點下標(biāo)*2+1,;

右子節(jié)點下標(biāo)=父節(jié)點下標(biāo)*2+2,;

堆的創(chuàng)建:

堆有兩種,一種是最大堆,,一種是最小堆,。顧名思義,最大堆就是堆頂?shù)脑厥亲畲蟮?,最小堆就是堆頂?shù)脑厥亲钚〉?。原理都差不多,我們只分析一個就行了,,我們就以數(shù)組【10,,4,8,,3,,5,1】為例來畫個圖分析一下最小堆是怎么創(chuàng)建的,。

這就是堆的創(chuàng)建,,把元素加入到堆中的時候,都要和父節(jié)點進行比對,,在最小堆中,,如果比父節(jié)點小,就要和父節(jié)點交換,,交換之后還要繼續(xù)和再和新的父節(jié)點對比……,。同理在最大堆中,,如果比父節(jié)點大,就要和父節(jié)點交換,。

我們看到上面堆創(chuàng)建之后數(shù)組的元素順序變?yōu)椤?,,4,3,,10,5,,8】

堆的刪除

堆的添加會往上調(diào)整,,堆的刪除不僅會往下調(diào)整而且還有可能往上調(diào)整。堆中元素的刪除我們可以從堆頂刪除,,也可以從中間某個位置刪除,,如果從堆頂刪除的話我們只需要往下調(diào)整即可,因為堆頂沒有父節(jié)點,,不需要往上調(diào)整,。如果從中間刪除的話,我們先要往下調(diào)整,,如果往下調(diào)整沒有成功(比如在最小堆中,,他比兩個子節(jié)點都小),,我們還要進行往上的調(diào)整,。調(diào)整的原理就是把數(shù)組最后一個元素放到要刪除的元素位置上,然后在刪除元素的位置上進行上下調(diào)整,,原理其實都差不多,,我們來看一下最小堆頂部元素刪除之后的調(diào)整。

上面是我們把堆頂元素1刪除之后堆的調(diào)整,,如果我們再把堆頂元素3刪除之后,,只需要用數(shù)組的最后一個元素5替換3,然后再往下調(diào)整即可……

代碼

堆的添加和刪除我們都分析過了,,如果把前面的分析都搞懂的話,,那么堆的代碼就很容易寫了,我們來看下

  1public class MyHeap<E{
 2    private Object[] data;//數(shù)據(jù)存放區(qū)
 3    private int size;//堆的大小
 4    private Comparator<? super E> comparator;//比較器
 5
 6    public MyHeap(int initialCapacity) {
 7        this(initialCapacity, null);
 8    }
 9
10    public MyHeap(int initialCapacity, Comparator<? super E> comparator) {
11        if (initialCapacity < 1)
12            throw new IllegalArgumentException("堆的大小必須大于0");
13        this.data = new Object[initialCapacity];
14        this.comparator = comparator;
15    }
16
17    /**
18     * @param e 向堆中添加元素
19     * @return
20     */

21    public boolean add(E e) {
22        if (e == null)//不能為空
23            throw new NullPointerException();
24        if (size >= data.length)//如果堆的空間不夠了就擴容,,這里是擴大2倍
25            data = Arrays.copyOf(datadata.length << 1);
26        if (size == 0)//如果堆是空的,,直接添加就可以了,不需要調(diào)整,,因為就一個沒發(fā)調(diào)整
27            data[0] = e;
28        else//如果堆不是空的,,就要往上調(diào)整。
29            siftUp(e);
30        size++;//添加完之后size要加1
31        return true;
32    }
33
34    public int getSize() {
35        return size;
36    }
37
38    //刪除堆頂元素
39    public E remove() {
40        if (size == 0)
41            return null;
42        size--;
43        E result = (E) data[0];//獲取堆頂?shù)脑?/span>
44        E x = (E) data[size];//取出數(shù)組的最后一個元素
45        data[size] = null;//然后把最后一個元素的位置置空
46        if (size != 0)
47            siftDown(x);//這里實際上是把數(shù)組的最后一個元素取出放到棧頂,,然后再往下調(diào)整。
48        return result;
49    }
50
51    //訪問堆頂元素,,不刪除
52    public E peek() {
53        return (size == 0) ? null : (E) data[0];
54    }
55
56    /**
57     * 返回數(shù)組的值
58     *
59     * @param a
60     * @param <T>
61     * @return
62     */

63    public <T> T[] toArray(T[] a) {
64        if (a.length < size)
65            return (T[]) Arrays.copyOf(data, size, a.getClass());
66        System.arraycopy(data0, a, 0, size);
67        if (a.length > size)
68            a[size] = null;
69        return a;
70    }
71
72    /**
73     * 往上調(diào)整,往上調(diào)整只需要和父節(jié)點比較即可,,如果比父節(jié)點大就不需要在調(diào)整
74     *
75     * @param e
76     */

77    private void siftUp(E e) {
78        int s = size;
79        while (s > 0) {
80            int parent = (s - 1) >>> 1;//根據(jù)子節(jié)點的位置可以找到父節(jié)點的位置
81            Object pData = data[parent];
82            //和父節(jié)點比較,,如果比父節(jié)點大就退出循環(huán)不再調(diào)整
83            if (comparator != null) {
84                if (comparator.compare(e, (E) pData) >= 0)
85                    break;
86            } else {
87                if (((Comparable<? super E>) e).compareTo((E) pData) >= 0)
88                    break;
89            }
90            //如果比父節(jié)點小,就和父節(jié)點交換,,然后再繼續(xù)往上調(diào)整
91            data[s] = pData;
92            s = parent;
93        }
94        //通過上面的往上調(diào)整,,找到合適的位置,再把e放進去
95        data[s] = e;
96    }
97
98    /**
99     * 往下調(diào)整,,往下調(diào)整需要和他的兩個子節(jié)點(如果有兩個子節(jié)點)都要比較,,哪個最小就和哪
100     * 個交換,如果比兩個子節(jié)點都小就不用再交換
101     *
102     * @param e
103     */

104    private void siftDown(E e) {
105        int half = size >>> 1;
106        int index = 0;
107        while (index < half) {
108            int min = (index << 1) + 1;//根據(jù)父節(jié)點的位置可以找到左子節(jié)點的位置
109            Object minChild = data[min];
110            int right = min + 1;//根據(jù)左子節(jié)點找到右子節(jié)點的位置
111            if (right < size) {//如果有右子節(jié)點就執(zhí)行這里的代碼
112                //如果有右子節(jié)點,,肯定會有左子節(jié)點,。那么就需要左右兩個子節(jié)點比較,把小的賦值給minChild
113                if (comparator != null) {
114                    if (comparator.compare((E) minChild, (E) data[right]) > 0)
115                        minChild = data[min = right];
116                } else {
117                    if (((Comparable<? super E>) minChild).compareTo((E) data[right]) > 0)
118                        minChild = data[min = right];
119                }
120            }
121            //用節(jié)點e和他的最小的子節(jié)點比較,,如果小于他最小的子節(jié)點就退出循環(huán),,不再往下調(diào)整了,
122            if (comparator != null) {
123                if (comparator.compare(e, (E) minChild) <= 0)
124                    break;
125            } else {
126                if (((Comparable<? super E>) e).compareTo((E) minChild) <= 0)
127                    break;
128            }
129            //如果e比它的最小的子節(jié)點小,,就用最小的子節(jié)點和e交換位置,,然后再繼續(xù)往下調(diào)整。
130            data[index] = minChild;
131            index = min;
132        }
133        data[index] = e;
134    }
135}

這里的刪除和添加操作的都是堆頂?shù)脑?,所以添加的時候會調(diào)用siftUp進行往上調(diào)整,,刪除的時候會調(diào)用siftDown進行往下調(diào)整。我把注釋都寫在代碼中了,,這里就不再詳細介紹,。

堆排序

通過上面的分析,我們知道最大堆就是堆頂元素始終保持的是最大值,,最小堆就是堆頂元素始終保持的是最小值,,假如在最小堆中我們把堆頂元素一個個給移除不就相當(dāng)于排序了嗎。我們來測試一下

 1    int[] array = {1048351};
2    System.out.print("數(shù)組原始的值:");
3    Util.printIntArrays(array);
4    MyHeap myHeap = new MyHeap(10new Comparator<Integer>() {
5        @Override
6        public int compare(Integer o, Integer t1
{
7            return (o - t1 > 0) ? 1 : -1;
8        }
9    });
10
11    for (int i = 0; i < array.length; i++) {
12        myHeap.add(array[i]);
13    }
14    System.out.println();
15    System.out.print("堆中元素的值:");
16    Util.printObjectArrays(myHeap.toArray(new Object[myHeap.getSize()]));
17    System.out.println();
18    System.out.print("排序之后的值:");
19    for (int i = 0, length = myHeap.getSize(); i < length; i++) {
20        System.out.print(myHeap.remove() + "\t");
21    }

我們來看一下打印結(jié)果

1數(shù)組原始的值:10    4   8   3   5   1
2堆中元素的值:1    4   3   10  5   8
3排序之后的值:1    3   4   5   8   10

我們看到堆中元素的值是【1,,4,,3,10,,5,,8】和我們最開始分析創(chuàng)建堆的結(jié)果完全一致。并且最后一行完成了從小到大的順序輸出

總結(jié):

上面我們主要分析是最小堆,,如果是最大堆代碼該怎么寫呢,,其實有兩種方式,一種是直接在上面代碼MyHeap類中修改,,還一種是在創(chuàng)建構(gòu)造函數(shù)的時候重寫比較器Comparator,,我們這里推薦使用第二種,,比如我們想按照從大到小的順序輸出,我們來看下該怎么寫

 1    int[] array = {1048351};
2    System.out.print("數(shù)組原始的值:");
3    Util.printIntArrays(array);
4    MyHeap myHeap = new MyHeap(10new Comparator<Integer>() {
5        @Override
6        public int compare(Integer o, Integer t1
{
7            return (o - t1 < 0) ? 1 : -1;
8        }
9    });
10
11    for (int i = 0; i < array.length; i++) {
12        myHeap.add(array[i]);
13    }
14    System.out.println();
15    System.out.print("堆中元素的值:");
16    Util.printObjectArrays(myHeap.toArray(new Object[myHeap.getSize()]));
17    System.out.println();
18    System.out.print("排序之后的值:");
19    for (int i = 0, length = myHeap.getSize(); i < length; i++) {
20        System.out.print(myHeap.remove() + "\t");
21    }

我們只需修改一個字符即可,,就是在上面第7行把之前的大于號改為小于號,,我們來看下運行結(jié)果

1數(shù)組原始的值:10    4   8   3   5   1
2堆中元素的值:10    5   8   3   4   1
3排序之后的值:10    8   5   4   3   1

最后完全實現(xiàn)了從大到小的輸出。

106,,排序-堆排序

367,,二叉樹的最大深度

    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多