Java-數構二叉樹

1.樹

1.1概念

樹是一種非線性的數據結構,它是由n個有限節點組成一個具有層次關系。這種結構有以下特點:

  • 一個特殊的結點,稱為根節點,根節點沒有前驅節點
  • 除根節點以外,其余節點分成M個互不相交的集合。每個集合又是一顆和樹類似的子樹。每顆子樹的根節點有且只有一個前驅,可以有0個或者多個后繼。
  • 樹是遞歸定義的。

樹形結構中,子樹之間是不能有交集的,否則就不是樹形結構。除了根節點以外,每一個節點有且只有一個父結點。

一棵樹有N個節點就有N-1條邊。

1.2概念

  • 結點的度:一個結點含有子樹的個數。
  • 樹的度:一棵樹中,所有結點度最大值稱之為樹的度。
  • 葉子結點或終端結點:度為0的結點稱作葉子結點;
  • 雙親結點或父結點:若一個結點含有子節點,則稱這個結點為其子節點的父節點。
  • 孩子結點或子結點:一個結點含有的子樹的根節點乘坐該結點的子節點。
  • 根結點:一棵樹中,沒有雙親結點的結點
  • 結點的層次:從根開始定義,根為第一層,根的子結點為第2層
  • 樹的高度或深度:樹中結點的最大層次
  • 兄弟結點:具有相同父節點的結點互相稱為兄弟結點
  • 子孫:以某結點為根的子樹中任一結點都稱為該結點的子孫
  • 森林:由m棵互不想接的樹構成的集合稱為森林

1.3樹的表示形式

雙親表示法孩子表示法孩子雙親表示法孩子兄弟表示法等等。我們這里就簡單的了解其中最常用的孩子兄弟表示法

2二叉樹

2.1概念

一顆二叉樹是結點的有限集合,該集合:或者為空,或者是由一個根節點加上兩顆別稱為左子樹和右子樹的二叉樹構成。

注意:

  • 二叉樹不存在度大于2的結點
  • 二叉樹的子樹有左右之分,次序不可顛倒,因此二叉樹是有序樹

2.2兩種特殊的二叉樹

  1. 滿二叉樹:一顆二叉樹,如果每層的節點數都達到最大值,則這顆二叉樹是滿二叉樹,也就是說如果有一顆二叉樹的層數為K,且結點總數為2^k - 1,則他是滿二叉樹。
  2. 完全二叉樹:完全二叉樹是一種效率很高的數據結構,完全二叉樹是由滿二叉樹引申出來的。對于深度為K的,有n個結點的二叉樹,當且僅當其中每一個結點都與深度為K的滿二叉樹中從0到n-1的結點對應時才為完全二叉樹。

2.3二叉樹的性質

  1. 若根節點的層數為1,則一顆非空二叉樹的第i層上最多有2^{i-1} \quad (i > 0)個結點
  2. 若只有根節點的二叉樹的深度為1,則深度為K 的二叉樹黨的最大結點數為2^{k-1} \quad (k >= 0)
  3. 對于任何一棵二叉樹,如果其葉節點個數為n0(度為0),度為2的非葉結點的個數為n2,則有n0=n2+1
  4. 具有n個結點的完全二叉樹的深度k為\lfloor \log_2(n+1) \rfloor向上取整
  5. 對于具有n個結點的完全二叉樹,如果按照從上至下,從左至右的順序對所有結點從0開始編號,則對于需要為i的結點有:
  • 若i>0,雙親的序號為:(i-1)/2;若i=0,i為根節點編號,沒有雙親結點
  • 若2i+1<n,左孩子序號為2i+1,否則沒有左孩子
  • 若2i+1>n,左孩子序號為2i+2,否則沒有右孩子

2.4二叉樹的存儲

存儲結構分為:順序存儲和類似于鏈表形式的鏈式存儲。二叉樹的鏈式存儲是通過一個一個的節點引用起來的,常見的表示方式有二叉和三叉表示方式

2.5二叉樹的基本操作

2.1二叉樹的遍歷

1.前中后序遍歷

