力扣刷题框架——数据结构(二)

原文链接:https://labuladong.github.io/algo/2/19/21/
其实力扣刷题不知道怎么入手可以先刷二叉树题目,因为很多经典算法(回溯,动规,分治)都是树的问题。树的问题逃不开递归遍历框架:

/* 二叉树遍历框架 */
void traverse(TreeNode root) {
    // 前序遍历
    traverse(root.left)
    // 中序遍历
    traverse(root.right)
    // 后序遍历
}

1. 二叉树重要性

快速排序就是个二叉树的前序遍历,归并排序就是个二叉树的后序遍历
快排逻辑:如果要对一个数组nums[low,…,high]排序,先要找一个分界点p,通过交换元素要使得分界点左边的元素都比nums[p]要小,右边的元素都比nums[p]要大。然后再递归寻找左边分界点和右边分界点。快排框架如下:

void sort(int[] nums, int lo, int hi) {
    /****** 前序遍历位置 ******/
    // 通过交换元素构建分界点 p
    int p = partition(nums, lo, hi);
    /************************/

    sort(nums, lo, p - 1);
    sort(nums, p + 1, hi);
}

先构造分界点,然后去左右子数组构造分界点,和二叉树先序遍历一样。
归并逻辑:如果要对一个数组nums[low,…,high]排序,先对nums[low,…,mid]排序,再对nums[mid+1,…,high]排序,再两者合并。代码框架:

void sort(int[] nums, int lo, int hi) {
    int mid = (lo + hi) / 2;
    sort(nums, lo, mid);
    sort(nums, mid + 1, hi);

    /****** 后序遍历位置 ******/
    // 合并两个排好序的子数组
    merge(nums, lo, mid, hi);
    /************************/
}

二叉树的递归就是要明确函数定义,用定义推结果。明确每个节点要干的事情,明确要干的事情是在前中后哪个位置干。

2. 翻转二叉树

2.1 题目描述

翻转一颗二叉树:
输入
在这里插入图片描述
输出
在这里插入图片描述

2.2 解题思路

翻转整棵树就是交换每个节点的左右子节点,可以把交换左右子节点的代码放在前序或者后序遍历的位置。
代码

    public TreeNode invertTree(TreeNode root) {
        if(root==null)
            return null;
        TreeNode tempnode = root.left;
        root.left = root.right;
        root.right = tempnode;
        invertTree(root.left);
        invertTree(root.right);
        return root;

    }

3. 填充二叉树右侧指针

3.1 题目描述

在这里插入图片描述
输入

root = [1,2,3,4,5,6,7]

