Leetcode刷题 2021.01.29

Leetcode950 按递增顺序显示卡牌

牌组中的每张卡牌都对应有一个唯一的整数。你可以按你想要的顺序对这套卡片进行排序。

最初,这些卡牌在牌组里是正面朝下的(即,未显示状态)。

现在,重复执行以下步骤,直到显示所有卡牌为止:

从牌组顶部抽一张牌,显示它,然后将其从牌组中移出。
如果牌组中仍有牌,则将下一张处于牌组顶部的牌放在牌组的底部。
如果仍有未显示的牌,那么返回步骤 1。否则,停止行动。
返回能以递增顺序显示卡牌的牌组顺序。

答案中的第一张牌被认为处于牌堆顶部。

乍看之下好像看不出什么规律,所以逆向思维反着来,这样就比较简单了,就是把每一次的队尾放到队头去然后再加上一个新的元素就可以了。java里可以使用LinkedList进行模拟。

class Solution {
    public int[] deckRevealedIncreasing(int[] deck) {
        LinkedList<Integer> queue = new LinkedList<>();
        //先排下序
        Arrays.sort(deck);
        //倒着模拟,每次将队尾放到对头去,然后在队头加入一个新的元素
        for(int i = deck.length - 1; i >= 0; i--){
            if (!queue.isEmpty()){
                 queue.offerFirst(queue.pollLast());
            }
            queue.offerFirst(deck[i]);
        }
        //返回数组
        int[] res = new int[deck.length];
        int i = 0;
        for(int ele : queue){
            res[i++] = ele;
        }

        return res;
    }
}

Leetcode1631 最小体力消耗路径

你准备参加一场远足活动。给你一个二维 rows x columns 的地图 heights ,其中 heights[row][col] 表示格子 (row, col) 的高度。一开始你在最左上角的格子 (0, 0) ,且你希望去最右下角的格子 (rows-1, columns-1) (注意下标从 0 开始编号)。你每次可以往 上,下,左,右 四个方向之一移动,你想要找到耗费 体力 最小的一条路径。

一条路径耗费的 体力值 是路径上相邻格子之间 高度差绝对值 的 最大值 决定的。

请你返回从左上角走到右下角的最小 体力消耗值 。

拿到这种题只会无脑dfs,然后直接TLE。看到题解说这道题也算是比较经典的题目了,还是题刷的不够多啊。有最小最大之类的,可以看看是否能用二分查找找到一个值。这题可以二分查找加搜索,或者使用并查集。

class Solution {
    int[][] directions = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    public int minimumEffortPath(int[][] heights) {
        int m = heights.length, n = heights[0].length;
        int l = 0, r = 1000000;
        //二分查找,看看给定阈值能否有通路
        while (l < r){
            int mid = l + (r - l) / 2;
            boolean[][] isVisited = new boolean[m][n];
            if (dfs(heights, 0, 0, mid, isVisited)){
                r = mid;
            }else{
                l = mid + 1;
            }
        }

        return l;
    }
	//正常dfs模板
    private boolean dfs(int[][] heights, int i, int j, int target, boolean[][] isVisited){
        if (i == heights.length - 1 && j == heights[0].length - 1) return true;
        isVisited[i][j] = true;

        for(int k = 0; k < directions.length; k++){
            int dx = i + directions[k][0], dy = j + directions[k][1];
            if (dx >= 0 && dx < heights.length && dy >= 0 && dy < heights[0].length && !isVisited[dx][dy] && Math.abs(heights[i][j] - heights[dx][dy]) <= target){
                if (dfs(heights, dx, dy, target, isVisited)) return true;
            }
        }
        return false;

    }
    public int minimumEffortPath(int[][] heights) {
        int m = heights.length, n = heights[0].length;
        List<Edge> edges = new ArrayList<>();
		//使用并查集,把矩阵的元素看成是图中的点,从小到大加入权值边,如果家了某个边连通的话,就是答案
		//构建邻接权值边
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if (i + 1 < m) edges.add(new Edge(i * n + j, (i + 1) * n + j, Math.abs(heights[i][j] - heights[i + 1][j])));
                if (j + 1 < n) edges.add(new Edge(i * n + j, i * n + j + 1, Math.abs(heights[i][j] - heights[i][j + 1])));
            }
        }
		
		//对边从小到大进行排序
        Collections.sort(edges, (x, y) -> (x.val - y.val));
        UnionFind uf = new UnionFind(m * n);
		
		//如果加了某个边连通的话,就是答案
        for(Edge edge : edges){
            int i = edge.i, j = edge.j, val = edge.val;
            uf.union(i, j);
            if (uf.find(0) == uf.find(m * n - 1)){
                return val;
            }
        }

        return 0;

    }
	
	//并查集模板
    class UnionFind{
        int[] parent;

        public UnionFind(int 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;
        }
    }

    class Edge{
        int i;
        int j;
        int val;
        public Edge(int i, int j, int val){
            this.i = i;
            this.j = j;
            this.val = val;
        }
    }

Leetcode974 和可被 K 整除的子数组

给定一个整数数组 A,返回其中元素之和可被 K 整除的(连续、非空)子数组的数目。

其实好像还是不是很懂同余定理,看题解吧,这种看能否被k整除的题基本就是这样了。

class Solution {
    //同余定理
    public int subarraysDivByK(int[] A, int K) {
        int n = A.length;
        int[] map = new int[K];
        map[0]++;
        int res = 0, sum = 0;
        for(int i = 0; i < n; i++){
            sum += A[i];
            //java里负数求余数会有问题,转换一下,记住就行了
            int mod = (sum % K + K) % K;
            res += map[mod];
            map[mod]++;
        }
        return res;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值