所謂遍歷是指沿著某條搜索路線,因此對樹種每個結點均做一次且僅做一次的訪問。

  • NLR:前序遍歷(Preorder Traversal 亦稱先序遍歷)——訪問根結點--->根的左子樹--->根的右子樹。
  • LNR:中序遍歷(Inorder Traversal)——根的左子樹--->根節點--->根的右子樹。
  • LRN:后序遍歷(Postorder Traversal)——根的左子樹--->根的右子樹--->根節點。

2.層序遍歷

設二叉樹的根節點所在 層數為1,層序遍歷就是從所在二叉樹的根節點出發,首先訪問第一層的樹根節點,然后從左到右訪問第2層 上的節點,接著是第三層的節點,以此類推,自上而下,自左到右逐層訪問樹的結點的過程就是層序遍歷。

2.5.二叉樹的基本方法

//樹種節點的個數
public void nodeSize(TreeNode root){if(root==null) return ;size++;nodeSize(root.left);nodeSize(root.right);}public int nodeSize2(TreeNode root){if(root==null) return 0;return nodeSize2(root.left)+nodeSize2(root.right)+1;}
// 子問題思路-求葉子結點個數
public int leafSize=0;public void getLeafNodeCount(TreeNode root){if(root==null)return ;if(root.left==null&root.right==null){leafSize++;}getLeafNodeCount(root.left);getLeafNodeCount(root.right);}//左數的葉子+右數的葉子public int getLeafNodeCount2(TreeNode root){if(root==null)return 0;if(root.left==null&&root.right==null){return 1;}return getLeafNodeCount2(root.left)+getLeafNodeCount2(root.right);}
//第K層結點的個數public int getKLevelNodeCount(TreeNode root,int k){if(root==null) return 0;if(k==1){return 1;}return getKLevelNodeCount(root.left,k-1)+getKLevelNodeCount(root.right,k-1);}
// 獲取二叉樹的高度
public int getHeight(TreeNode root){if(root==null) return 0;int leftHeight=getHeight(root.left);int rightHeight=getHeight(root.right);return (leftHeight>rightHeight? leftHeight+1:rightHeight+1);}
// 檢測值為value的元素是否存在public boolean find(TreeNode root,char key){if(root==null)return false;if(root.val==key){return true;}boolean leftVal=find(root.left,key);if (leftVal==true){return true;}boolean rightVal=find(root.right,key);if(rightVal==true){return  true;}return  false;}
//層序遍歷
public void levelOrder1(TreeNode root){//層序遍歷if(root==null){return;}Queue<TreeNode> queue=new LinkedList<>();queue.offer(root);while(!queue.isEmpty()){TreeNode cur=queue.poll();System.out.println(cur.val+" ");if(cur.left!=null){queue.offer(cur.left);}if(cur.right!=null){queue.offer(cur.right);}}}
public List<List<TreeNode>> levelOrder2(TreeNode root){//層序遍歷List<List<TreeNode>> ret=new ArrayList<>();if(root==null){return ret;}Queue<TreeNode> queue=new LinkedList<>();queue.offer(root);while(!queue.isEmpty()){int size= queue.size();//當前隊列的大小List<TreeNode> tmp=new ArrayList<>();while (size!=0){TreeNode cur=queue.poll();tmp.add(cur);size--;if(cur.left!=null){queue.offer(cur.left);}if(cur.right!=null){queue.offer(cur.right);}}ret.add(tmp);}return  ret;}

2.5.3常見0j

判斷一棵樹是否是完全二叉樹

// 判斷一棵樹是不是完全二叉樹
public boolean isCompleteTree(TreeNode root){if(root==null) return false;Queue<TreeNode> queue=new LinkedList<>();queue.offer(root);while(!queue.isEmpty()){TreeNode cur=queue.poll();if (cur!=null){queue.offer(root.left);queue.offer(root.right);}else {break;}}while (!queue.isEmpty()){TreeNode cur=queue.poll();if(cur==null){return false;}}return true;}

思路:先把當前節點放入隊列中,再彈出元素判斷是否為空節點,若不為空將該節點的左右節點再入隊,一直到隊列中都為空。再挨個彈出隊列中所剩下的元素,遇到null就說明不是完全二叉樹。若非空說明元素都是按序排列的。

100. 相同的樹

class Solution {public boolean isSameTree(TreeNode p, TreeNode q) {if(p==null&&q!=null||p!=null&&q==null){return false;}if(p==null&&q==null){return true;}if(p.val!=q.val){return false;}return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);}
}

思路:定義兩個節點位于兩棵樹,若一個節點為空一個不為空,說明不是相同的樹。若兩個節點都為空,說明是兩顆空樹,則相同。若兩個節點的值不相同,則不相同。遍歷兩樹的左子樹和右子樹,只有當左右子樹都相同的時候才是相同的樹。

572. 另一棵樹的子樹

class Solution {public boolean isSubtree(TreeNode root, TreeNode subRoot) {if(root==null){return false;} if(isSameTree(root,subRoot)){return true;}return isSubtree(root.left,subRoot)||isSubtree(root.right,subRoot);}private boolean isSameTree(TreeNode p,TreeNode q){if(p==null&&q!=null||p!=null&&q==null){return false;}if(p==null&&q==null){return true;}if(p.val!=q.val){return false;}return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);}
}

