目录
部分递归练习题在后面的二叉树模版中
二叉树
二叉树的先序、中序、后序遍历
先序:任何子树的处理顺序都是,先头节点、再左子树、然后右子树
中序:任何子树的处理顺序都是,先左子树、再头节点、然后右子树
后序:任何子树的处理顺序都是,先左子树、再右子树、然后头节点
递归序
每个结点都会经过三次
前序,第一到达打印
中序,第二次
后序,第三次
非递归遍历
前序遍历
准备一个栈,
1.第一步将非空根结点压栈
出栈并打印
2.先将非空右子树结点压栈
3.后将非空左子树结点压栈
出栈并打印
重复2,3
public static void pre(Node head) {
System. out. print("pre-order: ");
if (head != nu1l) {
Stack<Node> stack = new Stack<Node>();
stack. add(head); .
while (!stack. isEmpty()) {
head = stack. pop();
Sys tem . out . print(head. value +");
if (head.right != nu1l) {
stack. push(head. right);
}
if (head.left != nu11) {
stack. push(head.1eft);
}
}
}
System. out . print1n();
}
这样打印出来是 头 左右
如果先压入左孩子结点
打印顺序就是 头 右 左
逆序之后即为,左右头,即后序遍历的顺序
后序非递归:
public static void pos1(Node head) {
System. out . print(" pos-order: ");
if (head != nu1l) {
Stack<Node> s1 = new Stack<Node>();
Stack<Node> s2 = new Stack<Node>();
s1. push(head);
while (!s1. isEmpty()) {
head = s1.pop( );
s2. push(head);
if (head.1eft != nu11) {
s1. push(head.1eft);
}
if (head.right != nu11) {
s1. push(head.right);
}
while (!s2.isEmpty()) {
System . out . print(s2. pop().value + " ");
}
}
System. out . println();
}
中序非递归
1、先依次压入左边的结点,直到最左结点为空
2、弹出并打印,如果有右子数,压入栈中
继续执行1
public static void in(Node head) {
System. out . print(" in-order: ");
if (head != null) {
Stack<Node> stack = new Stack<Node>();
while (!stack. isEmpty()|| head != null) {
if (head != nu1l) {
stack. push(head);
head = head.left;
} else {
head = stack. pop();
System. out . print(head.value + " ");
head = head.right;
}
}
}
System. out . println( );
}
public static void main(String[] args) {
TreeNode node = new TreeNode(1);
TreeNode node2 = new TreeNode(2);
TreeNode node3 = new TreeNode(3);
TreeNode node4 = new TreeNode(4);
node.left=node2;
node.right=node3;
node2.right=node4;
in(node);
}
实现二叉树的按层遍历
1 其实就是宽度优先遍历,用队列
2可以通过设置flag变量的方式,来发现某一层的结束(看题目)
public static void 1evel(Node head) {
if (head == nu11) {
return;
}
Queue<Node> queue = new LinkedList<>();
queue . add(head);
while (!queue . isEmpty()) {
Node cur = queue.poll();
System . out . println(cur .value);
if (cur.left != null) {
queue. add(cur .left);
}
if (cur.right != null) {
queue. add(cur .right);
}
}
}
求二叉树最大宽度
此方法也可以求最大深度
public static int maxWidthUseMap(Node head) {
if (head == nu11) {
return 0;
Queue<Node> queue = new LinkedList<>();
queue . add(head);
//key在哪--层,value
HashMap<Node, Integer> levelMap = new HashMap<>();
levelMap. put(head, 1);
int curLevel = 1; //当前你正在统计哪一层的宽度
int curLevelNodes = 0; //当前层curLeve1层, 宽度目前是多少.
int max = 0;
while (!queue . isEmpty()) {
Node cur = queue. po11();
int curNodeLevel = levelMap. get(cur);
if (cur.1eft != nu1l) {
levelMap. put(cur .1eft, curNodeLevel + 1);
queue. add( cur.left);
}
if (cur .right != nu1l) {
levelMap. put(cur. right, curNodeLevel + 1);
queue . add(cur.right);
}
if (curNodeLevel == curLevel) {
curLeve 1Nodes++;
} else {
max = Math .max(max, curLevelNodes);
curLevel++;
curLevelNodes = 1;
}
}
// 因为最后一层没有结算,所以需要再比较一次
max = Math . max(max, curLevelNodes);
return max;
}
二叉树的序列化和反序列化
1)可以用先序或者中序或者后序或者按层遍历,来实现二叉树的序列化
2)用了什么方式序列化,就用什么样的方式反序列化
即实现保存整个树,然后再重塑这个树
比如
1 1
1 1
1 1
两颗树,但是只记录顺序是无法还原的
解决办法
1.不要忽略空结点,然后正常遍历这个树
public static Queue<String> preSerial(Node head) {
Queue<String> ans = new LinkedList<>();
pres(head, ans);
return ans ;
}
public static void pres(Node head, Queue<String> ans) {
if (head == nu1l) {
ans. add(nu11) ;
} else {
ans . add( String . valueOf(head. value));
pres(head.left, ans);
pres(head.right, ans);
}
}
层序遍历序列化
public static Queue<String> levelSerial(Node head) {
Queue<String> ans = new LinkedList<>();
if (head == nu11) {
ans . add(nu11);
} else {
ans . add(String . value0f(head.value));
Queue<Node> queue = new LinkedL ist<Node>() ;
queue. add(head);
while (!queue . isEmpty()) {
head = queue. po11();
if (head.1eft != nu11) {
ans . add(String. value0f(head. left.value));
queue. add(head.1eft);
} else {
ans . add(nu11);
if (head.right != nu1l) {
ans. add(String. valueOf(head. right .value));
queue . add( head. right) ;
} else {
ans . add(nu11);
}
}
}
return ans;
}
反序列化
public static Node preb(Queue<String> prelist) {
String value = prelist.pol1();
if (value == nu1l) {
return nu11 ;
}
Node head = new Node(Integer . value0f(value));
head.1eft = preb(prelist);
head.right = preb(prelist);
return head;
}
层序反序列
public static Node generateNode(String val) {
if (val == nu1l) {
return nu11 ;
}
return new Node(Integer . value0f(val));
}
public static Node buildByLeve1Queue (Queue<String> levelList) {
if (levelList == nu1l | | levelList.size() == 0) {
return nu1l ;
}
Node head = generateNode(levelList. po11());
Queue<Node> queue = new LinkedL ist<Node>();
if (head != nu1l) {
queue . add (head);
}
Node node = nu1l;
while (!queue . isEmpty()) {
node = queue. pol1( );
node.1eft = generateNode (levelList. pol1());
node.right = generateNode(levelList.po11());
if (node.1eft != nu1l) {
queue . add(node.1eft);
}
if (node.right != nu11) {
queue . add( node. right);
}
}
return head;
}
如何设计一个打印整棵树的打印函数
1.把树补成满二叉树
2.将每颗子树都按照头右左的顺序打印
public static void printTree (Node head) {
System. out . println("Binary Tree:");
printInOrder(head, 0,"H", 17);
System. out . println();
}
public static void printInOrder(Node head, int height, String to, int len) {
if (head == nu1l) {
return;
}
printInOrder(head.right, height + 1, "V", len);
// 控制空格数,
String val = to + head.value + to;
int 1enM = val.length();
int lenL = (1en - lenM) / 2;
int lenR = len - lenM - lenL;
val = getSpace(lenL) + val + getSpace(lenR);
System. out . println(getSpace(height * len) + val);
printInOrder(head.left, height + 1, "^", len);
}
给你二叉树中的某个节点,返回该节点的后继节点
二叉树结构如下定义:
Class Node {
V value;
Node left;
Node right;
Node parent;
}
后继节点指按中序遍历遍历后,该结点后面的节点即为当前节点的后继节点
1.找到头节点,然后中序遍历,找后继节点
2.能够直接找到?
-
某个节点的后续是右子数上的最左节点
-
某个节点没有右子树,往上找父亲节点的左孩子,该节点是该节点 父亲节点的左孩子 就停
那个父亲节点就是后继节点
3.整棵树的最右节点是没有后继的,返回null
public static Node getSuccessorNode(Node node) {
if (node = nu1l) {
return node;
}
if (node.right != null) {
// 情况1 ,有右树
return getLeftMost(node.right);
} else { //无右子树
Node parent = node . parent ;
while (parent != null && parent.right == node) { //当前节点是其父亲节点右孩子
node = parent;
parent = node. parent;
}
//只有情况2.3是一样的
}
return parent;
}
public static Node getLeftMost(Node node) {
if (node == nu1l) {
return node ;
}
while (node.1eft != nu11) {
node = node .left;
}
return node ;
}
变式题:找前驱节点
纸条折痕问题
请把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折1次,压出折痕
后展开。此时折痕是凹下去的,即折痕突起的方向指向纸条的背面。如果从
纸条的下边向,上方连续对折2次,压出折痕后展开,此时有三条折痕,从上到
下依次是下折痕、下折痕和上折痕。
给定一个输入参数N,代表纸条都从下边向上方连续对折N次。请从上到下打
印所有折痕的方向。
例如:N=1时,打印: down N=2时,打印: down down up
n-1次对折后,n-1次的折痕上方都是 凹折痕,下方都是凸折痕
3次的结果:
3凹
2凹
3凸
-----1凹-----
3凹
2凸
3凸
其实就是一个满二叉树,要想从上到下打印,就是中序遍历
左根右
N代表层数
public 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);
}
public static void main(String[] args) {
intN=3;
printAllFolds(N);
}
递归开N层,空间复杂度O(N)