Leetcode 864疑问

来源:1-5 大厂面试为什么总考算法?

甲骨文_0001

2022-11-12

/**
 * @param {string[]} grid
 * @return {number}
 */
var shortestPathAllKeys = function(grid) {
    const dirs = [[-1, 0], [1, 0], [0, -1], [0, 1]];
    let visited=[]; // 是否访问过
    const R = grid.length, C = grid[0].length;
    let startR = -1, startC = -1;
    let keyTotal = 0; // 总共有多少钥匙, lockKey == keyTotal,说明都找到钥匙了
    let lockKey = 0; // 找到的钥匙数量
    let key2lock={}; // 钥匙->锁
    let lock2key={}; // 锁->钥匙
    

    // 定义地图,并找到起点, 同时初始化visited数组
    for(let r = 0; r < R; r ++ ){
        visited[r]=[];
        for( let c = 0; c < C; c ++ ){
            visited[r][c] = false;
            if( grid[r][c] == '@' ){
                startR = r; // 起点
                startC = c;
            }else if( islower( grid[r][c] ) ){
                keyTotal ++;
            }
        } // for c
    } // for r

    let queue = [[startR, startC, 0]]; // bfs 队列, [行, 列, 步数]
    while( queue.length > 0 ){

        let [nowR, nowC, nowStep] = queue.shift(); // 出一个队列头

        // 看下手上是否有了所有钥匙
        if( lockKey == keyTotal ){
            return nowStep;
        }

        for( let d = 0; d < dirs.length; d ++ ){
            let newR = nowR + dirs[d][0], newC = nowC + dirs[d][1];
            if( !inArea(R, C, newR, newC) ){  // 如果超过边界
                continue;
            }
            if( grid[newR][newC] == '#' ){ // 如果是 墙壁
                continue;
            }

            // 是否访问过
            if( visited[newR][newC] ){
                continue;
            }

            // 是否是钥匙
            if( islower( grid[newR][newC] ) ){
                visited[newR][newC] = true;
                lockKey ++; // 找到一把钥匙
                key2lock[grid[newR][newC]] = grid[newR][newC].toUpperCase(); // 一把钥匙对应一把锁
                lock2key[grid[newR][newC].toUpperCase()] = grid[newR][newC]; // 一把锁对一把钥匙

                queue.push( [newR, newC, nowStep + 1] ); // 入队
            }else if( isupper( grid[newR][newC]  ) ){ // 是否是锁
                // 看有没有对应这把锁的钥匙
                if( lock2key[grid[newR][newC]] ){
                    // 说明能通过这把锁
                    visited[newR][newC] = true;

                    queue.push( [newR, newC, nowStep + 1] ); // 入队
                }
            }else{
                // 说明是空房间 '.'
                queue.push( [newR, newC, nowStep + 1] ); // 入队
                visited[newR][newC] = true;
            }

        } // for d

    } // end while queue.length

    return -1;
};

function inArea( R, C, r, c ){
    return r >= 0 && r < R && c >= 0 && c < C;
}

function islower(ch){
    let reg = /^[a-z]$/;
    return reg.test(ch);
}

function isupper(ch){
    let reg = /^[A-Z]$/;
    return reg.test(ch);
}

最新的代码,visited包含钥匙信息

/**
 * @param {string[]} grid
 * @return {number}
 */
var shortestPathAllKeys = function(grid) {
    const dirs=[[0, -1], [0, 1], [-1, 0], [1, 0]];
    const R = grid.length, C = grid[0].length;
    let startR, startC;
    let keyCount = 0;

    for( let r = 0; r < R; r ++ ){
        for( let c = 0; c < C; c ++ ){
            if( grid[r][c] == '@' ){
                startR = r;
                startC = c;
            }else if( islower( grid[r][c] )  ){
                keyCount ++;
            }
        } // for c
    } // for r

    const KeyLen = 1 << keyCount;
    let visited = [];
    for( let r = 0; r < R; r ++ ){
        visited[r] = [];
        for( let c = 0; c < C; c ++ ){
            visited[r][c] = [];
            for( let h = 0; h < KeyLen; h ++ ){
                 // ************变动点 **********
                visited[r][c][h] = -1; // 这里不再是布尔值变量,设定成 到达 【r, c, h】这三个状态的步数
            } // for h
        } // for c
    } // for r


    // BFS
    let queue = [ [startR, startC, 0] ];
    visited[startR][startC][0] = 0;
    let step = 0;

    while( queue.length > 0 ){

        let [nowR, nowC, keyState] = queue.shift();

        if( keyState == KeyLen - 1 ){
            return visited[nowR][nowC][keyState];
        }

        for( let d = 0; d < 4; d ++ ){
            let newR = nowR + dirs[d][0], newC = nowC + dirs[d][1];
            if( !inArea( R, C, newR, newC ) ){
                continue;
            }

            if( grid[newR][newC] == '#' ){
                continue;
            }

            if( isupper(grid[newR][newC]) && ((1<<(grid[newR][newC].charCodeAt(0) - 'A'.charCodeAt(0))) & keyState) == 0 ){
                continue;
            }

            let newKeyState = keyState;

            if( islower(grid[newR][newC]) ){
                newKeyState = keyState | ( 1 << (grid[newR][newC].charCodeAt(0) - 'a'.charCodeAt(0)) );
                
            }
             // ************变动点 **********
            if( visited[newR][newC][newKeyState] != -1 ){ // 如果是-1,说明未访问
                    continue;
                }
                // ************变动点 **********
            visited[newR][newC][newKeyState] = visited[nowR][nowC][keyState] + 1; //这里【nowR,nowC, keyState】=>【newR, newC, newKeyState】
            queue.push( [ newR, newC, newKeyState ] ); // 把新的【newR, newC, newKeyState】压入队列中

        } // for d
        // step ++;
    } // end while queue.length
    return -1;
};

function inArea( R, C, r, c ){
    return r >= 0 && r < R && c >= 0 && c < C;
}

function islower( ch ){
    const reg = /^[a-z]$/;
    return reg.test(ch);
}

function isupper( ch ){
    const reg = /^[A-Z]$/;
    return reg.test(ch);
}

老师,上述我对visited三维数组作了语义上的更改,对visited这个查找表,【行,列,钥匙状态】作为key对应的值不再是布尔值真假,而是改为到达【行,列,钥匙状态】的步数了,即
【正在考查的行,正在考查的列,正在考查的钥匙状态】的value=【现在的行,现在的列,现在的钥匙状态】的value + 1, 这样改就是您说的step转成了和(x, y)一样的状态变量了。程序通过了AC

写回答

1回答

liuyubobobo

2022-11-13

这个算法看起来有问题。当你到达同一个点的时候,由于路径的不同,有可能手中的钥匙是不同的,你的算法是如何区分的?

0
10
liuyubobobo
回复
甲骨文_0001
赞!继续加油!:)
2022-11-15
共10条回复

玩转算法面试-- Leetcode真题分门别类讲解

课程配套大量BAT面试真题,高频算法题解析,强化训练

7408 学习 · 1150 问题

查看课程