算法-搜索与回溯-目标和-offer32

回溯算法

概念

实际上一个类似枚举的搜索尝试过程。
满足回溯条件的某个状态的点称为“回溯点”。

搜索过程
在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根节点出发深度搜索解空间树。
当搜索到某一节点时,要先判断该节点是否包含问题的解。
如果包含,就从该节点出发继续探索下去;如果不包含,则逐层向其祖先元素节点回溯。

解题步骤

定义一个解空间,包含所有的解;
利用适于搜索的方法组织解空间;
利用深度优先法搜索解空间;
利用限界函数避免移动到不可能产生解的子控件。

广度优先搜索(BFS Breadth-First Search)

基本过程:从根节点开始,沿着树的宽度遍历树的节点。如果所有节点均被访问,则算法中止。
通常借助队列的先进先出特性来实现。
具体操作:

把起始节点放入queue
重复下述两步,直至queue为空为止;
从queue中取出队列头的节点;
找出与此点临近的且未遍历的点,进行标记,然后全部放入queue中。

做题步骤
根判断是否为空,为空返回;
队列根元素先进;
循环取队头;
看值左右,如果存在就进队列;

目标和(leetcode494)

需求
给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

示例
输入:nums = [1,1,1,1,1], target = 3 输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3

输入:nums = [1], target = 1 输出:1

回溯
class Solution {

    // 定义变量count 用于统计表达式等于target的数量
    int count = 0 ;

    public int findTargetSumWays(int[] nums, int target) {
        backrack(nums,target,0,0);
        return count;
    }

    public void backrack(int[] nums,int target,int index,int sum){
        //回溯点
        if(index == nums.length){
            if(sum == target){
                count++;
            }
        }else{
            backrack(nums,target,index+1,sum+nums[index]);
            backrack(nums,target,index+1,sum-nums[index]);
        }
    }
}
动态规划

2022/7/4写这个明天一早就写。(计划)
2022/7/6 下午写的。

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        // 题目分析,计算数组的和 以及定义要添加负号的元素的和
        // neg = 添加负号元素的和 sum-neg 表示添加正号元素的和 target = sum-2*neg
        int len = nums.length;
        int sum = 0;
        for(int num : nums){
            sum += num;
        }
        int neg = (sum-target) / 2 ;

        // 特殊值
        if(neg < 0 || (sum - target) %2 != 0)
            return 0;

        // 定义状态
        int[][] dp = new int[len+1][neg+1];
        // 表示前i个元素中,选取元素,使结果等于j的个数

        // 特殊值
        // 如果没有元素 则dp[0][j] = 0;其中dp[0][0]的结果是1 没有选元素,结果0 这个是满足的。
        dp[0][0] = 1;
        for(int j = 1; j < neg+1 ; j++){
            dp[0][j] = 0;
        }
        // 没有选择选择

        // 用于保存当前i对应的nums中的值
        int num = 0;
        // 转移方程
        for(int i = 1 ; i < len+1;i++){
            num = nums[i-1];
            for(int j = 0 ; j < neg+1 ; j++){
                if(j < num){
                    dp[i][j] = dp[i-1][j];
                }else{
                    dp[i][j] = dp[i-1][j] + dp[i-1][j-num];
                } 
            }
        }
        return dp[len][neg];
    }
}

offer32 从上到下打印二叉树I

需求
从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。

示例
给定二叉树: [3,9,20,null,null,15,7] 输出 [3,9,20,15,7]
在这里插入图片描述

方法:广度优先搜索

算法流程

public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
1 特例处理
	当树的根节点为空,则直接返回空列表
2 初始化
	打印结果列表res=[] 包含根节点的队列 queue = [root]
3 BFS循环 当队列queue为空时跳出
	出队:队首元素出队,记为node
	打印:将node.val添加到列表res的尾部
	添加子节点:若node的(左右)子节点不为空,则将(左右子节点加入到队列queue)。
4 返回res列表

代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] levelOrder(TreeNode root) {
        // BFS
        Queue<TreeNode> que = new LinkedList<TreeNode>();
        que.add(root);

        List<Integer> list = new ArrayList<Integer>();

        if(root == null)
            return new int[0];
        
        TreeNode node = new TreeNode(root.val);
        while(!que.isEmpty()){
            node = que.poll();
            list.add(node.val);
            if(node.left != null) que.add(node.left);
            if(node.right != null) que.add(node.right);
        
        }
        int[] arr = new int[list.size()];
        for(int i=0 ; i < list.size() ; i++)
            arr[i] = list.get(i);
        return arr;
    }
}

offer32 从上到下打印二叉树II

需求
从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。

示例
在这里插入图片描述

方法:广度优先搜索

incompatible types: ArrayList<ArrayList< Integer>> cannot be converted to List<List< Integer>>

代码错误分析

操作:
List<List<Integer>> = new ArrayList<ArrayList<Integer>>();

报错:
incompatible types: ArrayList<ArrayList<Integer>> cannot be converted to List<List<Integer>>

解决:
List<List<Integer>> = new ArrayList<List<Integer>>();
将第二个ArrayList改为List就可以了。