输出
[1,#,2,3,#,4,5,6,7,#]

解释
在这里插入图片描述

3.2 解题思路

题目的要求就是把每层二叉树的节点从左到右串起来,二叉树的问题难点在于,如何把题目的要求细化成每个节点需要做的事情。这边我们可以细化成把一个节点的左右子节点串起来,把相邻两个节点的右节点和左节点串起来。所以需要增加函数参数,传入两个节点。

public class connectNode {
    public Node connect(Node root) {
        if(root==null)
            return null;
        connectNodes(root.left,root.right);
        return root;
    }
    public void connectNodes(Node node1,Node node2){
        if(node1==null||node2==null)
            return;
        node1.next = node2;
        // 连接相同父节点的两个子节点
        connectNodes(node1.left,node1.right);
        // 连接跨越父节点的两个子节点
        connectNodes(node1.right,node2.left);
        // 连接相同父节点的两个子节点
        connectNodes(node2.left,node2.right);
    }
}

4. 将二叉树展开为链表

4.1 题目描述

在这里插入图片描述
输入
root = [1,2,5,3,4,null,6]

输出
[1,null,2,null,3,null,4,null,5,null,6]

4.2 解题思路

可以简化一下问题,如果root只有左右两个子节点,怎么拉平成一个链表?以下流程:将 root 的右子树接到左子树下方,然后将整个左子树作为右子树。注意递归框架是后序遍历,因为我们要先拉平左右子树才能进行后续操作。
在这里插入图片描述
代码

public class FlattenTree {
    public void flatten(TreeNode root) {
        if(root==null)
            return;
        flatten(root.left);
        flatten(root.right);
        TreeNode left = root.left;
        TreeNode right = root.right;
        root.left = null;
        //将整个左子树作为右子树
        root.right = left;
        TreeNode temp = root;
        //找到右子树的末端
        while (temp.right!=null){
            temp = temp.right;
        }
        //原来的右子树接到左子树下方
        temp.right = right;
        return;
    }
}

5. 构造最大二叉树

5.1 题目描述

给定一个不含重复元素的整数数组 nums 。一个以此数组直接递归构建的 最大二叉树 定义如下:

  1. 二叉树的根是数组 nums 中的最大元素。
  2. 左子树是通过数组中 最大值左边部分 递归构造出的最大二叉树。
  3. 右子树是通过数组中 最大值右边部分 递归构造出的最大二叉树。
    返回有给定数组 nums 构建的 最大二叉树 。

输入
nums = [3,2,1,6,0,5]

输出
[6,3,5,null,2,0,null,null,1]

解释

  • [3,2,1,6,0,5] 中的最大值是 6 ,左边部分是 [3,2,1] ,右边部分是 [0,5] 。
    • [3,2,1] 中的最大值是 3 ,左边部分是 [] ,右边部分是 [2,1] 。
      • 空数组,无子节点。
      • [2,1] 中的最大值是 2 ,左边部分是 [] ,右边部分是 [1] 。
        • 空数组,无子节点。
        • 只有一个元素,所以子节点是一个值为 1 的节点。
    • [0,5] 中的最大值是 5 ,左边部分是 [0] ,右边部分是 [] 。
      • 只有一个元素,所以子节点是一个值为 0 的节点。
      • 空数组,无子节点。
        在这里插入图片描述

5.2 解题思路

要明确根节点要做什么,这里根节点要做的就是把自己构造出来。遍历数组找最大值,把根节点root做出来,然后对最大值左边和右边数组做递归调用,作为根节点左右子树。
代码

public class ConstructMaxTree {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        int n = nums.length;
        if(n==0)
            return null;
        //找数组中最大值
        int maxVal = nums[0],index = 0;
        for(int i=0;i<n;i++){
            if(nums[i]>=maxVal){
                maxVal = nums[i];
                index = i;
            }
        }
        TreeNode root = new TreeNode(maxVal);
        //递归调用左右子树
        if(index>0) {
            int[] left = Arrays.copyOfRange(nums, 0, index);
            root.left = constructMaximumBinaryTree(left);
        }
        else
            root.left = null;
        if(index<n-1){
            int[] right = Arrays.copyOfRange(nums,index+1,n);
            root.right = constructMaximumBinaryTree(right);
        }
        else
            root.right = null;
        return root;


    }
}

6. 寻找重复子树

6.1 题目描述

给定一棵二叉树,返回所有重复的子树。对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。
两棵树重复是指它们具有相同的结构以及相同的结点值。

示例
在这里插入图片描述

6.2 解题思路

假设站在某个节点上,想知道以自己为根的子树是不是重复的,是否应该被加入结果列表中。需要知道以下两点:
1、以我为根的这棵二叉树(子树)长啥样?
2、以其他节点为根的子树都长啥样?

以该节点为根的子树长啥样——可以把二叉树进行序列化展示,例如示例1的二叉树,序列化出来:“1,2,4,#,3,2,4,#,#,#,4,#,#”(按照任意一种原则遍历即可)
以其他节点为根的子树都长啥样?——使用哈希表存贮每个节点为根的序列化结果,记录对应的数量。

代码

public class findDuplicateSubtrees652 {
    Map<String,Integer> subtree = new HashMap<>();//子树序列数量
    List<TreeNode> ans = new ArrayList<>();
    public List<TreeNode> findDuplicateSubtrees(TreeNode root) {

        findSubTree(root);
        return ans;

    }
    //返回序列化结果
    public String findSubTree(TreeNode root){
        if(root==null) return "#";
        String treeseq = root.val + "," + findSubTree(root.left) + "," +findSubTree(root.right);
        subtree.put(treeseq,subtree.getOrDefault(treeseq,0)+1);
        if (subtree.get(treeseq)==2){
            ans.add(root);
        }
        return treeseq;


    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值