笨蛋都看得懂的二叉樹介紹(Java)

2021-09-13 22:33:49 字數 4734 閱讀 1805

本文專門針對笨蛋介紹如何編寫二叉樹,包括二叉樹的結構、如何新增節點、如何刪除節點。

首先介紹二叉樹的結構。

二叉樹的結構有三個要點:

每個節點最多有兩個子節點,分別稱作左子節點和右子節點。

每個節點的左子節點的值比它小,右子節點的值比它大。

每個節點的左子樹每個節點的值都比它小,右子樹每個節點的值都比它大。

看上面這個例子,就完全符合這三點。

這時候笨蛋就會問了:前面兩點我理解,但是第三點是怎麼做到的?

所以接下來介紹下二叉樹是如何 「生長」 起來的:

如上圖所示,當加入乙個新節點時,從根節點開始對它進行比較。如果它比根節點小,則放入根節點的左子樹,如果比根節點大,則放入根節點的右子樹。

然後再進行下一級節點的比較,直到遇到最後一級節點,才將新節點加入為該節點的左或右子節點。

以第四幅圖的節點 25 為例,它第一次會與根節點 10 比較,結果就是 25 應該放入 10 的右子樹,這就排除了它放入左子樹的可能,即 25 不可能放到 4 的下面。

然後 25 再和節點 33 比較,結果是它比較小,所以應該放入 33 的左子樹。因為 33 沒有左子節點,那麼 25 就直接作為 33 的左子節點了。

通過這種生長方式,我們無論何時都能得到滿足前面三個要素的二叉樹。

那麼寫**該如何實現呢?所謂慢工出細活,我們一步一步來。

首先我們建立二叉樹節點的基本結構。每個二叉樹都有四個成員,如下所示。

public class basicbtree 

}

回頭看第一張圖,你會發現每個節點最多有三根線連著,上面的線就代表basicbtreeparent,下面兩根線就分別代表leftright了。而節點中的數字就是basicbtreevalue

接下來我們要為basicbtree編寫兩個簡單的方法,用來給它新增左子節點和右子節點:

// 將乙個節點加為當前節點的左子節點

public void setleft(basicbtree node)

this.left = node;

if (this.left != null)

}// 將乙個節點加為當前節點的右子節點

public void setright(basicbtree node)

this.right = node;

if (this.right != null)

}

在上面兩個方法的基礎上,我們可以新增乙個新增任意值節點的方法:

// 將乙個節點加為當前節點的左或右子節點

public void setchild(basicbtree node)

if (node.value < this.value) else if (node.value > this.value)

}

另外我們再新增乙個刪除左子節點或右子節點的方法:

// 刪除當前節點的乙個直接子節點

public void deletechild(basicbtree node)

if (node == this.left) else if (node == right)

}

這幾個方法都是非常簡單的,其中setchild()deletechild()這兩個方法,我們後面介紹刪除節點的時候會用到。

現在我們正式實現構造樹的方法,就是把乙個乙個數字加到樹裡面去,讓樹越長越大的方法:

// 向當前節點下面的樹中新增乙個值作為新節點

public void add(int value) else

} else if (value > this.value) else }}

這個方法稍微複雜一些,主要是因為邏輯上使用了遞迴。這個方法怎麼用呢?以最開始的樹為例,演示如何長成這棵樹:

public static void main(string args)
你可能會注意到,加入每一層的子節點時,層內節點的新增順序可以任意調換,構造出來的樹都是一樣的;但是如果將不同層的節點順序互換,構造出來的二叉樹就會變樣了。這當中的原因可以自己想想。

最後來介紹二叉樹中最複雜的操作:刪除節點。為什麼這個操作最複雜呢?因為刪除乙個節點之後,要把它下面的節點接上來,同時要保持這棵樹繼續滿足三要素。

如何把下面的節點接上來呢?最笨的方法當然是把被刪節點的所有子節點乙個個重新往樹裡面加。但是這樣做效率實在不高。想想如果被刪節點有上百萬個子節點,那操作步驟就太多了(如下圖所示)。

怎麼做才能效率高呢?有乙個辦法,就是從被刪節點的子節點中找到乙個合適的,替換掉被刪節點。這樣做的步驟就少得多了。

