今日份练习题
1、无重复字符的最长子串 1 2 3 4 5 6 编写一个函数 lengthOfLongestSubstring (s),该函数接受一个字符串 s,返回其中不包含重复字符的最长子串的长度。lengthOfLongestSubstring ("abcabcbb"); lengthOfLongestSubstring ("bbbbb"); lengthOfLongestSubstring ("pwwkew"); lengthOfLongestSubstring ("abcabcbb"); lengthOfLongestSubstring ("bbbbb"); lengthOfLongestSubstring ("pwwkew");
? 实现思路
1、使用滑动窗口的思路。
2、定义两个指针left和right表示当前窗口的左右边界,同时初始化为索引0,。
3、使用哈希表map记录每个字符上次出现的索引。
4、开始移动右指针,如果元素已经存在于map并且!!!!!!!!上一次出现的位置是在窗口内的(重复元素的索引位置>=left),就更新左边界left到重复元素的索引位置加一。
5、更新最长窗口距离
? 关键
最关键的是理解右边界在延长的过程中,如果发现出现了重复元素,要判断该元素的位置是不是在左边界的右边,也就是判断是否在滑动窗口内。满足条件再移动左边界,更新map中记录的元素最新索引。并且比较最长距离。
解答
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function lengthOfLongestSubstring(s){ let left = 0 let map = {} let max_length = 0 for (let right = 0 ; right < s.length; right ++){ let char = s[right ] if (char in map && map[char] >= left ){ left = map[char] + 1 } map[char] = right max_length = Math.max(max_length,right -left +1 ) } return max_length }
2、最长回文子串 1 2 3 实现一个函数 longestPalindrome (s),该函数接收一个字符串 s,返回 s 中最长的回文子串。如果存在多个答案,可以返回其中任意一个。longestPalindrome ("babad"); longestPalindrome ("cbbd");
? 实现思路
1、考虑最简单的情况:每个字符都是回文
2、再进一步,如果子串长度为2,只需要判断首尾字符是否相同
3、再复杂一些,子串长度增加到3、4、5等等。去掉首尾后,再判断首尾,再去掉首尾…
4、可以知道,中间子串的回文判断实际上在之前就已经判断过了
5、因此将中间结果进行存储,在判断的时候进行查询
6、使用动态规划,把大问题分解为小问题,存储小问题结果。
7、每次标记新的回文后,记得更新最长子串的信息(起始位置、子串长度)!!
二维数组
dp[i][j]
表示子串起始位置索引为 i
,结束位置索引为 j
,该子串的回文判断。
const dp = Array.from({length:n},()=>Array(n).fill(false))
使用类数组对象创建新的数组实例,映射函数对于生成的数组中每个元素进行处理,每个元素通过Array(n)生成,并且被填充为false。
解答
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 function longestPalindrome (s ){ const n = s.length let start = 0 let maxLength = 1 if (n < 1 ){ return "" } const dp = Array .from ({length :n},()=> Array (n).fill (false )) for (let i = 0 ; i < n; i++){ dp[i][i] = true } for (let i = 0 ; i < n - 1 ; i++){ if (s[i] === s[i+1 ]){ dp[i][i+1 ] = true start = i maxLength = 2 } } for (let length = 3 ; length <= n; length++){ for (let i = 0 ; i <= n-length; i++){ let j = i + length - 1 if (s[i] === s[j] && dp[i+1 ][j-1 ]){ dp[i][j] = true start = i maxLength = length } } } return s.slice (start,start+maxLength) }
3、三数之和 1 2 3 4 5 6 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a, b, c,使得 a + b + c = 0。请你找出所有满足条件且不重复的三元组,并返回这些三元组组成的数组集合。 注意: 答案中不允许包含重复的三元组。 // 示例输入: const nums = [-1 , 0, 1, 2, -1 , -4 ]; // 可能的输出: [[-1 , -1 , 2], [-1 , 0, 1]]
? 解题思路
1、排序数组。保证数组的元素是从小到大,使用sort((a,b)=>a-b),数组元素有顺序后,方便使用双指针
2、固定一个数,在剩余的部分的首尾作为左指针、右指针。需要找到两个指针对应位置的元素之和为固定数的相反数(目标数)。
3、计算当前左右指针和,判断与目标的关系。
4、如果等于目标。左右指针往内移动;如果大于目标,右指针往内移。如果小于目标,左指针往左移。
5、特别注意!!!!!需要考虑重复元素!!!在选择固定数前,先看固定数是不是和前一个固定数重复了,重复就进入下一轮。另外如果找到了目标,在把左右指针往内移动的时候,如果元素依然和上一次相同,继续移动。
解答
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 function threeNumsTotalZero(nums) { nums.sort((a,b)=>a-b) const n = nums.length; const res = [] for (let i = 0 ; i <= n -3 ; i++){ if (i > 0 && nums[i] === nums[i-1 ]) continue const pinNum = nums[i] const target = 0 - pinNum let left = i+1 let right = n-1 while (left < right ){ if (nums[left ] + nums[right ] === target){ res.push([pinNum, nums[left ], nums[right ]]) left ++ right -- while(left < right && nums[left ]===nums[left - 1 ]){ left ++ } while(left < right && nums[right ]===nums[right + 1 ]){ right -- } } else if (nums[left ] + nums[right ] < target){ left ++ } else if (nums[left ] + nums[right ] > target){ right -- } } } return res }