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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

算法【一】树

發布時間:2024/10/5 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法【一】树 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

關于樹你需要了解的一些概念:

?這是一棵普通的樹:

有節點,節點中有值,節點有指向下一個節點,節點還有度,節點還有層次(也叫高度或者深度)

?結點的度

結點擁有的子樹數目稱為結點的
?

節點關系?

結點子樹的根結點為該結點的孩子結點。相應該結點稱為孩子結點的雙親結點
圖2.2中,A為B的雙親結點,B為A的孩子結點。
同一個雙親結點的孩子結點之間互稱兄弟結點
圖2.2中,結點B與結點C互為兄弟結點。

后面接的全是空的,我們叫它們葉子結點如:G、H、I

節點的層次:

從根開始定義起,根為第一層,根的孩子為第二層,以此類推。

樹中結點的最大層次數稱為樹的深度或高度。圖2.1所示樹的深度為4。

??二叉樹

由二叉樹定義以及圖示分析得出二叉樹有以下特點:
1)每個結點最多有兩顆子樹,所以二叉樹中不存在度大于2的結點。
2)左子樹和右子樹是有順序的,次序不能任意顛倒。
3)即使樹中某結點只有一棵子樹,也要區分它是左子樹還是右子樹。

簡單來說就是:最多只有兩個字樹的樹就叫二叉樹

在日常編程中,二叉樹有一些性質我們可能可以用到,比如

1)在二叉樹的第i層上最多有2i-1次方 個節點 。(i>=1)
2)二叉樹中如果深度為k,那么最多有2k次方-1個節點。(k>=1)
3)n0=n2+1 n0表示度數為0的節點數,n2表示度數為2的節點數。
4)在完全二叉樹中,具有n個節點的完全二叉樹的深度為[log2n]+1,其中[log2n]是向下取整。
5)若對含 n 個結點的完全二叉樹從上到下且從左至右進行 1 至 n 的編號,則對完全二叉樹中任意一個編號為 i 的結點有如下特性:

(1) 若 i=1,則該結點是二叉樹的根,無雙親, 否則,編號為 [i/2] 的結點為其雙親結點;
(2) 若 2i>n,則該結點無左孩子, 否則,編號為 2i 的結點為其左孩子結點;
(3) 若 2i+1>n,則該結點無右孩子結點, 否則,編號為2i+1 的結點為其右孩子結點。

對于第五點我們來詳細講下并給出一些實例,因為這個筆試題中經常用到:?

用數組順序存儲二叉樹, 其顯著特征是: (n表示數組的索引值)?

  • 在數組中index=n的元素對應的父節點為 index=(n-1)/2的元素, 向下取整;
  • 在數組中index=n的元素對應的左子節點為 index=2*n+1的元素;
  • 在數組中index=n的元素對應的右子節點為 index=2*n+2的元素;

如果此時給我們一個順序存儲的數組,叫我們構建一顆二叉樹

給定二叉樹?[3,9,20,null,null,15,7],

Definition for a binary tree node.public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) { val = x; }} public static TreeNode createBT(int[] arr, int i) // 初始時,傳入的i==0 {TreeNode root = null; // 定義根節點if (i >= arr.length) // i >= arr.length 時,表示已經到達了根節點return null;root = new TreeNode(arr[i]); // 根節點root.left = createBT(arr, 2*i+1); // 遞歸建立左孩子結點root.right = createBT(arr, 2*i+2); // 遞歸建立右孩子結點return root; }public class Main {public static void main(String[] args) {int[] arr = {3,9,20,null,null,15,7};TreeNode root = createBT(arr, 0);System.out.println("先序遍歷:");PreOrder(root);System.out.println("\n中序遍歷:");InOrder(root);System.out.println("\n后序遍歷:");PostOrder(root);} // 這里只寫preOrder,中序和后序自己寫下public static void preOrder(TreeNode root){if (root==null){return;}System.out.println(root.val);preOrder(root.left) ;preOrder(root.right);}

