目录
1.1 用递归和非递归两种方式实现二叉树的先序、中序、后序遍历
1.2 如何完成二叉树的宽度优先遍历(常见题目:求一棵二叉树的宽度)
3 给定两个二叉树的节点node1和node2,找到他们的最低公共祖先节点
1 二叉树节点结构
class Node<V>{
V value;
Node left;
Node right;
}
用递归和非递归两种方式实现二叉树的先序、中序、后序遍历
如何完成二叉树的宽度优先遍历(常见题目:求一棵二叉树的宽度)
1.1 用递归和非递归两种方式实现二叉树的先序、中序、后序遍历
1.1.1 递归序
void f(Node head)
{
if(head == null)
{
return;
}
f(head.left);
f(head.right);
}
分析:会多次返回到原函数,虽然什么都没做,每个节点都会被使用3次
1.1.2 递归版的 先序 中序 后序
均由递归序加工到来,选择打印的时间不同。
先序即打印输出为(头左右):1,2,4,5,3,6,7 (第一次进入函数时打印,其余两次不打印)
中序即打印输出为(左头右):4,2,5,1,6,3,7(第二次进入函数时打印,其余两次不打印)
后序即打印输出为(左右头):4,5,2,6,7,3,1(第三次进入函数时打印,其余两次不打印)
1.1.3 非递归版的 先序 中序 后序
自己压栈。
先序步骤:
①从栈出弹出一个节点cur
②打印(处理)cur
③先将右孩子压入栈中再将左孩子压入栈中(如果有的话)
④回到①重复
后序步骤:
①从栈出弹出一个节点cur
②将cur放入收集栈
③先将右孩子压入栈中再将左孩子压入栈中(如果有的话)
④回到①重复
⑤直到没有数后将数据依次从收集栈中弹出打印
中序步骤:
①每棵子树整棵树左边界进栈
②依次弹出的过程中打印
③对弹出节点的右树进栈(进栈的时候同样是整棵子树左边界进栈)
④回到②,如果栈里没有数据了回到①
ex. step1:1,2,4进栈->弹出4->4没有右树回到①->弹出2->2有右树,将5进栈->····
1.2 如何完成二叉树的宽度优先遍历(常见题目:求一棵二叉树的宽度)
先序遍历即是二叉树的深度优先遍历
宽度优先遍历使用队列,
宽度优先遍历步骤:
①从队列出弹出一个节点cur
②打印(处理)cur
③先将左孩子放入队列中再将右孩子放入队列中(如果有的话)
④回到①重复
求一棵二叉树的宽度(哈希表方法)
Queue<Node> queue = new LinkedList<>();
queue.add(head);
HashMap<Node,Integer> levelMap = new HashMap<>(); //存储所有节点以及节点所在的等级
levelMap.put(head,1); //树的头节点,节点等级为1
int curLevel = 1;
int curLevelNodes = 0;
int max = Integer.MIN_VALUE; //全局变量的最小值 (或者取0也可)
while(!queue.isEmpty())
{
Node cur = queue.poll();
int curNodeLevel = levelMap.get(cur);//获取当前所在的等级
if(curNodeLevel == curLevel)
{
curLevelNodes++; //如果当前等级在需要计算的等级,则将数据弹出后++
}
else
{
max = Math.max(max,curLevelNodes); //如果不是,则当前是下一个等级,开始比较最大宽度
curLevel++; //开始需要计算的等级往下一级
curLevelNodes = 1; //初始化该等级节点数
}
if(cur.left != null)
{
levelMap.put(cur.left, curNodeLevel+1);
queue.add(cur.left);
}
if(cur.right != null)
{
levelMap.put(cur.right , curNodeLevel+1);
queue.add(cur.right);
}
}
2 二叉树的相关概念及其实现判断
2.1 如何判断一棵二叉树是否是搜索二叉树?
搜索二叉树:对于每棵子树来说,他的左树节点都比其小,他的右树节点都比其大。
方法:
中序遍历,得到的是一个升序的数据,即是一棵搜索二叉树
2.2 如何判断一棵二叉树是完全二叉树?
完全二叉树:一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。
方法:宽度遍历
①任一节点有右无左,返回false (即不是完全二叉树)
②在不满足①的情况下,如果遇到第一个左右孩子不全,后续皆叶节点(即没有子树)
③返回①继续判断
2.3 如何判断一棵二叉树是否是满二叉树?
满二叉树即除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树。
简单理解的方法:用一个函数求二叉树的最大深度D,用一个函数求二叉树的节点数N。满足,即为满二叉树
2.4 如何判断一棵二叉树是否是平衡二叉树?
平衡二叉树:平衡树(Balance Tree,BT) 指的是,任意节点的子树的高度差都小于等于1。以及左树和右树都是平衡树。
public static boolean isBalanced(Node head){
return process(head).isBalanced;
}
public static class ReturnType{
public boolean isBalanced;
public int height;
public ReturnType(boolean isB, int hei){
isBalanced = isB;
height = hei;
}
}
public static ReturnType process(Node x){
if( x == null)
{// base
return new ReturnType(true, 0);
}
ReturnType leftData = process(x.left);
ReturnType rightData = process(x.right);
int height = Math.max(leftData.height, rightData.height)+1;
boolean isBalanced = leftData.isBalanced&&rightData.isBalanced
&&Math.abs(leftData.height-rightData.height<2)
return new ReturnType(isBalanced, height);
}
3 给定两个二叉树的节点node1和node2,找到他们的最低公共祖先节点
解题思路:
①首先通过哈希表建立每个节点的父节点
②建立哈希表set1存储node1节点的序列
③让node2往上回溯,并判断新的值是否在set1中
4 在二叉树中找到一个节点的后继节点
现在有一种新的二叉树节点类型如下:
public class Node{
public int value;
public Node left;
public Node right;
public Node parent;
public Node(int val){
value = val;
}
}
该结构比普通二叉树节点结构多了一个指向父节点的parent指针
假设有一棵Node类型的节点组成的二叉树,树中每个节点的parent指针都正确地指向自己的父节点,头节点的parent指向null。
只给一个在二叉树中的某个节点node,请实现返回node的后继节点的函数。
在二叉树的中序遍历的序列,node的下一个节点叫作node的后继节点。
解题分析:
①x有右树的时候,此时x的后继节点为右树上的最左节点
②x无右树的时候,x一直向上追寻,判断当前是否是父亲的左节点,直到是的时候即y是x的后继节点
③当x为最右节点时,此时没有后继节点,其后继节点为null
public static Node getSuccessorNode(Node node){
if (node == null){
return node
}
if (node.right != null){
return getLeftMost(node.right);//情况①
} else{ //情况②
Node parent = node.parent;
while(parent != null && parent.left != node){//当前节点是其父亲节点右孩子
node = parent;
parent = node.parent;
}
return parent;
}
}
public static Node getLeftMost(Node node){
if(node == null){
return node;
}
while(node.left != null){
node = node.left;
}
return node;
}
5 二叉树的序列化和反序列化
就是内存里的一棵树如何变成字符串形式(序列化),又如何从字符串形式变成内存里的树(反序列化)
如何判断一棵二叉树是不是另一棵二叉树的子树
以“#”代表空,“_”代表结束
public static String serialByPre(Node head){
if(head == null){
return "#_";
}
String res = head.value + "_";
res += serialByPre(head.left);
res += serialByPre(head.right);
return res;
}
public static Node reconByPreString(String preStr){
String[] values = preStr.split("_");
Queue<String> queue = new LinkedList<String>();
for(int i = 0 ; i != value.length; i++){
queue.add(value[i]);
}
return reconByPreOrder(queue);
}
public static Node reconPreOrder(Queue<String> queue){
String value = queue.poll();
if(value.equals("#"){
return null;
}
Node head = new Node(Integer.valueOf(value));
head.left = reconPreOrder(queue);
head.right = reconPreOrder(queue);
return head;
}
6 折痕问题
请把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折一次,压出折痕后展开。
此时折痕是凹下去的,即折痕凸起的方向指向纸条的背面。
如果从纸条的下边向上方连续对折两次,压出折痕后展开,此时右三条折痕,从上到下依次是下折痕、下折痕和上折痕
给定一个输入参数N,代表纸条都从下边向上方连续对折N次。
请从上到下打印所有折痕的方向。
例如:N=1时,打印 down , N=2时,打印: down down up
解题分析:即是一棵左子树为凹,右子树为凸的满二叉树
pubulic static void printAllFolds(int N){
printProcess(1,N,true);
}
//递归过程,来到了某一个节点
//i是节点的层数,N一共的层数, down == true 凹, down == false 凸
public static void printProcess(int i, int N, boolean down){
if(i > N){
return;
}
printProcess(i + 1, N, true);
System.out.println(down ? "凹" : "凸");
printProcess(i + 1, N, false);
}