原因:
这是一个泛型应用的常踩坑。
在这里插入图片描述
代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        // BFS 广度优先搜索
        List<List<Integer>> list = new ArrayList<List<Integer>>();

        // if(list1.isEmpty())
        //     return new List<List<Integer>>();
        if(root == null)
            return new ArrayList<List<Integer>>();
        
        TreeNode node;

        Queue<TreeNode> que = new LinkedList<TreeNode>();
        if(root != null) que.add(root);

        while(!que.isEmpty()){
            List<Integer> list1 = new ArrayList<Integer>();
            for(int i = que.size() ; i > 0 ; i--){
             // 这里必须采用倒叙的形式,因为que队列的长度会改变。
                node = que.poll();
                list1.add(node.val);
                if(node.left != null) que.add(node.left);
                if(node.right != null) que.add(node.right);
            }
            list.add(list1);    
        }
        return list;
    }
}

重点
使用队列.sise() 倒叙遍历队列,因为正序的话,由于修改了que的元素个数,que.size()会发生变化,致使结果出现错误;

offer32 从上到下打印二叉树III

需求
请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

示例
在这里插入图片描述

方法1:层序遍历 + 双端队列

分析
在这里插入图片描述
需要注意的是存储TreeNode节点的队列是queue普通的队列,而List< List< Integer>> 集合的元素是 双端队列 LinkedList

代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        // 层级遍历 + 双端队列
        List<List<Integer>> list = new ArrayList<List<Integer>>();

        Queue<TreeNode> que = new LinkedList<TreeNode>();

        if(root == null) return new ArrayList<List<Integer>>();
        else que.add(root);

        TreeNode node; 

        while(! que.isEmpty()){
            LinkedList<Integer> temp = new LinkedList<Integer>();
            for(int i = que.size() ; i > 0 ; i--){
                // if(list.size() %2 == 0) node = que.pollFirst();
                // else node = que.pollLast();
                // 不可以通过奇偶数 抽取元素队首还是队尾。因为que会一直添加元素的。
                // temp.add(node.val);

                node = que.poll();
                if(list.size() % 2 != 0) temp.addFirst(node.val);
                else temp.addLast(node.val);
                if(node.left != null) que.add(node.left);
                if(node.right != null) que.add(node.right);

            }
            list.add(temp);
        }
        return list;
    }
}

时间复杂度O(N) : N为二叉树的节点数量,BFS需要循环N次,也就是for循环那里
空间复杂度O(N) : 最差的情况是满二叉树,最多有N/2个节点同时在LinkedList队列中

方法2:层序遍历 + 双端队列(奇偶层逻辑分离)

分析
在这里插入图片描述

代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        // 层次遍历 + 双端队列(奇偶层分离)

        Deque<TreeNode> que = new LinkedList<TreeNode>();

        List<List<Integer>> list = new ArrayList<List<Integer>>();

        if(root == null) return new ArrayList<List<Integer>>();
        else que.addLast(root);

        TreeNode node;
        while(! que.isEmpty()){
            // 奇数层
            List<Integer> temp = new ArrayList<Integer>();
            for(int i = que.size() ; i > 0 ; i--){
                node = que.removeFirst();
                temp.add(node.val);
                if(node.left != null) que.addLast(node.left);
                if(node.right != null) que.addLast(node.right);
            }
            list.add(temp);
            
            if(que.isEmpty()) break;

            temp =  new ArrayList<Integer>();
            for(int i = que.size(); i > 0 ; i--){
                node = que.removeLast();
                temp.add(node.val);
                if(node.right != null) que.addFirst(node.right);
                if(node.left != null) que.addFirst(node.left);
            }
            list.add(temp);
        }
        return list;
    }
}

时间复杂度O(N) : N为二叉树的节点数量,BFS需要循环N次,也就是for循环那里
空间复杂度O(N) : 最差的情况是满二叉树,最多有N/2个节点同时在LinkedList队列中

方法3:层序遍历 + 倒序

分析
通过层级判断,然后使用Collections.reverse完成翻转。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        // 层序遍历 + 倒序Collections.reverse();
        List<List<Integer>> list = new ArrayList<List<Integer>>();

        Queue<TreeNode> que = new LinkedList<TreeNode>();

        if(root == null) return new ArrayList<List<Integer>>();
        else que.add(root);

        TreeNode node;

        while(! que.isEmpty()){
            List<Integer> temp = new ArrayList<Integer>();
            for(int i = que.size() ; i > 0 ; i--){
                node = que.poll();
                temp.add(node.val);
                if(node.left != null) que.add(node.left);
                if(node.right != null) que.add(node.right);
            }
            if(list.size() %2 != 0) Collections.reverse(temp);
            list.add(temp);
        }
        return list;
    }
}

offer26 数的子结构

需求
在这里插入图片描述
在这里插入图片描述

方法

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        // 特殊值
        if( A == null || B == null)
            return false;
        return (recur(A,B) || isSubStructure(A.left,B) || isSubStructure(A.right,B));
        
    }
    public boolean recur(TreeNode A,TreeNode B){
        if(B == null)
            return true;
        if(A == null)
            return false;
        if(A.val != B.val)
            return false;
        return recur(A.left,B.left) && recur(A.right,B.right);
    }
}

分析
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值