日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构学习笔记(六):二叉树(Binary Tree)

發(fā)布時(shí)間:2025/3/12 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构学习笔记(六):二叉树(Binary Tree) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

1 背景知識:樹(Tree)

2 何為二叉樹(Binray Tree)

2.1 二叉樹的概念與結(jié)構(gòu)

2.2 滿二叉樹與完全二叉樹

2.3 二叉樹的三種遍歷方式

3 二叉樹及其遍歷的簡單實(shí)現(xiàn)(Java)


1 背景知識:樹(Tree)

在之前的筆記中,我們介紹的鏈表、棧、隊(duì)列、數(shù)組和字符串都是以線性結(jié)構(gòu)來組織數(shù)據(jù)的。本篇筆記要介紹的采用的是樹狀結(jié)構(gòu),這是一種非線性的數(shù)據(jù)組織形式。

樹結(jié)構(gòu)由節(jié)點(diǎn)構(gòu)成,且不存在環(huán)。我們曾在線性表型的數(shù)據(jù)結(jié)構(gòu)中介紹過循環(huán)鏈表和循環(huán)隊(duì)列,這兩種數(shù)據(jù)結(jié)構(gòu)使得存儲容器中的元素形成一個(gè)閉環(huán),具體可參看“數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)筆記”系列的相關(guān)博文,鏈接貼在下面:

鏈表:https://blog.csdn.net/weixin_45370422/article/details/116573863

隊(duì)列:https://blog.csdn.net/weixin_45370422/article/details/117376241

樹狀結(jié)構(gòu)與線性結(jié)構(gòu)最重要的區(qū)別在于:樹只能有分叉,不能有閉環(huán)。如下圖所示:

樹結(jié)構(gòu)不允許有環(huán)其實(shí)是樹的層級性決定的。樹結(jié)構(gòu)中最頂端的結(jié)點(diǎn)是根節(jié)點(diǎn),?根節(jié)點(diǎn)即整棵樹的頂級父節(jié)點(diǎn)。除了根節(jié)點(diǎn)只有子節(jié)點(diǎn),最底層的節(jié)點(diǎn)只有父節(jié)點(diǎn),其余各層的節(jié)點(diǎn)都是上層節(jié)點(diǎn)的子節(jié)點(diǎn)、下層節(jié)點(diǎn)的父節(jié)點(diǎn)。也就是說,樹中的數(shù)據(jù)只與其上下層的數(shù)據(jù)有關(guān),同層數(shù)據(jù)間不能有直接聯(lián)系,這也就是樹結(jié)構(gòu)不能有環(huán)的原因。

樹層級的多少往往被描述為樹的高度(height),由于我們是從上往下觀察樹結(jié)構(gòu)的,因此也被描述為樹的深度(depth)。上面圖示中兩顆樹的深度都是3.

?

2 何為二叉樹(Binray Tree)

2.1 二叉樹的概念與結(jié)構(gòu)

二叉樹顧名思義,即每個(gè)父節(jié)點(diǎn)最多只有兩個(gè)分叉的樹,這是數(shù)據(jù)結(jié)構(gòu)領(lǐng)域使用頻率極高的一種樹結(jié)構(gòu),這與我們常常用二元對立的觀點(diǎn)認(rèn)識世界的思維習(xí)慣有關(guān)。

二叉樹的結(jié)構(gòu)不僅具有層級性,還具有遞歸性,一個(gè)父節(jié)點(diǎn)連接左子節(jié)點(diǎn)右子節(jié)點(diǎn),而左右子節(jié)點(diǎn)又可以作為父節(jié)點(diǎn)再各自連接兩個(gè)子節(jié)點(diǎn),以此類推。因此二叉樹是一種層次嵌套的數(shù)據(jù)結(jié)構(gòu),除了根節(jié)點(diǎn)外,樹中任意一個(gè)父節(jié)點(diǎn)都能作為一棵子樹,位于上層父節(jié)點(diǎn)左側(cè)的子樹被稱為左子樹,位于右側(cè)的子樹被稱為右子樹。

