1.二叉树的镜像
操作给定的二叉树,将其变换为源二叉树的镜像。
思路:直接用先序遍历,左边和右边换一下,然后再分别遍历左子树和右子树。
public class Solution {
public void Mirror(TreeNode root) {
TreeNode node = null;
if(root != null) {
node = root.left;
root.left = root.right;
root.right = node;
if(root.left != null)
Mirror(root.left);
if(root.right != null)
Mirror(root.right);
}
}
}
2.二叉树的深度
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
递归写法:二叉树的深度即为左右字树深度较深的值再加1。
// 递归写法
public int TreeDepth(TreeNode root) {
if (root == null)
return 0;
int left = TreeDepth(root.left);
int right = TreeDepth(root.right);
return Math.max(left, right) + 1;
}
*/
非递归写法:层次遍历
// 层次遍历
public int TreeDepth(TreeNode root) {
if (root == null)
return 0;
Queue<TreeNode> queue = new LinkedList<>();
// 记录深度
int depth = 0;
// 记录每层的节点数
int width = 0;
// 记录当前扫描到第几个
int cur = 0;
queue.offer(root);
while (!queue.isEmpty()) {
width = queue.size();
while (cur < width) {
cur = 0;
TreeNode node = queue.poll();
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
cur ++;
}
depth ++;
}
return depth;
}
3.平衡二叉树
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
普通写法,得到深度,判断深度差
public boolean IsBalanced_Solution(TreeNode root) {
if (root == null) return true;
if(Math.abs(getDepth(root.left) - getDepth(root.right)) > 1) {
return false;
}
return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
}
private int getDepth(TreeNode root) {
if(root == null) return 0;
return Math.max(getDepth(root.left), getDepth(root.right)) + 1;
}
剪枝法,遇到不平衡就退出
// 剪枝做法
public boolean IsBalanced_Solution(TreeNode root) {
if (root == null) return true;
return getDepth(root) != -1;
}
private int getDepth(TreeNode root) {
if(root == null) return 0;
int left = getDepth(root.left);
if(left == -1) return -1;
int right = getDepth(root.right);
if(right == -1) return -1;
return Math.abs(left - right) > 1 ? -1 : Math.max(left, right) + 1;
}
4.把二叉树打印成多行
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
依然是层次遍历的思想,记录下当前层需要打印的个数和下一层需要打印的个数
ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
if (pRoot == null)
return res;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(pRoot);
ArrayList<Integer> layerList = new ArrayList<>();
// 当前层需要打印的节点数
int thisLayer = 0;
// 下一层需要打印的节点数,初始值即为根节点
int nextLayer = 1;
while (!queue.isEmpty()) {
TreeNode temp = queue.poll();
layerList.add(temp.val);
thisLayer++;
if (temp.left != null) {
queue.offer(temp.left);
}
if (temp.right != null) {
queue.offer(temp.right);
}
if (thisLayer == nextLayer) {
// 记下下层需要打印的节点数
nextLayer = queue.size();
thisLayer = 0;
res.add(layerList);
layerList = new ArrayList<>();
}
}
return res;
}
5.之字形顺序打印二叉树
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
依然是层序遍历,设置一个标志位,标记是偶数行还是奇数行。
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
if (pRoot == null)
return res;
boolean isOdd = true;
// oddStack存奇数层节点,evenStack存偶数层节点
Stack<TreeNode> oddStack = new Stack<>();
Stack<TreeNode> evenStack = new Stack<>();
oddStack.add(pRoot);
while (!oddStack.isEmpty() || !evenStack.isEmpty()) {
// 如果是奇数行
if (isOdd) {
ArrayList<Integer> temp = new ArrayList<>();
while (!oddStack.isEmpty()) {
TreeNode node = oddStack.pop();
temp.add(node.val);
if (node.left != null) {
evenStack.push(node.left);
}
if (node.right != null) {
evenStack.push(node.right);
}
}
res.add(temp);
isOdd = false;
} else {
ArrayList<Integer> temp = new ArrayList<>();
while (!evenStack.isEmpty()) {
TreeNode node = evenStack.pop();
temp.add(node.val);
if (node.right != null) {
oddStack.push(node.right);
}
if (node.left != null) {
oddStack.push(node.left);
}
}
res.add(temp);
isOdd = true;
}
}
return res;
}
6.二叉树的下一个节点
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
分三种情况讨论。
public TreeLinkNode GetNext(TreeLinkNode pNode)
{
if (pNode == null)
return null;
// 若当前节点的右子树不为空,则返回右子树中的最左边的节点,为下一个节点
if (pNode.right != null) {
pNode = pNode.right;
while (pNode.left != null) {
pNode = pNode.left;
}
return pNode;
// 当前节点的右子树为空,且当前节点是其父节点的左节点,则直接返回其父节点
} else if (pNode.next != null && pNode.next.left == pNode) {
return pNode.next;
// 当前节点的右子树为空,且当前节点是其父节点的右节点,一直向上找,
// 直到找到有一个节点的父节点的左节点是该节点的时候,返回其父节点
} else if (pNode.next != null && pNode.next.right == pNode) {
while (pNode.next != null && pNode.next.left != pNode) {
pNode = pNode.next;
}
return pNode.next;
} else {
return null;
}
}
7.序列化二叉树
请实现两个函数,分别用来序列化和反序列化二叉树
一定要注意sb在函数体里,因为每一步返回的sb都是往上层的sb中添加的。
// 此题考查对递归的理解
// sb对象一定要放在里面,这样每次递归产生的值才可以一点点放入最终的sb中
String Serialize(TreeNode root) {
StringBuffer sb = new StringBuffer();
if (root == null) {
sb.append("#,");
return sb.toString();
}
sb.append(root.val + ",");
sb.append(Serialize(root.left));
sb.append(Serialize(root.right));
return sb.toString();
}
int index = -1;
TreeNode Deserialize(String str) {
index++;
int len = str.length();
if (index >= len)
return null;
String[] strr = str.split(",");
TreeNode node = null;
if (!strr[index].equals("#")) {
node = new TreeNode(Integer.valueOf(strr[index]));
node.left = Deserialize(str);
node.right = Deserialize(str);
}
return node;
}
8.二叉搜索树的第K个结点
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
递归做法:直接中序遍历,找到第K个遍历到的结点。注意中序遍历的写法。
// 递归实现
int index = 0;
TreeNode KthNode(TreeNode pRoot, int k)
{
if (pRoot != null) {
TreeNode node = KthNode(pRoot.left, k);
if (node != null) {
return node;
}
index ++;
if (index == k) {
return pRoot;
}
node = KthNode(pRoot.right, k);
if (node != null) {
return node;
}
}
return null;
}
非递归做法:存放在栈中
// 非递归实现
TreeNode KthNode(TreeNode pRoot, int k) {
Stack<TreeNode> stack = new Stack<>();
while (pRoot != null || !stack.isEmpty()) {
if (pRoot != null) {
stack.push(pRoot);
pRoot = pRoot.left;
} else {
pRoot = stack.pop();
k--;
if (k == 0) {
return pRoot;
}
pRoot = pRoot.right;
}
}
return null;
}
9.重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
前序遍历和中序遍历构造二叉树。
方法一:
先序遍历第一个位置肯定是根节点,中序遍历的根节点位置在中间p,在p左边的肯定是node的左子树的中序数组,p右边的肯定是node的右子树的中序数组,记录下先序遍历和中序遍历的start和end,分左右递归调用即可。
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
TreeNode root=reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1);
return root;
}
//前序遍历{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}
private TreeNode reConstructBinaryTree(int [] pre,int startPre,int endPre,int [] in,int startIn,int endIn) {
if(startPre>endPre||startIn>endIn)
return null;
TreeNode root=new TreeNode(pre[startPre]);
for(int i=startIn;i<=endIn;i++)
// i的位置就记录下根节点的位置
if(in[i]==pre[startPre]){
// 先序遍历的左子树,即为startPre+1到循环中i移动的位置,就是startPre+i-startIn。
// 中序遍历的左子树,即为startIn到i-1。
root.left=reConstructBinaryTree(pre,startPre+1,startPre+i-startIn,in,startIn,i-1);
// 先序遍历的右子树,即为左子树end+1(startPre+i-startIn+1)到endPre。
// 中序遍历的右子树,即为i+1到endIn。
root.right=reConstructBinaryTree(pre,startPre+i-startIn+1,endPre,in,i+1,endIn);
break;
}
return root;
}
方法二,使用copyofRange函数。
// 先序序列的第一个必然是根节点,找到此节点在中序序列中对应的位置,则该位置左边必然是左子树,右边必然是右子数。通过copyofRange函数构造出左右子树的先序和中序序列,分别递归。
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if(pre.length == 0 || in.length == 0 || pre.length != in.length)
return null;
TreeNode node = new TreeNode(pre[0]);
for(int i = 0; i<pre.length; i++) {
if(pre[0] == in[i]) {
node.left = reConstructBinaryTree(Arrays.copyOfRange(pre,1,i+1),
Arrays.copyOfRange(in,0,i));
node.right = reConstructBinaryTree(Arrays.copyOfRange(pre,i+1,pre.length),
Arrays.copyOfRange(in,i+1,in.length));
}
}
return node;
}