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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

数据结构:二叉树

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

文章目錄

      • 前言
      • 1.樹概念及結(jié)構(gòu)
        • 1.1樹的概念
        • 1.2樹的實現(xiàn)
      • 2.二叉樹
        • 2.1概念
        • 2.2二叉樹的存儲結(jié)構(gòu)
        • 2.3特殊的二叉樹
        • 2.4二叉樹的存儲結(jié)構(gòu)
          • 2.4.1順序存儲
          • 2.4.2鏈?zhǔn)酱鎯?/li>
      • 3.二叉樹順序結(jié)構(gòu)及實現(xiàn)
        • 3.2二叉樹的順序結(jié)構(gòu)
        • 3.1代碼實現(xiàn)
      • 4.二叉樹鏈?zhǔn)浇Y(jié)構(gòu)及實現(xiàn)
        • 4.1二叉樹鏈?zhǔn)浇Y(jié)構(gòu)的遍歷
        • 4.2代碼實現(xiàn)

前言

為什么需要樹這種數(shù)據(jù)結(jié)構(gòu)

  • 1、數(shù)組存儲方式分析

    • 優(yōu)點:通過下表方式訪問元素,速度快,對于有序數(shù)組,還可以使用二分、插入以及斐波那契查找算法來提高檢索效率。
    • 缺點:如果要檢索具體的某個值,或者進行插入刪除操作,整個數(shù)組中的數(shù)據(jù)都需要移動索引,性能開銷極大并且效率極低。其次由于數(shù)組的特點,在創(chuàng)建時數(shù)組大小已經(jīng)固定,數(shù)組需進行擴容時極其不方便。
  • 2、鏈表存儲方式分析

    • 優(yōu)點:在一定程度上對數(shù)組存儲方式進行了優(yōu)化,當(dāng)插入數(shù)據(jù)時,只需要將節(jié)點之間指針稍作調(diào)整并將數(shù)據(jù)插入指定位置即可,刪除數(shù)據(jù)同理,對節(jié)點之間指針進行調(diào)整即可。
    • 缺點:進行檢索時,效率仍然較低,需要從頭結(jié)點進行遍歷。
  • 3、樹存儲方式的分析

    • 能提高數(shù)據(jù)存儲與讀取的效率,比如利用二叉排序樹(Binary Sort Tree)既可以保證數(shù)據(jù)檢索速度,同時也可以保證數(shù)據(jù)的插入、刪除已經(jīng)修改的速度。

1.樹概念及結(jié)構(gòu)

1.1樹的概念

不同于鏈表、棧以及隊列都是線性的數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)存儲較為簡單,元素只存在一個對一個的關(guān)系,樹是一種更為復(fù)雜的數(shù)據(jù)結(jié)構(gòu),元素存在一對多的關(guān)系,一個父節(jié)點可以包含多個子節(jié)點。它是一個由n(n>=0)個有限節(jié)點組成的有層次關(guān)系的集合。

樹可以由幾種方式定義,定義樹的一種自然的方式就是遞歸的方式。

  • 根節(jié)點(root)
  • 除了根節(jié)點之外,其余節(jié)點被分為一棵結(jié)構(gòu)與樹相類似的子樹。
  • 一個節(jié)點含有的子樹的根節(jié)點稱為該節(jié)點的子節(jié)點
  • 一個節(jié)點含有子節(jié)點,則將該節(jié)點稱之為子節(jié)點的父節(jié)點
  • 節(jié)點的度:一個節(jié)點含有的子樹的個數(shù)稱為該節(jié)點的度
  • 沒有子節(jié)點即度為0的節(jié)點稱之為葉子節(jié)點,也成為樹葉(leaf)
  • 路徑:從根節(jié)點到某個節(jié)點的路線
  • 節(jié)點的層次:從根開始定義,根為第一層,根的節(jié)點為第二層,以此類推
  • 樹的高度或深度:樹中節(jié)點的最大層次
  • 森林:由m棵互不相交的樹的集合稱為森林,即多棵子樹構(gòu)成森林

1.2樹的實現(xiàn)

