文章目录
- 📋前言
- 🎯删除有序数组中的重复项
- 📚题目内容
- ✅解答
- 🎯移动零
- 📚题目内容
- ✅解答
- 🎯长度最小的子数组
- 📚题目内容
- ✅解答
- 🎯反转字符串数组
- 📚题目内容
- ✅解答
- 📝最后
📋前言
这个系列的文章收纳的内容是来自于蓝桥云课的前端岗位笔面必刷题的内容,简介是:30天133题,本题单题目全部来自于近2年BAT等大厂前端笔面真题!因为部分题目是需要会员,所以该系列的文章内容并非完全全面(如果需要会员的题目,则从 leetcode 补充对应的题目,题目大概也是一样的考法)
。文章中题目涉及的内容包括原题、答案和解析等等。
🎯删除有序数组中的重复项
📚题目内容
给你一个升序排列的数组 nums
,请你原地删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度。元素的相对顺序应该保持 一致 。
不要使用额外的空间,你必须在原地修改输入数组 并在使用 O(1)
额外空间的条件下完成。
输入:nums = [1,1,2]
输出:2, nums = [1,2]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。
输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。
题目给的测试用例里有以下限制:
1 <= nums.length <= 22
。0 <= nums[i] <= 7
。nums
已按升序排列。
✅解答
初始提供代码
function removeDuplicates(nums) {
// 补充代码
}
答案
function removeDuplicates(nums) {
return [...new Set(nums)].length
}
删除数组中重复的数据,顾名思义可以理解为数组去重,这样就有很多种方法去解决这个问题,一行代码即可。使用 Set
数据结构的特性去掉数组中重复的元素,并将去重后的元素重新存储到一个新的数组中。最终返回这个新数组的长度,也就是去重后数组的元素个数。
🎯移动零
📚题目内容
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
输入: nums = [0, 1, 0, 3, 12];
输出: [1, 3, 12, 0, 0];
输入: nums = [0];
输出: [0];
题目给的测试用例里有以下限制:
1 <= nums.length <= 5
。0 <= nums[i] <= 12
。
✅解答
初始提供代码
function moveZeroes(nums) {
// 补充代码
}
这题很有意思,同样的解法也有很多种,题目中的一句话其实很关键“必须在不复制数组的情况下原地对数组进行操作”
。因此这题写看看多种答案的解法和情况。
答案1️⃣
我做的时候没想太多其他情况,所以就一股脑的解了,先遍历数组找到为 0 的项,然后先用 splice
方法删除 0 ,再用 push
方法添加 0,代码如下。
function moveZeroes(nums) {
for(let i=0;i<nums.length;i++){
if(nums[i]===0)
{
nums.splice(i,1)
nums.push(0)
}
}
return nums
}
如果直接使用 for
循环遍历数组并且在循环体内使用 splice
方法来删除元素,会导致数组长度发生变化,从而导致某些元素被遗漏或重复处理。为了避免这个问题,我们可以倒着遍历数组,并在循环体内使用 splice
方法来删除元素,这样就不会影响到前面的元素。代码如下。
function moveZeroes(nums) {
for(let i=0;i<nums.length-1;i++){
if(nums[i]===0)
{
nums.splice(i,1)
nums.push(0)
}
}
return nums
}
然这个算法也可以实现将所有 0 元素移动到数组末尾,但是它的时间复杂度是 O(n^2),因为每次删除元素都要移动后面的元素,所以在遇到大规模的数组时会比较慢。
答案2️⃣
function moveZeroes(nums) {
let i = 0, j = 0;
while (i < nums.length) {
if (nums[i] !== 0) {
nums[j++] = nums[i];
}
i++;
}
while (j < nums.length) {
nums[j++] = 0;
}
return nums;
}
该函数的实现中,定义两个指针 i 和 j,初始时都指向数组的第一个元素。然后遍历数组,如果当前元素 nums[i] 不为 0,则将其赋值给 nums[j],同时将指针 j 右移;否则将指针 i 右移,跳过这个 0 元素。最后,将 j 指针之后的所有元素都赋值为 0 即可。
这个算法只需要遍历数组一次,时间复杂度是 O(n),并且不需要额外的空间,是一种比较高效的原地操作方法。
答案3️⃣
这个答案是看到其他同学发的题解,我的评价是很炸裂,先不论性能以及可读性,这个答案在这题的题解中也是很亮眼,只用了一行代码解决了这题。代码如下。
function moveZeroes(nums) {
return nums.sort((a, b) => !a - !b)
}
这个算法的思路是先将数组中的所有元素转换为布尔值,然后根据布尔值的大小关系进行排序,最后返回排序后的结果。
由于 JavaScript 中 0 是 false,非 0 是 true,所以将一个数转换成布尔值其实就是判断它是否为 0 。因此,这个算法的核心就是利用 sort()
方法的排序函数来比较两个元素的布尔值大小。
具体来说,排序函数接受两个参数 a 和 b,如果 !a - !b 的结果为正数,则表示 a 的布尔值为 false,b 的布尔值为 true,此时应该交换 a 和 b 的位置,使得 a 排在 b 前面;否则就不需要交换位置。最终排序的结果就是将所有非零元素排在了前面,所有零元素排在了后面。
这个算法虽然也只需要遍历数组一次,但是由于使用了 sort() 方法,时间复杂度取决于具体的排序算法,最坏情况下可能会达到 O(n^2),并且这个算法改变了原数组的顺序,不是一个原地操作方法。我是没想到还可以这样的解法的,只能说大佬牛逼(我只是算法萌新),但是我觉得还是 答案2️⃣
效果较优,但是算法题能通过检测就完了,不是实际开发的逻辑处理,飘一下也不是不行。
🎯长度最小的子数组
📚题目内容
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其和 ≥ target
的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0 。
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
输入:target = 4, nums = [1,4,4]
输出:1
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
题目给的测试用例里有以下限制:
1 <= target <= 11
。1 <= nums.length <= 8
。1 <= nums[i] <= 4
。
✅解答
初始提供代码
function minSubArrayLen(target, nums) {
// 补充代码
}
答案
function minSubArrayLen(target, nums) {
let left = 0;
let sum = 0;
let res = Infinity;
for (let right = 0; right < nums.length; right++) {
sum += nums[right];
while (sum >= target) {
res = Math.min(res, right - left + 1);
sum -= nums[left];
left++;
}
}
return res === Infinity ? 0 : res;
}
这道题小难啊,可以用双指针来解,两个指针 left
和 right
分别表示当前子数组的左右端点位置,初始值都为 0。然后用 sum
表示当前子数组的和,初始值为 0 。
- 每一轮循环,将
right
向右移动一位,即right = right + 1
,同时将sum
加上nums[right]
。 - 如果
sum >= target
,则更新结果res = Math.min(res, right - left + 1)
,并将left
向右移动一位,即left = left + 1
,同时将sum
减去nums[left - 1]
,使得 sum 变为不包括nums[left - 1]
的子数组的和(如果sum >= target
,那么减去nums[left - 1]
并不会影响结果)。 - 如果
sum < target
,继续执行第 1 步。直到right
超出了数组范围,算法结束。
需要注意的是,在实现时需要初始化 res
为 INT_MAX
,而不是 0。这是因为当数组中所有元素之和都小于 target
的时候,算法应该返回 0,而不是第一个元素的下标 0。
该函数的时间复杂度是 O(n),其中 n 是数组的长度。
🎯反转字符串数组
📚题目内容
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s
的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]
输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]
题目给的测试用例里有以下限制:
1 <= s.length <= 6
。s[i]
都是 ASCII 码表中的可打印字符。
✅解答
初始提供代码
function reverseString(s) {
// 补充代码
}
答案
看到题目第一眼,是不是偷笑了?反转字符串数组,我一个 reverse
方法搞定它,没毛病吧!?如果这题真是这样那其实没啥意思,注意看这句话 “不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。”
所以我们要思考其他解决方法,符合题目要求的方法。代码如下。(虽然直接用 reverse
方法 通过了测试hhh)
function reverseString(s) {
let left = 0;
let right = s.length - 1;
while (left < right) {
const tmp = s[left];
s[left] = s[right];
s[right] = tmp;
left++;
right--;
}
return s
}
这题也是用双指针的方法来解决的,将指针分别指向字符串的头尾,然后不断交换两个指针所指向的字符。
- 初始化左指针
left
为 0,右指针right
为字符串长度减一。 - 当
left < right
时,交换s[left] 和 s[right]
,然后将left
加 1,将right
减 1。 - 当
left >= right
时,反转结束。
该函数接受一个字符数组 s 作为参数,直接修改了原数组,符合了题目的要求。
📝最后
感谢阅读到这,这就是Day2 的全部内容了,文章内容中题目的答案都是通过检测的了,如果有疑问和争议的内容,可以评论区留言和私信我,收到消息第一时间解答和回复。(来自詹姆斯的点赞👍)