算法练习笔记(五)

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寻找出现次数最多的数

②使用投票算法。https://leetcode-cn.com/problems/majority-element/solution/169-duo-shu-yuan-su-tou-piao-suan-fa-python-js-by-/

代码

//使用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 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器。

image-20211105105043757

来源:力扣(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

思路

参考:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/solution/ba-er-fen-fa-de-xi-jie-dian-shuo-tou-by-w9bcc/

代码

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. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

数独部分空格内已填入了数字,空白格用 '.' 表示。

注意:

  • 一个有效的数独(部分已被填充)不一定是可解的。
  • 只需要根据以上规则,验证已经填入的数字是否有效即可。

image-20211107141839431

image-20211107141854590

代码

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

思路

参考:https://leetcode-cn.com/problems/permutations/solution/chou-xiang-cheng-jue-ce-shu-yi-ge-pai-lie-jiu-xian/

代码

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

思路

参考:https://leetcode-cn.com/problems/merge-intervals/solution/shou-hua-tu-jie-56he-bing-qu-jian-by-xiao_ben_zhu/

代码

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

螺旋矩阵

给你一个 mn 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

image-20211109125834888

image-20211109125849740

思路

由题意可知,该螺旋矩阵的遍历顺序是从左往右,再从上往下,接着从右往左,最后从下往上 这样一个周期循环输出结果。

分别以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
}

矩阵置零

image-20211109141227517

image-20211109141251032

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
思路

参考:https://leetcode-cn.com/problems/subsets/solution/shou-hua-tu-jie-zi-ji-hui-su-fa-xiang-jie-wei-yun-/

代码

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 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

image-20211111132955037

image-20211111133108613

image-20211111133131193

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/word-search

思路

参考:https://leetcode-cn.com/problems/word-search/solution/shou-hua-tu-jie-79-dan-ci-sou-suo-dfs-si-lu-de-cha/

代码

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' 填充。

image-20211112131218523

来源:力扣(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)

image-20211113132554345

代码

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

思路

参考:https://leetcode-cn.com/problems/rotate-array/solution/shu-zu-fan-zhuan-xuan-zhuan-shu-zu-by-de-5937/

代码

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

思路

参考:https://leetcode-cn.com/problems/number-of-islands/solution/tong-ji-wan-yi-ge-dao-hou-yao-ba-ta-chen-liao-200-/

代码

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

思路

使用 厄拉多塞筛法

参考:https://leetcode-cn.com/problems/count-primes/solution/e-la-duo-sai-shai-fa-4xing-dai-ma-chao-90-by-manto/

代码

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
}
posted @ 2021-10-31 14:02  小风车吱呀转  阅读(51)  评论(0编辑  收藏  举报