實現(xiàn)樹的一種方法可以是在每一個節(jié)點除數(shù)據(jù)以外還要有一條鏈,使得該節(jié)點的每一個子節(jié)點都有一個鏈指向它。然而由于每一個節(jié)點的子節(jié)點個數(shù)可以變化很大并且事先不知道,因此在數(shù)據(jù)結(jié)構(gòu)中建立到各子節(jié)點直接的鏈接是不可行的,這樣會產(chǎn)生太多浪費的空間。實際上解決方法很簡單:將每一個節(jié)點的所有子節(jié)點都放在樹節(jié)點的鏈表中。

class TreeNode {Object element;TreeNode firstChild;TreeNode nextSibling; }

如上圖所示,向下的箭頭指向的firstChild的鏈,而水平箭頭指向的是nextSibling的鏈,null鏈過多所以沒有畫出。


2.二叉樹

2.1概念

二叉樹(Binary Tree)是一顆樹,其中每個節(jié)點都不能超過兩個子節(jié)點。

二叉樹的一個性質(zhì)是一顆平均二叉樹的深度要比節(jié)點個數(shù)N小得多,分析表明,其深度為O(N1/2),而對于特殊類型的二叉樹,即二叉查找樹(Binary Search Tree),其深度的平均值為O(log N),但最差的情況如下圖所示,深度可以達(dá)到N-1。

2.2二叉樹的存儲結(jié)構(gòu)

二叉樹常使用兩種存儲結(jié)構(gòu),一種為順序結(jié)構(gòu),一種為鏈?zhǔn)浇Y(jié)構(gòu)

2.3特殊的二叉樹

  • 滿二叉樹:一個二叉樹,如果所有葉子節(jié)點都在最后一層,并且節(jié)點總數(shù)為2n - 1,n為層數(shù),則稱該二叉樹為滿二叉樹
  • 完全二叉樹:一個二叉樹深度為h,除了第h層外,其他各層的節(jié)點數(shù)都達(dá)到最大個數(shù),第h層有葉子節(jié)點,并且葉子節(jié)點都是從左到右依次排布

2.4二叉樹的存儲結(jié)構(gòu)

2.4.1順序存儲

順序結(jié)構(gòu)存儲就是用數(shù)組來存儲,從數(shù)據(jù)存儲來看,數(shù)組存儲方式和樹的存儲方式可以相互轉(zhuǎn)換,一般使用數(shù)組只適合表示完全二叉樹,因為不是完全二叉樹會有空間的浪費。

順序存儲二叉樹特點

  • 通常只考慮完全二叉樹
  • 第n個元素的左子節(jié)點為2*n+1
  • 第n個元素的右子節(jié)點為2*n+2
  • 第n個元素的父節(jié)點為(n-1)/2
  • 對于具有n個節(jié)點的完全二叉樹,如果按照從左至右的數(shù)組順序?qū)λ泄?jié)點從0開始編號,則對序號為index的節(jié)點有:
    • 如果index>0,index位置的節(jié)點雙親序號為(index-1)/2,若index=0,則說明為根節(jié)點,無雙親節(jié)點
    • 若2index+1<n,則說明是序號為index的節(jié)點有左子節(jié)點,并且該子節(jié)點序號為2index+1;否則該序號為index的節(jié)點無子節(jié)點
    • 若2index+2<n,則說明是序號為index的節(jié)點有右子節(jié)點,并且該子節(jié)點序號為2*index+2;否則該序號為index的節(jié)點無子節(jié)點


2.4.2鏈?zhǔn)酱鎯?/h5>

二叉樹的鏈?zhǔn)酱鎯Y(jié)構(gòu)指用鏈表來表示一顆二叉樹,即用鏈來指示元素的邏輯關(guān)系,通常的方式是鏈表的每個節(jié)點由三個域表示,數(shù)據(jù)域和左右指針域,左右指針分別用來指向左右子節(jié)點的存儲地址。鏈?zhǔn)浇Y(jié)構(gòu)又分為二叉鏈和三叉鏈,當(dāng)前初學(xué)階段是使用二叉鏈,在后面高級數(shù)據(jù)結(jié)構(gòu)如紅黑樹中會使用到三叉鏈。

二叉鏈圖示:

三叉鏈圖示:

//二叉鏈 class BinaryTreeNode {BinaryTreeNode left;BinaryTreeNode right;Object element; } //三叉鏈 class BinaryTreeNode {BinaryTreeNode parent;BinaryTreeNode left;BinaryTreeNode right;Object element; }