思路與相同樹類似。只不過可能從根結點處開始相同,也有可能與左右子樹中任意一棵子樹相同。

226. 翻轉二叉樹

class Solution {public TreeNode invertTree(TreeNode root) {if(root==null){return null;}TreeNode tmp=root.left;root.left=root.right;root.right=tmp;invertTree(root.left);invertTree(root.right);return root;}
}

110. 平衡二叉樹

class Solution {public boolean isBalanced(TreeNode root) {return getHeight(root) != -1;}private int getHeight(TreeNode root) {if (root == null) {return 0;}int leftHeight = getHeight(root.left);if (leftHeight == -1) {return -1; // 左子樹不平衡}int rightHeight = getHeight(root.right);if (rightHeight == -1) {return -1; // 右子樹不平衡}if (Math.abs(leftHeight - rightHeight) > 1) {return -1; // 當前節點不平衡}return Math.max(leftHeight, rightHeight) + 1;}
}

思路:平衡二叉樹的概念是:平衡二叉樹是指該樹所有節點的左右子樹的高度相差不超過 1。基本思路與求樹的高度相同,遍歷左右子樹計算子樹的高度,比較高度并加1。但是這里為預防重復計算高度設置一個-1值說明該子樹已經不平衡了。

101. 對稱二叉樹

class Solution {public boolean isSymmetric(TreeNode root) {if(root==null){return true;}return isSymmetricChild(root.left,root.right);}public boolean isSymmetricChild(TreeNode p,TreeNode q){if(p==null&&q!=null||p!=null&&q==null){return false;}if(p==null&&q==null){return true;}if(p.val!=q.val){return false;}return isSymmetricChild(p.left,q.right)&&isSymmetricChild(p.right,q.left);}
}

思路:遍歷左右子樹,當p.left==q.right且p.right==q.left時才滿足對稱。

給定一個字符串生成一個二叉樹

編一個程序,讀入用戶輸入的一串先序遍歷字符串,根據此字符串建立一個二叉樹(以指針方式存儲)。 例如如下的先序遍歷字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空樹。

    public static  int i = 0;public static TreeNode creatrTree(String str) {TreeNode root = null;if(str.charAt(i) != '#') {root = new TreeNode(str.charAt(i));i++;root.left = creatrTree(str);root.right = creatrTree(str);}else {i++;}return root;}
}

思路:按照先序遍歷的方式創建二叉樹。

236. 二叉樹的最近公共祖先

class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if(root==null)return null;if(root==p||root==q){return root;}TreeNode left=lowestCommonAncestor(root.left,p,q);TreeNode right=lowestCommonAncestor(root.right,p,q);if(left!=null&&right!=null){return root;}else if(left==null){return right;}else{return left;}}
}

思路:p,q若分別位于左右子樹上那么最近公共祖先一定是根節點。若一個位于左或右子樹上,一個位于根節點,那么根節點就是最近公共祖先。?

105. 從前序與中序遍歷序列構造二叉樹

