问题一:岛屿数量
给一个01矩阵,1代表是陆地,0代表海洋, 如果两个1相邻,那么这两个1属于同一个岛。我们只考虑上下左右为相邻。 岛屿: 相邻陆地可以组成一个岛屿(相邻:上下左右) 判断岛屿个数。
题库地址:www.nowcoder.com/practice/0c…
思路:
利用dfs进行遍历,当找到陆地之后,向这个位置的上下左右继续寻找,如果依然找到陆地,则将矩阵中该位置代表陆地的’1’修改为’0’。这样,在之后的遍历中则不会再次计算该处陆地。
BFS,DFS介绍,可以重点看一下
参考链接:developer.aliyun.com/article/756…
深度优先遍历(Depth First Search, 简称 DFS) 与广度优先遍历(Breath First Search)是图论中两种非常重要的算法,生产上广泛用于拓扑排序,寻路(走迷宫),搜索引擎,爬虫等
js实现
* 判断岛屿数量
* @param grid char字符型二维数组
* @return int整型
*/
function solve(grid) {
let lands = 0
for (let h = 0; h < grid.length; h++) {
for (let w = 0; w < grid[0].length; w++) {
if (grid[h][w] == 1) {
lands++
dfs(grid, h, w)
}
}
}
return lands
}
function dfs(grid, h, w) {
grid[h][w] = 0
if (h - 1 >= 0 && grid[h - 1][w] == 1) {
dfs(grid, h - 1, w)
}
if (h + 1 < grid.length && grid[h + 1][w] == 1) {
dfs(grid, h + 1, w)
}
if (w - 1 >= 0 && grid[h][w - 1] == 1) {
dfs(grid, h, w - 1)
}
if (w + 1 < grid[0].length && grid[h][w + 1] == 1) {
dfs(grid, h, w + 1)
}
}
动态规划的解题思路
动态规划有点像初中归纳法,核心思想是拆分子问题,记住过往,减少重复计算。 并且动态规划一般都是自底向上,转换为计算机可识别的逻辑;
实际解决问题时,可以根据这个思路进行分析:
1、判断是否能用归纳法得到一个规律,
2、想办法记录每一次的结果,需要判断影响结果的变量,来用不同的方式进行记录
3、再自底向上进行循环处理,常见的循环都是先循环主要变量,在内部循环结果(或者循环影响结果的主要变量)进行判断和计算
动态规划有几个典型特征:最优子结构、状态转移方程、边界、重叠子问题
问题二:单词划分
给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
说明:
- 拆分时可以重复使用字典中的单词。
- 你可以假设字典中没有重复的单词
示例
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。
题库地址:leetcode.com/problems/wo…
思路: 动态规划,参考链接:zhuanlan.zhihu.com/p/365698607
f(1) = true(1)
f(2) = true(1) + true(2) / true(1-2)
f(3) = true(2) + true(3) / true(1) + true(2,3) / true(1,3)
f(4) = true(3) + true(4) / true(2) + true(3,4) / true(1) + true(2,4) / true(1,4)
f(n) = true(n-1) + true(n) / true(n-2) + true(n-2,n) / true(n-3) + true(n-3,n) /... / true(1,n)
上面已经归纳得到边界和状态转移公式,开始思考如何存储上一次记录的结果;
记录每一次的结果状态:每一次的结果只可能为true或者是false,可用通一个字符串长度的数组来进行记录
从下向上循环:先循环主要变量字符串长度,再循环结果进行记录和判断
套入上方思路,进行代码实现
js实现:
/**
* @param {string} s
* @param {string[]} wordDict
* @return {boolean}
*/
var wordBreak = function(s, wordDict) {
const wordSet = new Set(wordDict)
// 用来记录结果,第一项填充true,将边界情况套入公式
const dp = new Array(s.length + 1).fill(false)
dp[0] = true
for(let i=1;i<=s.length;i++) {
for(let j=0;j<i;j++) {
if(dp[j] && wordSet.has(s.substring(j, i))) {
dp[i] = true
break
}
}
}
return dp[s.length]
};
问题三:挖矿问题
10 个工人
5个金矿,每个金矿对应收益和所需工人如下
400kg/5人 500kg/5人 200kg/3人 300kg/4人 350kg/3人
每个金矿要么全挖,要么不挖,不能派出一半人挖取一半金矿
求最佳金矿收益
参考链接:www.sunbohao.com/font/conten…
思路: 动态规划
设工人数量为 workersNum,金矿数量为 mineNum,金矿含量为数组 mineValArr,金矿开采所需的人数为数组 workersUsedArr
mineNum === 0,
输出 : 0;
mineNum === 1,
workersNum < workersUsedArr[0],输出0;
workersNum > workersUsedArr[0],输出mineValArr[0];
mineNum === 2,
workersNum < workersUsedArr[0], workersNum < workersUsedArr[0],输出0;
workersNum > workersUsedArr[0], workersNum < workersUsedArr[1],输出mineValArr[0];
workersNum < workersUsedArr[0], workersNum > workersUsedArr[1],输出mineValArr[1];
workersNum > workersUsedArr[0], workersNum > workersUsedArr[1],输出mineValArr[0] + mineValArr[1];
f(0) = 0 ; // (mineNum === 0)
f(1) = 0 ; // (mineNum === 1,workersNum < workersUsedArr[0])
f(1) = mineValArr[0] ; //(mineNum ===1, workersNum > workersUsedArr[0]
一个变量无法形成状态转移方程,将 workersNum代入变量
f(0,0) = 0 ;
f(1,workersNum) = workersNum > workersUsedArr[0] ? mineValArr[0] ;0;
f(2,workersNum) = workersNum > (workersUsedArr[0] + workersUsedArr[1]) ? mineValArr[0] + mineValArr[1] : (workersNum > workersUsedArr[1] ? Max(f(1,workersNum), f(1,workersNum) : f(1,workersNum))
公式计算中,workersUsedArr,mineValArr也参与了计算,同样带入变量用于判断
f(0,workersNum, mineValArr, workersUsedArr) = 0 ;
f(1,workersNum, mineValArr, workersUsedArr) = workersNum > workersUsedArr[0] ? mineValArr[0] ;0;
f(2,workersNum, mineValArr, workersUsedArr) = workersNum > (workersUsedArr[0] + workersUsedArr[1]) ? mineValArr[0] + mineValArr[1] : (workersNum > workersUsedArr[1] ? Max(f(1,workersNum), f(1,workersNum) : f(1,workersNum))
f(n,workersNum, mineValArr, workersUsedArr) = workersNum > (workersUsedArr[0] + ... + workersUsedArr[n-1]) ? mineValArr[n-2] + mineValArr[n-1] : (workersNum > workersUsedArr[n-1] ? Max(f(n-1,workersNum), f(n-2,workersNum) : f(n-2,workersNum))
上述思路可以用来递归,但是无法用来进行自底向上的循环,得想办法得到状态转移方程,考虑状态方程时只考虑可变量
// 这一步还没想到具体的思路,是根据参考链接中结果与结论得到的公式
F(mineNum, workersNum) = 0 (mineNum=0或workersNum=0)
// mineNum>=1,workersNum < workersUsedArr[mineNum-1]
F(mineNum, workersNum) = F(mineNum-1, workersNum)
// mineNum>=1,workersNum >= workersUsedArr[mineNum-1]
F(mineNum, workersNum) = max(F(mineNum-1, workersNum), F(mineNum-1, workersNum-workersUsedArr[mineNum-1]+ mineValArr[mineNum-1]))
上面已经归纳得到边界和状态转移公式,开始思考如何存储上一次记录的结果;
记录每一次的结果状态:每一次的结果根据workersNum数量的不同,结果不同,没法用一个一维数组进行存储,考虑二维数组是否可以存储结果,用mineNum主要变量作为行数,用workersNum作为列数,可以记录每个mineNum对应的所有可能;
从下向上循环:先循环主要变量字符串长度,再循环影响结果的主要变量workersNum,进行记录和判断
套入上方思路,进行代码实现
js实现:
function f(mineNum, workersNum, mineValArr, workersUsedArr) {
/**
* 用mineNum主要变量作为行数,用workersNum作为列数,可以记录每个mineNum对应的所有可能
* 通过对结果最上一行和最左一列填充0,来减少对边界的判断
*/
let resultArr = new Array(mineNum + 1)
for (let i = 0; i < resultArr.length; i++) {
resultArr[i] = new Array(workersNum + 1).fill(0)
}
/**
* 先循环主要变量字符串长度,再循环结果进行记录和判断
*/
for (let i = 1; i <= mineNum; i++) {
for (let j = 1; j <= workersNum; j++) {
if (j < workersUsedArr[i - 1]) {
resultArr[i][j] = resultArr[i - 1][j]
} else {
resultArr[i][j] = Math.max(resultArr[i - 1][j], resultArr[i - 1][j - workersUsedArr[i - 1]] + mineValArr[i - 1])
}
}
}
return resultArr[mineNum][workersNum]
}
问题四:NC145 01背包
问题链接:www.nowcoder.com/practice/28…
描述
示例1
输入:10,2,[[1,3],[10,4]]
返回值:4
第一个物品的体积为1,重量为3,第二个物品的体积为10,重量为4。只取第二个物品可以达到最优方案,取物重量为4
备注:
思路: 这题目看变量与判断条件,和挖矿问题几乎没有什么区别,都是一种总量,一个限制变量,一个价值变量,感觉这种题已经是固定套路,做一个二维数组存放所有可能的情况,然后可能的结果为:
问题五、最大子序和
问题链接:leetcode-cn.com/problems/ma…
给定一个整数数组 nums
,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例 :
输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。 示例 2:
输入:nums = [1] 输出:1
思路1
求连续子序列最大和,比较当前值是否大于(当前值+前面的子序列之和),若是,则从当前位置重新开始累加
var maxSubArray = function (nums) {
if (!nums || nums.length < 1) {
return 0
}
let maxVal = nums[0]
let currentVal = nums[0]
for (let i = 1; i < nums.length; i++) {
currentVal = Math.max(nums[i], currentVal + nums[i])
maxVal = Math.max(currentVal, maxVal)
}
return maxVal
};