二叉樹體現(xiàn)了人們用二元思維認(rèn)識自然的方式。筆者的本行是語言學(xué),語言學(xué)界主流的對句法結(jié)構(gòu)的分析方法就是類似于二叉樹的二分法。拿漢語的句法結(jié)構(gòu)來說,有主謂、述賓、定中、狀中、述補(bǔ)等基本的結(jié)構(gòu)類型。句法結(jié)構(gòu)具有層次嵌套遞歸的特點(diǎn),同時(shí)也有對語序的要求,即句法二叉樹中的左右節(jié)點(diǎn)的位置并不是任意的。這種分析方法語言學(xué)上被稱為層次分析法,如果我們用該方法分析句子“文程同學(xué)熱愛編程”,傳統(tǒng)圖示和句法樹圖示分別如下:

?

2.2 滿二叉樹與完全二叉樹

二叉樹中有兩個(gè)特殊的結(jié)構(gòu)類型:滿二叉樹完全二叉樹。滿二叉樹的結(jié)構(gòu)特點(diǎn)是除了最后一層外,所有層級的節(jié)點(diǎn)都有兩個(gè)子節(jié)點(diǎn);完全二叉樹的結(jié)構(gòu)特點(diǎn)是除了最后兩層外,所有層級的節(jié)點(diǎn)都有兩個(gè)子節(jié)點(diǎn),倒數(shù)第二層的子節(jié)點(diǎn)(即最后一層節(jié)點(diǎn))全部靠左排列。如下圖所示:

由此可見,滿二叉樹一定是完全二叉樹,完全二叉樹可滿可不滿。這兩種二叉樹體現(xiàn)了我們采用樹狀結(jié)構(gòu)存儲數(shù)據(jù)時(shí),對于空間利用率的追求。比如我們設(shè)計(jì)一個(gè)深度為n的二叉樹,那么整個(gè)二叉樹能容納的最大節(jié)點(diǎn)數(shù)為2^n-1,滿二叉樹就是達(dá)到了最大節(jié)點(diǎn)數(shù),用足了二叉樹的容量。完全二叉樹除了n層沒有子節(jié)點(diǎn),除n-1層外各層父節(jié)點(diǎn)都充分利用了自己擁有子節(jié)點(diǎn)的名額,也算是盡可能做到了對空間的充分利用。

為了更好地理解完全二叉樹的空間利用率,我們看一個(gè)非完全二叉樹的例子,如下圖所示:

上圖是一個(gè)深度為4的非完全二叉樹,前3層的父節(jié)點(diǎn)都預(yù)留了左右兩個(gè)子節(jié)點(diǎn)的位置,然而第二層的第2個(gè)結(jié)點(diǎn)只使用了右子節(jié)點(diǎn)的空間,浪費(fèi)了左子節(jié)點(diǎn)的空間。如果二叉樹的深度很深,其中很多層級的父節(jié)點(diǎn)都存在浪費(fèi)子節(jié)點(diǎn)“名額”的現(xiàn)象,那么會造成相當(dāng)大的空間資源的浪費(fèi),二叉樹也失去了“二叉”的意義。但是完全二叉樹最多浪費(fèi)倒數(shù)第二層父節(jié)點(diǎn)的子節(jié)點(diǎn)名額,?整體上能夠保證較高的空間利用率。

?

2.3 二叉樹的三種遍歷方式

二叉樹的形狀整體上構(gòu)成一個(gè)三角形,最小的二叉樹由一個(gè)位于中間的父節(jié)點(diǎn)和位于左右兩側(cè)的子節(jié)點(diǎn)構(gòu)成,這導(dǎo)致遍歷訪問一棵二叉樹的所有節(jié)點(diǎn)有三種順序:前序遍歷(Preorder Traversal , VLR)、中序遍歷(Inorder Traversal , LDR)和后序遍歷(Inorder Traversal , LRD)。

無論哪種遍歷方式,二叉樹都是從上到下、從左到右遍歷的,即從父節(jié)點(diǎn)層到子節(jié)點(diǎn)層、從左子樹到右子樹。2.1解釋了二叉樹的遞歸性,遍歷二叉樹時(shí)采用的也是遞歸(recursion)的方式。對于整棵樹或某一子樹,都是從根開始,先遍歷其左子樹,再遍歷其右子樹;分別遍歷左右子樹時(shí),同樣是從根開始,從左向右遍歷;以此類推,直到遍歷到最后一個(gè)右子節(jié)點(diǎn)。

