算法练习笔记(五)
10.30
①反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
示例 1:
输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]
示例 2:
输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-string
思路:
采用双指针。
代码:
//递归
var reverseString = function(s){
	function reverseString(left,right){
		if(left>=right) return 
		[s[left],s[right]] = [s[right],s[left]]
		reverseString(left+1,right+.1)
	}
	reverseString(0,s.length+1)
	return s
}
//非递归
var reverseString = function(s){
    let left = 0,right = s.length-1
    while(left<right){
        [s[left],s[right]] = [s[right],s[left]]
        left++
        right--
    }
    return s
}
②Pow(x, n)
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。
示例 1:
输入:x = 2.00000, n = 10
输出:1024.00000
示例 2:
输入:x = 2.10000, n = 3
输出:9.26100
示例 3:
输入:x = 2.00000, n = -2
输出:0.25000
解释:2-2 = 1/22 = 1/4 = 0.25
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/powx-n
思路:
参考:https://leetcode-cn.com/problems/powx-n/solution/qi-ye-ji-li-jie-by-gang-feng-dlx4/
代码:
var myPow = function(x, n) {
    if(n == 0)return 1
    if(n<0){
        return 1/myPow(x,-n)
    }
    if(n%2){
        return x*myPow(x,n-1)
    }
    return myPow(x*x,n/2)
};
10.31
①两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
提示:
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/two-sum
思路:
利用哈希表记录已经遍历过的数值和它对应的下标,借助查表实现
代码:
var twoSum = function(nums, target) {
    const len = nums.length
    const map = new Map()
    for(let i=0;i<len;i++){
        //记录差值
        let difference = target - nums[i]
        //在map查找匹配
        if(map.has(difference)) return [map.get(difference),i]
        //匹配不上,将当前数存入map
        map.set(nums[i],i) 
    }
};
②删除有序数组中的重复项
给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
示例 1:
输入:nums = [1,1,2]
输出:2, nums = [1,2]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
示例 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 。不需要考虑数组中超出新长度后面的元素。
提示:
0 <= nums.length <= 3 * 104
-104 <= nums[i] <= 104
nums 已按升序排列
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array
思路:
采用双指针,将指针fast指向的元素与指针slow指向的元素进行比较:
- 如果nums[slow] = nums[fast],fast继续前进
- 如果nums[slow] ≠ nums[fast],nums[slow+1] = nums[fast]
通过这个步骤就可以将数组中不重复的元素放到前面 n 个,重复项虽然还在,但返回的是数组开头元素中不重复的长度。
代码:
var removeDuplicates = function(nums) {
    if(!nums.length)return 0
    let slow = 0,fast = 1
    while(fast<nums.length){
        if(nums[slow]!=nums[fast]){
            slow = slow + 1
            nums[slow] = nums[fast]
        }
        fast = fast+1
    }
    return slow+1
};
11.1
①加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。
示例 2:
输入:digits = [4,3,2,1]
输出:[4,3,2,2]
解释:输入数组表示数字 4321。
示例 3:
输入:digits = [0]
输出:[1]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/plus-one
思路:
数组末尾先加一,然后开始倒序遍历数组,判断当前位数字是否需要进位。当数组遍历完还存在进位,则在数组首位添加1.
代码:
var plusOne = function(digits){
	//进位标志
	let carry = false
	//数组末尾先+1
	digits[digits.length-1]++
	//倒序遍历数组
	for(let i=digits.length-1;i>=0;i--){
		//进位+1
		if(carry)digits[i]++
		//判断是否存在进位
		carry = digits[i]>9
		//数组每个元素只存储单个数字
		digits[i] %= 10
	}
	//还存在进位,在数组首位添加1
	if(carry)digits.unshift(1)
	return digits
}
②合并两个有序数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
示例 2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。
示例 3:
输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-sorted-array
思路:
创建三个指针,两个指针用于指向 nums1 和 nums2的元素数量的末位,还有一个指针,指向 nums1 数组末位即可,用于插入新元素。
先比较较大的数,把大的数放到数组nums1的后面。
这里只要保证nums2数组中的所有元素比较完即可
代码:
var merge = function(nums1,m,nums2,n){
	let k = m+n-1
	m--
	n--
	while(n>=0){
		if(nums1[m]>nums2[n]){
			nums1[k--] = nums1[m--]
		}else{
			nums1[k--] = nums2[n--]
		}
	}
	return nums1
}
11.2
①只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/single-number
思路:
使用异或运算,两数相同异或为0,两数相异异或为1,0异或任何数都等于异或的那个数。
代码:
var singleNumber = function(nums){
	let h = 0;
	for(let i in nums){
		h ^= nums[i]
	}
	return h
}
②多数元素
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:[3,2,3]
输出:3
示例 2:
输入:[2,2,1,1,1,2,2]
输出:2
进阶:
尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/majority-element
思路:
①使用map寻找出现次数最多的数
代码:
//使用map
var majorityElement = function(nums){
	const map = {}
	let n = nums.length/2
	for(let i in nums){
		map[nums[i]] = map[nums[i]] != undefined?map[nums[i]]+1:1
		if(map[nums[i]]>n) return nums[i]
	}
}
//使用投票算法
var majorityElement = function(nums){
    let sum = 1
    let target = nums[0]
    for(let i=1;i<nums.length;i++){
        if(sum == 0){
            target = nums[i]
        }
        if(nums[i] == target){
            sum++
        }else{
            sum--
        }
    }
    return target
}
11.3
① 存在重复元素
给定一个整数数组,判断是否存在重复元素。
如果存在一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。
示例 1:
输入: [1,2,3,1]
输出: true
示例 2:
输入: [1,2,3,4]
输出: false
示例 3:
输入: [1,1,1,3,3,4,3,2,4,2]
输出: true
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/contains-duplicate
代码:
var containsDuplicate = function(nums){
	const map = new Map()
	for(let i in nums){
        //查找该数是否已存在map中
		if(map.has(nums[i])){
			return true
		}else{
			map.set(nums[i],1)
		}
	}
	return false
}
②丢失的数字
给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。
示例 1:
输入:nums = [3,0,1]
输出:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。
示例 2:
输入:nums = [0,1]
输出:2
解释:n = 2,因为有 2 个数字,所以所有的数字都在范围 [0,2] 内。2 是丢失的数字,因为它没有出现在 nums 中。
示例 3:
输入:nums = [9,6,4,2,3,5,7,0,1]
输出:8
解释:n = 9,因为有 9 个数字,所以所有的数字都在范围 [0,9] 内。8 是丢失的数字,因为它没有出现在 nums 中。
示例 4:
输入:nums = [0]
输出:1
解释:n = 1,因为有 1 个数字,所以所有的数字都在范围 [0,1] 内。1 是丢失的数字,因为它没有出现在 nums 中。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/missing-number
思路:
使用异或运算,一下子就能看出丢失的数字。
| i | 0 | 1 | 2 | 3 | 
|---|---|---|---|---|
| nums[i] | 0 | 1 | 3 | |
| 异或运算 | 0 | 0 | 0^2=2 | 3^3=0 | 
代码:
var missingNumber = function(nums) {    const n = nums.length    let res = 0    res = 0^n    for(let i=0;i<n;i++){        res = res^i^nums[i]    }    return res};
11.4
①移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/move-zeroes
代码:
var moveZeroes = function(nums){
	let j = 0
	for(let i=0;i<nums.length;i++){
		if(nums[i]!==0){
			[nums[i],nums[j]] = [nums[j],nums[i]]
			j++
		}
	}
}
②两个数组的交集 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) {	const res = []	const map = {}	for(let num of nums1){		if(map[num]){			map[num]++		}else{			map[num] = 1		}	}	for(let num of nums2){		let val = map[num]		if(val>0){			res.push(num)			map[num]--		}	}	return res}
11.5
①盛最多水的容器
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/container-with-most-water
思路:
容纳最多的水取决于高度(min(height[left],height[right]))和宽度(right-left)
使用双指针分别指向数组首尾,当左边高度比较低,则left右移,右边高度比较低,righjt左移。从而找到最大容纳水量
代码:
var maxArea = function(height){
	let left = 0
	let right = height.length-1
	let max = 0
	while(left<right){
		max = Math.max(max,Math.min(height[left],height[right])*(right-left))
		if(height[left]<height[right]){
			left++
		}else{
			right--
		}
	}
	return max
}
②三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
思路:
参考:https://leetcode-cn.com/problems/3sum/solution/zhi-zhen-yi-dong-guo-cheng-zhong-tiao-guo-zhong-fu/
代码:
var threeSum = function(nums) {    const len = nums.length    const res = []    nums = nums.sort((a,b)=>{        return a-b    })    for(let i=0;i<len-2;i++){        //左指针        let j = i+1        //右指针        let k = len-1        if(nums[i]>0) break        if(nums[i] === nums[i-1]){            continue        }        while(j<k){            if(nums[i]+nums[j]+nums[k]<0){                j++                while(j<k&&nums[j]===nums[j-1]){                    j++                }            }else if(nums[i]+nums[j]+nums[k]>0){                k--                while(j<k&&nums[k]===nums[k+1]){                    k--                }            }else{                res.push([nums[i],nums[j],nums[k]])                j++                k--                while(j<k&&nums[j]===nums[j-1]){                    j++                }                while(j<k&&nums[k]===nums[k+1]){                    k--                }            }        }    }    return res};
11.7
①在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array
思路:
代码:
function find(isFindFirst,nums,target){	let start = 0,end = nums.length-1	while(start<=end){         let mid = start+((end-start)>>1)		//如果小于,则应往右找		if(nums[mid]<target){			start = mid+1		}else if(nums[mid]>target){ //如果大于则应往左找			end = mid-1		}else{			//查找左边界			if(isFindFirst){				//如果mid不是第一个元素并且前面一个相邻的元素也跟mid相等,则搜索区间往左缩小				if(mid>0&&nums[mid]===nums[mid-1]){					end = mid-1				}else{					return mid				}			}else{				//如果mid不是最后一个元素并且后面一个相邻的元素也跟mid相等,则搜索区域向右缩小				if(mid<nums.length-1&&nums[mid]===nums[mid+1]){					start = mid+1				}else{					return mid				}			}		}	}	return -1}var searchRange = function (nums, target){	if(nums === null||!nums.length){		return [-1,-1]	}	const left = find(true,nums,target)	const right = find(false,nums,target)	return [left,right]}
②搜索旋转排序数组
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
示例 2:
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
示例 3:
输入:nums = [1], target = 0
输出:-1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array
代码:
var search = function(nums,target){
	if(nums.length === 0){
		return -1
	}
	if(nums.length ===1){
		return nums[0] === target?0:-1
	}
	let left =0,right = nums.length-1
	while(left<right){
		let mid = left + ((right-left)>>1)
		if(nums[mid]===target){
			return mid
		}else{
			if(nums[left]<=nums[mid]){
				if(nums[left]<=target&&target<=nums[mid]){
					right = mid-1
				}else{
					left = mid+1
				}
			}else{
				if(nums[mid+1]<=target&&target<=nums[right]){
					left = mid+1
				}else{
					right = mid-1
				}
			}
		}
	}
	return nums[left] === target?left:-1
}
11.7
①有效的数独
请你判断一个 9x9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
- 数字 1-9在每一行只能出现一次。
- 数字 1-9在每一列只能出现一次。
- 数字 1-9在每一个以粗实线分隔的3x3宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 '.' 表示。
注意:
- 一个有效的数独(部分已被填充)不一定是可解的。
- 只需要根据以上规则,验证已经填入的数字是否有效即可。


代码:
var isValidSudoku = function(board){	//用于验证每一行数字	let rows = []	//用于验证每一列数字	let cols = []	//用于验证每一个3x3宫里的数字	let box = []	for(let i=0;i<9;i++){		rows[i] = new Map()		cols[i] = new Map()		box[i] = new Map()	}	for(let i=0;i<board.length;i++){		for(let j=0;j<board[i].length;j++){			if(board[i][j]!=="."){				//获取数字所在子数组的序号				let s = parseInt(i/3)*3+parseInt(j/3)				if(rows[i].has(board[i][j])||cols[j].has(board[i][j])||box[s].has(board[i][j]))					return false				else{					rows[i].set(board[i][j],1)					cols[j].set(board[i][j],1)					box[s].set(board[i][j],1)				}			}					}	}	return true}
②全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
输入:nums = [0,1]
输出:[[0,1],[1,0]]
示例 3:
输入:nums = [1]
输出:[[1]]
提示:
1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations
思路:
代码:
var permute = function(nums){
	const res = []
	const used = {}
	function dfs(path){
		if(path.length === nums.length){
			res.push(path.slice())
			return
		}
		for(const num of nums){
			if(used[num])continue
			path.push(num)
			used[num] = true
			dfs(path)
			path.pop()
			used[num] = false
		}
	}
	dfs([])
	return res
}
11.8
①旋转图像
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
示例 2:
输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]
示例 3:
输入:matrix = [[1]]
输出:[[1]]
示例 4:
输入:matrix = [[1,2],[3,4]]
输出:[[3,1],[4,2]]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-image
思路:
先对角线互换位置,再将每一行进行翻转
代码:
var rotate = function(matrix){	const len = matrix.length	for(let i=0;i<len;i++){		for(let j=i+1;j<len;j++){			//对角线互换位置			[matrix[i][j],matrix[j][i]] = [matrix[j][i],matrix[i][j]]		}	}	//将每一行进行翻转后返回	return matrix.map(item=>item.reverse())}
②合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-intervals
思路:
代码:
var merge = function(intervals){
	const res = []
	//按区间左端点排序,保证prev[0]<cur[0]
	intervals.sort((a,b)=>a[0]-b[0])
	//初始化prev
	let prev = intervals[0]
	for(let i=1;i<intervals.length;i++){
		let cur = intervals[i]
		//区间存在重合,可以合并
		if(prev[1]>=cur[0]){
			prev[1] = Math.max(prev[1],cur[1])
		}else{
            //不重合再推入 prev
			res.push(prev)
			prev = cur
		}
	}
    //最后区间可能不重合,需要单独补上
	res.push(prev)
	return res
}
11.9
①螺旋矩阵
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。


思路:
由题意可知,该螺旋矩阵的遍历顺序是从左往右,再从上往下,接着从右往左,最后从下往上 这样一个周期循环输出结果。
分别以top、left、bottom、right对应这四个边界进行循环遍历输出,需要注意的是,在循环中途可能就已经遍历完矩阵中的所有元素,此时应当及时break,不然就会重复遍历。
代码:
var spiralOrder = function(matrix){
	if(matrix.length === 0) return []
	//初始化四个方向
	let left = 0,top = 0,bottom = matrix.length-1,right = matrix[0].length-1
	const res = []
	//计算矩阵元素的数量
	const size = matrix.length*matrix[0].length
	while(res.length!==size){
		//从左到右
		for(let i=left;i<=right;i++) res.push(matrix[top][i])
		top++
		//从上到下
		for(let i=top;i<=bottom;i++) res.push(matrix[i][right])
		right--
		//中途需要判断,提前退出
		if(res.length === size) break
		//从右往左
		for(let i=right;i>=left;i--) res.push(matrix[bottom][i])
		bottom--
		//从下往上
		for(let i=bottom;i>=top;i--) res.push(matrix[i][left])
		left++
	}
	return res
}
②矩阵置零


var setZeroes = function(matrix) {    let row = matrix.length    let col = matrix[0].length    //第一行是否存在0的标志    let flag = false    //第一行特殊处理,判断是否存在0    for(let j=0;j<col;j++){        if(matrix[0][j] == 0){            flag = true            break        }    }    //从第二行开始,判断当前是否存在0    for(let i=1;i<row;i++){        for(let j=0;j<col;j++){            if(matrix[i][j] == 0){                //如果存在0,则将当前行和当前列的首个元素置为0                matrix[i][0] = 0                matrix[0][j] = 0            }        }    }    //赋值0    for(let i=1;i<row;i++){        //从末尾开始,防止matrix[0][0]==0导致matrix[0][j]=0影响整行        for(let j=col-1;j>=0;j--){            //如果当前行或者当前列的首个元素为0则证明当前位置应置为0            if(matrix[0][j] == 0 ||matrix[i][0] == 0){                matrix[i][j] = 0            }        }    }    //处理第一行的问题    if(flag){        for(let j=0;j<col;j++){            matrix[0][j] = 0        }    }    return matrix};
11.10
①颜色分类
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:
输入:nums = [2,0,1]
输出:[0,1,2]
示例 3:
输入:nums = [0]
输出:[0]
示例 4:
输入:nums = [1]
输出:[1]
提示:
n == nums.length
1 <= n <= 300
nums[i] 为 0、1 或 2
进阶:
你可以不使用代码库中的排序函数来解决这道题吗?
你能想出一个仅使用常数空间的一趟扫描算法吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-color
思路:
使用三个指针——pre、current和post,pre和post分别指向数组的首尾,用于元素交换的定位,current表示当前访问的元素。
- 当nums[current]===0时,交换nums[current]和nums[pre],pre与current都右移一位
- 当nums[current]===1时,current右移一位
- 当nums[current]===2时,交换nums[current]和nums[post],post左移一位
代码:
var sortColors = function(nums) {    if(nums.length<2) return nums    let pre = 0,current = 0,post=nums.length-1    while(current<=post){        if(nums[current] === 0){            [nums[current],nums[pre]] = [nums[pre],nums[current]]            current++            pre++        }else if(nums[current] === 1){            current++        }else{            [nums[current],nums[post]] = [nums[post],nums[current]]            post--        }    }    return nums};
②子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subsets
思路:
代码:
var subsets = function(nums){	const res = []	const dfs = function(index,list){		if(index === nums.length){			res.push(list.slice())			return		}        //选择这个数		list.push(nums[index])        //基于这个选择考察下一个数		dfs(index+1,list)        //撤销选择		list.pop()        //基于不选考察下一个数		dfs(index+1,list)	}	dfs(0,[])	return res}
11.11
①单词搜索
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。



来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/word-search
思路:
代码:
var exist = function(board, word) {
    const m = board.length
    const n = board[0].length
    //记录已经访问过的结点
    const used = new Array(m)
    for(let i=0;i<m;i++){
        used[i] = new Array(n)
    }
    //canFind判断当前点是否是目标路径上的点
    const canFind = (row,col,i)=>{
        // 递归的出口 i越界了就返回true
        if(i === word.length){
            return true
        }
        //当前点越界
        if(row<0||row>=m||col<0||col>=n){
            return false
        }
        //当前点已经访问过,或非目标结点
        if(used[row][col]||board[row][col]!==word[i]){
            return false
        }
        //排除掉所有false的情况,当前点暂时没毛病,记录为已经访问过的结点
        used[row][col] = true
        //选择方向继续访问(上下左右),找到剩余字符的路径,返回true或false
        const canFindRest = canFind(row+1,col,i+1)||canFind(row-1,col,i+1)||canFind(row,col-1,i+1)||canFind(row,col+1,i+1)
        if(canFindRest){// 基于当前点[row,col],可以为剩下的字符找到路径
            return true
        }
        used[row][col] = false;//不能为剩下字符找到路径,撤销当前点的访问状态,并返回false
        return false
    }
   
    //遍历网格找入口
    for(let i=0;i<m;i++){
        for(let j=0;j<n;j++){
            if(board[i][j]===word[0]&&canFind(i,j,0)){
                return true
            }
        }
    }
     // 怎么样都没有返回true,则返回false
    return false
};
②最长连续序列
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
示例 2:
输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-consecutive-sequence
代码:
var longestConsecutive = function(nums){	const set = new Set(nums)	let max = 0	//通过遍历数组查找尽可能小的起点,然后计算以该元素为起点的数字连续序列的长度    for(let val of nums){        //判断数组中是否存在比当前元素还小的数),没有则暂定为起点,有则跳过        if(set.has(val-1))continue        let count = 1        //遍历以当前元素为起点的数字连续序列,计算其长度        while(set.has(val+1)){            // 一旦查找过的直接删除即可,防止重复查找            set.delete(val+1)            val++            count++        }        max = Math.max(max,count) //检查count是否最大    }    return max}
11.12
①被围绕的区域
给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/surrounded-regions
var solve = function(board){
	const m = board.length
	if(m===0)return
	const n = board[0].length
	const dfs = (i,j)=>{
		if(i<0||i===m||j<0||j===n)return
		if(board[i][j]==='O'){ //找到非岛屿,记为NO
			board[i][j] = 'NO'
			// 对四个方向的邻居进行dfs
			dfs(i+1,j) 
			dfs(i-1,j)
			dfs(i,j+1)
			dfs(i,j-1)
		}
	}
	for(let i=0;i<m;i++){
		for(let j=0;j<n;j++){
			if(i==0||i==m-1||j==0||j==n-1){
				if(board[i][j]==='O') dfs(i,j) // 从最外层的O,开始DFS
			}
		}
	}
	for(let i=0;i<m;i++){
		for(let j=0;j<n;j++){
			if(board[i][j]==='NO')board[i][j] = 'O'  // 恢复为O
			else if(board[i][j]==='O')board[i][j] = 'X'  // O变为X
		}
	}
}
②加油站
在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
说明:
如果题目有解,该答案即为唯一答案。
输入数组均为非空数组,且长度相同。
输入数组中的元素均为非负数。
示例 1:
输入:
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。
示例 2:
输入:
gas = [2,3,4]
cost = [3,4,3]输出: -1
解释:
你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/gas-station
代码:
var canCompleteCircuit = function(gas, cost) {
    let rest = 0;//剩余油量
    let start = 0;//起点
    let totalGas = 0//总加油量
    let totalCost = 0//总耗油量
    for(let i=0;i<gas.length;i++){
        totalGas += gas[i]
        totalCost += cost[i]
        rest += gas[i]-cost[i]
        //小于0说明车到不了下一站即(i+1),说明[0,i]区间都不能作为起始位置,此时起点设置为i+1,rest从0算起
        if(rest<0){
            start = i+1
            rest = 0
        }
    }
    //总加油量小于总耗油量说明不可能绕环路行驶一周
    if(totalGas<totalCost){
        return -1
    }
    return start
};
11.13
①寻找峰值
峰值元素是指其值严格大于左右相邻值的元素。
给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。
你必须实现时间复杂度为 O(log n) 的算法来解决此问题。
示例 1:
输入:nums = [1,2,3,1]
输出:2
解释:3 是峰值元素,你的函数应该返回其索引 2。
示例 2:
输入:nums = [1,2,1,3,5,6,4]
输出:1 或 5
解释:你的函数可以返回索引 1,其峰值元素为 2;
或者返回索引 5, 其峰值元素为 6。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-peak-element
思路:
每次取中间元素mid
若nums[mid]>nums[mid+1],说明mid在下降的一小段中,峰值肯定在mid左边(包括mid),所以定位到mid左边区间(包括mid);
若nums[mid]<nums[mid+1],说明mid在上升的一小段中,峰值肯定在mid右边,mid不可能是峰值,所以定位到mid右边区间(不包括mid)

代码:
const findPeakElement = nums=>{
	let [left,right] = [0,nums.length-1]
	while(left<right){
		const mid = (left+right)>>1
		if(nums[mid]>nums[mid+1]){
			//下降
			right = mid
		}else{
			//上升
			left = mid+1
		}
	}
	return left
}
②轮转数组
给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-array
思路:
代码:
var rotate = function(nums,k){
	k %= nums.length
	let reverse = function(start,end){
		while(start<end){
			[nums[start++],nums[end--]] = [nums[end],nums[start]]
		}
	}
	//翻转整个数组
	reverse(0,nums.length-1)
	//左右子数组,各自翻转
	reverse(0,k-1)
	reverse(k,nums.length-1)
	return nums
}
11.14
①岛屿数量
给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
输出:1
示例 2:
输入:grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
输出:3
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-islands
思路:
代码
function turnZero(i,j,grid){
	if(i<0||i>=grid.length||j<0||j>=grid[0].length||grid[i][j]==='0') return
	grid[i][j]='0'
	turnZero(i,j+1,grid)
	turnZero(i,j-1,grid)
	turnZero(i+1,j,grid)
	turnZero(i-1,j,grid)
}
var numIsland = function(grid){
	let count = 0
	for(let i=0;i<grid.length;i++){
		for(let j=0;j<grid.length;j++){
			if(grid[i][j]==='1'){
				count++
				turnZero(i,j,grid)
			}
		}
	}
	return count
}
②计数质数
统计所有小于非负整数 n 的质数的数量。
示例 1:
输入:n = 10
输出:4
解释:小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
示例 2:
输入:n = 0
输出:0
示例 3:
输入:n = 1
输出:0
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-primes
思路:
使用 厄拉多塞筛法
代码:
var countPrimes = function(n){
	const flag = new Array(n).fill(true)
	let count = 0
	for(let i=2;i<n;i++){
		if(flag[i]){
			count++
			for(let j=i*i;j<n;j+=i){
				flag[j] = false
			}
		}
	}
	return count
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号