文章目录
- 前言
- 一、第一天:剑指 Offer 03. 数组中重复的数字 (6/28)
- 二、第二天:剑指 Offer 04. 二维数组中的查找 (6/29)
- 三、第三天:题目:剑指 Offer 10- II. 青蛙跳台阶问题(7/11)
- 四、第四天:剑指 Offer 11. 旋转数组的最小数字(7/26)
- 五、第五天:1. 两数之和(7/28)
- 六、第六天:53. 最大子序和(8/01)
- 七、第七天:88. 合并两个有序数组(8/02)
- 八、第八天:509. 斐波那契数(8/03)
- 九、第九天:350. 两个数组的交集 II(8/04)
- 十、第十天:121. 买卖股票的最佳时机(8/05)
- 十一、第一天:566. 重塑矩阵(8/06)
- 十二、第十二天:322. 零钱兑换(8/07)
- 十三、第十三天:46. 全排列(8/08)
- 十四、第十四天:51. N 皇后(8/08)
- 十五、第十五天:66. 加一(8/09)
- 十六、第十六天:111. 二叉树的最小深度(8/11)
- 十七、136. 只出现一次的数字
- 十八、剑指 Offer 24. 反转链表
- 总结
前言
害,本来标题叫做“每日一题算法,你的快乐源泉” 后面发现,男人就是大猪蹄子,还每日一题,每个星期一题都难保证,妈呀,没次回头看才发现又欠下一堆了,不忍心标题骗自己,就改了现在你看到得了~~
补充
1.一切数据结构都可以,转变为数组或链表问题
2.空间复杂度:执行该算法需要多少内存空间
3.时间复杂度:执行该算法需要多少时间
一、第一天:剑指 Offer 03. 数组中重复的数字 (6/28)
题目:剑指 Offer 03. 数组中重复的数字
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof
解题思路
这题应该是不难的,解决的办法应该很多,本着我这一渣渣,就不去考虑啥了,结出先是我现阶段的思考,优化交给不断变强变秃后的自己来优化吧!
这题感觉最暴力的是用两个for循环来,但考虑到这样太low了,不符合我英俊潇洒的气质,我换了种解法,我们用map方法来解,把每一个首次出现的加入到map里面,如果后面再出现就直接return。代码如下:
var findRepeatNumber = function(nums) {
const map = new Map;
for(let i = 0 ; i <= nums.length ; i++){
if(!map.has(nums[i])){
map.set(nums[i],1)
}else{
return nums[i]
}
}
return undefined
};
呜呜呜,好菜我!!
补充知识
简介Set
- Set和Map类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key。
最大特点:
- 能保证里面的值不重复
方法
- add():添加值
- delete():返回true或false来告知是否成功删除
- has():判断是否存在
- forEach():遍历
map
简介Map
- 是一种键值对的结构,具有极快的查找速度
方法
- set(key ,value):添加(如果添加的是已经存在的值,就会覆盖)
- get(key):获取
- size():获取长度
- has(key):判断是否存在
- delete(key):删除
- forEach/for…of:遍历
二、第二天:剑指 Offer 04. 二维数组中的查找 (6/29)
剑指 Offer 04. 二维数组中的查找
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof
解题思路
从右上角出发,小于目标值则向左移一位,大于就向下(因为我们要找一个数字只能从向右或向下找,那么我们要排除定位出目标,也要这样剔除无用的行和列)
var findNumberIn2DArray = function(matrix, target) {
//判断条件,基本要求啦
if(matrix == null || matrix.length == 0) {
return false;
}
//获取到长宽~~
let m = matrix.length, n = matrix[0].length;
let row = 0, col = n - 1;
while(row < m && col >= 0) {
if(matrix[row][col] > target) {
col--;
}else if(matrix[row][col] < target) {
row++;
}else {
return true;
}
}
return false;
};
补充知识
简单说一下数组和链表的区别吧!
- 数组是最常用的了一块连续的空间,查询速度快,但数组是没有索引的,对于要经常移动增删改查的话,效率会低很多。适用于不长改变操作的情况下。
- 链表是一块可不连续的动态空间,长度可变,每个节点要保存相邻结点指针,因为保存了指针,所以对于数据的增删改查很便捷高效。
- 简单来说,数组便于查询,链表便于插入删除。
- 一般在c++/java里数组是需要订长度的,但JavaScript中不需要,可以动态增长。
三、第三天:题目:剑指 Offer 10- II. 青蛙跳台阶问题(7/11)
题目:剑指 Offer 10- II. 青蛙跳台阶问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/qing-wa-tiao-tai-jie-wen-ti-lcof
解题思路
这题我们这么想,当我们走到最后一步的时候,那么前面就还有 n-1 或 n-2 步要走,那么我们是不是要知道当有 n-1 步和 n-2 步时有多少种方法,我们就知道走 n 步时共有多少种了 ,就得出转移方程 f(n) = f(n-1) + f(n-2),得出转移方程就容易啦,同时要注意一下边界问题,比如这里我们要判断当为n的值为0和1时的值。判断条件是常常要考虑到的。
var numWays = function (n) {
if (!n || n === 1) return 1;
let db = []
db[0] = 1;
db[1] = 2;
for (let i = 2; i <= n; i++) {
db[i] = (db[i-1] + db[i-2])%1000000007
}
return db[n-1];
};
呜呜呜,好菜我!!
补充知识
这题类似背包问题的,是动态规划类的题目。动态规划的题有下面的一些特性。
第一步
- 确定状态
- 最后一步,除了这一步的所有前面总和
- 以下面的背包问题来了说就是,最后一枚银币是a,总和为total-a,因为是最优,所以这样前面一定是最优的
- 所以我们的问题变为如何用最少的硬币拼出total-a,这里就变为子问题了
- 所以我们的状态就设为:f(x) = 最小用多少硬币拼出X
- 子问题
- 最后一步,除了这一步的所有前面总和
第二步
- 转移方程
- 就是上面的子问题转化为方程
第三步
- 初始条件
- 边界问题
四、第四天:剑指 Offer 11. 旋转数组的最小数字(7/26)
剑指 Offer 11. 旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof
解题思路
首先我们根据题意可以画出上面的这张图,我们可以看到,中点的位置可能在左边,也可能在右边,也或者刚好在分割点。那么就有三种情况要考虑了。
- (numbers[mident] < numbers[left]) 。
这时最小值肯定在mident的左边,所以left = mident - (numbers[mident] > numbers[left]) 。
这时,最小值肯定在mident的右边,所以right = mident + 1。 - (numbers[mident] = numbers[left]) 。
这时,就刚好了。
var minArray = function(numbers) {
let right = 0;
let left = numbers.length - 1;
while (right < left) {
const mident = Math.floor((right+left)/2);
if (numbers[mident] < numbers[left]) {
left = mident;
} else if (numbers[mident] > numbers[left]) {
right = mident + 1;
} else {
left -= 1;
}
}
return numbers[right];
};
补充知识
看到有序的情况下,我们就应该考虑二分法查找。
二分法查找我们要确定中点,定义左右,通过大小,不断的缩小范围,来确定出我们要找的值。
五、第五天:1. 两数之和(7/28)
1. 两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/two-sum
最简单粗暴的办法就是两个循环啦,但是这样无疑,导致的复杂度比较高,想想当年,第一次刷到这题时就是这么干的,那么现在当然不要这么干啦,我们通过得出目标与数组中每一个的差值,来构建字典,并且通过字典查询是否存在符合的值就可以判断了
var twoSum = function(nums, target) {
let map = new Map();
nums.forEach((item,index) => {
if(map.has(target - item)){
return [map.get(target - item), index];
}else{
map.set(item, index);
}
})
return [];
};
上面的写法是错的,不知道你发现没,开始十万个为什么~~,后面好好的去看了forEach的官方文档才发现。也可以看我写的另外篇关于 foreach 的文章
其实,上面我们直接用 for 就好了。
六、第六天:53. 最大子序和(8/01)
53. 最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-subarray/
let nums = [-2,1,-3,4,-1,2,1,-5,4]
var maxSubArray = function(nums) {
let max = nums[0];
let allmax = nums[0];
for (let i = 1 , n = nums.length; i < n ; i++) {
allmax = Math.max(allmax + nums[i] , nums[i]);
max = Math.max(allmax , max)
}
return max
};
这里他要求连续的最大数组和,那么我们可以先找出所以连续的数组中,每一个所能达到的最大值。
问题是如何获得呢?我们创建一个数组来存放所能达到的最大值的每一段连续数组,比如我们从 numerous[0] 开始开始向前累加,那么下一位是 1 那么比原来的大,我们就把numerous[0],加入到数组中,作为第一项。另外把起始位更新到下一位,重新开始向前累加
pre = Math.max(pre + x, x);
如果前边累加后还不如自己本身大,那就把前边的都扔掉,从此自己本身重新开始累加。反之添加。
七、第七天:88. 合并两个有序数组(8/02)
88. 合并两个有序数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
来源:来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-sorted-array
var merge = function(nums1, m, nums2, n) {
let index1 = m - 1, index2 = n - 1, indexAll = m + n - 1;
var cur;
while (index1 >= 0 || index2 >= 0) {
if (index1 === -1) {
cur = nums2[index2--];
} else if (index2 === -1) {
cur = nums1[index1--];
} else if (nums1[index1] > nums2[index2]) {
cur = nums1[index1--];
} else {
cur = nums2[index2--];
}
nums1[indexAll--] = cur;
}
};
首先这里我一开始的思考是从索引为0开始考虑合并,但是这时发现会出现大家都一样的情况下,要把前面的数组放前面的情况,似乎麻烦了点,这样的话,好像还得考虑有重复时,前面数组的元素还得考虑后移(数组中往前面添加元素,后面的元素都得全部后移一位)。
这时就考虑到为什么不从后面开始添加,并且谁大就放谁到追后一位就好了。所以就有了上面的解决方案了。
其实要是用一些 api 还是很好解决的,两行就搞定了
nums1.splice(m, nums1.length - m, ...nums2);
nums1.sort((a, b) => a - b);
补充说明
splice(index , number , add):
可删除从 index 处开始的number个元素,并且用参数列表中声明的一个或多个值来替换那些被删除的元素。会直接改变数组。
slice(start , end):
返回从数组start 到 end 的元素 ,不会改变数组
sort(sortby):
sort() 方法用于对数组的元素进行排序。会改变数组。会根据 sortby(a ,b) 这个方法,方法返回值大于 0,则位置互换, b 到 a 的前面 。返回值小于或等于0,则位置不变。
八、第八天:509. 斐波那契数(8/03)
509. 斐波那契数
斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fibonacci-number
这题也算大家基本在课程上都听老师说过的题目了,看起来,这题就是个递归,简单的一行代码就能实现了。
int fib( N) {
if (N == 1 || N == 2) return 1;
return fib(N - 1) + fib(N - 2);
}
但是这种解决方法,无疑是最低效率的,通过下图我们可以看出:
一层一层遍历下去,并且其中还有很多的重复计算。那么我们这里其实可以有一种思想就是通过建立一个备忘录(哈希表),来记录出现过的。不就能大大减少没必要的计算,实现对算法的剪枝。其实我们所有的算法优化都是对暴力穷举的优化,当我们能暴力解出问题后,我们就能针对其中那个地方,可以调优。
var fib = function(n) {
if(n === 0) return 0;
let dp = [];
dp[0] = 0 ;
dp[1] = 1;
for(let i = 2 ; i<=n ; i++ ) {
dp[i] = dp[i-1] + dp[i-2]
}
return dp[n]
};
上面问题的优化后的解决办法,采用自底向上思想。
好吧,今天我就是想偷懒,随便做了一题~~~
九、第九天:350. 两个数组的交集 II(8/04)
350. 两个数组的交集 II
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/intersection-of-two-arrays-ii
var intersect = function(nums1, nums2) {
let map = new Map();
let arr =[];
for(let i of nums1) {
let times = map.get(i)
if(times) {
times++
map.set(i,times)
}else {
map.set(i, 1)
}
}
for(let i of nums2){
let times = map.get(i)
if(times > 0) {
arr.push(i)
times--
map.set(i,times)
}
}
return arr
};
这题一看就会想到用哈希表~~~然后统计出现次数。构造好哈希表后,去和另外个数组比较,出现了就把它添加到我们的结果数组中,同时次数减一。并且当次数要大于0。
首先对两个数组进行排序,然后使用两个指针遍历两个数组。
另外个解法就是:排序加指针
初始时,两个指针分别指向两个数组的头部。每次比较两个指针指向的两个数组中的数字,如果两个数字不相等,则将指向较小数字的指针右移一位,如果两个数字相等,将该数字添加到答案,并将两个指针都右移一位。当至少有一个指针超出数组范围时,遍历结束。
十、第十天:121. 买卖股票的最佳时机(8/05)
121. 买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
示例 1:
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock
var maxProfit = function(prices) {
let localMax=0, max=0;
for (let i=1;i<prices.length;i++) {
localMax = Math.max(0, localMax += prices[i]-prices[i-1]);
max = Math.max(localMax, max);
}
return max;
};
我们转变一下不就变为最大子序列和了
[7,1,5,3,6,4]
[-6,4,-2,3,-2]
首先我们思考这个点买不买,买了第二天就亏了,那七这个点肯定就不买了,那么我们确定买不买后,我们确定的就是什么时候卖了,我们下一天卖的钱,等于前一天赚到的加上下一天价格减去前一天的。
十一、第一天:566. 重塑矩阵(8/06)
566. 重塑矩阵
在 MATLAB 中,有一个非常有用的函数 reshape ,它可以将一个 m x n 矩阵重塑为另一个大小不同(r x c)的新矩阵,但保留其原始数据。
给你一个由二维数组 mat 表示的 m x n 矩阵,以及两个正整数 r 和 c ,分别表示想要的重构的矩阵的行数和列数。
重构后的矩阵需要将原始矩阵的所有元素以相同的 行遍历顺序 填充。
如果具有给定参数的 reshape 操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reshape-the-matrix
var matrixReshape = function(mat, r, c) {
//判断是否有效
const m = mat.length;
const n = mat[0].length;
if (m * n != r * c) {
return mat;
}
//创建多维数组
const ans = new Array(r).fill(0).map(() => new Array(c).fill(0));
//填入数据
for (let x = 0; x < m * n; ++x) {
ans[Math.floor(x / c)][x % c] = mat[Math.floor(x / n)][x % n];
}
return ans;
};
这题的关键在于理解下面这两个式子
-
(i,j)→i×n+j (行数为 m,列数为 n)
-
i=x / n , j=x % n
所以就得出下面这个关键的式子:
ans[Math.floor(x / c)][x % c] = mat[Math.floor(x / n)][x % n];
十二、第十二天:322. 零钱兑换(8/07)
322. 零钱兑换
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-change
var coinChange = function(coins, amount) {
// 本题采用 自底向上 的动态规划解法
/*
首先,构造出amount+1的数组,
之所以+1,是为了保障最后的金额(最初的原始金额)有位置可以存放
例如:amount[11]存放着总金额11的最少金币组合
*/
// 数组中每一项都事先赋为正无穷,便于与最小值的判断
let dp = new Array(amount + 1).fill(Infinity);
// 首先预先赋值为0,因为金额0的解法有0种
dp[0] = 0;
/*
破题关键
每种金额的解法至1金币始,循环到金额amount为止。
每次外层for循环时,内部的for...of循环来判断是否可用现有的金币组合来组成amount金币量
举例:amount为11,coins为[1,2,5],则取以下解法的最小值
coins为1时,amount[11] = 1(利用硬币金额1来解,故占一个金额的位置) + amount[11-1](假设已知,且为最小值)
coins为2时,amount[11] = 1(利用硬币金额2来解,故占一个金额的位置) + amount[11-2](假设已知,且为最小值)
coins为5时,amount[11] = 1(利用硬币金额5来解,故占一个金额的位置) + amount[11-5](假设已知,且为最小值)
*/
for(let i = 1; i <= amount; i++) {
for(let coin of coins) {
if (i - coin >= 0) {
// dp[i]本身的解法 和 dp[当前的总金额i(即amount) - 遍历的icon] + 1(遍历的icon) 的解法的最小值
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
}
// 如果结果为无穷大,则无解
return dp[amount] === Infinity ? -1 : dp[amount];
};
其实这题很早前做过,也算动态规划的入门题了,但我想说的是,这题和上面的斐波那契数一样,都有优化的方法,就是创建备忘录,来减少重复的计算。同时明确,动态规划中,我们要针对性的找出转移方程,把问题化简。如何得出转移方程:
就如算法小抄里面写到的:(这个网站不错,学习算法可以多参考)
十三、第十三天:46. 全排列(8/08)
46. 全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations
var permute = function(nums) {
let res = [];
let set = new Set();
backtracking();
return res;
function backtracking() {
if (set.size === nums.length) {
res.push(Array.from(set));
return;
}
for (let i = 0; i < nums.length; i++) {
if (set.has(nums[i])) continue; // 已经上一轮选择过了,跳过
set.add(nums[i]);//加入选择
backtracking();//递归继续选择没有选择过的
set.delete(nums[i]);//撤销返回上一步
}
}
};
这题涉及的就是一个回溯算法的知识点了,回溯的模板如下:
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
那么简单来说,就是我们在进入下一步前做出选择,在递归之后撤销刚才的选择,那么我们就能得到每个节点的选择列表和路径。
由于就是暴力遍历全部的结果,所以复杂度很高~~~
十四、第十四天:51. N 皇后(8/08)
51. N 皇后
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-queens
var solveNQueens = function(n) {
const res = [];
// 棋盘的初始化
const board = new Array(n).fill('.').map(() => new Array(n).fill('.'));
backtrack(board, 0);
return res;
function backtrack (board, row) {
if (row === board.length) {
res.push(board.map(item => item.join('')))
return;
}
for (let col = 0; col < n; col++) {
//判断是否已经选择过
if (!isValid(board, row, col)) continue
// 没有放过,放置皇后
board[row][col] = "Q";
//递归
backtrack(board, row + 1);
//回退
board[row][col] = '.';
}
}
};
//判断是否能放置皇后
function isValid (board, row, col) {
const n = board.length;
// 检查列中
for (let i = 0; i < row; i++) {
if (board[i][col] === 'Q') {
return false;
}
}
//右上方
for (let i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (board[i][j] === 'Q') {
return false;
}
}
//左上方
for (let i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (board[i][j] === 'Q') {
return false;
}
}
return true;
}
话说我们上面做了全排列,了解了回溯,咋能不来道这么金典的N皇后的问题呢,其实我们可以发现,这个问题可以看为一个多叉树的问题,那么是不是我们遍历全部,找出符合的~~其实也就和上面全排列的一个意思的,不同处在于我们的判断条件。这里判断能不能放置就比较麻烦,我们把它单独领出来一个方法判断,那么实际它的框架就如我们上面的一样的。
十五、第十五天:66. 加一(8/09)
66. 加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/plus-one
var plusOne = function(digits) {
for(i = digits.length-1 ; i >= 0 ; i--) {
if(digits[i] !== 9) {
digits[i]++
return digits
}else {
digits[i] = 0
}
}
const result = [1,...digits]
return result
};
养生~~~
十六、第十六天:111. 二叉树的最小深度(8/11)
111. 二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/
var minDepth = function(root) {
if(!root) return 0;
const queue = [root];
console.log(queue);
let dep = 0;
while(true) {
let size = queue.length;
console.log(size);
dep++;
while(size--){
const node = queue.shift();
// 到第一个叶子节点 返回 当前深度
if(!node.left && !node.right) return dep;
node.left && queue.push(node.left);
node.right && queue.push(node.right);
}
}
};
这里实际就是使用BFC算法,我们从第一层开始,每层每层的去递进,这样的话就是牺牲空间复杂度,同时他能保证我们找到的就是最小的,因为我们每递进一个节点,深度就加一,保证我们遇到的时候就一定是最短的了。
十七、136. 只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/single-number
解法
//异或运算满足交换律
//a^b^a=a^a^b=b,因此ans相当于nums[0]^nums[1]^nums[2]^nums[3]^nums[4].....
// 再根据交换律把相等的合并到一块儿进行异或(结果为0),然后再与只出现过一次的元素进行异或,
// 这样最后的结果就是,只出现过一次的元素(0^任意值=任意值)
var singleNumber = function(nums) {
let ans = 0;
for(const num of nums) {
ans ^= num;
}
return ans;
};
十八、剑指 Offer 24. 反转链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof
解法
var reverseList = function(head) {
let cur = head
let pre = null
while(cur != null) {
const next = cur.next
cur.next = pre
pre = cur
cur = next
}
return pre
};
总结
每天刷题,每天变强。