class Solution {public int preindex=0;public TreeNode buildTree(int[] preorder, int[] inorder) {return buildTreeChild(preorder,inorder,0,inorder.length-1);}private TreeNode buildTreeChild(int[] preorder,int[] inorder,int inbegin,int inend){if(inbegin>inend){return null;}TreeNode root=new TreeNode(preorder[preindex]);int rootIndex=findIdex(inorder,inbegin, inend,preorder[preindex] );if(rootIndex==-1){return null;}preindex++;root.left=buildTreeChild(preorder,inorder,inbegin,rootIndex-1);root.right=buildTreeChild(preorder,inorder,rootIndex+1,inend);return root;}private int findIdex(int[] inorder,int inbegin,int inend,int key){for(int i=inbegin;i<=inend;i++){if(inorder[i]==key){return i;}}return -1;}
}

思路:按照手動畫樹的思路,前序遍歷第一個數就是根節點,而中序遍歷以這個數分作左右兩棵子樹。那么遍歷前序遍歷數組,并在中序遍歷數組中找到對應下標,構建根節點。同時按照中序遍歷順序先構建左子樹再構建右邊子樹。在中序遍歷數組定義一個數組開頭和結尾。調用是改變結尾或者開頭的位置,一直到開頭下標大于結尾下標時,說明子樹構建完成。

?106. 從中序與后序遍歷序列構造二叉樹

class Solution {public int postIndex;public TreeNode buildTree(int[] inorder, int[] postorder) {postIndex=postorder.length-1;return buildTreeChild(postorder,inorder,0,inorder.length-1);}private TreeNode buildTreeChild(int[] postorder,int[] inorder,int inbegin,int inend){if(inbegin>inend){return null;}TreeNode root=new TreeNode(postorder[postIndex]);int rootIndex=findIdex(inorder,inbegin, inend,postorder[postIndex]);if(rootIndex==-1){return null;}postIndex--;root.right=buildTreeChild(postorder,inorder,rootIndex+1,inend);root.left=buildTreeChild(postorder,inorder,inbegin,rootIndex-1);return root;}private int findIdex(int[] inorder,int inbegin,int inend,int key){for(int i=inbegin;i<=inend;i++){if(inorder[i]==key){return i;}}return -1;}}

思路:與前中序遍歷創建子樹同理。后序遍歷最后一個節點為根節點。且子樹創建順序應該是先右子樹后左子樹。

606. 根據二叉樹創建字符串

class Solution {public String tree2str(TreeNode root) {StringBuilder stringBuider=new StringBuilder();tree2strChild(root,stringBuider);return stringBuider.toString();}private void tree2strChild(TreeNode t,StringBuilder stringBuider){if(t==null){return;}stringBuider.append(t.val);if(t.left!=null){stringBuider.append("(");tree2strChild(t.left,stringBuider);stringBuider.append(")");}else{if(t.right==null){return ;}else{stringBuider.append("()");}}if(t.right!=null){stringBuider.append("(");tree2strChild(t.right,stringBuider);stringBuider.append(")");}else{return ;}}
}

思路:左子樹不為空的時候添加左括號,在添加字符后添加右括號。若右子樹節點為空那么添加左右括號。可以將情況分為:1左邊不為空(右邊為空,右邊不為空)2.左邊為空(右邊為空,右邊不為空)3.右邊空4.右邊不為空。

145. 二叉樹的后序遍歷

(非遞歸遍歷)

