队列与栈复习I

133. 克隆图

给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。

图中的每个节点都包含它的值 valint) 和其邻居的列表(list[Node])。

class Node {
    public int val;
    public List<Node> neighbors;
}

 深拷贝就是新建一个对象,和源对象的任何属性都相同,只是在内存中的地址不同,然后返回这个对象;浅拷贝就是用一个新指针指向源对象

深拷贝也就是克隆,我们要新建对象,赋值,同时还要处理邻接表,保证每个邻居也都是克隆的,同时为了防止死循环,还要用visited来储存已经处理好的节点,注意如果只考虑防止死循环用集合即可,但是为了考虑返回值,我们要使用哈希表,如果已处理过,我们返回目标节点即可

1.DFS,递归处理

/*
 * @lc app=leetcode.cn id=133 lang=java
 *
 * [133] 克隆图
 */

// @lc code=start
/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> neighbors;
    public Node() {
        val = 0;
        neighbors = new ArrayList<Node>();
    }
    public Node(int _val) {
        val = _val;
        neighbors = new ArrayList<Node>();
    }
    public Node(int _val, ArrayList<Node> _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
}
*/

class Solution {
    public Node cloneGraph(Node node) {
        HashMap<Integer,Node>visited=new HashMap<>();
        return Clone(node, visited);
    }
    Node Clone(Node node,HashMap<Integer,Node>visited){
        //剪枝一:空
        if(node==null)return null;
        int val=node.val;
        //剪枝二:已处理过,直接返回
        if(visited.containsKey(val))
            return visited.get(val);
        //新建节点,赋值
        Node res=new Node(val);
        //将节点加入哈希表中
        visited.put(res.val,res);
        //处理节点的邻接表,邻居也必须要是克隆节点
        for(Node neighbor:node.neighbors){
            Node n=Clone(neighbor, visited);
            res.neighbors.add(n);
        }
        return res;
    }
}

2.BFS

        //和DFS的区别在于
        //DFS时我们可以先处理每个结点的邻居再处理本节点的邻接表,不需要显示储存原节点
        //而BFS时我们需要记录本结点的邻接表再逐个处理,因此不如保留源节点来处理邻接表
        //处理完邻接表再返回新节点

 代码

/*
 * @lc app=leetcode.cn id=133 lang=java
 *
 * [133] 克隆图
 */

// @lc code=start
/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> neighbors;
    public Node() {
        val = 0;
        neighbors = new ArrayList<Node>();
    }
    public Node(int _val) {
        val = _val;
        neighbors = new ArrayList<Node>();
    }
    public Node(int _val, ArrayList<Node> _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
}
*/

class Solution {
    public Node cloneGraph(Node node) {
        HashMap<Integer,Node>visited=new HashMap<>();
        //剪枝一:空
        if(node==null)return null;
        Deque<Node>queue=new LinkedList<>();
        Node res=new Node(node.val);
        //和DFS的区别在于
        //DFS时我们可以先处理每个结点的邻居再处理本节点的邻接表
        //而BFS时我们需要记录本结点的邻接表再逐个处理,因此不如保留源节点来处理邻接表啊
        //处理完邻接表再返回新节点
        visited.put(res.val, res);
        //注意传入队列的是源节点
        queue.offer(node);
        while(!queue.isEmpty()){
            //从队列中取出的是原图的节点p
            Node p=queue.poll();
            //加入队列的节点比如已建立映射关系,找到其在新图中对应的节点cur
            Node cur=visited.get(p.val);
            //处理邻接表
            for(Node neigh:p.neighbors){
                if(visited.containsKey(neigh.val)){
                    //邻居已处理,直接赋值给新节点
                    cur.neighbors.add(visited.get(neigh.val));
                }else{
                    //邻居未处理,要将邻居加入队列
                    //新建一个节点并赋值给新节点的邻接表
                    //同时记录本节点,加入visited
                    queue.offer(neigh);
                    Node temp=new Node(neigh.val);
                    cur.neighbors.add(temp);
                    visited.put(temp.val, temp);
                }
            }
        }
        return res;
    }
}

 494. 目标和

给你一个整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :

例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

方法一:经典回溯

这是一颗二叉决策树,虽然有剪枝,但是复杂度仍然是2^n级别

class Solution {
    int way=0;
    public int findTargetSumWays(int[] nums, int target) {
       dfs(nums, target, 0); 
       return way;
    }
    void dfs(int[]nums,int target,int start){
        if(start>=nums.length){
            if(start==nums.length&&target==0)way++;
            return;
        }
        int num=nums[start];
        dfs(nums, target-num, start+1);
        dfs(nums, target+num, start+1);
    } 
}

用时感人,这合理吗,再想想还有没有更好的方法,遂开始DP 

方法二:动态规划

动态规划要找到状态转移方程式,本题有个极其重要的数学推论,如果我们正常思路做,是没有办法找出关系式的,正负分开的数字枚举起来可以是可以但是很复杂,但是枚举原数据很简单,所以我们在这个公式的基础上,目标就是找到,从前面n个数字中找出方法,使得和为neg,然后返回方法数

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        //neg=(sum-target)/2
        int sum=0;
        for(int num:nums){
            sum+=num;
        }
        int sub=sum-target;
        //检验是否为非负偶数
        if(sub<0||sub%2!=0)
            return 0;
        int n=nums.length,neg=sub/2;
        //dp[i][j]
        //从前i个数中选取一些数字,使他们的和为j的方法数
        int[][]dp=new int[n+1][neg+1];
        //00初始化
        dp[0][0]=1;
        for(int i=1;i<=n;i++){
            for(int j=0;j<=neg;j++){
                //前i个数,从零开始,所以要-1
                int num=nums[i-1];
                //当前数不能取,和前面没区别
                dp[i][j]=dp[i-1][j];
                //若当前数字可以取,在原先方法数的基础上,会多出一些方法
                //选择了当前数字,可以在前面i个数找出新的sum,所求方法数,所以相加
                if(j>=num){
                    dp[i][j]+=dp[i-1][j-num];
                }
            }
        }
        //
        return dp[n][neg];
    }
}

 滚动数组优化空间复杂度

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        //neg=(sum-target)/2
        int sum=0;
        for(int num:nums){
            sum+=num;
        }
        int sub=sum-target;
        //检验是否为非负偶数
        if(sub<0||sub%2!=0)
            return 0;
        int n=nums.length,neg=sub/2;
        //dp[i][j]
        //从前i个数中选取一些数字,使他们的和为j的方法数
        int[][]dp=new int[2][neg+1];
        //00初始化
        dp[0][0]=1;
        for(int i=1;i<=n;i++){
            for(int j=0;j<=neg;j++){
                //前i个数,从零开始,所以要-1
                int num=nums[i-1];
                //当前数不能取,和前面没区别
                dp[i%2][j]=dp[(i+1)%2][j];
                //若当前数字可以取,在原先方法数的基础上,会多出一些方法
                //选择了当前数字,可以在前面i个数找出新的sum,所求方法数,所以相加
                if(j>=num){
                    dp[i%2][j]+=dp[(i+1)%2][j-num];
                }
            }
        }
        //
        return dp[n%2][neg];
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值