Redis研究-3.3資料結構之樹與尋找、排序等(後續)

來源:互聯網
上載者:User

標籤:

3.樹、二叉樹、森林之間的轉換

   前面我們又說到,二叉樹中的節點我們可以表示成一個具有左孩子域、右孩子域、雙親域、自身資料域的一個資料結構,那麼對於一般的樹或者森林中的節點來說,能不能也這樣子表示呢?答案是可以的,表示成二叉樹節點的形式,我們就能很好的使用二叉樹的一些特性和演算法。

在二叉樹中,left表示節點的左孩子、right表示節點的右孩子,那麼,對於一般的樹節點來看,如果存在孩子,第一個孩子就是對應的left地區,如果有第二個、第三個孩子等,就用right形成一個鏈表,那麼,這種樹就轉換為二叉樹啦,只是這裡兩個指標域的說法不太一樣而已。實際上,我們對於節點來說,我們可以改進一下得到如下的表示:

//修改後的一般的樹節點表示typedef struct gTBiTreeNode{    struct gTBiTreeNode *left;    struct gTBiTreeNode *right;    void *data;    struct gTBiTreeNode *next;//兄弟節點}gTBiTreeNode,*gTBiTreeNode;



3.1 樹轉換為二叉樹

  上面已經改造了樹節點的表示,那麼一般的樹怎麼轉換為我們常見的二叉樹呢?只需要三個步驟即可:

  1).加線。在所有的兄弟節點之間加一條連接線;

  2).去線。對數中的每個節點,只保留他與第一個孩子節點的串連,刪除他與其他孩子節點之間的連接線。

  3).層次調整。以樹的根為軸心,將整棵樹順時針旋轉一定的角度,使之層次分明。這裡要注意的是,第一個孩子是二叉樹節點的左孩子,兄弟轉換過來的孩子是節點的右孩子。

我們用圖來表示一下

 

 

 

我們重點來看看,怎麼調整成最後的這個層次了,首先我們應該清楚第三個步驟調整的原則:

  第一個孩子是節點的左孩子,那麼B當然是A的左孩子啦。根據第二條原則,兄弟轉換過來的孩子是節點的右孩子,因為C是B的兄弟,所以,轉換過來後,就變成了B的右孩子。同樣的,因為E是B的做孩子,所以轉換後當然是B的左孩子。同樣的,根據第二個原則,F是E的兄弟,所以,轉換後,F變成了E的右孩子,G先前是F的兄弟,現在變成了F的右孩子。同樣的,我們的先前的樹中C的第一個孩子就是H,所以,現在H當然是C的左孩子,同樣的,因為D是C的兄弟,所以現在變成了C的右孩子。I在以前的樹上就是D的第一個孩子,所以,現在是D的左孩子,又因為J先前是I的兄弟,所以,現在變成了I的右孩子。

通過上面的文字描述,我們要特別注意,第二個原則,就是"兄弟孩子變成了節點的右孩子這個說法".

3.2 森林轉換為二叉樹

什麼是森林?森林當然是由很多的樹組成的啦。那麼,我們當然可以把其中的每一顆樹看做是兄弟,因此,我們就可以得到下面的轉換步驟了。

1).把每棵樹轉換成一顆二叉樹;

2).第一顆二叉樹保持不動,從第二棵二叉樹開始,依次把後一棵樹的根節點作為前一棵二叉樹的根節點的右孩子,然後用線串連起來。

用圖來示範一下:

 

OK,應該說清楚了,那麼,二叉樹又怎麼轉換成樹呢?

3.3 二叉樹轉換成樹

前面我們已經從樹轉換成二叉樹了,他要經曆過三個步驟,分別是加線,去線,調整層次,那麼,二叉樹轉換為樹,也就是這個過程的一個逆過程,怎麼做呢?

任然是

1).加線。如果節點的左孩子存在,則將這個左孩子的右孩子節點、右孩子的右孩子節點、。。。都作為這個節點的孩子節點,將該節點與這些右孩子節點連線。

2).去線。刪除原二叉樹中所有節點與其右孩子節點的連線。

3).層次調整。

有圖有真相。

 

 

so easy,不是嗎?

3.4 二叉樹轉換成森林?

   一棵樹能否轉換成森林,判斷的標準很簡單,就是看這個二叉樹的根節點有沒有右孩子節點,如果有,那就可以轉換。轉換步驟是:

1).從根節點開始,若右孩子存在,則把與右孩子及誒單的連接線刪除,分離以後,繼續迭代。

2).將每棵分離後的二叉樹轉換為樹即可。

估計是傻瓜也能看得懂了吧???O(∩_∩)O哈哈~

3.5 哈夫曼編碼

  我不知道大家在大學時候有沒有學過運籌學,運籌學裡面很重要的一個分支就是講動態規劃的(哈哈,在下本科就是數學系的哈,當時的運籌學考了67分,低分飄過,不過最高分也就是72啦)。在某些求解最佳化問題的演算法中,每個步驟都面臨著多種選擇,動態規劃是這種問題的殺手級演算法,但是有時候又會顯得有點笨重,所以,在這個時候,我們需要一種更簡單、更高效的演算法,貪心演算法就是這樣一種演算法,貪心演算法的核心就是在每一步都做出當時看起來最佳的選擇,或者叫做局部最優的選擇,通過這種選擇來得到最後的一個全域最優解。當然,這隻是一種希望,所以,貪心演算法並不保證能得到一個最優解。我們這裡就先學習一種貪心演算法-哈夫曼編碼。

在說這玩意兒之前,先看個我們現實生活中的例子(這個例子來自《大話資料結構》,請各位參考)。裡面就是說,老師在給學生評“不及格”、“及格”、“中等”、“良好”、“優秀”的時候,是根據學生的分數段來進行的,通常情況下,我們使用下面的一個結構來判斷:

int degree(int score){  if(score<60){    printf("%s","不及格");  }else if(score<70){    printf("%s","及格");  }else if(score<80){    printf("%s","中等");  }else if(score<90){    printf("%s","良好");  }else{    printf("%s","優秀");  }}



得到的圖化結構是:

當我們看到在實際的學習生活中,學生的成績階段比例是如下所示的時候,我們就會感到這個演算法是大有問題的了


分數 0-59 60-69 70-79 80-89 90-100
比例 5% 15% 40% 30% 10%
,要查看70分以上的學生資料,至少要通過3此比較才能做出判斷,那麼,怎樣來改進呢?
int degree(int score){  if(score<80){    if(score<70){        if(score<60){           printf("%s","不及格");        }else{          printf("%s","及格");        }    }else {        printf("%s","中等");    }  }else if(score<90){      printf("%s","良好");  }else {    printf("%s","優秀");  }}



通過這次改進以後,70-79之間的分數最多需要兩次就能判斷了,是不是更最佳化了呢?二叉樹的表示方法如下:

 

 

假如,現在有1000學生,那麼沒改進之前,需要的判斷次數是3150次,而改進後,需要用到的次數是2200次,效果很明顯,特別是資料量大的時候。

 為了說清楚接下來的內容,有幾個概念需要明確一下:

 

1).從樹中一個節點到另外一個節點之間的分支構成兩個節點之間的路徑,路徑上的分支資料叫做路徑長度(走得通的路徑)。

2).樹的路徑長度是從根到每一個節點的路徑長度之和。樹A的路徑是:1+1+2+2+3+3+4+4=20.

3).節點的帶權的路徑長度是從該節點到樹根之間的路徑長度與節點上權的乘積。樹A中的及格的帶權路徑是15*2=30;

4).樹的帶權路徑路徑是樹中所有葉子節點的帶權路徑長度之和。樹A的帶權路徑是:5*1+15*2+40*3+30*4+10*4=315;

5).帶權路徑長度WPL最小的二叉樹就是哈夫曼樹。

 

那麼,怎麼來構建哈夫曼樹呢?遵循以下步驟

1).先把帶有全職的葉子節點按照從小到大的順序來排列成一個有序序列。

2).從這個有序序列中選擇較小的兩個來構造一個新的二叉樹,較小的權值的節點作為新二叉樹的左孩子,較大的作為右孩子,新的二叉樹的根節點的權值是兩個孩子的權值之和。

3).從序列中刪除已經選擇的兩個較小權值的節點,並把步驟2中構造的新二叉樹的根節點帶到這個序列中排序。

4).重複步驟2、3就可以得到最終的哈夫曼樹。

 

我們從樹B來看怎麼構造一個哈夫曼樹:

 

 

新排序:N=15,B,D,C

 

 

重新排序:N=30,D,C

重新排序:C,N=60

構造後的這顆樹的帶權路徑=40*1+30*2+15*3+10*4+5*4=205;

而原來的這顆樹的帶權路徑是:5*3+15*3+40*2+30*2+10*2=220;

我們算一下需要多少判斷步驟3050次,反而比不是哈夫曼樹的演算法還低效?那這說明哈夫曼樹沒用嗎?不是的。

 

我們再看一下:

假設我要給你遠程發送一段“BADCADFEED”的內容,網路傳輸中,一般都是用二進位來表示的,在這段文字中出現了A,B,C,D,E,F這6個字元,假設我們分別以三位二進位來代替一個字母,則可以得到下面的對應表:


A B C D E F
000 001 010 011 100 101

那麼,這段內容的編碼就是:001000011010000011101100100011,一共是30位。但是我們發現,在這段內容中,各個字母出現的頻率是不一樣的,他們出現的頻率分別是:


A B C D E F
0.2 0.1 0.1 0.3 0.2 0.1

也就是說,我們可以用哈夫曼樹來進行一個構建

 

我們將權值做分支改為0,右分支改為1:

所以,得到的對應的字母編碼映射表是:


A B C D E F
110 11110 11111 0 10 1110

那麼,使用這種編碼以後,發送內容應該是:111101100111111100111010100,一共是27位。

也就是說,我們改進後,節約了10的儲存或者傳輸成本。

實際上,我們從上面來看到,這其實就是一種變長的編碼,核心思想就是將高頻的字元用最短的碼字來表示,低頻的用長的碼字來表示。

其實我們還可以得到,我們構造的這棵二叉樹,是一顆滿二叉樹。

哈夫曼編碼的正確性的證明請參考由殷建平、徐雲等人翻譯的《演算法導論-原書第三版》第248頁。

二叉搜尋樹:就是在二叉樹的基礎上滿足一個特性的二叉樹就叫做二叉搜尋樹,這個特性就是對於樹上任何節點X,其左子樹的關鍵字不能超過X的關鍵字,其右子樹的的關鍵字不能小於X的關鍵字。

紅/黑樹狀結構:就是在二叉搜尋樹的每個節點上增加了一個顏色屬性,這個屬性有兩個黑色和紅色取值。同時要滿足下面的特性:

1).每個節點要麼是紅色的,要麼是黑色的。

2).根節點是黑色的。

3).每個葉子節點是黑色的;

4).如果一個節點是紅色的,則他的兩個子節點都是黑色的;

5).對每個節點,從該節點到其所有的後代分葉節點的簡單路勁上,均包含相同數目的黑色節點。

 遇到其他的樹,再來寫,基本上都是在二叉樹的基礎上加了一些限制

下一節,將繼續說排序、尋找

歡迎拍磚,同時也可以加QQ:359311095討論

Redis研究-3.3資料結構之樹與尋找、排序等(後續)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.