至于二叉樹的遍歷比較簡單:遞歸就完事兒

// 先序遍歷public static void PreOrder(TreeNode root){if (root == null)return;System.out.print(root.val+" ");PreOrder(root.left);PreOrder(root.right);} // 中序遍歷public static void InOrder(TreeNode root){if (root == null)return;InOrder(root.left);System.out.print(root.val+" ");InOrder(root.right);} // 后序遍歷public static void PostOrder(TreeNode root){if (root == null)return;PostOrder(root.left);PostOrder(root.right);System.out.print(root.val+" ");}

接下來我們來介紹一下二叉樹的種類:

1、滿二叉樹

所有葉結點同處于最底層(非底層結點均是內部結點),一個深度為k(>=-1)且有2^(k+1) - 1個結點。如圖(圖來源于veil的博客):

2、完全二叉樹

葉結點只能出現在最底層的兩層,且最底層葉結點均處于次底層葉結點的左側。設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層所有的結點都連續集中在最左邊。

?一個完全二叉樹的初始化和遍歷:

我們來一道力扣題目:

完全二叉樹是每一層(除最后一層外)都是完全填充(即,節點數達到最大)的,并且所有的節點都盡可能地集中在左側。

設計一個用完全二叉樹初始化的數據結構?CBTInserter,它支持以下幾種操作:

CBTInserter(TreeNode root)?使用頭節點為?root?的給定樹初始化該數據結構;
CBTInserter.insert(int v)??向樹中插入一個新節點,節點類型為 TreeNode,值為 v 。使樹保持完全二叉樹的狀態,并返回插入的新節點的父節點的值;
CBTInserter.get_root() 將返回樹的頭節點。
?

示例 1:

輸入:inputs = ["CBTInserter","insert","get_root"], inputs = [[[1]],[2],[]]
輸出:[null,1,[1,2]]

初始化一個完全二叉樹,然后做一個前序遍歷的代碼

