1.给定一颗二叉树,以及其中的两个node(地址均非空),要求给出这两个node的一个公共父节点,使得这个父节点与两个节点的路径之和最小。描述你程序的最坏时间复杂度,并实现具体函数
C++函数原型:
1
2
3
4
5
6
7
|
strucy TreeNode{
TreeNode* left;
//指向左子树
TreeNode* right;
//指向右子树
TreeNode* father;
//指向父亲节点
};
TreeNode* LowestCommonAncestor(TreeNode* first,TreeNode* second){
}
|
这里给出了父节点指针,难度一下就降下来了
方法一:首先找到两个节点的高度差,然后从较靠近根节点的一层开始向上找,若父节点为同一节点则该节点为解
最坏时间复杂度为O(n)
public class SameFather {
class Node{
public Node father;
public Node left;
public Node right;
}
public int getHeight(Node node){
int h=0;
while(node!=null){
h++;
node=node.father;
}
return h;
}
public Node LowestCommonAncestor(Node first,Node second){
int h1=getHeight(first);
int h2=getHeight(second);
int diff=h1-h2;
if(diff<0){//first在上面
diff=-diff;
while(diff--!=0){//先判断不为0,之后再减1
second=second.father;//second往上赶
}
}else{
while(diff--!=0){
first=first.father;
}
}
while(first!=second){
first=first.father;
second=second.father;
}
return first;
}
}
2..二叉树的下一个结点
解析:
画一个二叉树,求出该二叉树的中序遍历
(1)如果树为空,return null
(2)如果树有右子树,找到右子树最左节点
(3)如果没有右节点,它的下一个节点应该是,以自己为左孩子的父节点,如果自己不是自己父节点的左孩子,就继续向上找
<span style="font-family:Microsoft YaHei;font-size:14px;">/*
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;
if(pNode.right!=null){//如果右子树不为空
pNode=pNode.right;
if(pNode.left!=null)
pNode=pNode.left;
return pNode;
}
while(pNode.next!=null){//因为要一直往上找
if(pNode.next.left==pNode)
return pNode.next;
pNode=pNode.next;
}
return null;
}
}</span>
3.对称的二叉树
解析:
一看到这种题,就有感觉要用递归来做
判断两个子树是否对称
左子树的左子树应该等于右子树的右子树
左子树的右子树应该等于右子树的左子树
/*
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 same(pRoot.left,pRoot.right);
}
boolean same(TreeNode leftNode,TreeNode rightNode){
if(leftNode==null&&rightNode!=null)return false;
if(leftNode!=null&&rightNode==null)return false;
if(leftNode==null&&rightNode==null)return true;
if(leftNode.val==rightNode.val)
return same(leftNode.left,rightNode.right)&&same(leftNode.right,rightNode.left);
return false;
}
}
4.按之字形顺序打印二叉树
解析:
层次遍历+每一层单行输出,这个是之前做过的题,改编一下
定义两个变量,curNum和nextNum来分别保存当前层的节点数和下一层的节点数
初始化时curNum=1,nextNum=0
层次遍历用队列,先把根节点压入,开始循环
弹出队列头节点,输出,curNum--,判断弹出的节点有没有左右孩子,有的话压入队列,每压入一个,nextNum++
判断if(curNum==0),等于0的话说明,这一行已经遍历完了,curNum=nextNum,nextNum=0;
这里每输出一层,按顺序存放在arrayList中,可以用一个标志位flag,true的时候从左到右,false的时候从右到左,每当curNum==0时转换,
转换之前判断是true还是false,true的话正常把这一次的arraylist加到总的结果中,否则,反转这个arraylist后再添加进去
import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer> > res=new ArrayList<ArrayList<Integer> >();
if(pRoot==null)//不要忘记判断
return res;
int curNum=1;
int nextNum=0;
boolean flag=true;
ArrayList<Integer> list=new ArrayList<Integer>();
Queue<TreeNode> queue=new LinkedList<TreeNode>();//记得这里是链表,才能生成的队列
queue.add(pRoot);
while(!queue.isEmpty()){
TreeNode node=queue.poll();
list.add(node.val);
curNum--;
if(node.left!=null){
queue.add(node.left);
nextNum++;
}
if(node.right!=null){
queue.add(node.right);
nextNum++;
}
if(curNum==0){
if(flag)
res.add(list);
else{
res.add(reverse(list));
}
flag=!flag;
curNum=nextNum;
nextNum=0;
list=new ArrayList<Integer>();//只有打印完一行才再重新声明
}
}
return res;
}
public ArrayList<Integer> reverse(ArrayList<Integer> list){
int len=list.size();
ArrayList<Integer> list2=new ArrayList<Integer>();
for(int i=len-1;i>=0;i--){
list2.add(list.get(i));
}
return list2;
}
}
5.把二叉树打印成多行
解析:这个就是层次遍历按层输出的那个
import java.util.ArrayList;
import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer> > res=new ArrayList<ArrayList<Integer> >();
if(pRoot==null)
return res;
ArrayList<Integer> list=new ArrayList<Integer>();
Queue<TreeNode> queue=new LinkedList<TreeNode>();
int curNum=1;
int nextNum=0;
queue.add(pRoot);
while(!queue.isEmpty()){
TreeNode node=queue.poll();
list.add(node.val);
curNum--;
if(node.left!=null){
queue.add(node.left);
nextNum++;
}
if(node.right!=null){
queue.add(node.right);
nextNum++;
}
if(curNum==0){
res.add(list);
curNum=nextNum;
nextNum=0;
list=new ArrayList<Integer>();
}
}
return res;
}
}
6.序列化二叉树
解析:
第一种方法:
序列化二叉树,就是把二叉树变成一串字符串,先序遍历,最开始str="",不停往上面加,最后返回str
每遍历一个节点后面加上!,遇到空节点输出#!
遇到空节点返回#!,先加上根节点!,再对根节点的左孩子调用这个方法,然后对根节点的右孩子调用这个方法,最后返回str,递归
反序列化:对用先序遍历序列化的字符串进行反序列化
用到队列,先把字符串用split("!")变成字符串数组,遍历把每个元素添加到队列中,空的时候返回null,这个在后面的递归中要用到
弹出的第一个节点是根节点,然后根节点的左孩子是队列中剩下的元素调用这个函数得到的节点,遍历到#后,说明当前结点的左孩子后面没有东西了,返回当前结点,看他的右孩子,再遍历到#后,再回到当前结点,找到当前结点的父节点,也使用递归实现
!!注意,因为是递归,所以每轮只算自己的!!不用哪一步都str+=
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.*;
public class Solution {
//序列化
String Serialize(TreeNode root) {
if(root==null){
return "#!";//这里return#!就行,不用加上str
}
String str=root.val+"!";//每次只算自己的!!
str+=Serialize(root.left);
str+=Serialize(root.right);
return str;
}
//反序列化!
TreeNode Deserialize(String str) {
Queue<String> queue=new LinkedList<String>();//这里存放的是String类型
String[] s=str.split("!");
for(int i=0;i<s.length;i++){
queue.add(s[i]);
}
return reconPre(queue);
}
public TreeNode reconPre(Queue<String> queue){
String value=queue.poll();
if(value.equals("#"))
return null;
TreeNode head=new TreeNode(Integer.valueOf(value));
head.left=reconPre(queue);
head.right=reconPre(queue);
return head;
}
}
第二种方法:
层次遍历来序列化:序列化的时候要用到队列,序列化的字符串是str,初始时为空
最开始判断是否为空,为空的话return #!
先将节点压入队列,队列是TreeNode类型的,然后层次遍历
先压入头节点,当队列不为空的时候,循环,弹出队首的元素,将val!加入到str中,
看看这个节点有没有左孩子右孩子,有的话分别压入队列,没有的话str+"#!" , 循环,
反序列化:先把字符串split("!")拆成String数组
其实反序列化就相当于再来一遍层次遍历,之前用先序遍历时就相当于再来一遍先序遍历。遇到#,表示是null,其他的就是正常节点
声明一个队列,TreeNode类型的,把String数组中第一个数放进去,然后index++,队列不为空的时候循环
取出队首元素,通过一个函数把它变成节点,然后按理来说后面两个是它的左右孩子,判断一下是不是空节点,是的话不要管了,
是正常节点的话以左孩子或者右孩子的身份添加到队列中
思路一定要清晰!!
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.*;
public class Solution {
String Serialize(TreeNode root) {
Queue<TreeNode> queue=new LinkedList<TreeNode>();
if(root==null)
return "#!";
String str=root.val+"!";
queue.add(root);
while(!queue.isEmpty()){
root=queue.poll();
if(root.left!=null){
str+=root.left.val+"!";
queue.add(root.left);
}else{
str+="#!";
}
if(root.right!=null){
str+=root.right.val+"!";
queue.add(root.right);
}else{
str+="#!";
}
}
return str;
}
TreeNode Deserialize(String str) {
String[] s=str.split("!");
Queue<TreeNode> queue=new LinkedList<TreeNode>();
int index=0;//指针在数组中移动
TreeNode head=createNode(s[index++]);
if(head!=null){
queue.add(head);
}
TreeNode node=null;//这个node要放在外面,因为里面一直用一个
while(!queue.isEmpty()){
node=queue.poll();
node.left=createNode(s[index++]);
node.right=createNode(s[index++]);
if(node.left!=null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
}
return head;
}
public TreeNode createNode(String a){
if(a.equals("#"))
return null;
return new TreeNode(Integer.valueOf(a));
}
}
7.二叉搜索树的第k个结点
解析:
中序遍历,存到数组里,取出第k-1个
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.*;
public class Solution {
ArrayList<TreeNode> list=new ArrayList<TreeNode>();
public void inorder(TreeNode node){
if(node==null)
return ;
inorder(node.left);
list.add(node);
inorder(node.right);
}
TreeNode KthNode(TreeNode pRoot, int k)
{
inorder(pRoot);
if(k>list.size()||k<=0)
return null;
return list.get(k-1);
}
}
8.
二叉树的深度
解析:
递归求解:
假如是空节点,则返回0;
否则,原树的深度由左右子树中深度较的深度加1,为原树的深度。
/*
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 pRoot)
{
if(pRoot==null)
return 0;
return Math.max(1+TreeDepth(pRoot.left),1+TreeDepth(pRoot.right));
}
}
9.平衡二叉树
/*
* 输入一棵二叉树,判断该二叉树是否是平衡二叉树
*/
public class BalanceTree {
class TreeNode{
TreeNode left;
TreeNode right;
}
//声明了一个全局变量来判断是否是平衡树
private boolean isBalanced=true;
public boolean IsBalanced_Solution(TreeNode root) {
getDepth(root);
return isBalanced;
}
//这个函数其实是在求树的高度
public int getDepth(TreeNode root){
if(root==null)//递归到底部的时候
return 0;
int left=getDepth(root.left);//递归来求树的高度,递归就相当于自底向上了
int right=getDepth(root.right);
//每次求完子树的高度,就判断一下
if(Math.abs(left-right)>1)
isBalanced=false;//不是记为false,但无论如何都要遍历一遍,最后把isBalanced输出来
return right>left?right+1:left+1;
}
}
/*
* 输入一棵二叉树,判断该二叉树是否是平衡二叉树
*/
public class BalanceTree2 {
class TreeNode{
TreeNode left;
TreeNode right;
}
//这里用了一个class来传值,声明对象后就可以根据对象来保存这个值n
//这里n其实相当于树的深度
private class Holder{
int n;
}
public boolean IsBalanced_Solution(TreeNode root){
return judge(root,new Holder());
}
public boolean judge(TreeNode root,Holder h){
if(root==null)
return true;
Holder l=new Holder();
Holder r=new Holder();
//这边的两个judge是在递归,先跑到树的最下面
//如果最下面的两个子数都是平衡树,那就判断一下他俩的高度差
if(judge(root.left,l)&&judge(root.right,r)){
if(Math.abs(l.n-r.n)>1)
return false;
h.n+=(l.n>r.n?l.n:r.n)+1;
return true;
}
return false;
}
}