算法练习笔记(六)

11.15

数组中的第K个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/kth-largest-element-in-an-array

思路

采用快速排序法从大到小将数组排序,数组中的第k个最大元素。

代码

//偷懒版
var findKthLargest = function(nums, k){
    nums.sort((a,b)=>b-a)
    return nums[k-1]
}
//快速排序
var findKthLargest = function(nums,k){
    quickSort(nums)
    return nums[k-1]
}
function quickSort(arr,left=0,right=arr.length-1){
    //定义递归边界,若数组只有一个元素,则没有排序必要
    if(arr.length>1){
        //表示下一次划分左右子数组的索引位
    	const lineIndex = partition(arr,left,right)
        //如果左边子数组的长度不小于1,则递归快排这个子数组
        if(left<lineIndex-1) quickSort(arr,left,lineIndex-1)
        //如果右边子数组的长度不小于1,则递归快排这个子数组
        if(lineIndex<right) quickSort(arr,lineIndex,right)
    }
}
function partition(arr,left,right){
    //基准值默认中间位置的元素
    const pivotValue = arr[(left+right)>>1]
    //初始化左右指针
	let i = left
    let j = right
    //当左右指针不越界时,循环执行以下操作
    while(i<=j){
        //左指针所指元素若大于基准值,则右移左指针
        while(arr[i]>pivotValue){
            i++
        }
        //右指针所指元素若小于基准值,则左移右指针
        while(arr[j]<pivotValue){
            j--
        }
        //若i<=j,则说明基准值左边存在较小元素或右边较大元素,交换两个元素确保左右两侧有序
        if(i<=j){
            swap(arr,i,j)
            i++
            j--
        }
    }
    //返回左指针索引作为下一次划分左右子数组的依据
    return i
}
function swap(arr,i,j){
    [arr[i],arr[j]] = [arr[j],arr[i]]
}

除自身以外数组的乘积

给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。

示例:

输入: [1,2,3,4]
输出: [24,12,8,6]

提示:题目数据保证数组之中任意元素的全部前缀元素和后缀(甚至是整个数组)的乘积都在 32 位整数范围内。

说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。

进阶:
你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/product-of-array-except-self

思路

参考:https://leetcode-cn.com/problems/product-of-array-except-self/solution/output-shu-zu-cheng-dan-liang-chong-jiao-se-yi-ci-/

代码

var productExceptSelf = function(nums) {
    const len = nums.length
    const res = []
    //数组第一项没有左边积,初始化为1
    res[0] = 1
    //从左遍历,每个元素的左边积保存到结果数组
    for(let i=1;i<len;i++){
        res[i] = res[i-1]*nums[i-1]
    }
    //临时变量保存右边积
    let temp = 1
    //从右遍历,求左边积×右边积然后更新右边积。
    for(let i=len-1;i>=0;i--){
        res[i] *=temp
        temp *= nums[i]
    }
    //返回结果数组
    return res
};

11.16

搜索二维矩阵 II

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

每行的元素从左到右升序排列。
每列的元素从上到下升序排列。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/search-a-2d-matrix-ii

image-20211116162347809

思路

参考:https://leetcode-cn.com/problems/search-a-2d-matrix-ii/solution/240sou-suo-er-wei-ju-zhen-tong-guo-cong-9mc0k/

代码

var searchMatrix = function(matrix, target) {
    let m = matrix.length,n=matrix[0].length
    //定义左下角坐标
    let x=m-1,y=0
    while(x>=0&&y<n){
        if(matrix[x][y] === target){
            return true
        }
        //比目标值大应往上走
        if(matrix[x][y]>target){
            x--
        }else{
        //比目标值小应往右走
            y++
        }
    }
    return false
};

寻找重复数

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。

你设计的解决方案必须不修改数组 nums 且只用常量级 O(1) 的额外空间。

示例 1:

输入:nums = [1,3,4,2,2]
输出:2

示例 2:

输入:nums = [3,1,3,4,2]
输出:3

示例 3:

输入:nums = [1,1]
输出:1

示例 4:

输入:nums = [1,1,2]
输出:1

提示:

