参考资料:
剑指offer题目汇总
树和二叉树(共15道题目) 模板:
题目描述:
解题思路 :
注意问题 :
补充知识点 :
4、重建二叉树 (09.17)
17、树的子结构 (09.18)
18、二叉树的镜像 (09.19)
22、从上往下打印二叉树 (09.20)
23、二叉搜索树的后序遍历序列 (09.21)
24、二叉树中和为某一值的路径 (09.22)*
26、二叉搜索树与双向链表 (09.23)*
38、二叉树的深度 (09.24)
39、平衡二叉树 (09.25)
57、二叉树的下一个结点 (09.26)
58、对称的二叉树 (09.27)
59、按之字形顺序打印二叉树 (09.28)*
60、把二叉树打印成多行 (09.29)*
61、序列化二叉树 (09.30)*
62、二叉搜索树的第k个结点 (10.01)
4、重建二叉树
题目描述:
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回根结点。
解题思路 :
注意问题 :
1.递归最后要有一个return root;
补充知识点 :关于树的层次和递归深度的思考、递归与分治
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public TreeNode reConstructBinaryTree (int [] pre,int [] in) { if (pre==null ||in==null ||pre.length==0 ) return null ; return reConstructBinaryTree(pre,in,0 ,pre.length-1 ,0 ,in.length-1 ); } public TreeNode reConstructBinaryTree (int [] pre,int [] in,int pre_begin, int pre_end,int in_begin,int in_end) { if (pre_begin>pre_end || in_begin>in_end) return null ; int rootValue=pre[pre_begin]; TreeNode root=new TreeNode(rootValue); if (pre_begin==pre_end || in_begin==in_end) return root; int rootIn=in_begin; while (rootIn<=in_end && in[rootIn]!=rootValue) rootIn++; int left_count=rootIn-in_begin; root.left=reConstructBinaryTree(pre,in,pre_begin+1 ,pre_begin+left_count, in_begin,rootIn-1 ); root.right=reConstructBinaryTree(pre,in,pre_begin+left_count+1 , pre_end,rootIn+1 ,in_end); return root; }
17、树的子结构
题目描述:
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
解题思路 :递归
注意问题 :
补充知识点 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public boolean HasSubtree (TreeNode root1,TreeNode root2) { if (root1==null || root2==null ) return false ; boolean res=false ; if (root1.val==root2.val) res=doesTree1HasTree2(root1,root2); if (!res) res=HasSubtree(root1.left,root2); if (!res) res=HasSubtree(root1.right,root2); return res; } public boolean doesTree1HasTree2 (TreeNode root1,TreeNode root2) { if (root2==null ) return true ; if (root1==null ) return false ; if (root1.val!=root2.val) return false ; return doesTree1HasTree2(root1.left,root2.left) && doesTree1HasTree2(root1.right,root2.right); }
18、二叉树的镜像
题目描述:
操作给定的二叉树,将其变换为原二叉树的镜像。
——树和二叉树(共15道题目)/18.png)
解题思路 :递归
注意问题 :
1.中序遍历不行,先序遍历和后序遍历可以;
2.提倡使用先序遍历;
补充知识点 :
1 2 3 4 5 6 7 8 9 10 11 12 13 public void Mirror (TreeNode root) { if (root!=null ){ if (root.left!=null || root.right!=null ){ TreeNode temp=root.left; root.left=root.right; root.right=temp; Mirror(root.left); Mirror(root.right); } } }
22、从上往下打印二叉树
题目描述:
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
解题思路 :二叉树的层次遍历:队列;
注意问题 :
1.要导入:import java.util.LinkedList; import java.util.Queue;否则编译通不过;
2.左右子节点入队列之前要判空;
3.queue.size()返回值是整数;并且不可以用queue!=null来判断;仔细理解队列为空和队列元素为零的区别:前者是指针为空;
补充知识点 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import java.util.ArrayList;import java.util.LinkedList;import java.util.Queue;public class Solution { public ArrayList<Integer> PrintFromTopToBottom (TreeNode root) { ArrayList<Integer> res=new ArrayList(); if (root==null ) return res; Queue<TreeNode> queue=new LinkedList<>(); queue.add(root); while (queue.size()!=0 ) { TreeNode temp=queue.poll(); res.add(temp.val); if (temp.left!=null ) queue.add(temp.left); if (temp.right!=null ) queue.add(temp.right); } return res; } }
23、二叉搜索树的后序遍历序列
题目描述:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
解题思路 :最后一个节点肯定时根,前面的序列中,比根小的是左子树,比分大的是右子树,找到左右子树的分界点,左子树递归判断;右子树中出现比根节点小的数则返回false,否则同左子树一样,递归判断,当左子树和右子树同时成立时,整棵树就是;
注意问题 :
1.终止条件:end<=begin;
2.找左右子树的分界点时:用for循环,如果循环里已经有i++了,那么条件里面就不要写I++了,否则i++执行两次;
3.一层递归有true和false两个出口,不要忽略false的出口;
补充知识点 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Solution { public boolean VerifySquenceOfBST (int [] sequence) { if (sequence==null || sequence.length==0 ) return false ; return VerifySquence0fBST(sequence,0 ,sequence.length-1 ); } public boolean VerifySquence0fBST (int [] a,int begin,int end) { if (end<=begin) return true ; int i; for (i=begin;i<end;) { if (a[i]<a[end]) i++; else break ; } int j; for (j=i;j<end;j++) if (a[j]<a[end]) return false ; boolean left=VerifySquence0fBST(a,begin,i-1 ); boolean right=VerifySquence0fBST(a,i,end-1 ); return left && right; } }
38、二叉树的深度
题目描述:
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
解题思路 :递归
注意问题 :递归结束条件判断;
补充知识点 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class TreeNode { int val = 0 ; TreeNode left = null ; TreeNode right = null ; public TreeNode (int val) { this .val = val; } } public class Solution { public int TreeDepth (TreeNode root) { if (root==null ) return 0 ; int leftDepth=TreeDepth(root.left); int rightDepth=TreeDepth(root.right); int depth=leftDepth>rightDepth?leftDepth:rightDepth; return 1 +depth; } }
39、平衡二叉树
题目描述:
输入一棵二叉树,判断该二叉树是否是平衡二叉树。这里的定义是:如果某二叉树中任意结点的左、右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
解题思路 :后序遍历:先判断左子树是否平衡,再判断右子树是否平衡,当左右子树均平衡时,再判断根是否平衡。
注意问题 :
1.在求树的深度的算法的基础上,增加平衡的判断:求出左子树深度之后,要及时判断是否平衡;求出右子树之后,也要及时判断是否平衡;(如果不判断,只判断整棵树是否平衡,当二叉树为左子树单支链时,其左子树深度为-1,右子树深度为0,则|-1-0|=1不大于1,即该二叉树是平衡二叉树,与事实相反)在返回整棵树的深度之前,也要进行平衡的判断;
补充知识点 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Solution { public boolean IsBalanced_Solution (TreeNode root) { if (root==null ) return true ; int res=getDepth(root); if (res==-1 ) return false ; return true ; } public 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 ; if (Math.abs(left-right)>1 ) return -1 ; return left>right?left+1 :right+1 ; } }
57、二叉树的下一个结点
题目描述:
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
解题思路 :
找一个结点在中序遍历序列中的下一个结点共有三种不同的情况 :
如果一个结点有右子树,那么它的下一个结点就是它的右子树中最左边的那个结点,也就是说,从它的右子结点出发一直访问左指针,最后就可以找到这个最左结点。
如果一个结点没有右子树,那么需要再进一步考虑,不难知道:如果这个结点是其父结点的左结点,那么根据中序遍历规则,它的下一个结点就是它的父结点。
第三种情况略复杂一些,当一个结点既没有右子树,也不是其父结点的左结点时,我们可以沿着指向父结点的指针一直向上遍历,直到找到一个是它自身的父结点的左孩子的结点,如果这样的结点存在,那么这个结点的父结点就是我们要找的下一个结点。
注意问题 :
补充知识点 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class TreeLinkNode { int val; TreeLinkNode left = null ; TreeLinkNode right = null ; TreeLinkNode next = null ; TreeLinkNode(int val) { this .val = val; } } public class Solution { public TreeLinkNode GetNext (TreeLinkNode pNode) { if (pNode==null ) return null ; TreeLinkNode res=null ; if (pNode.right!=null ){ res=pNode.right; while (res.left!=null ) res=res.left; }else { TreeLinkNode parent=pNode.next; if (parent!=null && pNode==parent.left) res=parent; else { while (parent!=null && parent.next!=null && parent!=parent.next.left) parent=parent.next; res=parent==null ? null : parent.next; } } return res; } }
58、对称的二叉树
题目描述:
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
解题思路 :有相当的技巧性:对称二叉树的规律:根结点的左右子树相同,左子树的左子树和右子树的右子树相同,左子树的右子树和右子树的左子树相同即可 。
注意问题 :
补充知识点 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class TreeNode { int val = 0 ; TreeNode left = null ; TreeNode right = null ; public TreeNode (int val) { this .val = val; } } public class Solution { boolean isSymmetrical (TreeNode pRoot) { if (pRoot==null ) return true ; return isSymmetrical(pRoot.left,pRoot.right); } boolean isSymmetrical (TreeNode pleft,TreeNode pright) { if (pleft==null && pright==null ) return true ; if (pleft==null || pright==null ) return false ; if (pleft.val!=pright.val) return false ; return isSymmetrical(pleft.left,pright.right) && isSymmetrical(pleft.right,pright.left); } }
59、按之字形顺序打印二叉树
题目描述:
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
解题思路 :
注意问题 :
补充知识点 :
60、把二叉树打印成多行
题目描述:
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
解题思路 :
注意问题 :
补充知识点 :
62、二叉搜索树的第k个结点
题目描述:
给定一棵二叉搜索树,请找出其中的第k小的结点。例如(5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
解题思路 :二叉搜索树的特点:左结点的值<根结点的值<右结点的值。因此,我们不难得到如下结论:如果按照中序遍历的顺序对一棵二叉搜索树进行遍历,那么得到的遍历序列就是递增排序的。设置两个全局变量,num和res,中序遍历,当num==1时,用res把当前节点记录下来。
注意问题 :
1.if(num==1){res=pRoot;num--;}
这一段代码,如果没有num—,则当num==1之后,条件永远成立,起不到只执行一次的作用;
2.递归前中后序遍历的时候,要先判空;
补充知识点 :二叉搜索树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class TreeNode { int val = 0 ; TreeNode left = null ; TreeNode right = null ; public TreeNode (int val) { this .val = val; } } public class Solution { int num; TreeNode res; TreeNode KthNode (TreeNode pRoot, int k) { if (pRoot==null || k<1 ) return null ; num=k; findKthNode(pRoot); return res; } void findKthNode (TreeNode pRoot) { if (pRoot==null ) return ; findKthNode(pRoot.left); if (num==1 ) { res=pRoot; num--; } else num--; findKthNode(pRoot.right); } }
补充:
前序、中序、后续遍历递归算法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 typedef struct TreeNode { int data; TreeNode * left; TreeNode * right; TreeNode * parent; }TreeNode; void pre_order (TreeNode * Node) { if (Node == NULL ) return ; printf ("%d " , Node->data); pre_order(Node->left); pre_order(Node->right); } void middle_order (TreeNode *Node) { if (Node == NULL ) return ; middle_order(Node->left); printf ("%d " , Node->data); middle_order(Node->right); } void post_order (TreeNode *Node) { if (Node == NULL ) return ; post_order(Node->left); post_order(Node->right); printf ("%d " , Node->data); }