如果我們以打印節(jié)點(diǎn)數(shù)據(jù)的方式來表示對節(jié)點(diǎn)的訪問,那么前序、中序和后序的區(qū)別就在于打印節(jié)點(diǎn)的時(shí)機(jī)不同。前序遍歷的操作順序是打印節(jié)點(diǎn)在遍歷左子樹和遍歷右子樹之前中序遍歷的操作順序是打印節(jié)點(diǎn)在遍歷左子樹和遍歷右子樹之間后序遍歷的操作順序是打印節(jié)點(diǎn)在遍歷左子樹和遍歷右子樹之后。子樹遍歷的過程是遞歸實(shí)現(xiàn)的。?

如果我們想遍歷2.1演示的“文程同學(xué)熱愛編程”的句法二叉樹,那么用三種遍歷方法得到的遍歷結(jié)果分別如下:

?

3 二叉樹及其遍歷的簡單實(shí)現(xiàn)(Java)

我們用Java語言實(shí)現(xiàn)“文程同學(xué)熱愛編程”這個(gè)句子對應(yīng)的句法二叉樹,設(shè)計(jì)思路是:將同層級的父節(jié)點(diǎn)(二叉樹及其各子樹的根節(jié)點(diǎn))存入數(shù)組中,數(shù)組中存入的是結(jié)點(diǎn),包括數(shù)據(jù)左右指針,左右指針分別指向位于下一層節(jié)點(diǎn)的左右子節(jié)點(diǎn),如果沒有子節(jié)點(diǎn)則指針為空指針。

用數(shù)組的好處是,可以通過節(jié)點(diǎn)所在的索引建立上下層級父節(jié)點(diǎn)和子節(jié)點(diǎn)的指針聯(lián)系。假設(shè)父節(jié)點(diǎn)在它所在的層級數(shù)組中的索引為i,那么左子節(jié)點(diǎn)在它所在層級數(shù)組中的索引為(i+1)*2-2,右子節(jié)點(diǎn)的索引為(i+1)*2-1,即左子節(jié)點(diǎn)的索引+1。

遍歷默認(rèn)從整棵二叉樹的根節(jié)點(diǎn)開始,通過方法的重寫實(shí)現(xiàn)默認(rèn)參數(shù)的效果。

準(zhǔn)備工作1:MyBinaryTree.java,創(chuàng)建一個(gè)二叉樹的類