3.二叉樹順序結(jié)構(gòu)及實現(xiàn)

3.2二叉樹的順序結(jié)構(gòu)

普通的二叉樹是不適合用數(shù)組來存儲的,因為可能會存在大量的空間浪費。而完全二叉樹更適合使用順序結(jié)構(gòu)存儲。現(xiàn)實中我們通常把堆(一種二叉樹)使用順序結(jié)構(gòu)的數(shù)組來存儲,需要注意的是這里的堆和操作系統(tǒng)虛擬進程地址空間中的堆是兩回事,一個是數(shù)據(jù)結(jié)構(gòu),一個是操作系統(tǒng)中管理內(nèi)存的一塊區(qū)域分段。

完全二叉樹圖示:不會存在大量的空間浪費現(xiàn)象

非完全二叉樹圖示:存在大量的空間浪費現(xiàn)象

3.1代碼實現(xiàn)

/*** 編寫一個ArrayBinaryTree實現(xiàn)順序存儲二叉樹*/ class ArrayBinaryTree {/*** 存儲數(shù)據(jù)節(jié)點的數(shù)組*/private int[] arr;public ArrayBinaryTree(int[] arr){this.arr = arr;}public void preOrder(){this.preOrder(0);System.out.println();}public void infixOrder(){this.infixOrder(0);System.out.println();}public void postOrder(){this.postOrder(0);System.out.println();}public boolean isEmpty(){return arr[0] == null;}/*** 前序遍歷* @param index*/public void preOrder(int index){//如果數(shù)組為空或者array.length = 0if (arr == null || arr.length == 0){System.out.println("數(shù)組為空....");}//數(shù)組當(dāng)前節(jié)點數(shù)據(jù)System.out.println(arr[index]);//向左遞歸遍歷if ((2 * index + 1) < arr.length){preOrder(2 * index + 1);}//向右遞歸遍歷if ((2 * index + 2) < arr.length){preOrder(2 * index + 2);}}/*** 中序遍歷* @param index*/public void infixOrder(int index){//如果數(shù)組為空或者array.length = 0if (arr == null || arr.length == 0){System.out.println("數(shù)組為空....");}if ((2 * index + 1) < arr.length){infixOrder(2 * index + 1);}//數(shù)組當(dāng)前節(jié)點數(shù)據(jù)System.out.println(arr[index]);if ((2 * index + 2) < arr.length){infixOrder(2 * index + 2);}}/*** 后序遍歷* @param index*/public void postOrder(int index){//如果數(shù)組為空或者array.length = 0if (arr == null || arr.length == 0){System.out.println("數(shù)組為空....");}if ((2 * index + 1) < arr.length){postOrder(2 * index + 1);}if ((2 * index + 2) < arr.length){postOrder(2 * index + 2);}//數(shù)組當(dāng)前節(jié)點數(shù)據(jù)System.out.println(arr[index]);} }

4.二叉樹鏈?zhǔn)浇Y(jié)構(gòu)及實現(xiàn)

4.1二叉樹鏈?zhǔn)浇Y(jié)構(gòu)的遍歷

二叉樹的遍歷(Traversal)就是指沿著某條搜索路線,依次對樹中的節(jié)點做一次訪問。訪問時做的操作依賴于實際問題。遍歷時二叉樹中最重要的操作之一,也是對二叉樹進行其他操作的基礎(chǔ)。

遞歸遍歷主要有三種方式:前序/中序/后序遍歷

  • 前序遍歷(Preorder Traversal):訪問根節(jié)點的操作發(fā)生在遍歷其左右節(jié)點之前
  • 中序遍歷(Inorder Traversal):訪問根節(jié)點的操作發(fā)生在遍歷其左右節(jié)點中間
  • 后序遍歷(Postorder Traversal):訪問根節(jié)點的操作發(fā)生在遍歷其左右節(jié)點之后

4.2代碼實現(xiàn)

存儲數(shù)據(jù)的節(jié)點

