• 算法练习笔记(五)


    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
    }
    
  • 相关阅读:
    Google Plus 是什么?Google+让Google在线资产在日常生活中更普及
    再谈JavaScript的数据类型问题
    [置顶]信息发布系统 Jquery+MVC架构开发(5)DAL层
    Android:一个简单查询界面的实现
    定制圆角带背景色的矩形边框
    提高c#位图操作的速度[抄袭之作]
    使用XmlTextWriter和XmlTextReader操作Xml文件
    使用Mutex实现单程序实例
    单例模式实现延迟加载
    A记录、MX记录、CNAME 记录、URL转发、NS记录解释
  • 原文地址:https://www.cnblogs.com/Small-Windmill/p/15489344.html
Copyright © 2020-2023  润新知