这是我参与8月更文挑战的第19天,活动详情查看:8月更文挑战
旋转数组的最小数字
剑指Offer 11.旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组【3,4,5,1,2】为【1,2,3,4,5】的一个旋转,该数组的最小值为1。
示例1:
输入:[3,4,5,1,2]
输出:1
示例2:
输入:[2,2,2,0,1]
输出:0
题解
暴力法
根据题意,我们第一时间就能通过暴力解决,用一个变量记录一下当前遍历过程中遇到的最小值是多少,然后遍历结束后,返回最小值即可。
但这样子的暴力不够好,因为其实数组是被旋转的,所以,在遍历过程中,只要有小于numbers[0],那么该数字一定是最小值。
function minArray(numbers){
for(let i=0;i<numbers.length;i++){
if(numbers[i]<numbers[0]){
return numbers[i]
}
}
return numbers[0]
}
但暴力不是我们的目标,我们目的应该是降低算法的复杂度,所以采用二分法更好。
二分法
首先,创建两个指针left、right分别指向numbers首尾数字,然后计算出两指针之间的中间索引值middle,然后我们会遇到以下三种情况:
- middle > right:代表最小值一定在middle右侧,所以left移到middle+1的位置。
- middle < right:代表最小值一定在middle左侧或者就是middle,所以right移到middle到位置。
- middle既不大于left指针,也不小于right指针的值,代表着middle可能等于left指针的值,或者right指针的值,我们这时候只能让right指针递减,来一个一个找最小值了。
/**
* @param {number[]} numbers
* @return {number}
*/
var minArray = function(numbers){
let left = 0, right = numbers.length-1;
while(left < right){
let middle = left + ~~((right - left) / 2); // 避免(left+right)出现溢出
if(numbers[middle] > numbers[right]){
left = middle + 1;
}else if(numbers[middle] < numbers[right]){
right = middle;
}else{
right--;
}
}
return numbers[left];
}
矩阵中的路径
剑指Offer 12.矩阵中的路径
给定一个m x n二维字符网格board和一个字符串单词word。如果word存在于网格中,返回true;否则,返回false。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
例如,在下面的3x4的矩阵中包含单词“ABCCED”(单词中的字母已标出)。
示例1:
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
示例2:
输入:board = [["a","b"],["c","d"]], word = "abcd"
输出:false
提示:
- 1 <= board.length <= 200
- 1 <= board[i].length <= 200
- board 和 word 仅由大小写英文字母组成
题解
典型的矩阵搜索问题,可使用深度优先搜索(DFS)+ 剪枝解决。
- 深度优先搜索: 可以理解为暴力法遍历矩阵中所有字符串可能性。DFS通过递归,先朝一个方向搜索到底,再回溯至上个节点,沿另一个方向搜索,以此类推。
- 剪枝: 在搜索中,遇到
这条路不可能和目标字符串匹配成功的情况(例如:此矩阵元素和目标字符不同、此元素已被访问),则应立即返回,称之为可行性剪枝。
DFS解析:
-
递归参数:当前元素在矩阵board中的行列索引 x 和 y,当前目标字符在word中的索引 k。
-
终止条件:
-
返回false:
- 行或列索引越界
- 当前矩阵元素与目标字符不同
- 当前矩阵元素已被访问
-
返回true:
- k === word.length - 1,即字符串word已全部匹配。
-
-
递推工作:
- 标记当前矩阵元素:将board[ x ] [ y ] 修改为字符串 ‘ - ’,代表此元素已访问过,防止之后搜索时重复访问。
- 搜索下一单元格:朝当前元素的 上、右、下、左 四个方向开启下层递归,使用 或 连接(代表只需找到一条可行路径就直接返回,不再做后续DFS),并记录结果至 res。
- 还原当前矩阵元素:将board [ i ] [ j ]元素还原至初始值
/**
* @param {character[][]} board
* @param {string} word
* @return {boolean}
*/
var exist = function(board, word){
var xLen = board.length;
var yLen = board[0].length;
var k = 0;
var dfs = function(x,y,k){
if(x < 0 || x >= xLen || y < 0 || y >= yLen || board[x][y] !== word[k])
return false;
if(k === word.length - 1) // word 遍历完了
return true;
let temp = board[x][y]; // 记录board的值
board[x][y] = '-'; //
let res = dfs(x - 1, y, k + 1) || dfs(x, y + 1, k + 1) || dfs(x + 1, y, k + 1) || dfs(x, y - 1, k + 1); // 上 右 下 左
board[x][y] = temp;
return res;
}
for(let i = 0; i < xLen; i++){
for(let j = 0; j < yLen; j++){
if(dfs(i,j,k)) return true;
}
}
return false;
}
坚持每日一练!前端小萌新一枚,希望能点个赞哇~