LeetCode 815. 公交路线 / 909. 蛇梯棋(还是bfs)/ 168. Excel表列名称 / 171. Excel表列序号

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 815. 公交路线 / 909. 蛇梯棋(还是bfs)/ 168. Excel表列名称 / 171. Excel表列序号相关的知识,希望对你有一定的参考价值。

815. 公交路线

2021.6.28 每日一题

题目描述

给你一个数组 routes ,表示一系列公交线路,其中每个 routes[i] 表示一条公交线路,第 i 辆公交车将会在上面循环行驶。

例如,路线 routes[0] = [1, 5, 7] 表示第 0 辆公交车会一直按序列 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> ... 这样的车站路线行驶。
现在从 source 车站出发(初始时不在公交车上),要前往 target 车站。 期间仅可乘坐公交车。

求出 最少乘坐的公交车数量 。如果不可能到达终点车站,返回 -1 。

 

示例 1:

输入:routes = [[1,2,7],[3,6,7]], source = 1, target = 6
输出:2
解释:最优策略是先乘坐第一辆公交车到达车站 7 , 然后换乘第二辆公交车到车站 6 。 
示例 2:

输入:routes = [[7,12],[4,5,15],[6],[15,19],[9,12,13]], source = 15, target = 12
输出:-1

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/bus-routes
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

还是BFS,一遍过
与之前有所不同的是,这里开始加入队列的可能有多个公交车,因为可能有多个公交车经过source站点
需要预处理,将每个站点上经过的公交车存储在一个哈希表中,方便后面的查找

class Solution {
    public int numBusesToDestination(int[][] routes, int source, int target) {
        //感觉就还是和前两天的一样,还是广度优先搜索吧
        //从第一个点开始遍历所有能到达的结点,然后直到到达target
        if(target == source)
            return 0;
        int l = routes.length;
        //先将每个站点上经过的公交车号存储在哈希表中,方便查找
        Map<Integer, List<Integer>> map = new HashMap<>();

        for(int i = 0; i < l; i++){
            for(int pos : routes[i]){
                if(map.containsKey(pos)){
                    map.get(pos).add(i);
                }else{
                    List<Integer> list = new ArrayList<>();
                    list.add(i);
                    map.put(pos, list);
                }
            }
        }
        //存储走过的公交车吧
        Set<Integer> set = new HashSet<>();

        Queue<Integer> queue = new LinkedList<>();
        //第一个站点,可以乘坐的公交车有多个
        List<Integer> first = map.get(source);
        for(int num : first){
            queue.add(num);
            set.add(num);
        }
        int step = 0;
        while(!queue.isEmpty()){
            step++;
            int size = queue.size();
            while(size-- > 0){
                //第几辆公交车
                int index = queue.poll();
                //能到达的站点
                int[] pos = routes[index];
                for(int p : pos){
                    //如果坐这辆车能到达target,就返回step
                    if(p == target)
                        return step;
                    //否则得到这个站点能坐的公交车
                    List<Integer> temp = map.get(p);
                    for(int num : temp){
                        if(set.contains(num))
                            continue;
                        set.add(num);
                        queue.add(num);
                    }
                }
            }
        }
        return -1;
        
    }
}

本来觉得官解的方法麻烦了,就看别的了,但是转念一想,好像不是这样,所以也学习了一下

class Solution {
    //看一下官解的这个思路
    //建图,如果两个公交车之间能到达,就为true,并且同样存储经过每个站点的公交车
    //然后用一个dis存储出发点到其他每个车站所需要乘坐公交车的数目
    //然后进行广度优先搜索更新这个dis数组
    //最后在目标为target的站点中找需要乘坐公交车最少的
    public int numBusesToDestination(int[][] routes, int source, int target) {
        if (source == target) {
            return 0;
        }

        int n = routes.length;
        boolean[][] edge = new boolean[n][n];
        Map<Integer, List<Integer>> rec = new HashMap<Integer, List<Integer>>();
        for (int i = 0; i < n; i++) {
            for (int site : routes[i]) {
                List<Integer> list = rec.getOrDefault(site, new ArrayList<Integer>());
                //两个站点可以连通,即为true
                for (int j : list) {
                    edge[i][j] = edge[j][i] = true;
                }
                list.add(i);
                rec.put(site, list);
            }
        }
        //出发点到其他公交车经过的公交车数目
        int[] dis = new int[n];
        Arrays.fill(dis, -1);
        Queue<Integer> que = new LinkedList<Integer>();
        for (int bus : rec.getOrDefault(source, new ArrayList<Integer>())) {
            dis[bus] = 1;
            que.offer(bus);
        }

        while (!que.isEmpty()) {
            int x = que.poll();
            for (int y = 0; y < n; y++) {
                //如果两个公交车能换乘,并且此时到达y车的距离还没有被更新过,那么就进行更新
                if (edge[x][y] && dis[y] == -1) {
                    dis[y] = dis[x] + 1;
                    que.offer(y);
                }
            }
        }
        //遍历目标站点对应的公交车
        int ret = Integer.MAX_VALUE;
        for (int bus : rec.getOrDefault(target, new ArrayList<Integer>())) {
            if (dis[bus] != -1) {
                ret = Math.min(ret, dis[bus]);
            }
        }
        return ret == Integer.MAX_VALUE ? -1 : ret;
    }
}

