Leetcode刷题 2021.01.27

Leetcode784 字母大小写全排列

给定一个字符串S,通过将字符串S中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。

这题还是比较容易的,基础的回溯而已,因为对于一个字母来说只有两种路径,所以也只要写两个条件即可。

class Solution {
    List<String> res = new ArrayList<>();
    public List<String> letterCasePermutation(String S) {
        if (S == null || S.length() == 0) return res;
        char[] arr = S.toCharArray();
        helper(arr, 0);

        return res;

    }

    private void helper(char[] arr, int index){
    	//递归终止条件
        if (index >= arr.length) {
            res.add(new String(arr));
            return;
        }
		
		//如果是小写字母,要改成大写,回溯一次,回溯完再改回来,大写同理
        if (arr[index] >= 'a' && arr[index] <= 'z'){
            arr[index] = Character.toUpperCase(arr[index]);
            helper(arr, index + 1);
            arr[index] = Character.toLowerCase(arr[index]);
        }else if (arr[index] >= 'A' && arr[index] <= 'Z'){
            arr[index] = Character.toLowerCase(arr[index]);
            helper(arr, index + 1);
            arr[index] = Character.toUpperCase(arr[index]);
        }
        //自己本身也要做一次,对于数字来说就只做这一次
        helper(arr, index + 1);
    }
}

Leetcode845 数组中的最长山脉

我们把数组 A 中符合下列属性的任意连续子数组 B 称为 “山脉”:

B.length >= 3
存在 0 < i < B.length - 1 使得 B[0] < B1 < … B[i-1] < B[i] > B[i+1] > … > B[B.length - 1]
(注意:B 可以是 A 的任意子数组,包括整个数组 A。)

给出一个整数数组 A,返回最长 “山脉” 的长度。

如果不含有 “山脉” 则返回 0。

这题就是用常规思路写的,先找到山顶,再找山脚。这种双指针的题就是一些细节和边界的地方要多想想,多调试。看题解里面有一些技巧,就算看了面试的时候也想不起来,还是用最朴素的做法做吧。

class Solution {
    public int longestMountain(int[] arr) {
        int res = 0, i = 0, n = arr.length;

        while (i < n){
            int start = i + 1;
            //先找山顶
            while (start < n && arr[start] > arr[start - 1]){
                start++;
            }
            //注意边界
            if (start >= n) return res;
            //如果没有山顶,就继续便利
            if (start == i + 1){
                i++;
            }else if (arr[start] == arr[start - 1]){
                i = start;
            }else{
            	//找山脚
                int end = start + 1;
                while (end < n && arr[end] < arr[end - 1]){
                    end++;
                }
                //更新全局最大
                res = Math.max(res, end - i);
                //这里更新为end-1,因为前一个的终点也有可能是下一个山的起点
                i = end - 1;
            }
        }

        return res;
    }
}

Leetcode1579 保证图可完全遍历

Alice 和 Bob 共有一个无向图,其中包含 n 个节点和 3 种类型的边:

类型 1:只能由 Alice 遍历。
类型 2:只能由 Bob 遍历。
类型 3:Alice 和 Bob 都可以遍历。
给你一个数组 edges ,其中 edges[i] = [typei, ui, vi] 表示节点 ui 和 vi 之间存在类型为 typei 的双向边。请你在保证图仍能够被 Alice和 Bob 完全遍历的前提下,找出可以删除的最大边数。如果从任何节点开始,Alice 和 Bob 都可以到达所有其他节点,则认为图是可以完全遍历的。

返回可以删除的最大边数,如果 Alice 和 Bob 无法完全遍历图,则返回 -1 。

又是困难的并查集,其实有一些思路的,但是要编码的时候又不知道从何下手。最后看了题解的时候才知道也没有多难,应该是可以独立完成的。还是要继续努力啊。
话说对这种反向思维的技巧都不是很在行,这题要求可以删除的边的条数。那么可以反着来,一开始不加任何边,然后先加入公共边,再加入各自需要的边,如果有重复的情况,那说明这条边就是可以删除的。

class Solution {
    public int maxNumEdgesToRemove(int n, int[][] edges) {
    	//初始化两份并查集
        UnionFind a = new UnionFind(n);
        UnionFind b = new UnionFind(n);
        int res = 0;
		
		//让节点都从0开始,并且优先加入公共边,如果已经连通,说明这条边是可以删除的。
        for(int[] edge : edges){
            edge[1]--;
            edge[2]--;
            if (edge[0] == 3){
                if (a.find(edge[1]) != a.find(edge[2])){
                    a.union(edge[1], edge[2]);
                    b.union(edge[1], edge[2]);
                }else{
                    res++;
                }   
            }
        }
		
		//加入各自的边,如果也是已经连通的,那么也可以删除
        for(int[] edge : edges){
            if (edge[0] == 1){
                if (a.find(edge[1]) != a.find(edge[2])){
                    a.union(edge[1], edge[2]);
                }else{
                    res++;
                }
            }else if (edge[0] == 2){
                if (b.find(edge[1]) != b.find(edge[2])){
                    b.union(edge[1], edge[2]);
                }else{
                    res++;
                }
            }
        }
		
		//最后看看各自的连通分量是否都为1,不为1说明无法连通,返回-1
        if (a.getCount() != 1 || b.getCount() != 1){
            return -1;
        }

        return res;
    }

    class UnionFind{
        int[] parent;
        int count;

        public int getCount(){
            return this.count;
        }

        public UnionFind(int n){
            this.count = n;
            parent = new int[n];
            for(int i = 0; i < n; i++){
                parent[i] = i;
            }
        }

        public int find(int i){
            if (parent[i] == i){
                return i;
            }
            return parent[i] = find(parent[i]);
        }

        public void union(int i, int j){
            int root1 = find(i);
            int root2 = find(j);
            if (root1 == root2) return;
            parent[root1] = root2;
            count--;
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值