package com.notes.data_structure6;import com.notes.data_structure6.NumberOfNodesException;public class MyBinaryTree {// 樹的根結(jié)點(diǎn)private Node[] root;// 樹的深度(當(dāng)前層級數(shù))private int depth;// 將每一層所有的 結(jié)點(diǎn) 都存儲在數(shù)組中,結(jié)點(diǎn)數(shù)是 2的 層數(shù) 次冪private Node[] currentLevel;public MyBinaryTree(String data) {Node[] rootArray = new Node[] {new Node(data)};this.root = rootArray;this.currentLevel = rootArray;}// 定義一個(gè)結(jié)點(diǎn)類private class Node{private String data; // 數(shù)據(jù)private Node leftNext; // 左指針private Node rightNext; // 右指針// 構(gòu)造方法:Node實(shí)例化時(shí)傳入數(shù)據(jù)public Node(String data) {this.data = data;} }// 向樹中增加一層結(jié)點(diǎn)public void add(String[] datas) throws NumberOfNodesException {// 層級數(shù)增加1depth++;// 新增 層級 的最大結(jié)點(diǎn)數(shù)int nodeNum = numberOfNextNodes();// 如果傳入的 數(shù)據(jù)數(shù) 與 當(dāng)前層級 最大結(jié)點(diǎn)數(shù) 不符,拋出異常if(datas.length != nodeNum) {throw new NumberOfNodesException("第"+depth+"層最大父節(jié)點(diǎn)數(shù)為"+nodeNum);}// 將傳入的 數(shù)據(jù)數(shù)組 轉(zhuǎn)換為 結(jié)點(diǎn)數(shù)組Node[] newLevel = new Node[nodeNum];// 當(dāng)前 層級的 結(jié)點(diǎn)數(shù)量(新增層級的父)int nodeNum2 = (int) Math.pow(2, depth-1);// 讓每一個(gè)結(jié)點(diǎn)都與上層 父結(jié)點(diǎn) 建立連接for(int i=0;i<nodeNum2;i++) {// 讓父結(jié)點(diǎn) 的左指針 指向 左子結(jié)點(diǎn)int leftIndex = (i+1)*2-2; // 計(jì)算左子結(jié)點(diǎn)對應(yīng)的新層級數(shù)組的索引currentLevel[i].leftNext = new Node(datas[leftIndex]); // 建立指針指向newLevel[leftIndex] = currentLevel[i].leftNext; // 將結(jié)點(diǎn)加入新層級結(jié)點(diǎn)數(shù)組// 讓父結(jié)點(diǎn) 的右指針 指向 右子結(jié)點(diǎn)int rightIndex = leftIndex+1; // 計(jì)算右子結(jié)點(diǎn)對應(yīng)的新層級數(shù)組的索引currentLevel[i].rightNext = new Node(datas[rightIndex]); // 建立指針指向newLevel[rightIndex] = currentLevel[i].rightNext; // 將結(jié)點(diǎn)加入新層級結(jié)點(diǎn)數(shù)組}// 讓新增層級的數(shù)組成為當(dāng)前層級的數(shù)組currentLevel = newLevel;}// 前序遍歷所有結(jié)點(diǎn)public void preTraversal(Node node) {if(node==null) {return;}System.out.print(node.data+" ");preTraversal(node.leftNext);preTraversal(node.rightNext);}// 重寫 前序遍歷 方法,讓遍歷從 根結(jié)點(diǎn) 開始public void preTraversal() {Node node = root[0];if(node==null) {return;}// 遞歸時(shí)調(diào)用帶參數(shù)的方法System.out.print(node.data+" ");preTraversal(node.leftNext);preTraversal(node.rightNext);}// 中序遍歷所有結(jié)點(diǎn)public void midTraversal(Node node) {if(node==null) {return;}midTraversal(node.leftNext);System.out.print(node.data+" ");midTraversal(node.rightNext);}// 重寫中序遍歷 方法,讓遍歷從 根結(jié)點(diǎn) 開始public void midTraversal() {Node node = root[0];if(node==null) {return;}// 遞歸時(shí)調(diào)用帶參數(shù)的方法midTraversal(node.leftNext);System.out.print(node.data+" ");midTraversal(node.rightNext);}// 后序遍歷所有結(jié)點(diǎn)public void posTraversal(Node node) {if(node==null) {return;}posTraversal(node.leftNext);posTraversal(node.rightNext);System.out.print(node.data+" ");}// 重寫后序遍歷 方法,讓遍歷從 根結(jié)點(diǎn) 開始public void posTraversal() {Node node = root[0];if(node==null) {return;}// 遞歸時(shí)調(diào)用帶參數(shù)的方法posTraversal(node.leftNext);posTraversal(node.rightNext);System.out.print(node.data+" ");}// 查看 新增層級 的最大結(jié)點(diǎn)數(shù)public int numberOfNextNodes() {return (int) Math.pow(2,depth);}// 查看 樹 的深度(層級數(shù))public int getDepth() {return depth;}}

準(zhǔn)備工作2:NumberOfNodesException.java,為add()方法創(chuàng)建一個(gè)自定義異常,如果傳入的數(shù)據(jù)數(shù)與當(dāng)前層級最大結(jié)點(diǎn)數(shù)不符,則拋出該異常(如果二叉樹不滿,在數(shù)組的相應(yīng)位置傳入null)。

package com.notes.data_structure6;public class NumberOfNodesException extends Exception{public NumberOfNodesException(String message) {super(message);} }

句法二叉樹的實(shí)現(xiàn)及其遍歷:TreeDemo.java

package com.notes.data_structure6;public class TreeDemo {public static void main(String[] args) throws NumberOfNodesException {// 實(shí)例化二叉樹類,并且傳入根節(jié)點(diǎn)的數(shù)據(jù)MyBinaryTree tree = new MyBinaryTree("句子");// 加入第一層節(jié)點(diǎn)的數(shù)據(jù)tree.add(new String[] {"主語","謂語"});// 加入第二層節(jié)點(diǎn)的數(shù)據(jù)tree.add(new String[] {"定語","中心語","述語","賓語"});// 前序遍歷tree.preTraversal(); // 句子 主語 定語 中心語 謂語 述語 賓語 System.out.println();// 中序遍歷tree.midTraversal(); // 定語 主語 中心語 句子 述語 謂語 賓語 System.out.println();// 后序遍歷tree.posTraversal(); // 定語 中心語 主語 述語 賓語 謂語 句子 } }

?

總結(jié)

以上是生活随笔為你收集整理的数据结构学习笔记(六):二叉树(Binary Tree)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。