目錄
??1.什么是二叉樹?
??2.滿二叉樹和完全二叉樹
??2.二叉樹的性質(zhì)
??3.二叉樹的創(chuàng)建與遍歷
3.1 創(chuàng)建二叉樹
3.2 前中后序遍歷——遞歸和非遞歸
??4.二叉樹的實現(xiàn)
1??獲取樹中節(jié)點的個數(shù)
2??獲取葉子節(jié)點的個數(shù)
3??獲取第K層節(jié)點的個數(shù)
4??獲取二叉樹的高度
5??檢測值為value的元素是否存在
6??判斷兩棵樹是否相同【leetcod】
7??另一棵樹的子樹【leetcod】
8??翻轉(zhuǎn)二叉樹【leetcod】
??平衡二叉樹【leetcod】
?二叉樹的層序遍歷
二叉樹的分層遍歷【leetcod】
??5.二叉樹的練習(xí)
1??二叉樹遍歷【??途W(wǎng)】
2??二叉樹的最近公共祖先【leetcod】
3??從前序與中序遍歷序列構(gòu)造二叉樹【leetcod】
4??從中序與后序遍歷序列構(gòu)造二叉樹【leetcod】
5??根據(jù)二叉樹創(chuàng)建字符串【leetcod】
6??判斷一棵樹是不是完全二叉樹
?文章來源地址http://www.zghlxwxcb.cn/news/detail-447140.html
??1.什么是二叉樹?
結(jié)點的度:一個結(jié)點含有子樹的個數(shù)稱為該結(jié)點的度; 如上圖:A的度為2
樹的度:一棵樹中,所有結(jié)點度的最大值稱為樹的度; 如上圖:樹的度為2
葉子結(jié)點或終端結(jié)點:度為0的結(jié)點稱為葉結(jié)點; 如上圖:H、I、J、K、G等節(jié)點為葉結(jié)點
雙親結(jié)點或父結(jié)點:若一個結(jié)點含有子結(jié)點,則這個結(jié)點稱為其子結(jié)點的父結(jié)點; 如上圖:A是B的父結(jié)點
孩子結(jié)點或子結(jié)點:一個結(jié)點含有的子樹的根結(jié)點稱為該結(jié)點的子結(jié)點; 如上圖:B是A的孩子結(jié)點
根結(jié)點:一棵樹中,沒有雙親結(jié)點的結(jié)點;如上圖:A
結(jié)點的層次:從根開始定義起,根為第1層,根的子結(jié)點為第2層,以此類推
樹的高度或深度:樹中結(jié)點的最大層次; 如上圖:樹的高度為4
??一棵二叉樹是結(jié)點的一個有限集合,該集合:
1??或者為空 2??或者是由一個根節(jié)點加上兩棵別稱為左子樹和右子樹的二叉樹組成。
?對于二叉樹來說:每一個節(jié)點的度不能大于2,并且二叉樹是一個有序樹
??2.滿二叉樹和完全二叉樹
滿二叉樹: 一棵二叉樹,如果每層的結(jié)點數(shù)都達到最大值,則這棵二叉樹就是滿二叉樹。也就是說,如果一棵二叉樹的層數(shù)為K,且結(jié)點總數(shù)是 2^k - 1,第k層節(jié)點數(shù)是 2^(k-1) ,則它就是滿二叉樹。如果一共有n個節(jié)點,那么k=log2(n+1)
完全二叉樹(最小深度): 完全二叉樹是效率很高的數(shù)據(jù)結(jié)構(gòu),完全二叉樹是由滿二叉樹而引出來的。對于深度為K的,有n個結(jié)點的二叉樹,當且僅當其每一個結(jié)點都與深度為K的滿二叉樹中編號從0至n-1的結(jié)點一一對應(yīng)時稱之為完全二叉樹。 要注意的是滿二叉樹是一種特殊的完全二叉樹?!?strong>從左往右不可以空結(jié)點
??2.二叉樹的性質(zhì)
1??一顆 N 節(jié)點的樹有 N - 1 條邊
2??若規(guī)定根結(jié)點的層數(shù)為1,則一棵非空二叉樹的第i層上最多有 2^i - 1(i>0)個結(jié)點
3??若規(guī)定只有根結(jié)點的二叉樹的深度為1,則深度為K的二叉樹的最大結(jié)點數(shù)是? 2^k - 1?(k>=0)
4??具有n個結(jié)點的完全二叉樹的深度k為 log2(n+1)?上取整
5??對任何一棵二叉樹, 如果其葉結(jié)點個數(shù)為 n0, 度為2的非葉結(jié)點個數(shù)為 n2,則有n0=n2+1 (度為0的節(jié)點是度為2的節(jié)點多1個)
?6??偶數(shù)個結(jié)點的完全二叉樹:度為1的結(jié)點個數(shù)為1;奇數(shù)個結(jié)點的完全二叉樹:度為1的結(jié)點個數(shù)為0;
7??對于具有n個結(jié)點的完全二叉樹,如果按照從上至下從左至右的順序?qū)λ泄?jié)點從0開始編號,則對于序號為i的結(jié)點有:
若i>0, 雙親序號: (i-1)/2; i=0 , i 為根結(jié)點編號,無雙親結(jié)點若 2i+1<n ,左孩子序號: 2i+1 ,否則無左孩子若 2i+2<n ,右孩子序號: 2i+2 ,否則無右孩子
已知父親結(jié)點下標是i:
左孩子:2*i+1? ?右孩子:2*i+2
已知孩子結(jié)點下標是i:
父親結(jié)點下標:(i-1)/2
??3.二叉樹的創(chuàng)建與遍歷
3.1 創(chuàng)建二叉樹
?對于一個二叉樹,我們定義一個數(shù)據(jù)域、左孩子域、右孩子域、根結(jié)點。對于一個二叉樹,如圖所示,總會有一個左結(jié)點和右結(jié)點(可以為null)
//創(chuàng)建二叉樹
public class TreeNode {
public char val;//數(shù)據(jù)域
public TreeNode left;//左孩子的引用
public TreeNode right;//右孩子的引用
//構(gòu)造函數(shù)
public TreeNode(char val) {
this.val = val;
}
}
public TreeNode root;//二叉樹的根結(jié)點
public TreeNode createTree() {
TreeNode A = new TreeNode('A');
TreeNode B = new TreeNode('B');
TreeNode C = new TreeNode('C');
TreeNode D = new TreeNode('D');
TreeNode E = new TreeNode('E');
TreeNode F = new TreeNode('F');
TreeNode G = new TreeNode('G');
TreeNode H = new TreeNode('H');
A.left = B;
A.right = C;
B.left = D;
B.right = E;
C.left = F;
C.right = G;
E.right = H;
this.root = A;
return root;
}
3.2 前中后序遍歷——遞歸和非遞歸
遍歷:歷(Traversal)是指沿著某條搜索路線,依次對樹中每個結(jié)點均做一次且僅做一次訪問。訪問結(jié)點所做的操作依賴于具體的應(yīng)用問題(比如:打印節(jié)點內(nèi)容、節(jié)點內(nèi)容加1)。
1??前序遍歷(Preorder Traversal 亦稱先序遍歷)——訪問根結(jié)點--->根的左子樹--->根的右子樹
?注意:每棵樹都有根、左、右結(jié)點。并且這里邊每個結(jié)點都可以作為孩子結(jié)點的根結(jié)點
//前序遍歷 遞歸
public void preOrder(TreeNode root) {
if(root == null) {
return;
}
System.out.println(root.val + " ");//先打印再遞歸
preOrder(root.left);
preOrder(root.right);
}
非遞歸實現(xiàn)二叉樹的前序遍歷:使用棧完成——定義一個cur = root,把根節(jié)點放入棧中,同時進行打印,把根左邊放入棧中,再進行打印,繼續(xù)往左邊走放入棧中,再進行打??;再往左邊走,如果為null,則從棧中彈出這個元素給top;如果這個元素右邊為null,則再彈出一個元素給top,循環(huán)以上過程
/非遞歸:
public void preOrderNor(TreeNode root) {
if(root == null) {
return;
}
TreeNode cur = root;
Deque<TreeNode> stack = new ArrayDeque<>();
while (cur != null || stack.isEmpty()) {
while (cur != null) {
stack.push(cur);
System.out.print(cur.val + " ");
cur = cur.left;
}
TreeNode top = stack.pop();
cur = top.right;
}
System.out.println();
}
2??中序遍歷(Inorder Traversal)——根的左子樹--->根節(jié)點--->根的右子樹。
?文章來源:http://www.zghlxwxcb.cn/news/detail-447140.html
//中序遍歷
public void inOrder(TreeNode root) {
if(root == null) {
return;
}
inOrder(root.left);
System.out.println(root.val + " ");
inOrder(root.right);
}
//中序遍歷非遞歸
public void inOrderNor(TreeNode root) {
if(root == null) {
return;
}
TreeNode cur = root;
Deque<TreeNode> stack = new ArrayDeque<>();
while (cur != null || !stack.isEmpty()) {
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
TreeNode top = stack.pop();
System.out.print(top.val + " ");
cur = top.right;
}
System.out.println();
}
3??后序遍歷(Postorder Traversal)——根的左子樹--->根的右子樹--->根節(jié)點
//后序遍歷
public void postOrder(TreeNode root) {
if(root == null) {
return;
}
postOrder(root.left);
postOrder(root.right);
System.out.println(root.val + " ");
}
//后序遍歷非遞歸
public void postOrderNor(TreeNode root) {
if(root == null) {
return;
}
TreeNode cur = root;
TreeNode prev = null;
Deque<TreeNode> stack = new ArrayDeque<>();
while (cur != null || !stack.isEmpty()) {
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
TreeNode top = stack.peek();
if(top.right == null || top.right == prev) {
System.out.print(top.val+" ");
stack.pop();
prev = top;
}else {
cur = top.right;
}
}
System.out.println();
}
??4.二叉樹的實現(xiàn)
1??獲取樹中節(jié)點的個數(shù)
??左樹的結(jié)點 + 右樹的結(jié)點 + 1? ? ? ? ? ? ?遍歷思路:遇到結(jié)點就+1
//子問題思路:
public int size(TreeNode root) {
if(root == null) {
return 0;
}
int leftSize = size(root.left);
int rightSize = size(root.right);
return leftSize + rightSize + 1;
}
//遍歷思路:
public int nodeSize;//靜態(tài)成員變量
public void size2(TreeNode root) {
if(root == null) {
return ;
}
nodeSize++;
size2(root.left);
size2(root.right);
}
2??獲取葉子節(jié)點的個數(shù)
??左樹的葉子結(jié)點 + 右樹的結(jié)點? ? ? ?
// 獲取葉子節(jié)點的個數(shù)——子問題思路
int getLeafNodeCount(TreeNode root) {
if(root == null) {
return 0;
}
if(root.left == null && root.right == null){
return 1;
}
int leftSize = getLeafNodeCount(root.left);
int rightSize = getLeafNodeCount(root.right);
return leftSize+rightSize;
}
// 獲取葉子節(jié)點的個數(shù)——遍歷思路
public int leafSize;
void getLeafNodeCount2(TreeNode root) {
if(root == null) {
return ;
}
if(root.left == null && root.right == null){
leafSize++;
}
getLeafNodeCount2(root.left);
getLeafNodeCount2(root.right);
}
3??獲取第K層節(jié)點的個數(shù)
??左樹的第k-1層? +? 右樹的第k-1層(對于root來說是第k層)
// 獲取第K層節(jié)點的個數(shù)
int getKLevelNodeCount(TreeNode root,int k) {
if(root == null) {
return 0;
}
if(k == 1) {
return 1;
}
int leftSize = getKLevelNodeCount(root.left,k-1);
int rightSize = getKLevelNodeCount(root.right,k-1);
return leftSize+rightSize;
}
4??獲取二叉樹的高度
??左樹的高度和右樹的高度的最大值+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);
}
5??檢測值為value的元素是否存在
??遍歷二叉樹,是否存在value的元素——前序
// 檢測值為value的元素是否存在
TreeNode find(TreeNode root, int val) {
if(root == null) {
return null;
}
if(root.val == val) {
return root;
}
TreeNode leftTree = find(root.left,val);//左子樹第一個元素為新的root,開始尋找value
//如果沒有找到,繼續(xù)遞歸;如果找到返回上一層直到返回這個值;如果左子樹沒有找到返回空
if(leftTree != null) {
return leftTree;
}
TreeNode rightTree = find(root.right,val);
if(rightTree != null) {
return rightTree;
}
return null;//沒有找到
}
6??判斷兩棵樹是否相同【leetcod】
??leetcod題目:給你兩棵二叉樹的根節(jié)點 p 和 q ,編寫一個函數(shù)來檢驗這兩棵樹是否相同。如果兩個樹在結(jié)構(gòu)上相同,并且節(jié)點具有相同的值,則認為它們是相同的。
??題目鏈接:相同的樹
??做題思路:判斷根,左子樹、右子樹是否相同——1.判斷結(jié)構(gòu)是否相同? ? ?2.判斷val是否相同
/**
* 時間復(fù)雜度:O(min(m,n)),其中 m 和 n 分別是兩個二叉樹的節(jié)點數(shù)。
* 兩個二叉樹同時進行深度優(yōu)先搜索,只有當兩個二叉樹中的對應(yīng)節(jié)點都不為空時才會訪問到該節(jié)點,因此被訪問到的節(jié)點數(shù)不會超過較小的二叉樹節(jié)點數(shù)
* 空間復(fù)雜度:O(min(m,n)),其中 m 和 n 分別是兩個二叉樹的節(jié)點數(shù)。
* 空間復(fù)雜度取決于遞歸調(diào)用的層數(shù),遞歸調(diào)用的層數(shù)不會超過較小的二叉樹的最大高度,最壞情況下,二叉樹的高度等于節(jié)點數(shù)。
* @param p
* @param q
* @return
*/
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;
}
//p q都不空 且 值一樣
return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
}
7??另一棵樹的子樹【leetcod】
??leetcod題目:給你兩棵二叉樹?root
?和?subRoot
?。檢驗?root
?中是否包含和?subRoot
?具有相同結(jié)構(gòu)和節(jié)點值的子樹。如果存在,返回?true
?;否則,返回?false
?。二叉樹?tree
?的一棵子樹包括?tree
?的某個節(jié)點和這個節(jié)點的所有后代節(jié)點。tree
?也可以看做它自身的一棵子樹。
??題目鏈接:另一棵樹的子樹
??做題思路:1.是不是相同的樹? ? 2.是不是root的左子樹? 3.是不是root的右子樹
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(root == null || subRoot == null) {
return false;
}
if(isSameTree(root,subRoot)) return true;
if(isSubtree(root.left,subRoot)) return true;
if(isSubtree(root.right,subRoot)) return true;
return false;
}
//判斷相同的樹
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);
}
8??翻轉(zhuǎn)二叉樹【leetcod】
??leetcod題目:給你一棵二叉樹的根節(jié)點?root
?,翻轉(zhuǎn)這棵二叉樹,并返回其根節(jié)點。
??題目鏈接:翻轉(zhuǎn)二叉樹
??做題思路:
public TreeNode invertTree(TreeNode root) {
if(root == null) {
return null;
}
//交換左右結(jié)點
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
invertTree(root.left);//左樹遞歸
invertTree(root.right);//右樹遞歸
return root;
}
9??平衡二叉樹【leetcod】
??leetcod題目:給定一個二叉樹,判斷它是否是高度平衡的二叉樹。本題中,一棵高度平衡二叉樹定義為:一個二叉樹每個節(jié)點 的左右兩個子樹的高度差的絕對值不超過 1 。
??題目鏈接:平衡二叉樹
??做題思路:1.求整棵樹的高度? ? 2.左樹高度和右樹高度的絕對值之差小于2
/**
* 時間復(fù)雜度:O(N^2)——每個結(jié)點都需要求高度
* @param root
* @return
*/
public boolean isBalanced(TreeNode root) {
if(root == null) {
return true;
}
//求左樹和右樹的高度
int leftH = getHeight(root.left);
int rightH = getHeight(root.right);
//左樹和右樹絕對值之差小于2
return Math.abs(leftH-rightH) < 2 && isBalanced(root.left) &&isBalanced(root.right);
}
// 獲取二叉樹的高度
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);
}
/**
* 時間復(fù)雜度:O(N)——求高度的過程中就判斷是否平衡
* @param root
* @return
*/
public boolean isBalanced2(TreeNode root) {
return maxDepth2(root) >= 0;
}
public int maxDepth2(TreeNode root) {
if(root == null) {
return 0;
}
int leftHeight = maxDepth2(root.left);
if(leftHeight < 0) return -1;
int rightHeight = maxDepth2(root.right);
if(rightHeight < 0) return -1;
if(Math.abs(leftHeight-rightHeight) <= 1) {
return Math.max(leftHeight,rightHeight)+1;
}else {
return -1;
}
}
??平衡二叉樹【leetcod】
??leetcod題目:給你一個二叉樹的根節(jié)點?root
?, 檢查它是否軸對稱。
??題目鏈接:平衡二叉樹
??做題思路:判斷整棵樹是不是抽對稱的——判斷左子樹和右子樹是不是對稱的——左子樹的左樹和右子樹的右樹、左子樹的右樹和右子樹的左樹
public boolean isSymmetric(TreeNode root) {
if(root == null) {
return true;
}
return isSymmetricChild(root.left,root.right);
}
public boolean isSymmetricChild(TreeNode leftTree,TreeNode rightTree) {
if(leftTree != null && rightTree == null || leftTree == null && rightTree != null) {
return false;
}
if(leftTree == null && rightTree == null) {
return true;
}
if(leftTree.val != rightTree.val) {
return false;
}
return isSymmetricChild(leftTree.left,rightTree.right) && isSymmetricChild(leftTree.right,rightTree.left);
}
?二叉樹的層序遍歷
??做題思路:使用隊列進行遍歷:定義一個cur——從根結(jié)點出發(fā),放入隊列中,判斷隊列是否為空,不為空彈出隊列的第一個元素,放入cur然后打??;然后把cur的左邊和右邊放入隊列中,判斷隊列是否為空,不為空再彈出隊列的隊頭(每次彈出1個元素),再放入cur中打印。一直循環(huán)直到遍歷完成結(jié)束
//二叉樹的層序遍歷
public void levelOrder(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);
}
}
}
二叉樹的分層遍歷【leetcod】
??leetcod題目:給你二叉樹的根節(jié)點?root
?,返回其節(jié)點值的?層序遍歷?。 (即逐層地,從左到右訪問所有節(jié)點)
??題目鏈接:二叉樹的分層遍歷
??做題思路:每一層都是一個集合——得確定哪一層有哪些結(jié)點:如果隊列不等于空,求當前隊列的長度,這次彈出元素為當前隊列長度的元素
public List<List<Integer>> levelOrder2(TreeNode root) {
List<List<Integer>> list = new ArrayList<>();
if(root == null) {
return list;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
List<Integer> tmp = new ArrayList<>();
while (size != 0) {
TreeNode cur = queue.poll();
//System.out.print(cur.val + " ");
tmp.add(cur.val);
size--;
if (cur.left != null) {
queue.offer(cur.left);
}
if (cur.right != null) {
queue.offer(cur.right);
}
}
list.add(tmp);
}
return list;
}
??5.二叉樹的練習(xí)
1??二叉樹遍歷【??途W(wǎng)】
????途W(wǎng)題目:編一個程序,讀入用戶輸入的一串先序遍歷字符串,根據(jù)此字符串建立一個二叉樹(以指針方式存儲)。 例如如下的先序遍歷字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空樹。建立起此二叉樹以后,再對二叉樹進行中序遍歷,輸出遍歷結(jié)果。
輸入描述:輸入包括1行字符串,長度不超過100。
輸出描述:可能有多組測試數(shù)據(jù),對于每組數(shù)據(jù), 輸出將輸入字符串建立二叉樹后中序遍歷的序列,每個字符后面都有一個空格。 每個輸出結(jié)果占一行
輸入:abc##de#g##f###? ? ? ? ? 輸出:c b e g d f a
??題目鏈接:二叉樹遍歷
???做題思路:
class TreeNode {
public char val;//數(shù)據(jù)域
public TreeNode left;//左孩子的引用
public TreeNode right;//右孩子的引用
//構(gòu)造函數(shù)
public TreeNode(char val) {
this.val = val;
}
}
// 注意類名必須為 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的區(qū)別
while(in.hasNextLine()) {
String str = in.nextLine();//讀字符串放入str中
TreeNode root = createTree(str);
inorder(root);
}
}
public static int i = 0;
//讀到字符串,緊接著來創(chuàng)造二叉樹
public static TreeNode createTree(String str) {
TreeNode root = null;
if(str.charAt(i) != '#') {//查看i下標的值是字符還是#
root = new TreeNode(str.charAt(i));//創(chuàng)建一個節(jié)點為根
i++;
root.left = createTree(str);
root.right = createTree(str);
}else {
i++;//如果為#,則繼續(xù)往后走一格
}
return root;
//1.如果為#,則繼續(xù)往后走一格,返回上一層遞歸返回root
//2.如果根的左邊和右邊都為空,則返回當前根給上一層
}
//中序遍歷
public static void inorder(TreeNode root) {
if(root == null) {
return;
}
inorder(root.left);
System.out.print(root.val + " ");
inorder(root.right);
}
}
2??二叉樹的最近公共祖先【leetcod】
??leetcod題目:給定一個二叉樹, 找到該樹中兩個指定節(jié)點的最近公共祖先。最近公共祖先的定義為:“對于有根樹 T 的兩個節(jié)點 p、q,最近公共祖先表示為一個節(jié)點 x,滿足 x 是 p、q 的祖先且 x 的深度盡可能大(一個節(jié)點也可以是它自己的祖先)。”
??題目鏈接:二叉樹的最近公共祖先
???做題思路:1.p、q要么在跟的左右兩邊、2.要么是根的左邊 或者 右邊、3.其中一個節(jié)點是公共祖先
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) {
return null;
}
//判斷根結(jié)點是不是其中一個公共結(jié)點
if(root == p || root == q) {
return root;
}
TreeNode leftRet = lowestCommonAncestor(root.left,p,q);//去root的左邊找p、q
TreeNode rightRet = lowestCommonAncestor(root.right,p,q);//去root的右邊找p、q
if(leftRet != null && rightRet != null) {//root的左邊和右邊不為空即返回root——1.root為根 2.root為左邊根
return root;
}else if(leftRet != null) {
return leftRet;
}else if(rightRet != null) {
return rightRet;
}
return null;
}
第二種 ——??做題思路:如果每個結(jié)點有了父親結(jié)點的地址,那么這個題就變成了求相交結(jié)點——此時我們可以使用棧
從根結(jié)點到指定的p、q路徑上的所有結(jié)點放入不同的兩個棧中,求棧中元素的個數(shù)差,哪個棧多幾個元素,就讓哪個棧中彈出個數(shù)差的元素;之后讓每個棧中都彈出一個元素比較兩個元素是否相同,如果相同則為公共結(jié)點
???難點: 如何把從根節(jié)點到指定節(jié)點,路徑上的所有節(jié)點找到,放入棧里邊?怎么知道這個節(jié)點要彈出去?
??:從根節(jié)點開始放入第一個棧中,從根節(jié)點的左邊開始,每個元素放入棧中,直到某個元素的左邊和右邊為null,彈出這個元素,然后從這個元素的右邊去找,找不到則彈出此元素,直到找到為止
/**
* 找到從根節(jié)點到指定節(jié)點node路徑上的所有的節(jié)點,存放到棧當中
* @param root
* @param node
* @param stack
* @return
*/
//找p、q節(jié)點
public boolean getPath(TreeNode root, TreeNode node, Deque<TreeNode> stack) {
if(root == null || node == null) {
return false;
}
stack.push(root);
//放完之后 要檢查
if(root == node) {
return true;
}
boolean ret1 = getPath(root.left,node,stack);
if(ret1) {
return true;
}
boolean ret2 = getPath(root.right,node,stack);
if(ret2) {
return true;
}
stack.pop();//如果沒有找到,彈出這個元素,返回上一層
return false;
}
public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) {
//1、兩個棧當中 存儲好數(shù)據(jù)
Deque<TreeNode> stack1 = new LinkedList<>();//第一個棧
getPath(root,p,stack1);
Deque<TreeNode> stack2 = new LinkedList<>();//第二個棧
getPath(root,q,stack2);
//2.判斷棧的大小
int size1 = stack1.size();
int size2 = stack2.size();
if(size1 > size2) {
int size = size1 - size2;
while(size != 0) {
stack1.pop();//彈出元素,直到size為0;
size--;
}
}else {
int size = size2 - size1;
while(size != 0) {
stack2.pop();//彈出元素,直到size為0;
size--;
}
}
//此時棧里面數(shù)據(jù)的個數(shù) 是一樣的
while(!stack1.isEmpty() && !stack2.isEmpty()) {
if(stack1.peek() != stack2.peek()) {//查看棧頂元素是否相同
//不同彈出棧頂元素
stack1.pop();
stack2.pop();
}else {
return stack1.peek();//相同返回這個元素,即為公共節(jié)點
}
}
return null;
}
3??從前序與中序遍歷序列構(gòu)造二叉樹【leetcod】
??leetcod題目:給定兩個整數(shù)數(shù)組?preorder
?和?inorder
?,其中?preorder
?是二叉樹的先序遍歷,?inorder
?是同一棵樹的中序遍歷,請構(gòu)造二叉樹并返回其根節(jié)點。
??題目鏈接:從前序與中序遍歷序列構(gòu)造二叉樹
???做題思路:1.根據(jù)前序遍歷找到根? ? ? ??2.在中序遍歷當中,找到根的位置
public int i = 0;
public TreeNode buildTree(int[] preorder, int[] inorder) {
return buildTreeChild(preorder,inorder,0,inorder.length-1);
}
public TreeNode buildTreeChild(int[] preorder,int[] inorder,int inbegin,int inend) {
if(inbegin > inend) {
return null;//沒有子樹了
}
TreeNode root = new TreeNode(preorder[i]);
//找到當前根,在中序遍歷的位置
int rootIndex = findIndex(inorder,inbegin,inend,preorder[i]);//在inbegin和inend尋找preorder[i]
//找打根之后,左邊就是左樹,右邊就是右樹
i++;
//此時的根左邊——開始位置沒有變,結(jié)束位置為找到根位置-1,即rootIndex-1
root.left = buildTreeChild(preorder,inorder,inbegin,rootIndex-1);
//此時的根右邊——開始位置為rootIndex+1,結(jié)束位置沒有變
root.right = buildTreeChild(preorder,inorder,rootIndex+1,inend);
return root;
}
//在中序遍歷當中尋找這個元素
private int findIndex(int[] inorder,int inbegin, int inend, int key) {
for(int i = inbegin; i <= inend; i++) {
if(inorder[i] == key) {
return i;
}
}
return -1;
}
4??從中序與后序遍歷序列構(gòu)造二叉樹【leetcod】
??leetcod題目:給定兩個整數(shù)數(shù)組?inorder
?和?postorder
?,其中?inorder
?是二叉樹的中序遍歷,?postorder
?是同一棵樹的后序遍歷,請你構(gòu)造并返回這顆?二叉樹?。
??題目鏈接:從中序與后序遍歷序列構(gòu)造二叉樹
???做題思路:此題做題思路和上一題大致一樣,但是有一點小問題,就是從后序遍歷的結(jié)尾開始往前走,先創(chuàng)建右樹,再創(chuàng)建左樹
前序遍歷:根、左、右? ? ? ? ? ? ?后序遍歷:左、右、根
public int i = 0;
public TreeNode buildTree1(int[] inorder, int[] postorder) {
i = postorder.length-1;//從最后開始
return buildTreeChild2(postorder,inorder,0,inorder.length-1);
}
public TreeNode buildTreeChild2(int[] postorder, int[] inorder,
int inbegin,int inend) {
if(inbegin > inend) {
return null;//沒有子樹了
}
TreeNode root = new TreeNode(postorder[i]);
//找到當前根,在后序遍歷的位置
int rootIndex = findIndex1(inorder,inbegin,inend,postorder[i]);
//找打根之后,左邊就是左樹,右邊就是右樹
i--;
//此時的根右邊——開始位置為rootIndex+1,結(jié)束位置沒有變
root.right = buildTreeChild2(postorder,inorder,rootIndex+1,inend);
//此時的根左邊——開始位置沒有變,結(jié)束位置為找到根位置-1,即rootIndex-1
root.left = buildTreeChild2(postorder,inorder,inbegin,rootIndex-1);
return root;
}
//在后序遍歷當中尋找這個元素
private int findIndex1( int[] inorder,int inbegin,int inend, int key) {
for(int i = inbegin;i <= inend; i++) {
if(inorder[i] == key) {
return i;
}
}
return -1;
}
5??根據(jù)二叉樹創(chuàng)建字符串【leetcod】
??leetcod題目:給你二叉樹的根節(jié)點?root
?,請你采用前序遍歷的方式,將二叉樹轉(zhuǎn)化為一個由括號和整數(shù)組成的字符串,返回構(gòu)造出的字符串。
空節(jié)點使用一對空括號對?"()"
?表示,轉(zhuǎn)化后需要省略所有不影響字符串與原始二叉樹之間的一對一映射關(guān)系的空括號對。
輸入:root = [1,2,3,4] 輸出:"1(2(4))(3)" 解釋:初步轉(zhuǎn)化后得到 "1(2(4)())(3()())" ,但省略所有不必要的空括號對后,字符串應(yīng)該是"1(2(4))(3)"
??題目鏈接:根據(jù)二叉樹創(chuàng)建字符串
???做題思路:
public String tree2str(TreeNode root) {
if(root == null) {
return null;
}
StringBuilder stringBuilder = new StringBuilder();
tree2strChilde(root,stringBuilder);
return stringBuilder.toString();
}
public void tree2strChilde(TreeNode t,StringBuilder stringBuilder) {
if (t == null) {
return;
}
stringBuilder.append(t.val);
if (t.left != null) {
stringBuilder.append("(");
tree2strChilde(t.left, stringBuilder);
stringBuilder.append(")");
} else {
//左邊為空了
if (t.right != null) {
//右邊不為空
stringBuilder.append("()");
} else {
//右邊為空
return;
}
}
if (t.right == null) {
return;
} else {
stringBuilder.append("(");
tree2strChilde(t.right, stringBuilder);
stringBuilder.append(")");
}
}
6??判斷一棵樹是不是完全二叉樹
??做題思路:使用隊列完成——把根放入隊列當中,如果隊列不為空,彈出一個元素給cur,不管左邊、右邊是否為空,把左子樹和右子樹都放入隊列中(如果為空,放入null),如果隊列不為空,繼續(xù)彈出一個元素給cur,循環(huán),直到cur里邊放入null。這是隊列中如果不為空,則不是二叉樹,如果為空,則為二叉樹
?
boolean isCompleteTree(TreeNode root){
if(root == null) {
return true;
}
//創(chuàng)建隊列
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode cur = queue.poll();
if(cur != null) {
queue.offer(cur.left);
queue.offer(cur.right);
}else {
break;
}
}
while (!queue.isEmpty()) {
TreeNode tmp = queue.poll();
if(tmp != null) {
return false;
}
}
return true;
}
?
?
?
到了這里,關(guān)于二叉樹【數(shù)據(jù)結(jié)構(gòu)】【超詳細,一學(xué)就會】的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!