public class CBTInserter {private static TreeNode root;//輸入就是一棵樹public CBTInserter(TreeNode root) {this.root = root;}public int insert(int val) {TreeNode newNode = new TreeNode(val);// 我最開始這么寫的,給的一個測試用例也通過了,但是想想有什么問題Deque<TreeNode> stack =new LinkedList<>();if (root==null){root = newNode;}stack.push(root);TreeNode node=null;while (stack!=null && stack.size()>0){node= stack.pop();if (node.left==null){node.left =newNode;}else if (node.right==null){node.right = newNode;}else if (node.left!=null){stack.push(node.left);}else if (node.right !=null){stack.push(node.right);}}return node.val;}public TreeNode get_root() {return root;}public static void main(String[] args) {int[] arr = {1,2,3,4,5,6}; //這就是不通過的,實際上我并沒有找到最小的TreeNode root =CreateTree.createBT(arr, 0);//這個前面有CBTInserter obj = new CBTInserter(root);obj.insert(7);obj.insert(8);obj.get_root();} }

思路:回想一下我們的目的

將所有節點編號,按照從上到下從左到右的順序。

實際上我們希望,在每個插入步驟中,我們希望插入到一個編號最小的節點(并且它的字樹有空閑,那邊沒有我就插哪里)。

那么我們可以利用二叉樹的性質,想到沒?我們只要找到我要新插入節點的父節點實際上就解決問題了 我們很容易得到父節點在數組中的下標是這個n/2 -1,不會的自己手畫一下就推出來了

// 實際上你用一個 int[]即可 class CBTInserter {private List<TreeNode> array = new LinkedList<>();public CBTInserter(TreeNode root) {Queue<TreeNode> queue = new LinkedList<>();queue.add(root);while (!queue.isEmpty()){TreeNode treeNode = queue.remove();if (treeNode.left != null) {queue.add(treeNode.left);}if (treeNode.right != null) {queue.add(treeNode.right);}array.add(treeNode);}}public int insert(int v) {TreeNode node = new TreeNode(v);array.add(node);TreeNode p = array.get(array.size() / 2-1);if (p.left == null) {p.left = node;}else {p.right = node;}return p.val;}public TreeNode get_root() {return array.get(0);}}

?實際上按照剛剛的思路其實也能做出來

讓我們來改造下:

// 就是力扣的官方答案了。 bfs將最編號并且子樹有一個部位null的取到 class CBTInserter {TreeNode root;Deque<TreeNode> deque;public CBTInserter(TreeNode root) {this.root = root;deque = new LinkedList();Queue<TreeNode> queue = new LinkedList();queue.offer(root);// BFS to populate dequewhile (!queue.isEmpty()) {TreeNode node = queue.poll();if (node.left == null || node.right == null)deque.offerLast(node);if (node.left != null)queue.offer(node.left);if (node.right != null)queue.offer(node.right);}}public int insert(int v) {TreeNode node = deque.peekFirst();deque.offerLast(new TreeNode(v));if (node.left == null)node.left = deque.peekLast();else {node.right = deque.peekLast();deque.pollFirst();}return node.val;}public TreeNode get_root() {return root;} }作者:LeetCode 鏈接:https://leetcode-cn.com/problems/complete-binary-tree-inserter/solution/wan-quan-er-cha-shu-cha-ru-qi-by-leetcode/ 來源:力扣(LeetCode) 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

3、平衡二叉樹

?平衡二叉樹(Self-balancing binary search tree)又被稱為AVL樹(有別于AVL算法),且具有以下性質:它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,并且左右兩個子樹都是一棵平衡二叉樹。平衡二叉樹的常用實現方法有紅黑樹、AVL、替罪羊樹、Treap、伸展樹等。 最小二叉平衡樹的節點的公式如下 F(n)=F(n-1)+F(n-2)+1 這個類似于一個遞歸的數列,可以參考Fibonacci(斐波那契)數列,1是根節點,F(n-1)是左子樹的節點數量,F(n-2)是右子樹的節點數量。(百度百科)

關于平衡二叉樹可以查看這個博客:平衡二叉樹詳解 - zhangbaochong - 博客園

主要是清楚平衡二叉樹的幾種情況

1.對α的左兒子的左子樹進行一次插入

2.對α的左兒子的右子樹進行一次插入

3.對α的右兒子的左子樹進行一次插入

4.對α的右兒子的右子樹進行一次插入

情形1和情形4是關于α的鏡像對稱,二情形2和情形3也是關于α的鏡像對稱,因此理論上看只有兩種情況,但編程的角度看還是四種情形。

第一種情況是插入發生在“外邊”的情形(左左或右右),該情況可以通過一次單旋轉完成調整;第二種情況是插入發生在“內部”的情形(左右或右左),這種情況比較復雜,需要通過雙旋轉來調。

那么旋轉究竟是做個什么事情??針對四種情況的旋轉算法都不同。下面是針對左左情況的單旋轉算法的步驟

如果給你一個順序數組,如何來構建成一棵樹呢?

來實戰一下
從上到下打印二叉樹

從上到下打印出二叉樹的每個節點,同一層的節點按照從左到右的順序打印。

如:
給定二叉樹:?[3,9,20,null,null,15,7],

? ? 3
? ?/ \
? 9 ?20
? ? / ?\
? ?15 ? 7
返回:

[3,9,20,15,7]

分析一下,從上到下,即代表從根節點開始。從左到右,那就是從左子樹到右子樹。想到了什么?

樹的前序遍歷對不對?所謂的前序、中序、后序不過是訪問根節點的順序。那么思路很簡單了,

樹的變種

字典樹(前綴樹題目)

前綴樹的定義:
又稱單詞查找樹,字典樹,Trie樹,是一種樹形結構,是一種哈希樹的變種。典型應用是用于統計,排序和保存大量的字符串(但不僅限于字符串),所以經常被搜索引擎系統用于文本詞頻統計。它的優點是:利用字符串的公共前綴來減少查詢時間,最大限度地減少無謂的字符串比較,查詢效率比哈希樹高。

前綴樹的性質:

1)根節點不包含字符,除根節點外每一個節點都只包含一個字符
2)從根節點到某一節點,路徑上經過的字符連接起來,為該節點對應的字符串
3)每個節點的所有子節點包含的字符都不相同
注:每個節點都含有26個鏈接表示出現的26個小寫字母,即每個節點表示的字符是26個字符中的一個,當字符串插入完成時,我們就會標記該字符串就是完整的字符串了。
?

