在chapter0 中,我們設計了一個產品,叫私人衣櫥。這個手機app幫助我們管理衣物,方便我們查找衣物的位置,也可以展示我們的衣服。
現在這個app要添加新功能:
- 找出過去一年中,自己最不常用的10件物品,看是否需要轉賣或者扔掉。
怎么實現這個功能呢?方法有很多,我們先來介紹第一種方法:
“堆”
這個堆不是土堆、人堆,而是數據的堆。
特征
計算機語言里的描述:堆是指一個特定的基于樹結構的數據結構。
我們先不管這個抽象的描述,我們只需要記住堆的特征:
一.任何一個節點不大于父親節點(大根堆)
比如下面這幅圖,2的父節點是17,17的父節點是19,是不是子節點小于父節點。不過19不是2的父節點,因為中間跨了一層。其他以此類推。100叫根節點,因為它已經到頂了,沒有父節點。
二.它必須是一棵完全二叉樹:就是除了最后一層節點外,其他層節點數必須是兩個,最后一層的所有節點集中在最左側。
如下圖:
它是一個正確的例子:55必須有兩個子節點:22;25。22和25 不一定是有兩個子節點,因為他們已經最后一層。
又如下圖:
它是一個錯誤的例子,為什么?最后一層的所有節點沒有集中在最左側。因為子節點必須從上往下,從左往右添加。
了解完堆的概念,那么堆有哪些分類呢?
首先是:大根堆,即根節點的值最大,逐層遞減,最下面一層的數據最小。
再次是小根堆:正好和大根堆相反。
介紹完堆,我們回到上面的問題,假設我們有一系列的衣物,他們的使用頻率分別是:35 33 42 10 14 19 27 44 26 31,我們將組數據構建成一個大根堆,從下往上層級的節點必然是小的值。
最終的結果是這樣的:
具體構建大根堆的步驟:
Step 1 ? 在堆的底部創建一個新節點
Step 2 ? 將值放在該節點
Step 3 ? 比較新關鍵的節點值與它的父節點值大小
Step 4 ? 如何父節點值小于新節點值,那么交換他們
Step 5 ? 重復第三步和第四部
好了,現在堆創建好,我們需要將頻率最低的幾個數據取出,堆是一種特殊的樹(一種數據結構,后面再介紹),遍歷數據有兩種方式:廣度遍歷和深度遍歷,方式如名字:
廣度遍歷方向:
遍歷出來的數據是:
44 42 35 33 31 19 27 10 26 14
深度遍歷方向:
遍歷出來的數據是:
44 42 33 10 26 31 14 35 19 27
利用廣度遍歷,已經基本是從大到小了,后面的幾個就已經基本滿足我們的要求了。而且我們還可以知道,根節點就是我們使用頻率最高的衣物。
但是我們也發現,廣度遍歷出來的數據,并不絕對是最后一個數據就是頻率最低,這個怎么解決呢?
解決辦法是:
排序
將使用頻率:35 33 42 10 14 19 27 44 26 31,這組數據進行從高到底排序。也就實現我們的功能。
那排序的算法怎么設計呢?
選擇排序(常規思維想到的第一個方法)
思想:首先在未排序序列中找到最?。ù螅┰?,存放到排序序列的起始位置,然后,再從剩余未排序元素中繼續尋找最?。ù螅┰兀缓蠓诺揭雅判蛐蛄械哪┪?。以此類推,直到所有元素均排序完畢。
算法描述
n個記錄的直接選擇排序可經過n-1趟直接選擇排序得到有序結果。具體算法描述如下:
- 初始狀態:無序區為R[1..n],有序區為空;
- 第i趟排序(i=1,2,3…n-1)開始時,當前有序區和無序區分別為R[1..i-1]和R(i..n)。該趟排序從當前無序區中-選出關鍵字最小的記錄 R[k],將它與無序區的第1個記錄R交換,使R[1..i]和R[i+1..n)分別變為記錄個數增加1個的新有序區和記錄個數減少1個的新無序區;
- n-1趟結束,數組有序化了。
冒泡排序
冒泡排序是一種簡單的排序算法。它重復地走訪過要排序的數列,一次比較兩個元素,如果它們的順序錯誤就把它們交換過來。走訪數列的工作是重復地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端。
算法描述
- 比較相鄰的元素。如果第一個比第二個大,就交換它們兩個;
- 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最后一對,這樣在最后的元素應該會是最大的數;
- 針對所有的元素重復以上的步驟,除了最后一個;
- 重復步驟1~3,直到排序完成。
歸并排序(Merge Sort)
歸并排序是建立在歸并操作上的一種有效的排序算法。該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合并,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合并成一個有序表,稱為2-路歸并。
算法描述
- 把長度為n的輸入序列分成兩個長度為n/2的子序列;
- 對這兩個子序列分別采用歸并排序;
-
將兩個排序好的子序列合并成一個最終的排序序列。
歸并排序.gif
基數排序(Radix Sort)
基數排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次類推,直到最高位。有時候有些屬性是有優先級順序的,先按低優先級排序,再按高優先級排序。最后的次序就是高優先級高的在前,高優先級相同的低優先級高的在前。
算法描述
- 取得數組中的最大數,并取得位數;
- arr為原始數組,從最低位開始取每個位組成radix數組;
-
對radix進行計數排序(利用計數排序適用于小范圍數的特點);
基數排序.gif
排序的算法還有很多,就不一一介紹了。
總結
選擇排序:不停找最小值(效率也是最低的)
冒泡排序:相鄰比較,不停交換,冒泡
歸并排序:分組,不同組之間比較
基數排序:非比較類算法,先低優先級再高優先級排序
上面的堆也是也可以達到排序的目的。
通過排序,我們就可以得到降序的一個數組:
10 14 19 26 27 31 33 35 42 44
還怕找不到你最不常用的衣物嗎?好了,最不常用的衣物已經找到了,快去處理掉把。
其他知識
堆的刪除
從葉子開始添加,刪除的時候從根節點開始刪除。
具體的步驟是:
Step 1 ? 刪除根節點.
Step 2 ? 移動最近一層的最近一個元素到根節點位置
Step 3 ? 比較它與子節點值的大小
Step 4 ? 加入父節點值小于子節點值,則相互交換
Step 5 ? 重復第三步和第四步