整理五道演算法題系列(1)二叉樹相關
阿新 • • 發佈:2019-01-08
背景
最近想整理一些有意思的題目,特別是給力的破題技巧與思想,均為學習筆記,於此做個長期記錄。不多說,進入演算法世界了~~
說明
=> 五道 / 篇
=> Java
=> 二叉樹相關
題目
1、重建二叉樹
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。
思路:首先得了解先序、中序的特點,接著從先序定位根,然後再去中序找出左右子樹,依舊是遞迴實現。
public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
return reConstructBinaryTree(pre, 0, pre.length - 1,
in, 0, in.length - 1);
}
/**
* @param pre 前序
* @param startPre 前序首位
* @param endPre 前序末位
* @param in 中序
* @param startIn 中序首位
* @param endIn 中序末位
* @return
*/
public TreeNode reConstructBinaryTree(int[] pre, int startPre, int endPre,
int[] in, int startIn, int endIn) {
// constraint
if (startPre > endPre || startIn > endIn) {
return null;
}
// 先序第一位即為根
TreeNode root = new TreeNode(pre[startPre]);
for (int i = startIn; i <= endIn; i++) {
// 中序找尋根所在位置
if (in[i] == pre[startPre]) {
/*
重點解析
1. startPre + i - startIn : 根據中序所找到的根,從而得知下一次遞迴的左子樹,接著找到
前序的左子樹部分,再次根據前序“根左右”的特性傳入startPre 和 endPre,而由於startPre每次都在向
右,因此endPre應該根據startPre來決定
2. (startPre + i - startIn) + 1 :根據中序所找到的根,從而得知下一次遞迴的右子樹,而上一步已
經在前序序列中分隔出根、左子樹、右子樹,那基本可以確定傳入的陣列下標
*/
root.left = reConstructBinaryTree(pre, startPre + 1, startPre + i - startIn,
in, startIn, i - 1);
root.right = reConstructBinaryTree(pre, (startPre + i - startIn) + 1, endPre,
in, i + 1, endIn);
}
}
return root;
}
private class TreeNode {
int val;
TreeNode left;
TreeNode right;
public TreeNode(int val) {
this.val = val;
}
}
2、樹的子結構
輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)
思路:土方法,一個個比較,但是遞迴比較,以B樹為參考系,從根開始匹配。
public boolean HasSubtree(TreeNode root1, TreeNode root2) {
boolean result = false;
if (root2 != null && root1 != null) {
// 如果找到了對應Tree2的根節點的點
if (root1.val == root2.val) {
result = doesTree1HaveTree2(root1, root2);
}
// 如果找不到,那麼就再將root的左兒子當作起點,去判斷時包含Tree2
if (!result) {
result = doesTree1HaveTree2(root1.left, root2);
}
// 如果還找不到,那麼就再將root的右兒子當作起點,去判斷時候包含Tree2
if (!result) {
result = HasSubtree(root1.right, root2);
}
}
return result;
}
public boolean doesTree1HaveTree2(TreeNode node1, TreeNode node2) {
/*
node1 和 node2 的為空判斷務必得記住,一開始挺容易漏掉這個約束的!
*/
// 如果Tree2已經遍歷完了都能對應得上,返回true
if (node2 == null) {
return true;
}
// 如果Tree2還沒遍歷完,Tree1卻遍歷完了。返回false
if (node1 == null) {
return false;
}
// 如果其中有一個點沒有對應上,返回false
if (node1.val != node2.val) {
return false;
}
// 如果根節點對應的上,那麼就分別去子節點裡面匹配
return doesTree1HaveTree2(node1.left, node2.left) && doesTree1HaveTree2(node1.right, node2.right);
}
private class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
3、映象二叉樹
操作給定的二叉樹,將其變換為源二叉樹的映象。
輸入描述:
思路:遞迴交換左右節點
public void Mirror(TreeNode root) {
TreeNode tmp;
if (root != null) {
tmp = root.left;
root.left = root.right;
root.right = tmp;
if (root.left != null) {
Mirror(root.left);
}
if (root.right != null) {
Mirror(root.right);
}
}
}
private class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
4、列印二叉樹
從上往下打印出二叉樹的每個節點,同層節點從左至右列印。
思路:先序的遍歷列印方式,巧妙運用Queue實現,利用其“先進先出”的特點。
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> list = new ArrayList<>();
if (root == null) {
return list;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
/*
while迴圈主要針對的是queue是否為空,而queue實際儲存的是每層的根節點,因此便可每層按順序存進list
*/
while (!queue.isEmpty()) {
TreeNode treeNode = queue.poll();
if (treeNode.left != null) {
queue.offer(treeNode.left);
}
if (treeNode.right != null) {
queue.offer(treeNode.right);
}
list.add(treeNode.val);
}
return list;
}
private class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
5、二叉搜尋樹的後序遍歷序列
輸入一個整數陣列,判斷該陣列是不是某二叉搜尋樹的後序遍歷的結果。如果是則輸出Yes,否則輸出No。假設輸入的陣列的任意兩個數字都互不相同。
思路:首先得了解二叉搜尋樹(BST),接著遞迴判斷是否滿足BST的條件。
public boolean VerifySquenceOfBST(int[] sequence) {
if (sequence.length == 0) {
return false;
}
if (sequence.length == 1) {
return true;
}
return judge(sequence, 0, sequence.length - 1);
}
public boolean judge(int[] a, int start, int end) {
if (start > end) {
return true;
}
int i = start;
/*
找尋左右子樹的分界點
*/
while (a[i] < a[end]) {
++i;
}
for (int j = i; j < end; j++) {
if (a[j] < a[end]) {
return false;
}
}
/*
遞迴,分出左右子樹再次遞迴判斷
*/
return judge(a, start, i - 1) && judge(a, i, end - 1);
}