添加與搜索單詞 - 數據結構設計

請你設計一個數據結構,支持 添加新單詞 和 查找字符串是否與任何先前添加的字符串匹配 。

實現詞典類 WordDictionary :

WordDictionary() 初始化詞典對象
void addWord(word) 將 word 添加到數據結構中,之后可以對它進行匹配
bool search(word) 如果數據結構中存在字符串與?word 匹配,則返回 true ;否則,返回??false 。word 中可能包含一些 '.' ,每個?. 都可以表示任何一個字母。
?

示例:

輸入:
["WordDictionary","addWord","addWord","addWord","search","search","search","search"]
[[],["bad"],["dad"],["mad"],["pad"],["bad"],[".ad"],["b.."]]
輸出:
[null,null,null,null,false,true,true,true]

解釋:
WordDictionary wordDictionary = new WordDictionary();
wordDictionary.addWord("bad");
wordDictionary.addWord("dad");
wordDictionary.addWord("mad");
wordDictionary.search("pad"); // return False
wordDictionary.search("bad"); // return True
wordDictionary.search(".ad"); // return True
wordDictionary.search("b.."); // return True
?

提示:

1 <= word.length <= 500
addWord 中的 word 由小寫英文字母組成
search 中的 word 由 '.' 或小寫英文字母組成
最多調用 50000 次 addWord 和 search

思路分析:

非常明顯的一道前綴樹的問題,我們只需要構造一棵前綴樹,然后每個一次去遍歷這棵樹就行了 怎么實現呢? 第一:需要有一個根節點;每次從根節點開始遍歷取出單詞的每一個單詞,從根開始遍歷,單詞的word,是否是這個單詞的結束 那么我們需要先構造一個樹的節點的這樣的數據結構:包含了下一個節點和單詞是都結束的標識

好了直接上代碼了:

public class WordDictionary {/** Initialize your data structure here. */public class ThreeNode {Map<Character,ThreeNode> childdren;Boolean wordEnd;public ThreeNode(){childdren = new HashMap<Character, ThreeNode>();wordEnd = false;}}private ThreeNode root;public WordDictionary() {root = new ThreeNode();root.wordEnd = false;}public void addWord(String word) {ThreeNode temp = root;// 取到根節點if (!word.isEmpty()){for (int i = 0; i < word.length(); i++) {if (!temp.childdren.containsKey( word.charAt(i)))temp.childdren.put(word.charAt(i),new ThreeNode());temp = temp.childdren.get(word.charAt(i));//這一步很重要,表示繼續從下一個節點開始遍歷}//遍歷完將這個節點單詞置為結束temp.wordEnd =true;}}public boolean search(String word) {ThreeNode temp = root;// 取到根節點boolean isFound =true;if (!word.isEmpty()){for (int i = 0; i < word.length(); i++) {//注意到題目中有個要求就是點號可以代替任何字母if (!temp.childdren.containsKey( word.charAt(i)) && !(word.charAt(i) =='.'))//搜索和插入很類似// temp.childdren.put(word.charAt(i),new ThreeNode());return false;temp = temp.childdren.get(word.charAt(i));//這一步很重要,表示繼續從下一個節點開始遍歷}}//解釋一下 題目是說的輸入的字符串完全匹配。所以必須要跟上字符串是否結束。如果是包含則直接返回了return isFound && temp.wordEnd;} }

總結

以上是生活随笔為你收集整理的算法【一】树的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。