不過這樣的節點是否存在呢?答案是,除非被刪節點沒有子節點,否則是一定存在的。

而且這樣的節點可能不止乙個。原則上講,被刪節點的左子樹的最大值,或右子樹的最小值,都是滿足條件的,都可以用來替換被刪節點。比如說,將左子樹的最大值節點替換上去之後,左子樹的剩餘節點的值都仍然小於該位置的節點。下面是乙個例子:

比如要刪除節點 33,而該節點左子樹的最大值為 31,那麼直接將 31 替換到 33 的位置即可,整棵樹仍然滿足三要素。

同理,被刪節點右子樹的最小值也可以用來替換被刪節點。比如上圖中 33 節點的右子節點 46 也可以用來替換 33,整棵樹仍然滿足三要素。

所以這個問題就轉化為:如何尋找被刪節點的左子樹的最大值和右子樹的最小值。顯然,因為二叉樹所有的左節點都比較小,右節點都比較大,所以要找最大值,順著右節點找即可;要找最小值,順著左節點找即可。下面是實現的**:

// 搜尋當前節點左子樹中的最大值節點,如果沒有左子節點則返回 null

public basicbtree leftmax()

basicbtree result = this.left; // 起始節點

while (result.right != null)

return result;

}// 搜尋當前節點右子樹中的最小值節點,如果沒有右子節點則返回 null

public basicbtree rightmin()

basicbtree result = this.right; // 起始節點

while (result.left != null)

return result;

}

我們還剩下兩個準備工作,第乙個是實現節點的查詢:

// 查詢指定值的節點,如果找不到則返回 null

public basicbtree find(int value)

while (result.left != null || result.right != null) else if (value > result.value && result.right != null)

if (result.value == value)

}return null;

}

第二個是實現節點的替換:

// 將節點 node 替換為節點 replace

public basicbtree replace(basicbtree node, basicbtree replace)

// 3. node 原來的 parent 接管 replace

if (node.parent != null)

// 注意 2 必須在 3 之前,1 位置不論

return replace;

}

注意這裡用到了之前的setchild()deletechild()兩個方法。而replace()方法之所以設計為返回replace引數,是為了使用方便。

最後我們就可以正式實現二叉樹刪除節點的方法了:

// 從樹的子節點中刪除指定的值,並重組剩餘節點

public basicbtree delete(int value)

// 沒有子節點,直接刪除即可

if (node.left == null && node.right == null) else

}// 如果有子節點,則取左子樹的最大值或者右子樹的最小值都可以,

// 來取代該節點。這裡優先取左子樹的最大值

basicbtree replace;

if (node.left != null) else

// 如果被刪除的是根節點,則返回用於替換的節點,否則還是返回根節點

return node == this ? replace : this;

}

二叉樹介紹

常見二叉樹有完全二叉樹 滿二叉樹 滿二叉樹除了滿足普通二叉樹的性質,還具有以下性質 1.滿二叉樹中第 i 層的節點數為 2n 1 個。2.深度為 k 的滿二叉樹必有 2k 1 個節點 葉子數為 2k 1。3.滿二叉樹中不存在度為 1 的節點,每乙個分支點中都兩棵深度相同的子樹,且葉子節點都在最底層。...

二叉樹 48 二叉樹 二叉樹的高度

目的 使用c 模板設計並逐步完善二叉樹的抽象資料型別 adt 內容 1 請參照鍊錶的adt模板,設計二叉樹並逐步完善的抽象資料型別。由於該環境目前僅支援單檔案的編譯,故將所有內容都集中在乙個原始檔內。在實際的設計中,推薦將抽象類及對應的派生類分別放在單獨的標頭檔案中。參考教材 課件,以及網盤中的鍊錶...

二叉樹 二叉樹的查詢

三種查詢方式 前序查詢 中序查詢 後序查詢。前序查詢 比較當前節點,如果是,返回查詢結果。如果不是就向左遞迴查詢,如果左邊沒有就向右邊遞迴查詢。設定no 5,針對該題,前序遍歷共4次。中序遍歷 先向左遞迴查詢,如果左子樹沒有,再比較當前節點,如果仍然不是則向右遍歷查詢。後序遍歷 先向左遞迴查詢,如果...