面試題7:重建二叉樹
題目:
輸入某二叉樹的前序遍歷和中序遍歷的結(jié)果。請(qǐng)重建該二叉樹暂题。假設(shè)輸入的前序遍歷和中序遍歷的結(jié)果中都不含重復(fù)的數(shù)字承边。例如,輸入的前序遍歷序列{1,2梳猪,4,7蒸痹,3春弥,5,6叠荠,8}和中序遍歷序列{4匿沛,7,2榛鼎,1逃呼,5鳖孤,3,8抡笼,6}苏揣,則重建如圖所示的二叉樹并輸出它的頭節(jié)點(diǎn)。
1
/ \
2 3
/ / \
4 5 6
\ /
7 8
思路:
前序遍歷的第一個(gè)數(shù)字就是根節(jié)點(diǎn)的值推姻,掃描中序遍歷序列平匈,就能確定根節(jié)點(diǎn)的值。根據(jù)終須遍歷的特點(diǎn)藏古,在根節(jié)點(diǎn)的值前面的數(shù)字都是左子樹的值增炭,位于根節(jié)點(diǎn)后面的值都是右子樹的節(jié)點(diǎn)的值。
代碼實(shí)現(xiàn):
/**
* Definition of TreeNode:
* public class TreeNode {
* public int val;
* public TreeNode left, right;
* public TreeNode(int val) {
* this.val = val;
* this.left = this.right = null;
* }
* }
*/
/******
* 前序遍歷的第一個(gè)數(shù)便是根節(jié)點(diǎn)拧晕,在中序遍歷的數(shù)組中找到根節(jié)點(diǎn)位置隙姿,便可以分別找到左子樹
* 和右子樹的數(shù)目和位置,再分別遞歸左右子樹厂捞,得到根節(jié)點(diǎn)的左右子節(jié)點(diǎn)
**/
public class Solution {
/**
*@param preorder : A list of integers that preorder traversal of a tree
*@param inorder : A list of integers that inorder traversal of a tree
*@return : Root of a tree
*/
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder.length != inorder.length ) {
return null;
}
return myBuildTree(preorder, 0, preorder.length-1, inorder, 0, inorder.length-1);
}
private TreeNode myBuildTree(int[] preorder, int prestart, int preend,
int[] inorder, int instart, int inend) {
//
if (instart > inend || prestart > preend) {
return null;
}
TreeNode root = new TreeNode(preorder[prestart]);
int pos = findPos(inorder, instart, inend, preorder[prestart]);
root.left = myBuildTree(preorder, prestart+1,prestart+pos-instart,
inorder,instart, pos-1);
root.right = myBuildTree(preorder, prestart+pos-instart+1,preend,
inorder, pos+1, inend);
return root;
}
private int findPos(int[] nums,int start, int end, int target) {
int pos = 0;
for(int i = start; i <= end; i++) {
if (nums[i] == target) {
pos = i;
}
}
return pos;
}
}
面試題8:二叉樹的下一個(gè)節(jié)點(diǎn)
題目:
給定一個(gè)二叉樹和其中的一個(gè)結(jié)點(diǎn)孟辑,請(qǐng)找出中序遍歷順序的下一個(gè)結(jié)點(diǎn)并且返回。注意蔫敲,樹中的結(jié)點(diǎn)不僅包含左右子結(jié)點(diǎn)饲嗽,同時(shí)包含指向父結(jié)點(diǎn)的指針。
思路:
此題包含三步:
- 如果此節(jié)點(diǎn)有右子樹奈嘿,下一個(gè)節(jié)點(diǎn)為右子節(jié)點(diǎn)的最左邊的節(jié)點(diǎn)貌虾。
- 如果此節(jié)點(diǎn)沒有右子樹,并且如果此節(jié)點(diǎn)是其父節(jié)點(diǎn)的左子節(jié)點(diǎn)裙犹,則下一個(gè)節(jié)點(diǎn)為父節(jié)點(diǎn)尽狠。
- 如果此節(jié)點(diǎn)沒有右子樹,并且如果此節(jié)點(diǎn)是其父節(jié)點(diǎn)的右子節(jié)點(diǎn)叶圃,則一直向上找袄膏,直到找到第一個(gè)是其父節(jié)點(diǎn)左節(jié)點(diǎn)的節(jié)點(diǎn),下一個(gè)節(jié)點(diǎn)就為此節(jié)點(diǎn)掺冠。
代碼實(shí)現(xiàn) :
/**
* Definition of TreeNode:
* public class TreeNode {
* public int val;
* public TreeNode left, right;
* public TreeNode(int val) {
* this.val = val;
* this.left = this.right = null;
* }
* }
*/
public class Solution {
/**
* @param T1, T2: The roots of binary tree.
* @return: True if T2 is a subtree of T1, or false.
*/
public boolean isSubtree(TreeNode T1, TreeNode T2) {
//先判斷T2沉馆,再判斷T1
if (T2 == null) {
return true;
}
if (T1 == null) {
return false;
}
//從根節(jié)點(diǎn)出發(fā)判斷,就相當(dāng)于判斷二者是否相等
if (isEqual(T1, T2)) {
return true;
}
if (isSubtree(T1.left, T2) || isSubtree(T1.right, T2)) {
return true;
}
return false;
}
private boolean isEqual(TreeNode n1, TreeNode n2) {
if (n1 == null || n2 == null) {
return n1 == n2;
}
if (n1.val != n2.val) {
return false;
}
return isEqual(n1.left, n2.left) && isEqual(n1.right, n2.right);
}
}
面試26:樹的子結(jié)構(gòu)
題目:
輸入兩顆二叉樹A和B德崭,判斷B是不是A的子結(jié)構(gòu)
樣例:
下面的例子中 T2 是 T1 的子樹:
1 3
/ \ /
T1 = 2 3 T2 = 4
/
4
下面的例子中 T2 不是 T1 的子樹:
1 3
/ \ \
T1 = 2 3 T2 = 4
/
4
代碼實(shí)現(xiàn):
/**
* Definition of TreeNode:
* public class TreeNode {
* public int val;
* public TreeNode left, right;
* public TreeNode(int val) {
* this.val = val;
* this.left = this.right = null;
* }
* }
*/
public class Solution {
/**
* @param T1, T2: The roots of binary tree.
* @return: True if T2 is a subtree of T1, or false.
*/
public boolean isSubtree(TreeNode T1, TreeNode T2) {
//必須先判斷T2斥黑,再判斷T1
if (T2 == null) {
return true;
}
if (T1 == null) {
return false;
}
if (isEqual(T1, T2)) {
return true;
}
if (isSubtree(T1.left, T2) || isSubtree(T1.right, T2)) {
return true;
}
return false;
}
private boolean isEqual(TreeNode T1, TreeNode T2) {
if (T1 == null || T2 == null) {
return T1 == T2;
}
if (T1.val != T2.val) {
return false;
}
return isEqual(T1.left, T2.left) && isEqual(T1.right, T2.right);
}
}
面試27:二叉樹的鏡像
題目:
請(qǐng)完成一個(gè)函數(shù),輸入一顆二叉樹眉厨,該函數(shù)輸出它的鏡像
樣例:
1 1
/ \ / \
2 3 => 3 2
/ \
4 4
代碼實(shí)現(xiàn):
/**
* Definition of TreeNode:
* public class TreeNode {
* public int val;
* public TreeNode left, right;
* public TreeNode(int val) {
* this.val = val;
* this.left = this.right = null;
* }
* }
*/
public class Solution {
public void invertBinaryTree(TreeNode root) {
if (root == null) {
return;
}
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
invertBinaryTree(root.left);
invertBinaryTree(root.right);
}
}
面試題28: 對(duì)稱的二叉樹
題目:
請(qǐng)實(shí)現(xiàn)一個(gè)函數(shù)锌奴,用來判斷一顆二叉樹是不是對(duì)稱的。如果一顆二叉樹和它的鏡像一樣憾股,那么它是對(duì)稱的鹿蜀。
樣例:
8 8 8
/ \ / \ / \
6 6 6 9 7 7
/ \ / \ / \ / \ / \ /
5 7 7 5 5 7 7 5 7 7 7
3棵二叉樹箕慧,其中第一棵是對(duì)稱的,另外兩棵不是
代碼實(shí)現(xiàn):
/**
* Definition of TreeNode:
* public class TreeNode {
* public int val;
* public TreeNode left, right;
* public TreeNode(int val) {
* this.val = val;
* this.left = this.right = null;
* }
* }
*/
public class Solution {
public boolean isSymmetrical(TreeNode root) {
if (root == null) {
return true;
}
return check(root.left, root.right);
}
private boolean check(TreeNode T1, TreeNode T2) {
if (T1 == null || T2 == null) {
return T1 == T2;
}
if (T1.val != T2.val) {
return false;
}
return check(T1.left, T2.right) && check(T1.right, T2.left);
}
}
面試題33:二叉搜索樹的后序遍歷序列
題目:
輸入一個(gè)整數(shù)數(shù)組茴恰,判斷該數(shù)組是不是某二叉搜索書的后序遍歷結(jié)果销钝。如果是則返回true,否則返回false琐簇。假設(shè)輸入的數(shù)組的任意連個(gè)數(shù)字都不相同蒸健。
樣例:
8
/ \
6 10
/ \ / \
5 7 9 11
后序遍歷序列{5,7婉商,6似忧,9,11丈秩,10盯捌,8}對(duì)應(yīng)的二叉搜索樹
思路:
在后序遍歷得到的序列中,最后一個(gè)數(shù)字是樹的根結(jié)點(diǎn)的值蘑秽。數(shù)組中前面的數(shù)字可以分為兩部分:第一部分是左子樹根結(jié)點(diǎn)的值饺著,它們都比根結(jié)點(diǎn)的值小肠牲;第二部分是右子樹根結(jié)點(diǎn)的值幼衰,它們都比根結(jié)點(diǎn)的值大。
如果要求處理一棵二叉樹的遍歷序列缀雳,我們可以先找到二叉樹的根結(jié)點(diǎn)渡嚣,再基于根結(jié)點(diǎn)把整棵樹的遍歷序列拆分成左子樹對(duì)應(yīng)的子序列和右子樹對(duì)應(yīng)的子序列,接下來再遞歸地處理這兩個(gè)子序列肥印。
代碼實(shí)現(xiàn):
public class VerifySequenceBST {
public static boolean verifySequenceBST(int[] seq) {
if (seq == null || seq.length == 0) {
return false;
}
return verifySequenceBST(seq, 0, seq.length-1);
}
private static boolean verifySequenceBST(int[] seq, int start, int end) {
if (start > end) {
return true;
}
int root = seq[end];
//在二叉搜索樹中左子樹節(jié)點(diǎn)的值小于根節(jié)點(diǎn)的值
int i = start;
while (i < end) {
if (seq[i] > root) {
break;
}
i++;
}
//在二叉搜索樹中右子樹節(jié)點(diǎn)的值大于根節(jié)點(diǎn)的值
int j = i;
while (j < end) {
if (seq[j] < root) {
return false;
}
j++;
}
boolean left = true;
//判斷左子樹是不是二叉搜索樹
left = verifySequenceBST(seq, start, i-1);
boolean right = true;
//判斷右子樹是不是二叉搜索樹
right = verifySequenceBST(seq, i, end - 1);
return left & right;
}
public static void main(String[] args) {
int[] seq1 = {5,7,6,9,11,10,8};
int[] seq2 = {7,4,6,5};
System.out.println(verifySequenceBST(seq1));
System.out.println(verifySequenceBST(seq2));
}
}
面試題34: 二叉樹中和為某一值的路徑
題目:
輸入一棵二叉樹和一個(gè)整數(shù)识椰,打印出二叉樹中節(jié)點(diǎn)值的和為輸入整數(shù)的所有路徑。從根節(jié)點(diǎn)開始往下一直到葉節(jié)點(diǎn)所經(jīng)過的節(jié)點(diǎn)形成一條路徑
樣例
給定一個(gè)二叉樹深碱,和 目標(biāo)值 = 5:
1
/ \
2 4
/ \
2 3
返回:
[
[1, 2, 2],
[1, 4]
]
代碼實(shí)現(xiàn)
/**
* Definition of TreeNode:
* public class TreeNode {
* public int val;
* public TreeNode left, right;
* public TreeNode(int val) {
* this.val = val;
* this.left = this.right = null;
* }
* }
*/
public class Solution {
/**
* @param root the root of binary tree
* @param target an integer
* @return all valid paths
*/
// List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> binaryTreePathSum(TreeNode root, int target) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) {
return result;
}
List<Integer> path = new ArrayList<>();
path.add(root.val);
dfs(root, root.val, target, path, result);
return result;
}
private void dfs(TreeNode root, int sum, int target, List<Integer> path,
List<List<Integer>> result) {
// meat leaf
if (root.left == null && root.right == null) {
if (sum == target) {
result.add(new ArrayList<>(path));
}
return;
}
// go left
if (root.left != null) {
path.add(root.left.val);
// notdfs(root, sum+root.left.val, target, path, result);
dfs(root.left, sum+root.left.val, target, path, result);
//去掉上一次加入的節(jié)點(diǎn) dfs
path.remove(path.size()-1);
}
//go right
if (root.right != null) {
path.add(root.right.val);
//dfs(root, sum+root.right.val, target, path, result);
dfs(root.right, sum+root.right.val, target, path, result);
path.remove(path.size()-1);
}
}
}
面試題36:二叉搜索樹與雙向鏈表
題目:
輸入一棵二叉搜索樹腹鹉,將該二叉搜索樹轉(zhuǎn)換成一個(gè)排序的雙向鏈表。要求不能創(chuàng)建任何新的節(jié)點(diǎn)敷硅,只能調(diào)整樹中節(jié)點(diǎn)指針的指向功咒。
思路:
按照中序遍歷的順序,當(dāng)遍歷轉(zhuǎn)換到根節(jié)點(diǎn)時(shí)竞膳,它的左子樹已經(jīng)轉(zhuǎn)換成一個(gè)排序鏈表了航瞭,并且處在鏈表中的最后一個(gè)節(jié)點(diǎn)是當(dāng)前值最大的節(jié)點(diǎn)坦辟。把左子樹中最大的節(jié)點(diǎn)和根節(jié)點(diǎn)連接起來,此時(shí)鏈表中的最后一個(gè)節(jié)點(diǎn)就是根節(jié)點(diǎn)锉走。接著遍歷右子樹滨彻,并把根結(jié)點(diǎn)和右子樹中最小的節(jié)點(diǎn)連接起來亭饵。至于怎么轉(zhuǎn)換它的左子樹和右子樹,由于遍歷和轉(zhuǎn)換過程是一樣的辜羊,可以用遞歸。
代碼實(shí)現(xiàn):
public static BinaryTreeNode convert(BinaryTreeNode root){
BinaryTreeNode lastNodeInList = null;
lastNodeInList = convertNode(root,lastNodeInList);
//lastNOdeList指向雙向鏈表的尾節(jié)點(diǎn)
//我們需要返回頭節(jié)點(diǎn)
BinaryTreeNode headOfList = lastNodeInList;
while(headOfList!=null && headOfList.left!=null){
headOfList = headOfList.left;
}
return headOfList;
}
public static BinaryTreeNode convertNode(BinaryTreeNode node, BinaryTreeNode lastNodeInList){
if(node==null)
return null;
BinaryTreeNode current = node;
if(current.left!=null)
lastNodeInList = convertNode(current.left,lastNodeInList);
current.left = lastNodeInList;
if(lastNodeInList!=null)
lastNodeInList.right = current;
lastNodeInList = current;
if(current.right!=null)
lastNodeInList = convertNode(current.right,lastNodeInList);
return lastNodeInList;
}
}
面試題37 : 二叉樹的序列化和反序列化
題目:
設(shè)計(jì)一個(gè)算法八秃,并編寫代碼來序列化和反序列化二叉樹肉盹。將樹寫入一個(gè)文件被稱為 “序列化”,讀取文件后重建同樣的二叉樹被稱為 “反序列化”上忍。
如何反序列化或序列化二叉樹是沒有限制的,你只需要確鼻侠叮可以將二叉樹序列化為一個(gè)字符串,并且可以將字符串反序列化為原來的樹結(jié)構(gòu)吓笙。
代碼實(shí)現(xiàn):
/**
* Definition of TreeNode:
* public class TreeNode {
* public int val;
* public TreeNode left, right;
* public TreeNode(int val) {
* this.val = val;
* this.left = this.right = null;
* }
* }
*/
class Solution {
/**
* This method will be invoked first, you should design your own algorithm
* to serialize a binary tree which denote by a root node to a string which
* can be easily deserialized by your own "deserialize" method later.
*/
public String serialize(TreeNode root) {
if(root == null) {
return "{}";
}
ArrayList<TreeNode> queue = new ArrayList<TreeNode>();
queue.add(root);
//add all node to queue
for(int i = 0; i < queue.size(); i++) {
//TreeNode node = queue.get(i);
if (queue.get(i) == null) {
continue;
}
queue.add(queue.get(i).left);
queue.add(queue.get(i).right);
}
//remove last null node
//serialize
StringBuilder sb = new StringBuilder();
sb.append("{");
sb.append(queue.get(0).val);
for(int j = 1; j < queue.size(); j++) {
if (queue.get(j) == null) {
sb.append(",#");
} else {
sb.append("," + queue.get(j).val);
}
}
sb.append("}");
return sb.toString();
}
/**
* This method will be invoked second, the argument data is what exactly
* you serialized at method "serialize", that means the data is not given by
* system, it's given by your own serialize method. So the format of data is
* designed by yourself, and deserialize it here as you serialize it in
* "serialize" method.
*/
public TreeNode deserialize(String data) {
// write your code here
if (data.equals("{}")) {
return null;
}
String[] vals = data.substring(1, data.length() - 1).split(",");
ArrayList<TreeNode> queue = new ArrayList<TreeNode>();
TreeNode root = new TreeNode(Integer.parseInt(vals[0]));
queue.add(root);
int index = 0;
boolean isLeftChild = true;
for (int i = 1; i < vals.length; i++) {
if (!vals[i].equals("#")) {
TreeNode node = new TreeNode(Integer.parseInt(vals[i]));
if (isLeftChild) {
queue.get(index).left = node;
} else {
queue.get(index).right = node;
}
queue.add(node);
}
if (!isLeftChild) {
index++;
}
isLeftChild = !isLeftChild;
}
return root;
}
}
面試題54: 二叉搜索樹的第K大節(jié)點(diǎn)
題目:
給定一棵二叉搜索樹,請(qǐng)找出其中第K大的節(jié)點(diǎn)观蓄。
樣例:
5
/ \
3 7
/ \ / \
2 4 6 8
按節(jié)點(diǎn)數(shù)值的大小順序混移,第三大節(jié)點(diǎn)的值是4
思路 :
如果按照中序遍歷的順序遍歷一棵二叉搜索樹,則遍歷序列的數(shù)值是遞增序列的侮穿。例如歌径,中序遍歷序列是{2,3亲茅,4回铛,5,6克锣,7茵肃,8}。因此袭祟,只需要用中序遍歷算法遍歷一棵二叉搜索樹验残,我們就很容易找出它的第K大節(jié)點(diǎn)。
代碼實(shí)現(xiàn):
public class KthLargestNumInBST {
List<TreeNode> arr = new ArrayList<>();
public TreeNode kthNum(TreeNode root, int k) {
if (root == null || k == 0) {
return null;
}
inOrder(root);
if (k <= arr.size()) {
return arr.get(k);
}
return null;
}
private void inOrder(TreeNode root) {
if (root == null) {
return;
}
if (root.left != null) {
inOrder(root.left);
}
arr.add(root);
if (root.right != null) {
inOrder(root.right);
}
}