0%

<剑指offer> 剑指offer 11、12、13

摘要:
11 旋转数组的最小数字
12 矩阵的路径
13 机器人的运动范围

面试题11 旋转数组的最小数字

题目:

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。

解法:

思路一:

遍历数组查询到反转点,时间复杂度未O(n),空间复杂度O(1);

1
2
3
4
5
6
7
8
9
10
class Solution {
public int minArray(int[] numbers) {
for(int i=0;i<numbers.length-1;i++){
if(numbers[i]>numbers[i+1]){
return numbers[i+1];
}
}
return numbers[0];
}
}
1
2
3
4
5
6
class Solution:
def minArray(self, numbers: List[int]) -> int:
for i in range(len(numbers)-1):
if numbers[i]>numbers[i+1]:
return numbers[i+1]
return numbers[0]

思路二:

使用二分法查找数组元素,时间复杂度降低至对数级别:

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
public int minArray(int[] numbers) {
int i=0,j=numbers.length-1;
while(i<j){
int m=(i+j)/2;
if(numbers[m]>numbers[j]) i=m+1;
else if(numbers[m]<numbers[j]) j=m;
else j--;
}
return numbers[i];
}
}
1
2
3
4
5
6
7
8
9
class Solution:
def minArray(self, numbers: List[int]) -> int:
i,j=0,len(numbers)-1
while i<j:
m=(i+j)//2
if numbers[m]<numbers[j]: j=m
elif numbers[m]>numbers[j]: i=m+1
else: j-=1
return numbers[i]

面试题12 矩阵的路径

题目:

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。
[[“a”,”b”,”c”,”e”],
[“s”,”f”,”c”,”s”],
[“a”,”d”,”e”,”e”]]
但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。

解法:

深度优先搜索(DFS)+ 剪枝

  • 递归参数: 当前元素在矩阵 board 中的行列索引 i 和 j ,当前目标字符在 word 中的索引 k 。
  • 终止条件
    1.返回 falsefalse : ① 行或列索引越界 或 ② 当前矩阵元素与目标字符不同 或 ③ 当前矩阵元素已访问过 。

2.返回 truetrue : 字符串 word 已全部匹配,即 k = len(word) - 1 。

  • 递推工作

1.标记当前矩阵元素: 将 board[i][j] 值暂存于变量 tmp ,并修改为字符 ‘/‘ ,代表此元素已访问过,防止之后搜索时重复访问。
2.搜索下一单元格: 朝当前元素的 上、下、左、右 四个方向开启下层递归,使用 或 连接 (代表只需一条可行路径) ,并记录结果至 res 。
3.还原当前矩阵元素: 将 tmp 暂存值还原至 board[i][j] 元素。

  • 回溯返回值: 返回 res ,代表是否搜索到目标字符串。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class Solution {
    public boolean exist(char[][] board, String word) {
    char[] words=word.toCharArray();
    for(int i=0;i<board.length;i++){
    for(int j=0;j<board[0].length;j++){
    if(dfs(board,words,i,j,0)) return true;
    }
    }
    return false;
    }
    public boolean dfs(char[][] board,char[] words,int i,int j,int k){
    if(i<0||i>board.length-1||j<0||j>board[0].length-1||words[k]!=board[i][j]) return false;

    if(k==words.length-1) return true;
    boolean res;
    char tmp=board[i][j];
    board[i][j]='/';
    res=dfs(board,words,i+1,j,k+1)||dfs(board,words,i-1,j,k+1)|dfs(board,words,i,j+1,k+1)||dfs(board,words,i,j-1,k+1);
    board[i][j]=tmp;
    return res;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
    def dfs(i, j, k):
    if not 0 <= i < len(board) or not 0 <= j < len(board[0]) or board[i][j] != word[k]: return False
    if k == len(word) - 1: return True
    tmp, board[i][j] = board[i][j], '/'
    res = dfs(i + 1, j, k + 1) or dfs(i - 1, j, k + 1) or dfs(i, j + 1, k + 1) or dfs(i, j - 1, k + 1)
    board[i][j] = tmp
    return res

    for i in range(len(board)):
    for j in range(len(board[0])):
    if dfs(i, j, 0): return True
    return False

    面试题13 机器人的运动范围

    题目:

    地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

解法一:深度优先遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
int n,m,k;
boolean[][] visited;
public int movingCount(int m, int n, int k) {
// 深度优先遍历
this.m=m;
this.n=n;
this.k=k;
this.visited=new boolean[m][n];
return dfs(0,0,0,0);
}
public int dfs(int i,int j,int si,int sj){
if(i>=m||j>=n||k<si+sj||visited[i][j]) return 0;
visited[i][j]=true;
return 1+dfs(i+1,j,(i+1)%10!=0 ? si+1:si-8,sj)+dfs(i,j+1,si,(j+1)%10!=0 ? sj+1:sj-8);
}
}
1
2
3
4
5
6
7
8
9
class Solution:
def movingCount(self, m: int, n: int, k: int) -> int:
深度优先遍历
def dfs(i,j,si,sj):
if i>=m or j>=n or k<si+sj or (i,j)in visited: return 0
visited.add((i,j))
return 1+dfs(i+1,j,si+1 if (i+1)%10 else si-8,sj)+dfs(i,j+1,si, sj+1 if (j+1)%10 else sj-8)
visited=set()
return dfs(0,0,0,0)

解法二:广度优先遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 广度优先遍历
class Solution {
public int movingCount(int m, int n, int k) {
boolean[][] visited = new boolean[m][n];
int res = 0;
Queue<int[]> queue= new LinkedList<int[]>();
queue.add(new int[] { 0, 0, 0, 0 });
while(queue.size() > 0) {
int[] x = queue.poll();
int i = x[0], j = x[1], si = x[2], sj = x[3];
if(i >= m || j >= n || k < si + sj || visited[i][j]) continue;
visited[i][j] = true;
res ++;
queue.add(new int[] { i + 1, j, (i + 1) % 10 != 0 ? si + 1 : si - 8, sj });
queue.add(new int[] { i, j + 1, si, (j + 1) % 10 != 0 ? sj + 1 : sj - 8 });
}
return res;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
# 广度优先遍历
class Solution:
def movingCount(self, m: int, n: int, k: int) -> int:
queue,visited=[(0,0,0,0)],set()
while queue:
i,j,si,sj=queue.pop(0)
if i>=m or j>=n or k<si+sj or (i,j)in visited:
continue
visited.add((i,j))
queue.append((i+1,j,si+1 if (i+1)%10 else si-8,sj))
queue.append((i,j+1,si, sj+1 if(j+1)%10 else sj-8))
return len(visited)