909. 蛇梯棋

2021.6.27 每日一题

题目描述

N x N 的棋盘 board 上,按从 1 到 N*N 的数字给方格编号,编号 从左下角开始,每一行交替方向。

例如,一块 6 x 6 大小的棋盘,编号如下:

在这里插入图片描述

r 行 c 列的棋盘,按前述方法编号,棋盘格中可能存在 “蛇” 或 “梯子”;如果 board[r][c] != -1,那个蛇或梯子的目的地将会是 board[r][c]。

玩家从棋盘上的方格 1 (总是在最后一行、第一列)开始出发。

每一回合,玩家需要从当前方格 x 开始出发,按下述要求前进:

选定目标方格:选择从编号 x+1,x+2,x+3,x+4,x+5,或者 x+6 的方格中选出一个目标方格 s ,目标方格的编号 <= N*N。
该选择模拟了掷骰子的情景,无论棋盘大小如何,你的目的地范围也只能处于区间 [x+1, x+6] 之间。
传送玩家:如果目标方格 S 处存在蛇或梯子,那么玩家会传送到蛇或梯子的目的地。否则,玩家传送到目标方格 S。 
注意,玩家在每回合的前进过程中最多只能爬过蛇或梯子一次:就算目的地是另一条蛇或梯子的起点,你也不会继续移动。

返回达到方格 N*N 所需的最少移动次数,如果不可能,则返回 -1。


示例:

输入:[
[-1,-1,-1,-1,-1,-1],
[-1,-1,-1,-1,-1,-1],
[-1,-1,-1,-1,-1,-1],
[-1,35,-1,-1,13,-1],
[-1,-1,-1,-1,-1,-1],
[-1,15,-1,-1,-1,-1]]
输出:4
解释:
首先,从方格 1 [第 5 行,第 0 列] 开始。
你决定移动到方格 2,并必须爬过梯子移动到到方格 15。
然后你决定移动到方格 17 [第 3 行,第 5 列],必须爬过蛇到方格 13。
然后你决定移动到方格 14,且必须通过梯子移动到方格 35。
然后你决定移动到方格 36, 游戏结束。
可以证明你需要至少 4 次移动才能到达第 N*N 个方格,所以答案是 4。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/snakes-and-ladders
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

这个题,首先得认真读题。而且我觉得题还不一定对,做起来好难受
刚开始我还是常规bfs思路写的,每次移动一步,然后用一个used数组存储到达过的点,然后过了202个例子,解答错误了,代码如下

class Solution {
    int[][] board;
    int n;
    public int snakesAndLadders(int[][] board) {
        //还是广度优先搜索,想了一下,感觉也不需要考虑矩阵啊,到点跳就行了,o,需要在矩阵中找标号
        this.board = board;
        this.n = board.length;
        int target = n * n;

        Queue<Integer> queue = new LinkedList<>();
        boolean[] used = new boolean[n * n + 1];
        used[1] = true;
        queue.add(1);
        
        int step = 0;
        while(!queue.isEmpty()){
            step++;
            int size = queue.size();
            while(size-- > 0){
                //当前位置编号
                int temp = queue.poll();
                for(int i = 1; i <= 6; i++){
                    if(temp + i == target)
                        return step;
                    if(used[temp + i])
                        continue;
                    int index = temp + i;
                    used[index] = true;
                    //得到当前位置在board中对应的下标
                    int pos = getPos(index);
                    if(pos != -1){
                        if(pos == target)
                            return step;
                        if(!used[pos]){
                            used[pos] = true;
                            queue.add(pos);
                        }
                    }else{
                        queue.add(index);
                    }
                }
            }
        }
        return -1;
    }

    public int getPos(int index){
        //偶数行正向,奇数行反向
        int col = (index - 1) / n;
        int row = (index - 1) % n;
        return col % 2 == 0 ? board[n - 1 - col][row] : board[n - 1 - col][n - row - 1]; 
    }
}

然后我去研究了一下这个测试用例:
[[-1,-1,-1,46,47,-1,-1,-1],[51,-1,-1,63,-1,31,21,-1],[-1,-1,26,-1,-1,38,-1,-1],[-1,-1,11,-1,14,23,56,57],[11,-1,-1,-1,49,36,-1,48],[-1,-1,-1,33,56,-1,57,21],[-1,-1,-1,-1,-1,-1,2,-1],[-1,-1,-1,8,3,-1,6,56]]
发现这个例子中梯子到达的点位置还有梯子,然后我还以为是能连环跳的,就把代码改了一下,让可以进行连环的跳跃,结果又失败了,这个例子给出的答案是4,连环跳跃给出的答案是3,然后我看了一下题目要求,发现每次只能跳一次,意思是由多个梯子连接也只能跳一次
然后我又改回原来的代码,因为原来的代码就是跳一次,但是有个问题不太明白,就是如果跳一次,而跳过去的位置有梯子,那么下一次是继续爬梯子呢,还是掷骰子了,如果爬上去梯子是否还能掷骰子。然后我就凌乱了,不知道咋写了,就去看答案了