class Node {private int num;private Object data;private Node left;private Node right;public Node(int num, Object data){this.num = num;this.data = data;}/*** 前序遍歷*/public void preOrder(){//先輸出父節(jié)點信息System.out.println(this);//判斷此節(jié)點的左節(jié)點是否為空if (this.left != null){//向左前序遍歷this.left.preOrder();}//判斷此節(jié)點的右節(jié)點是否為空if (this.right != null){//向右前序遍歷this.right.preOrder();}}/*** 中序遍歷*/public void infixOrder(){//判斷此節(jié)點的左節(jié)點是否為空if (this.left != null){//向左中序遍歷this.left.infixOrder();}//輸出父節(jié)點信息System.out.println(this);//判斷此節(jié)點的右節(jié)點是否為空if (this.right != null){//向右中序遍歷this.right.infixOrder();}}/*** 后序遍歷*/public void postOrder(){//判斷此節(jié)點的左節(jié)點是否為空if (this.left != null){//向左后序遍歷this.left.postOrder();}//判斷此節(jié)點的右節(jié)點是否為空if (this.right != null){//向右后序遍歷this.right.postOrder();}//輸出父節(jié)點信息System.out.println(this);}/*** 前序遍歷查找節(jié)點* @param num 待查找節(jié)點的編號* @return 如果找到返回該節(jié)點 否則返回null*/public Node preOrderSearch(int num){System.out.println("進行前序遍歷查找");//判斷當(dāng)前節(jié)點是否匹配if (this.num == num){return this;}Node result = null;//判斷當(dāng)前節(jié)點的左子節(jié)點是否為空if (this.left != null){//向左前序查找result = this.left.preOrderSearch(num);}//如果不為空說明已經(jīng)找到直接返回該節(jié)點并回溯if (result != null){return result;}//判斷當(dāng)前節(jié)點的右子節(jié)點是否為空if (this.right != null){//向右前序查找result = this.right.preOrderSearch(num);}return result;}/*** 中序遍歷查找節(jié)點* @param num 待查找節(jié)點的編號* @return 如果找到返回該節(jié)點 否則返回null*/public Node infixOrderSearch(int num){Node result = null;//判斷當(dāng)前節(jié)點的左子節(jié)點是否為空if (this.left != null){//向左中序查找result = this.left.infixOrderSearch(num);}//如果不為空說明已經(jīng)找到直接返回該節(jié)點并回溯if (result != null){return result;}System.out.println("進入中序遍歷查找");//判斷當(dāng)前節(jié)點是否匹配if (this.num == num){return this;}//判斷當(dāng)前節(jié)點的右子節(jié)點是否為空if (this.right != null){//向右中序查找result = this.right.infixOrderSearch(num);}return result;}/*** 后序遍歷查找節(jié)點* @param num 待查找節(jié)點的編號* @return 如果找到返回該節(jié)點 否則返回null*/public Node postOrderSearch(int num){Node result = null;//判斷當(dāng)前節(jié)點的右子節(jié)點是否為空if (this.left != null){//向左后序查找result = this.left.postOrderSearch(num);}//如果不為空說明已經(jīng)找到直接返回該節(jié)點并回溯if (result != null){return result;}//判斷當(dāng)前節(jié)點的右子節(jié)點是否為空if (this.right != null){//向右后序查找result = this.right.postOrderSearch(num);}//如果不為空說明已經(jīng)找到直接返回該節(jié)點并回溯if (result != null){return result;}System.out.println("進入后序遍歷查找");//判斷當(dāng)前節(jié)點是否匹配if (this.num == num){return this;}return null;}/*** 遞歸刪除節(jié)點* 1.如果刪除的節(jié)點是葉子節(jié)點則刪除該節(jié)點* 2.如果刪除的節(jié)點是非葉子節(jié)點則刪除該書* @param num*/public void deleteNode(int num){//1.因為該二叉樹是單向的所以先判斷當(dāng)前節(jié)點的子節(jié)點是否需要刪除節(jié)點而不能判斷當(dāng)前這個節(jié)點是否需要刪除//2.如果當(dāng)前節(jié)點的子左節(jié)點不為空,而且左子節(jié)點就是需要刪除的節(jié)點,就將this.left=null,并且返回,結(jié)束遞歸if (this.left != null && this.left.num == num){this.left = null;return;}//3.如果當(dāng)前節(jié)點的子右節(jié)點不為空,而且右子節(jié)點就是需要刪除的節(jié)點,就將this.right=null,并且返回,結(jié)束遞歸if (this.right != null && this.right.num == num){this.right = null;return;}//4.如果仍然沒有刪除節(jié)點就需要向左子樹進行遞歸刪除if (this.left != null){this.left.deleteNode(num);}//5.如果仍然沒有刪除節(jié)點就需要向右子樹進行遞歸刪除if (this.right != null){this.right.deleteNode(num);}}//get set方法省略@Overridepublic String toString(){return "Node{" +"num=" + num +", data=" + data +'}';} }

二叉樹

class BinaryTree {/*** 根節(jié)點*/private Node root;public void setRoot(Node root){this.root = root;}public void createBinaryTree(ArrayList<Node> list){this.createBinaryTree(list,0);}/*** 創(chuàng)建二叉樹* @param list 節(jié)點列表* @param index 用于創(chuàng)建的索引* @return*/private Node createBinaryTree(ArrayList<Node> list,int index){Node node = null;if (isEmpty()){setRoot(list.get(0));}if (index < list.size()){node = list.get(index);node.setLeft(createBinaryTree(list, (2*index+1)));node.setRight(createBinaryTree(list, (2*index+2)));}return node;}/*** 判斷二叉樹是否為空* @return */public boolean isEmpty(){return root == null;}/**** @return 返回二叉樹中的節(jié)點個數(shù)*/public int getBinaryTreeSize(){return this.getBinaryTreeSize(root);}private int getBinaryTreeSize(Node root){if (root == null){return 0;}return getBinaryTreeSize(root.getLeft()) + getBinaryTreeSize(root.getRight()) + 1;}/*** 葉子節(jié)點:無子節(jié)點的節(jié)點稱之為葉子節(jié)點* @return 返回二叉樹中的葉子節(jié)點個數(shù)*/public int getBinaryTreeLeafSize(){return this.getBinaryTreeLeafSize(root);}private int getBinaryTreeLeafSize(Node root){if (root == null){return 0;}if (root.getLeft() == null && root.getRight() == null){return 1;}return getBinaryTreeSize(root.getLeft()) + getBinaryTreeLeafSize(root.getRight());}/**** @param deep 深度* @return 返回第deep層節(jié)點個數(shù)*/public int getBinaryTreeDeepSize(int deep){return this.getBinaryTreeDeepSize(root,deep);}private int getBinaryTreeDeepSize(Node root,int deep){if (root == null){return 0;}/*** Deep等于1的情況* 1.第一次傳進來的值就是1時,第一層只有根節(jié)點root,故返回1* 2.當(dāng)遍歷到指定層數(shù)時,deep等于1*/if (deep == 1){return 1;}return getBinaryTreeDeepSize(root.getLeft(),deep-1) + getBinaryTreeDeepSize(root.getRight(),deep-1);}/*** 前序遍歷*/public void preOrder(){if (root != null){root.preOrder();}else{System.out.println("二叉樹為空,無法遍歷");}}/*** 中序遍歷*/public void infixOrder(){if (root != null){root.infixOrder();}else{System.out.println("二叉樹為空,無法遍歷");}}/*** 后序遍歷*/public void postOrder(){if (root != null){root.postOrder();}else{System.out.println("二叉樹為空,無法遍歷");}}/*** 前序遍歷查找num匹配的節(jié)點* @param num * @return 找到返回該節(jié)點 否則返回null*/public Node preOrderSearch(int num){if (root != null){return root.preOrderSearch(num);}return null;}/*** 中序遍歷查找num匹配的節(jié)點* @param num* @return 找到返回該節(jié)點 否則返回null*/public Node infixOrderSearch(int num){if (root != null){return root.infixOrderSearch(num);}return null;}/*** 后序遍歷查找num匹配的節(jié)點* @param num* @return 找到返回該節(jié)點 否則返回null*/public Node postOrderSearch(int num){if (root != null){return root.postOrderSearch(num);}return null;}/*** 刪除節(jié)點* @param num 待刪除節(jié)點編號*/public void deleteNode(int num){if (root != null){if (root.getNum() == num){root = null;}else{root.deleteNode(num);}}else{System.out.println("該樹為空無法刪除數(shù)據(jù)....");}} }

以上。
如有不足或錯誤歡迎評論指正,整理不易,留個三連吧!

總結(jié)

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

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