1、
链表遍历分两种结构,迭代和递归。
同样根据链表可以 推导出二叉树和N叉树的遍历框架。
再由n叉树的遍历扩展到图的遍历。
手把手带你刷二叉树(第一期)/* 二叉树遍历框架 */
void traverse(TreeNode root) {
// 前序遍历
traverse(root.left)
// 中序遍历
traverse(root.right)
// 后序遍历
}
快速排序可以看作 二叉树的前序遍历,归并可以看作后序遍历
写递归算法的秘诀
写树相关的算法,简单说就是,先搞清楚当前 root
节点「该做什么」以及「什么时候做」,然后根据函数定义递归调用子节点,递归调用会让孩子节点做相同的事情。
所谓「该做什么」就是让你想清楚写什么代码能够实现题目想要的效果,所谓「什么时候做」,就是让你思考这段代码到底应该写在前序、中序还是后序遍历的代码位置上。
226 题-二叉树上的每一个节点的左右子节点进行交换
二叉树上的每一个节点的左右子节点进行交换。
递归实现: --深度优先
转换左右孩子的时候,左右孩子的孩子已经到位了。/或者先转换左右孩子,再转换子孙
可以放在前序遍历和后续遍历的位置。
错误1:没有return 错误2,左右孩子搞反了 错误3:没有判断为空
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root==null){
return null;
}
TreeNode tmp= root.right;
root.right=root.left;
root.left=tmp;
invertTree(root.left);
invertTree(root.right);
return root;
}
}
迭代实现: --广度优先
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null) {
return null;
}
//将二叉树中的节点逐层放入队列中,再迭代处理队列中的元素
LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
while(!queue.isEmpty()) {
//每次都从队列中拿一个节点,并交换这个节点的左右子树
TreeNode tmp = queue.poll();
TreeNode left = tmp.left;
tmp.left = tmp.right;
tmp.right = left;
//如果当前节点的左子树不为空,则放入队列等待后续处理
if(tmp.left!=null) {
queue.add(tmp.left);
}
//如果当前节点的右子树不为空,则放入队列等待后续处理
if(tmp.right!=null) {
queue.add(tmp.right);
}
}
//返回处理完的根节点
return root;
}
}
LinkedList创建: LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
( )别忘了
poll 删除返回第一个元素,add加入一个元素。
解析: 当换完左右子树后,再对孙子节点交换,也就是队列中推入孩子节点
注意: 循环退出的条件,然后循环里面要干什么:用临时变量交换左右孩子。 tmp是某一个根节点
错误:要用while不能用 if
116. 填充每个节点的下一个右侧节点指针
tips: 转换为节点要做的事情,这里设置为两个节点。
方法1:递归函数法
从根节点开始,定义函数conect_two 。 如果connect的两个节点不为空,就连接。
left.left - left.right /left.right --right.left /right.left-right.right
class Solution {
public Node connect(Node root) {
if(root==null){
return null; }
connectTwo(root.left,root.right);
return root;
}
public void connectTwo(Node first,Node second){
if(first==null || second==null ){
return ; //不是null
}
first.next=second;
connectTwo(first.left,first.right);
connectTwo(first.right,second.left);
connectTwo(second.left,second.right);
return;
}
}
错误: 忘记了 first.next=second; 即本层忘了连接了。
函数可以不加public。
方法2递归
两个指针,从根节点开始往中间靠拢,直到最后一层。
class Solution {
public Node connect(Node root) {
dfs(root);
return root;
}
void dfs(Node root) {
if(root==null) {
return;
}
Node left = root.left;
Node right = root.right;
//配合动画演示理解这段,以root为起点,将整个纵深这段串联起来
while(left!=null) {
left.next = right;
left = left.right;
right = right.left;
}
//递归的调用左右节点,完成同样的纵深串联
dfs(root.left);
dfs(root.right);
}
}
方法3 迭代
一种是有共同父节点,可以直接填充下一个。一种是跨了父节点,可以通过上一层的串联来获得next即为root.right.next => root.next.left
class Solution {
public Node connect(Node root) {
if(root==null) {
return root;
}
Node pre = root;
//循环条件是当前节点的left不为空,当只有根节点
//或所有叶子节点都出串联完后循环就退出了
while(pre.left!=null) {
Node tmp = pre;
while(tmp!=null) {
//将tmp的左右节点都串联起来
//注:外层循环已经判断了当前节点的left不为空
tmp.left.next = tmp.right;
//下一个不为空说明上一层已经帮我们完成串联了
if(tmp.next!=null) {
tmp.right.next = tmp.next.left;
}
//继续右边遍历
tmp = tmp.next;
}
//从下一层的最左边开始遍历
pre = pre.left;
}
return root;
}
}
114. 二叉树展开为链表
假设左右子树已经拉直了
同样用递归的方法,假设左右子树已经拉直了。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public void flatten(TreeNode root) {
if (root==null){
return ;
}
flatten(root.left);
flatten(root.right);
//错了
// root.left.right=root.right;
// root.right=root.left;
// root.left=null; 这里把p 设置为根节点比较好,否则p会为空
TreeNode left_=root.left;
TreeNode right_=root.right;
root.right=left_;
TreeNode p=root;
while(p.right!=null){
p=p.right;
}
p.right=right_;
root.left=null;
}
错误: 把左子树放到右孩子后, 原来的右子树要 放在 最下节点(要一直遍历到下面) 的右边
左子树要置空
迭代的方法
将左子树放到右子树的位置,右子树放到左子树的最右下。
public void flatten(TreeNode root) {
while (root != null) {
//左子树为 null,直接考虑下一个节点
if (root.left == null) {
root = root.right;
} else {
// 找左子树最右边的节点
TreeNode pre = root.left;
while (pre.right != null) {
pre = pre.right;
}
//将原来的右子树接到左子树的最右边节点
pre.right = root.right;
// 将左子树插入到右子树的地方
root.right = root.left;
root.left = null;
// 考虑下一个节点
root = root.right;
}
}
}
关键: 找到右子树的前驱节点 然后再考虑 下一个节点root = root.right;
类后序遍历
其实要做的是前序遍历的结果倒过来,1-2-3-4-5-6 到6-5-4-3-2-1
遍历顺序 右 左 中。
--- 还有一种解,先略过。
手把手带你刷二叉树(第二期)654. 最大二叉树
class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
return construct(nums,0,nums.length-1); //可以直接return
}
public TreeNode construct(int[] nums,int left,int right){
TreeNode root;
// if (left>right || left==right){
// root =new TreeNode(nums[left],null,null);
// //这里不能用left 或者right , 直接改成 low>high返回null
// return root;
// }
if (left>right){
return null;
}
if(left<right || left==right){
int index = findMax(nums,left,right);
root =new TreeNode(nums[index]);
root.left=construct(nums,left,index-1);
root.right=construct(nums,index+1,right);
return root;
}
return null;
}
//不是 0改成 最小值,其次要改成小于等于,index是-1
public int findMax (int[] nums,int l,int r){
if (l==r){
return l;
}
int max =Integer.MIN_VALUE; int index =0;
for (int i=l;i<=r;i++){
if (nums[i]>max){
max=nums[i];
index=i;
}
}
return index;
}
}
数组参数: .length 可以获得长度 最小值:Integer.MIN_VALUE
问题:
判断节点条件,可以把子右孩子看作节点单独判断,若left>right则返回空,这样比较好处理
其次,索引i<=r,因为传入的就是length-1
105通过前序和中序遍历结果构造二叉树
递归版本
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return construct(preorder,inorder,0,preorder.length-1,0,inorder.length-1);
}
public TreeNode construct(int[] pre,int[] ino,int left_1,int right_1,int left_2,int right_2)
{
int index=0;
TreeNode root=new TreeNode(); //注意要初始化
if (left_1 >right_1){
return null;
}
int root_val=pre[left_1];
for(int i=left_2;i<=right_2;i++){
if (ino[i]==root_val){
index=i;
break;
//加个break
}
//错误2:index-left2 =size,不用+1
int size =index-left_2;
root.val=root_val;
root.left=construct(pre,ino,left_1+1,left_1+size,left_2,index-1);
root.right=construct(pre,ino,left_1+1+size,right_1,index+1,right_2 );
return root;
}
}
问题:
找index时,可以加个break加快速度。 其次是算size时不用+1。 最后变量注意要初始化
迭代版本
补充
给一个随机数组,生成相应的二叉搜索树
https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/
根据前序和中序,输出后序