class Solution {public List<Integer> postorderTraversal(TreeNode root) {List<Integer> res=new ArrayList<Integer>();if(root==null) return res;Stack<TreeNode> stack=new Stack<>();TreeNode cur=root;TreeNode top=null;TreeNode prev=null;while(cur!=null||!stack.isEmpty()){while(cur!=null){stack.push(cur);cur=cur.left;}top=stack.peek();if(top.right==null||top.right==prev){stack.pop();res.add(top.val);prev=top;//當前結點被打印過了}else{cur=top.right;}} return res;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/90951.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/90951.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/90951.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

編程中水合的理解

在編程中&#xff0c;水合&#xff08;Hydration&#xff09; 是一個常見概念&#xff0c;尤其在 前端開發 和 服務端渲染&#xff08;SSR&#xff09; 場景中頻繁出現。它的核心含義是&#xff1a;將靜態內容“激活”為交互式動態內容。1. 水合的本質簡單理解&#xff1a;水合…

使用ffmpeg轉碼h265后mac默認播放器不支持問題

由于mac自帶錄屏是mov并且文件特別大&#xff0c;我使用ffmpeg轉碼視頻為h265使用如下命令ffmpeg_command [ffmpeg_path,"-i", input_path,"-c:v", "libx265","-preset", "veryslow","-map_metadata", "0&q…

支持MySQL、PostgreSQL和Redis集群部署,1Panel開源面板v2.0.5版本發布

2025年7月24日&#xff0c;現代化、開源的Linux服務器運維管理面板1Panel正式發布v2.0.5版本。在這一版本中&#xff0c;1Panel新增數據庫集群部署、郵件告警和主從節點靈活切換三項功能&#xff0c;聚焦為企業級運維場景提供更優使用體驗。 1Panel v2.0.5版本是1Panel開源面板…

GaussDB 數據庫架構師修煉(九) 邏輯備份實操

1 邏輯備份定義 邏輯備份是指與業務有關的對象進行備份&#xff0c;這個對象包括表、表的數據、視圖、索引、過程、函數等等。GaussDB支持邏輯備份的工具為gs_dump、gs_restore&#xff0c;以下舉例說明。 2 創建舉例數據 以下創建testdb庫&#xff0c;創建test1模式&#xf…

c# Winform發布成獨立文件

改造前&#xff1a; 通過發布頁面&#xff0c;修改部署模式為獨立&#xff0c;輸出文件目錄沒有完全包含所有dll改造后&#xff1a;通過修改項目文件方式修改csproj前&#xff1a;<PropertyGroup><OutputType>WinExe</OutputType><TargetFramework>net…

Android基礎(一) 運行HelloWorld

Android基礎&#xff08;一&#xff09; 運行HelloWorld一、創建你的第一個Android項目二、創建HelloWorld項目三、安裝并啟動模擬器四、安裝三方模擬器五、使用真機一、創建你的第一個Android項目 學習任何一門編程語言&#xff0c;編寫的第一個程序都是Hello World&#xff0…

MongoDB 和 Elasticsearch(ES)區別

MongoDB 和 Elasticsearch&#xff08;ES&#xff09;都是流行的 NoSQL 數據庫&#xff0c;但設計目標和適用場景有顯著區別。以下是它們的核心差異和典型使用場景對比&#xff1a;1. 核心定位特性MongoDBElasticsearch數據庫類型文檔數據庫&#xff08;通用型 OLTP&#xff09…

【C++算法】89.多源BFS_01 矩陣

文章目錄題目鏈接&#xff1a;題目描述&#xff1a;解法C 算法代碼&#xff1a;題目鏈接&#xff1a; 542. 01 矩陣 題目描述&#xff1a; 解法 先看懂題目 解法一&#xff1a;一個位置一個位置求&#xff08;最差的情況下會非常恐怖&#xff09; 解法二&#xff1a;多源BFS正…

數據結構之 【排序】(歸并排序)

目錄 1.遞歸實現歸并排序的思想及圖解 2.遞歸實現歸并排序的代碼邏輯 2.1嵌套子函數 2.2遞歸過程 2.3遞歸結束條件 2.4歸并及拷貝過程 3.非遞歸實現歸并排序的思想及圖解 4.非遞歸實現歸并排序的代碼邏輯 4.1邊歸并邊拷貝 4.2某一gap下歸并完成才進行拷貝 5.歸并排…

企業如何選擇適合的高防服務器?

高防服務器租用哪家好&#xff1f;這個問題困擾著許多站長&#xff0c;建立的網站經常受到各種網絡攻擊&#xff0c;雖然高防服務器有著較高的防御性能&#xff0c;十分適合經常被攻擊的行業網站&#xff0c;但是如何租到滿意的高防服務器呢&#xff01;徐州高防服務器是部署在…

告別重復勞動:Ansible 自動化運維超詳細學習路線圖

在運維的世界里&#xff0c;我們總是在與重復性任務作斗爭&#xff1a;部署同一套環境 N 次、在幾十臺服務器上修改同一個配置文件、一遍又一遍地執行相同的發布流程……這些工作不僅枯燥&#xff0c;還極易出錯。 如果你也為此感到煩惱&#xff0c;那么 Ansible 就是為你量身打…

UDS 0x29 身份驗證服務 Authentication service

背景 0x29服務的目的是為客戶端提供一種證明其身份的方法&#xff0c;在ECU端&#xff0c;有些服務或者數據因信息安全、排放或功能安全原因而受到嚴格限制。 只有身份驗證通過之后&#xff0c;才能夠允許其訪問數據和/或診斷服務。 例如&#xff0c;用于將數據下載/上傳到ECU以…

【python高階】-1- python工程和線程并發

一、項目工程守則1.pdm新建一個項目命令行終端&#xff1a;pip install pdmpdm init版本號&#xff1a;x.y.zx:兼容版本y:新增功能z:補丁版本pdm add pytest requests (添加依賴)pdm是協助管理我們的項目 2. black就是規范我們的代碼風格的&#xff1a;pdm add blackblackblack…

YOLOv8 剪枝模型加載踩坑記:解決 YAML 覆蓋剪枝結構的問題

1. 問題背景模型剪枝是實現模型輕量化、加速推理的關鍵步驟。然而&#xff0c;在 Ultralytics YOLOv8 的生態中&#xff0c;在成功剪枝后&#xff0c;進行微調&#xff08;Fine-tuning&#xff09;時會遇到一個令人困惑的現象&#xff1a;明明加載的是剪枝后的模型&#xff08;…

js的學習1

1.數組 數組方法 push()數組尾部添加unshift()數組頭部添加pop()數組尾部刪除shift()數組頭部刪除splice(起始位置&#xff0c;刪除幾個元素&#xff0c;要替換的元素)刪除指定的元素&#xff0c;改變了原數組&#xff0c;返回值是被刪除的元素indexOf()第一次查到的索引&#…

LeetCode 2563.統計公平數對的數目

給你一個下標從 0 開始、長度為 n 的整數數組 nums &#xff0c;和兩個整數 lower 和 upper &#xff0c;返回 公平數對的數目 。 如果 (i, j) 數對滿足以下情況&#xff0c;則認為它是一個 公平數對 &#xff1a; 0 < i < j < n&#xff0c;且 lower < nums[i] n…

ZABBIX配置自動發現與自動注冊,網易郵箱告警和釘釘告警

一、自動發現zabbix server 主動的去發現所有的客戶端&#xff0c;然后將客戶端的信息登記在服務端上。缺點是如果定義的網段中的主機數量多&#xff0c;zabbix server 登記耗時較久&#xff0c;且壓力會較大。1、部署準備準備三臺虛擬機192.168.80.151&#xff1b;192.168.80.…

QT(五)常用類

1. QString字符串類(掌握) QString是Qt的字符串類&#xff0c;與C的string相比&#xff0c;不再使用ASCII編碼&#xff0c;QString使用的是Unicode編碼。 QString中每個字符都是一個16位的QChar&#xff0c;而不是8位的char。 QString完全支持中文&#xff0c;但是由于不同的技…

EXCEL怎么提取表名

錯誤的方法&#xff1a;使用以下方法提取表名的時候&#xff0c;會存在1個問題&#xff0c;公式只在當前工作表生效&#xff0c;換工作表會出現表名覆蓋的情況。RIGHT(CELL("filename"),LEN(CELL("filename"))-FIND("]",CELL("filename&quo…

springboot校園外賣配送系統

目 錄 第一章 緒 論 1.1背景及意義 1.2國內外研究概況 1.3 研究的內容 第二章 關鍵技術的研究 2.1開發技術 2.2 Springboot框架介紹 2.3 Vue.js 主要功能 2.4 MVVM模式介紹 2.4 B/S體系工作原理 2.5 MySQL數據庫 第三章 系統分析 3.1 系統設計目標 3.2 系統可行性…