1 <= n <= 105
nums.length == n + 1
1 <= nums[i] <= n
nums 中 只有一个整数 出现 两次或多次 ,其余整数均只出现 一次

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-the-duplicate-number

思路

参考:https://leetcode-cn.com/problems/find-the-duplicate-number/solution/zhe-ge-shu-zu-you-dian-te-shu-suo-yi-ke-yi-yong-ku/

代码

var findDuplicate = function(nums) {
    //定义最大区间[1,n]
    let left = 1,right = nums.length-1,mid
    while(left<right){
        //获取中间区间
        mid = (left+right)>>1
        let count = 0
        //查找比mid小的元素
        for(let i=0;i<nums.length;i++){
            if(nums[i]<=mid) count++
        }
        if(count>mid){
            right = mid
        }else{
            left = mid+1
        }
    }
    return left
};

11.17

生命游戏

根据 百度百科 ,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。

给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态:1 即为活细胞(live),或 0 即为死细胞(dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:

如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。给你 m x n 网格面板 board 的当前状态,返回下一个状态。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/game-of-life

image-20211117153813157

思路

参考:https://leetcode-cn.com/problems/game-of-life/solution/289-sheng-ming-you-xi-zhong-xin-ding-yi-gg3ld/

代码

var gameOfLife = function(board){
	const m = board.length,n=board[0].length
	//遍历加一,用于取负方便
	for(let i=0;i<m;i++){
		for(let j=0;j<n;j++){
			board[i][j]++
		}
	}
	//遍历第二遍计算细胞存活状态
	for(let i=0;i<m;i++){
		for(let j=0;j<n;j++){
			changeStatus(board,i,j)
		}
	}
	//遍历第三遍更新细胞存活状态
	for(let i=0;i<m;i++){
		for(let j=0;j<n;j++){
			if(board[i][j]===1||board[i][j]===-2){
				//把结果为死细胞的位置置为0
				board[i][j]=0
			}else{
				//否则都是活细胞
				board[i][j]=1
			}
		}
	}
}
var changeStatus = function(board,r,c){
		const m = board.length,n=board[0].length
		//活细胞数量
		let num = 0
		for(let i=Math.max(0,r-1);i<=Math.min(r+1,m-1);i++){
			for(let j=Math.max(0,c-1);j<=Math.min(c+1,n-1);j++){
				//记录包括自己在内的,周围的活细胞数量
				if((i!==r||j!==c)&&Math.abs(board[i][j])===2){
					num++
				}
			}
		}
		//改变细胞状态
		if((board[r][c]===2&&(num<2||num>3))||(board[r][c]===1&&num===3)){
			//活细胞死亡或者死细胞复活,反转正负
			board[r][c]=-board[r][c]
		}
}

摆动排序 II

给你一个整数数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]... 的顺序。

你可以假设所有输入数组都可以得到满足题目要求的结果。

示例 1:

输入:nums = [1,5,1,1,6,4]
输出:[1,6,1,5,1,4]
解释:[1,4,1,5,1,6] 同样是符合题目要求的结果,可以被判题程序接受。

示例 2:

输入:nums = [1,3,2,2,3,1]
输出:[2,3,1,3,1,2]

提示:

1 <= nums.length <= 5 * 104
0 <= nums[i] <= 5000
题目数据保证,对于给定的输入 nums ,总能产生满足题目要求的结果

进阶:你能用 O(n) 时间复杂度和 / 或原地 O(1) 额外空间来实现吗?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/wiggle-sort-ii

思路

深拷贝数组并按升序排序。遍历原数组,将原数组奇数位元素变为新数组的大数,偶数位为新数组的小数,这样就可以达到nums[0] < nums[1] > nums[2] < nums[3]... 的顺序。

代码

var wiggleSort = function(nums) {
    const sortNums = nums.slice().sort((a,b)=>a-b)
    //mid指向新数组的小数,r指向新数组的大数
    let r = nums.length,mid = Math.floor((nums.length+1)/2)
    for(let l=0;l<nums.length;l++){
        nums[l]=l%2===0?sortNums[--mid]:sortNums[--r]
    }
    return nums
};

11.18

递增的三元子序列

给你一个整数数组 nums ,判断这个数组中是否存在长度为 3 的递增子序列。