官解给的答案我看了一下,还是相当于每次只能跳一个梯子,但是如果两个梯子相连,第二次是不会去走梯子的,而是去掷骰子。。。然后我就觉得我的代码应该没问题啊,为什么会错呢
然后仔细和官解对照了一下,发现区别在于,我的逻辑中,如果当前位置已经为true,那么我认为这个位置的情况已经遍历过了,就直接continue;而官解中对标记为true的位置同样进行遍历。
想想为什么,对于我出现错误的这个用例,如果按照我的逻辑,4的位置有梯子到达8位置,然后标记为true了,但是这个位置其实是还有梯子可以跳跃的,那么当再遍历到这个位置的时候,我的代码将不会发生跳跃。而按官解中的逻辑,这个位置同样会发生跳跃,到达56的位置。

这道题就这样吧,看看就完事了,感觉出的乱七八糟的,最起码描述没描述清楚

class Solution {
    int[][] board;
    int n;
    public int snakesAndLadders(int[][] board) {
        //还是广度优先搜索,想了一下,感觉也不需要考虑矩阵啊,到点跳就行了,o,需要在矩阵中找标号
        this.board = board;
        this.n = board.length;
        int target = n * n;

        Queue<Integer> queue = new LinkedList<>();
        boolean[] used = new boolean[n * n + 1];
        used[1] = true;
        queue.add(1);
        
        int step = 0;
        while(!queue.isEmpty()){
            step++;
            int size = queue.size();
            while(size-- > 0){
                //当前位置编号
                int temp = queue.poll();
                for(int i = 1; i <= 6; i++){
                    int index = temp + i;
                    if(index == target)
                        return step; 
                    //得到当前位置在board中对应的下标
                    int pos = getPos(index);
                    if(pos != -1){
                        used[index] = true;
                        //如果数组中这个位置不为-1,那么继续找下一个位置
                        if(pos == target)
                            return step;
                        if(!used[pos]){
                            used[pos] = true;
                            queue.add(pos);
                        }
                    }else{
                        if(!used[index]){
                            used[index] = true;
                            queue.add(index);
                        }
                    }
                }
            }
        }
        return -1;
    }

    public int getPos(int index){
        //偶数行正向,奇数行反向
        int row = (index - 1) / n;
        int col = (index - 1) % n;
        return row % 2 == 0 ? board[n - 1 - row][col] : board[n - 1 - row][n - col - 1]; 
    }
}

168. Excel表列名称

2021.6.29每日一题

题目描述

给定一个正整数,返回它在 Excel 表中相对应的列名称。

例如,

    1 -> A
    2 -> B
    3 -> C
    ...
    26 -> Z
    27 -> AA
    28 -> AB 
    ...
示例 1:

输入: 1
输出: "A"
示例 2:

输入: 28
输出: "AB"
示例 3:

输入: 701
输出: "ZY"

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/excel-sheet-column-title
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

相当于一个26进制的数

class Solution {
    public String convertToTitle(int columnNumber) {
        StringBuffer sb = new StringBuffer();
        while(columnNumber != 0){
            int remainder = (columnNumber - 1) % 26;
            columnNumber = (columnNumber - 1) / 26;
            sb.append((char)(remainder + 'A'));
        }
        return sb.reverse().toString();
    }
}

171. Excel表列序号

题目描述

给定一个Excel表格中的列名称,返回其相应的列序号。

例如,

    A -> 1
    B -> 2
    C -> 3
    ...
    Z -> 26
    AA -> 27
    AB -> 28 
    ...
示例 1:

输入: "A"
输出: 1
示例 2:

输入: "AB"
输出: 28
示例 3:

输入: "ZY"
输出: 701

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/excel-sheet-column-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

class Solution {
    public int titleToNumber(String columnTitle) {
        int l = columnTitle.length();
        int res = 0;
        for(int i = 0; i < l; i++){
            res = res * 26 + columnTitle.charAt(i) + 1 - 'A';
        }
        return res;
    }
}

以上是关于LeetCode 815. 公交路线 / 909. 蛇梯棋(还是bfs)/ 168. Excel表列名称 / 171. Excel表列序号的主要内容,如果未能解决你的问题,请参考以下文章

[Hbfs] lc815. 公交路线(建图+多源bfs+bfs最短路+思维+好题)

LeetCode 815 公交线路[BFS Map] HERODING的LeetCode之路

[LeetCode] 815. Bus Routes 巴士路线

LeetCode 1184. 公交站间的距离

LeetCode1184. 公交站间的距离(C++)

安卓百度地图怎么没有公交路线查询