如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否则,返回 false 。

示例 1:

输入:nums = [1,2,3,4,5]
输出:true
解释:任何 i < j < k 的三元组都满足题意

示例 2:

输入:nums = [5,4,3,2,1]
输出:false
解释:不存在满足题意的三元组

示例 3:

输入:nums = [2,1,5,0,4,6]
输出:true
解释:三元组 (3, 4, 5) 满足题意,因为 nums[3] == 0 < nums[4] == 4 < nums[5] == 6

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/increasing-triplet-subsequence

思路

假设递增的三元子序列最小值min为数组第一位,中位数mid为无穷大

遍历数组,碰到比min大的则设置为mid,再碰到比mid大的则递增成立。

若碰到比min小的则更新最小值。

代码

var increasingTriplet = function(nums){
	//假设min为数组第一位,mid为无穷大
	let min = nums[0],mid = Infinity
	for(let i=1;i<nums.length;i++){
        //呈现递增返回true
		if(nums[i]>mid) return true
        //根据条件更新min或者mid
		nums[i]<=min?min=nums[i]:mid=nums[i]
	}
    return false
}

前 K 个高频元素

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

输入: nums = [1], k = 1
输出: [1]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/top-k-frequent-elements

思路

参考:https://leetcode-cn.com/problems/top-k-frequent-elements/solution/mapji-lu-cun-ru-dui-xiang-shu-zu-pai-xu-den89/

代码

var topKFrequent = function(nums,k){
	const map = new Map()
	//使用map记录数组中每个元素出现的频率
	for(let num of nums){
		if(map.has(num)){
			let count = map.get(num)+1
			map.set(num,count)
		}else{
			map.set(num,1)
		}
	}
	const res = []
	for(let [key,val] of map){
		res.push({key,val})
	}
	//将对象数组按照频率排序
    res.sort((a,b)=>b.val-a.val)
    //返回前k个高频元素
    return res.slice(0,k).map((item)=>{
        return item.key
    })
}

11.19

有序矩阵中第 K 小的元素

给你一个 n x n 矩阵 matrix ,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
请注意,它是 排序后 的第 k 小元素,而不是第 k 个 不同 的元素。

示例 1:

输入:matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8
输出:13
解释:矩阵中的元素为 [1,5,9,10,11,12,13,13,15],第 8 小元素是 13

示例 2:

输入:matrix = [[-5]], k = 1
输出:-5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/kth-smallest-element-in-a-sorted-matrix

思路

由题意中的每行和每列元素均按升序排序可知,有序矩阵中的左上角元素是下限,右下角元素是上限。

对矩阵的值域进行二分查找,算出[下限,上限]的中间值,求出矩阵里不大于该中间值的元素有几个。

  • 若统计数量比k小,则说明中间值小了,调整值域范围,提高下限;
  • 若统计数量比k大,则说明中间值大了,调整值域范围,提高上限;

通过不断调整值域范围,锁定范围值。

求出矩阵里不大于该中间值的元素可以按以下步骤:

  • 从矩阵第一行开始比较,将中间值先与当前行的最右元素进行比较
  • 如果中间值大于等于最右元素,统计当前行的所有个数,然后进入下一行比较
  • 如果中间值小于最右元素,则留在当前行,继续和最右元素的左边元素进行比较
  • 以此类推,统计出不大于该中间值的元素数量。

代码

const countInMatrix = function(matrix,midVal){	const n = matrix.length	let count = 0	//从第一行最右元素开始比较	let row = 0	let col = n-1	while(row<n&&col>=0){		if(midVal>=matrix[row][col]){//大于等于当前行的最右元素			count += col+1 			//统计个数			row++          			//进入下一行		}else{			//否则留在当前行,继续和左边元素比较			col--		}	}	return count}const kthSmallest = function(matrix,k){	const n = matrix.length	//初始化值域范围	let low = matrix[0][0]	let high = matrix[n-1][n-1]	while(low<=high){		//计算中间值		let midVal = low+((high-low)>>>1)		//统计矩阵中不大于中间值的元素个数		let count = countInMatrix(matrix,midVal)		if(count<k){			//如果比k小,说明中间值小了			low = midVal+1		}else{			//如果比k大,说明中间值大了			high = midVal-1		}	}	return low}

O(1) 时间插入、删除和获取随机元素

实现RandomizedSet 类:

RandomizedSet() 初始化 RandomizedSet 对象
bool insert(int val) 当元素 val 不存在时,向集合中插入该项,并返回 true ;否则,返回 false 。
bool remove(int val) 当元素 val 存在时,从集合中移除该项,并返回 true ;否则,返回 false 。
int getRandom() 随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。每个元素应该有 相同的概率 被返回。
你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1) 。

示例:

输入
["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"]
[[], [1], [2], [2], [], [1], [2], []]
输出
[null, true, false, true, 2, true, false, 2]

解释
RandomizedSet randomizedSet = new RandomizedSet();
randomizedSet.insert(1); // 向集合中插入 1 。返回 true 表示 1 被成功地插入。
randomizedSet.remove(2); // 返回 false ,表示集合中不存在 2 。
randomizedSet.insert(2); // 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。
randomizedSet.getRandom(); // getRandom 应随机返回 1 或 2 。
randomizedSet.remove(1); // 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。
randomizedSet.insert(2); // 2 已在集合中,所以返回 false 。
randomizedSet.getRandom(); // 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。

提示:

-231 <= val <= 231 - 1
最多调用 insert、remove 和 getRandom 函数 2 * 105 次
在调用 getRandom 方法时,数据结构中 至少存在一个 元素。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/insert-delete-getrandom-o1

代码

var RandomizedSet = function() {    //数值保存在动态数组中    this.nums = []    //哈希表存储每个数值及其在数值nums的下标    this.map = new Map()};/**  * @param {number} val * @return {boolean} */RandomizedSet.prototype.insert = function(val) {    if(this.map.has(val)){        return false    }    this.map.set(val,this.nums.length)    this.nums.push(val)    return true};/**  * @param {number} val * @return {boolean} */RandomizedSet.prototype.remove = function(val) {    if(this.map.has(val)){        //获取被删元素在数组中的索引        const len = this.nums.length        const index = this.map.get(val)        //用数组末位元素覆盖被删元素,并更新其索引值        this.map.set(this.nums[len-1],index)        this.nums[index] = this.nums[len-1]        //删除元素        this.nums.pop()        this.map.delete(val)        return true    }    return false};/** * @return {number} */RandomizedSet.prototype.getRandom = function() {    //得到随机索引    const randomNumber = parseInt(Math.random()*this.nums.length)    return this.nums[randomNumber]};/** * Your RandomizedSet object will be instantiated and called as such: * var obj = new RandomizedSet() * var param_1 = obj.insert(val) * var param_2 = obj.remove(val) * var param_3 = obj.getRandom() */

11.20

打乱数组

给你一个整数数组 nums ,设计算法来打乱一个没有重复元素的数组。

实现 Solution class:

Solution(int[] nums) 使用整数数组 nums 初始化对象
int[] reset() 重设数组到它的初始状态并返回
int[] shuffle() 返回数组随机打乱后的结果

示例:

输入
["Solution", "shuffle", "reset", "shuffle"]
[[[1, 2, 3]], [], [], []]
输出
[null, [3, 1, 2], [1, 2, 3], [1, 3, 2]]

解释
Solution solution = new Solution([1, 2, 3]);
solution.shuffle(); // 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。例如,返回 [3, 1, 2]
solution.reset(); // 重设数组到它的初始状态 [1, 2, 3] 。返回 [1, 2, 3]
solution.shuffle(); // 随机返回数组 [1, 2, 3] 打乱后的结果。例如,返回 [1, 3, 2]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shuffle-an-array

代码

/**
 * @param {number[]} nums
 */
var Solution = function(nums) {
    this.nums = nums
};

/**
 * @return {number[]}
 */
Solution.prototype.reset = function() {
    return this.nums
};

/**
 * @return {number[]}
 */
Solution.prototype.shuffle = function() {
    //浅拷贝复制
    const nums = this.nums.slice()
    for(let i=0;i<nums.length;i++){
        let index = Math.floor((i+1)*Math.random());
        [nums[index],nums[i]] = [nums[i],nums[index]]
    }
    return nums
};

/**
 * Your Solution object will be instantiated and called as such:
 * var obj = new Solution(nums)
 * var param_1 = obj.reset()
 * var param_2 = obj.shuffle()
 */

四数相加 II

给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

示例 1:

输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释:
两个元组如下:

  1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
  2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
    示例 2:

输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
输出:1

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/4sum-ii

思路

将四个数组进行两两分组,先计算两个数组中的元素之和和出现的次数,存入map中;再计算剩余两个数组中的元素之和(取反),在map中找是否存在相加为0的情况,同时记录次数累加到count上。

代码

var fourSumCount = function(nums1,nums2,nums3,nums4){	const map = new Map();	let count = 0;	for(let a of nums1){		for(let b of nums2){			let res = a+b;			map.has(res)?map.set(res,map.get(res)+1):map.set(res,1);		}	}	for(let c of nums3){		for(let d of nums4){			let res = -(c+d);			if(map.has(res)){				count += map.get(res);			}		}	}	return count}

11.21

最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""。

示例 1:

输入:strs = ["flower","flow","flight"]
输出:"fl"

示例 2:

输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-common-prefix

代码

var longestCommonPrefix = function(strs){
	let ans = ''
	for(let ch of strs[0]){
		if(!strs.every((str)=>str.startsWith(ans+ch))){
			break
		}
		ans += ch
	}
	return ans
}

罗马数字转整数

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。

字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。

示例 1:

输入: s = "III"
输出: 3

示例 2:

输入: s = "IV"
输出: 4

示例 3:

输入: s = "IX"
输出: 9

示例 4:

输入: s = "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.

示例 5:

输入: s = "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/roman-to-integer

代码

const map = {    "I":1,    "V":5,    "X":10,    "L":50,    "C":100,    "D":500,    "M":1000}var romanToInt = function(s){	let res = 0	for(let i = 0;i<s.length;i++){		if(map[s[i]]<map[s[i+1]]){			res -= map[s[i]]		}else{			res += map[s[i]]		}	}	return res}

11.23

实现 strStr()

实现 strStr() 函数。

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。

说明:

当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。

示例 1:

输入:haystack = "hello", needle = "ll"
输出:2

示例 2:

输入:haystack = "aaaaa", needle = "bba"
输出:-1

示例 3:

输入:haystack = "", needle = ""
输出:0

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

代码

//投机取巧 indexOf()var strStr = function(haystack,needle){	return haystack.indexOf(needle)	}//利用substringvar strStr = function(haystack,needle){	if(needle === "") return 0	    for(let i=0;i<haystack.length;i++){        if(haystack[i] === needle[0]){            if(haystack.substring(i,i+needle.length)=== needle) return i        }    }    return -1}

验证回文串

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

说明:本题中,我们将空字符串定义为有效的回文串。

示例 1:

输入: "A man, a plan, a canal: Panama"
输出: true
解释:"amanaplanacanalpanama" 是回文串

示例 2:

输入: "race a car"
输出: false
解释:"raceacar" 不是回文串

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

思路

1.处理字符串,正则替换所有非数字、字母为空,然后统一字母大小写.

2.双指针从前后出发,对比每一项

3.如果相同,则指针一起移动,对比下一位;否则直接返回false。

代码

var isPalindrome = function(s) {    const newS = s.replace(/[^0-9a-zA-Z]/g,'').toLowerCase()    if(!newS) return true    let m=0,n=newS.length-1    while(m<n){        if(newS[m]===newS[n]){            m++;            n--;        }else{            return false        }    }       return true};

11.24

Excel 表列序号

给你一个字符串 columnTitle ,表示 Excel 表格中的列名称。返回该列名称对应的列序号。

例如,

A -> 1B -> 2C -> 3...Z -> 26AA -> 27AB -> 28 ...

示例 1:

输入: columnTitle = "A"
输出: 1

示例 2:

输入: columnTitle = "AB"
输出: 28

示例 3:

输入: columnTitle = "ZY"
输出: 701

示例 4:

输入: columnTitle = "FXSHRXW"
输出: 2147483647

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/excel-sheet-column-number
思路

利用Unicode码,计算26进制

代码:

var titleToNumber = function(columnTitle) {	let res = 0	for(let ch of columnTitle){		res = res*26+(ch.charCodeAt()-64)	}	return res}

有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

示例 1:

输入: s = "anagram", t = "nagaram"
输出: true

示例 2:

输入: s = "rat", t = "car"
输出: false

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

思路

用HashMap来映射s的字符串和对应的出现次数;

然后遍历t,将对应的字符在map中的值减1;

如果map中所有出现的字符的次数都变为0,则返回true。

代码

var isAnagram = function(s,t){	const map = {}	for(let ch of s){		if(!map[ch]){			map[ch] = 1		}else{			map[ch]++		}	}	for(let ch of t){		if(!map[ch]||map[ch]==0) return false		map[ch]--	}	for(let ch of s){		if(map[ch]!==0) return false	}	return true}

11.25

Fizz Buzz

给你一个整数 n ,找出从 1 到 n 各个整数的 Fizz Buzz 表示,并用字符串数组 answer(下标从 1 开始)返回结果,其中:

answer[i] == "FizzBuzz" 如果 i 同时是 3 和 5 的倍数。
answer[i] == "Fizz" 如果 i 是 3 的倍数。
answer[i] == "Buzz" 如果 i 是 5 的倍数。
answer[i] == i 如果上述条件全不满足。

示例 1:

输入:n = 3
输出:["1","2","Fizz"]

示例 2:

输入:n = 5
输出:["1","2","Fizz","4","Buzz"]

示例 3:

输入:n = 15
输出:["1","2","Fizz","4","Buzz","Fizz","7","8","Fizz","Buzz","11","Fizz","13","14","FizzBuzz"]

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

代码

var fizzBuzz = function(n) {
    const getStr = (num)=>{
        if(num%15==0){
            return 'FizzBuzz';
        }else if(num%5==0){
            return 'Buzz';
        }else if(num%3==0){
            return 'Fizz';
        }else{
            return `${num}`
        }
    }
    const res = []
    let count = 1
    while(count<=n){
        res.push(getStr(count))
        count++
    }
    return res
};

无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

示例 4:

输入: s = ""
输出: 0

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters
思路

用双指针维护一个滑动窗口,set记录滑动窗口的元素同时计算长度,若set中有重复元素,就让左指针前进并删除窗口之外的元素直到滑动窗口内没有重复的元素。

代码

var lengthOfLongestSubstring = function(s) {
    const set = new Set();
    const len = s.length;
    let maxLen = 0
    //定义滑动窗口的左右指针
    let l =0;
    for(let r=0;r<len;r++){
        if(!set.has(s[r])){//当前元素不在set中 就加入set 然后更新最大长度
            set.add(s[r]);
            maxLen = Math.max(maxLen,set.size)
        }else{
            //set中有重复元素不断让l++ 并删除窗口之外的元素 直到滑动窗口内没有重复的元素
            while(set.has(s[r])){
                set.delete(s[l])
                l++
            }
            set.add(s[r])
        }
    }
    return maxLen
};

11.26

字符串转换整数 (atoi)

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

函数 myAtoi(string s) 的算法如下:

读入字符串并丢弃无用的前导空格
检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
返回整数作为最终结果。
注意:

本题中的空白字符只包括空格字符 ' ' 。
除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。

示例 1:

输入:s = "42"
输出:42
解释:加粗的字符串为已经读入的字符,插入符号是当前读取的字符。
第 1 步:"42"(当前没有读入字符,因为没有前导空格)
^
第 2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
^
第 3 步:"42"(读入 "42")
^
解析得到整数 42 。
由于 "42" 在范围 [-231, 231 - 1] 内,最终结果为 42 。

示例 2:

输入:s = " -42"
输出:-42
解释:
第 1 步:" -42"(读入前导空格,但忽视掉)
^
第 2 步:" -42"(读入 '-' 字符,所以结果应该是负数)
^
第 3 步:" -42"(读入 "42")
^
解析得到整数 -42 。
由于 "-42" 在范围 [-231, 231 - 1] 内,最终结果为 -42 。

示例 3:

输入:s = "4193 with words"
输出:4193
解释:
第 1 步:"4193 with words"(当前没有读入字符,因为没有前导空格)
^
第 2 步:"4193 with words"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
^
第 3 步:"4193 with words"(读入 "4193";由于下一个字符不是一个数字,所以读入停止)
^
解析得到整数 4193 。
由于 "4193" 在范围 [-231, 231 - 1] 内,最终结果为 4193 。

示例 4:

输入:s = "words and 987"
输出:0
解释:
第 1 步:"words and 987"(当前没有读入字符,因为没有前导空格)
^
第 2 步:"words and 987"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
^
第 3 步:"words and 987"(由于当前字符 'w' 不是一个数字,所以读入停止)
^
解析得到整数 0 ,因为没有读入任何数字。
由于 0 在范围 [-231, 231 - 1] 内,最终结果为 0 。

示例 5:

输入:s = "-91283472332"
输出:-2147483648
解释:
第 1 步:"-91283472332"(当前没有读入字符,因为没有前导空格)
^
第 2 步:"-91283472332"(读入 '-' 字符,所以结果应该是负数)
^
第 3 步:"-91283472332"(读入 "91283472332")
^
解析得到整数 -91283472332 。
由于 -91283472332 小于范围 [-231, 231 - 1] 的下界,最终结果被截断为 -231 = -2147483648 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/string-to-integer-atoi

代码

//正则表达式
var myAtoi = function(s){
	//去掉字符串首尾空格
	const input = s.trim();
	let reg = new RegExp(/^[\+\-]?\d+/g);
    //查找字符串是否匹配
    if(!reg.test(input)){
        return 0;
    }
    return Math.max(Math.min(input.match(reg)[0],Math.pow(2,31)-1),Math.pow(-2,31));
}
//使用ParseInt()
var myAtoi = function(s) {
    const number = parseInt(s);
    const max = Math.pow(2,31)-1;
    const min = Math.pow(-2,31)
    //无法转换的情况返回0
    if(isNaN(number)){
        return 0
    }
    //转换结果超出范围
    if(number<min||number>max){
        return number<0?min:max
    }
    return number
};

电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例 1:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

示例 2:

输入:digits = ""
输出:[]

示例 3:

输入:digits = "2"
输出:["a","b","c"]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number

代码

//BFSconst letterCombinations = (digits) => {    if(digits.length == 0) return [];    const map = { '2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl', '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz' };    const queue = [];    queue.push('');    for(let i=0;i<digits.length;i++){ //BFS的层数        const len = queue.length;//当前层的节点个数        for(let j=0;j<len;j++){            const curStr = queue.shift();            const letters = map[digits[i]];            for(const letter of letters){                queue.push(curStr+letter);            }        }    }    return queue};//DFSconst letterCombinations = (digits) => {    if(digits.length == 0) return [];    const map = { '2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl', '6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz' };    const res = []    const dfs = (curStr,i)=>{        if(i>digits.length-1){ //指针越界,递归的出口            res.push(curStr);   //将解推入res            return;        }        const letters = map[digits[i]];//当前数字对应的字母        for(let letter of letters){ //一个字母是一个选择,对应一个分支            dfs(curStr+letter,i+1);//基于这个选择,进入下一层        }    }    dfs('',0);    return res;};

11.27

外观数列

给定一个正整数 n ,输出外观数列的第 n 项。

「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。

你可以将其视作是由递归公式定义的数字字符串序列:

countAndSay(1) = "1"
countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。
前五项如下:

  1. 1
    
  2. 11
    
  3. 21
    
  4. 1211
    
  5. 111221
    第一项是数字 1 
    描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 "11"
    描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 "21"
    描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 "1211"
    描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 "111221"
    要 描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。
    

示例 1:

输入:n = 1
输出:"1"
解释:这是一个基本样例。

示例 2:

输入:n = 4
输出:"1211"
解释:
countAndSay(1) = "1"
countAndSay(2) = 读 "1" = 一 个 1 = "11"
countAndSay(3) = 读 "11" = 二 个 1 = "21"
countAndSay(4) = 读 "21" = 一 个 2 + 一 个 1 = "12" + "11" = "1211"

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-and-say

代码

//递归+for循环var countAndSay = function(n) {    if(n === 1) return "1";    else{        const str = countAndSay(n-1);        //临时变量存储指向需要比较的字符        let temp = str[0];        let count = 0;        let ans = '';        for(let i=0;i<str.length;i++){            if(str[i] === temp) count++;            else{                ans += ''+count+temp;                temp = str[i];                count = 1;            }            if(i === str.length-1) ans+=''+count+temp;        }        return ans    }};//递归+正则var countAndSay = function(n){    var countAndSay = function(n) {    if(n === 1) return "1";    //\d 匹配数字 \1指第一个匹配括号内的元素 *代表匹配0次以上 连起来就是 匹配连续0次以上的数字    //全局匹配连续相同的数字将其替换成该连续数字的长度+数字 '11'=>'21'    return countAndSay(n-1).replace(/(\d)\1*/g, item =>`${item.length}${item[0]}`);};}

字母异位词分组

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母都恰好只用一次。

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

示例 2:

输入: strs = [""]
输出: [[""]]

示例 3:

输入: strs = ["a"]
输出: [["a"]]

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

代码

var groupAnagrams = function(strs){
	let map = new Map();
	strs.forEach((str)=>{
		//将所有异位词排序为同一字符串,作为key
		const key = str.split("").sort().join("");
		map.has(key)?map.get(key).push(str):map.set(key,[str]);
	})
	//map传数组输出
	return Array.from(map.values());
}

11.28

分数到小数

给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以 字符串形式返回小数 。

如果小数部分为循环小数,则将循环的部分括在括号内。

如果存在多个答案,只需返回 任意一个 。

对于所有给定的输入,保证 答案字符串的长度小于 104 。

示例 1:

输入:numerator = 1, denominator = 2
输出:"0.5"

示例 2:

输入:numerator = 2, denominator = 1
输出:"2"

示例 3:

输入:numerator = 2, denominator = 3
输出:"0.(6)"

示例 4:

输入:numerator = 4, denominator = 333
输出:"0.(012)"

示例 5:

输入:numerator = 1, denominator = 5
输出:"0.2"

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fraction-to-recurring-decimal

思路

参考:https://leetcode-cn.com/problems/fraction-to-recurring-decimal/solution/mo-ni-chu-fa-javascript-by-lzxjack-qbfj/

代码

/**
 * @param {number} a
 * @param {number} b
 * @return {string}
 */
var fractionToDecimal = function(a, b) {
    //如果没有小数部分,直接返回结果
    if(!(a%b)) return `${a/b}`;
    
    //整数部分
    let res = '';
    //如果有一个小于0,需要添加符号
    if(a*b<0) res +='-';
    a = Math.abs(a);
    b = Math.abs(b);
    //整数部分+小数点.
    res += `${Math.floor(a/b)}.`;

    //小数部分
    const arr = [];
    const map = new Map();
    let index = 0;
    //取余
    a%=b;
    while(a&&!map.has(a)){
        map.set(a,index++);
        a *= 10;
        arr.push(Math.floor(a/b));
        a%=b;
    }
    if(a){
        //a不是0说明小数存在循环小数
        const insertIndex = map.get(a);
        //在循环开始的位置插入"("
        arr.splice(insertIndex,0,'(');
        //尾部插入')'
        arr.push(')');
    }
    return res+arr.join('');
};

最大数

给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。

注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。

示例 1:

输入:nums = [10,2]
输出:"210"

示例 2:

输入:nums = [3,30,34,5,9]
输出:"9534330"

示例 3:

输入:nums = [1]
输出:"1"

示例 4:

输入:nums = [10]
输出:"10"

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

思路

参考:https://leetcode-cn.com/problems/largest-number/solution/te-shu-pai-xu-chao-jian-ji-by-zn_-25ou/

代码

var largestNumber = function(nums) {
    //通过排序尽可能使前面的数字较大
    const res = nums.sort((a,b)=>(b+''+a)-(a+''+b)).join('');
    return res.startsWith('0')?'0':res;
};
posted @ 2021-11-15 17:20  小风车吱呀转  阅读(51)  评论(0编辑  收藏  举报