├── 双指针.md ├── README.md ├── 链表.md ├── 区间.md ├── 圈排序.md ├── 快慢指针.md └── 滑动窗口.md /双指针.md: -------------------------------------------------------------------------------- 1 | # 双指针 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # algorithms-routine -------------------------------------------------------------------------------- /链表.md: -------------------------------------------------------------------------------- 1 | 翻转链表 2 | 3 | ```js 4 | function reverse(head) { 5 | let prev = null; 6 | while (head) { 7 | let next = head.next; 8 | head.next = prev; 9 | prev = head; 10 | head = next; 11 | } 12 | return prev; 13 | } 14 | ``` 15 | 16 | 翻转链表中的一部分 17 | 18 | ```js 19 | /** 20 | * Definition for singly-linked list. 21 | * function ListNode(val) { 22 | * this.val = val; 23 | * this.next = null; 24 | * } 25 | */ 26 | /** 27 | * @param {ListNode} head 28 | * @param {number} m 29 | * @param {number} n 30 | * @return {ListNode} 31 | */ 32 | var reverseBetween = function (head, m, n) { 33 | let current = head; 34 | let pre = null; 35 | let i = 0; 36 | while (current && i < m - 1) { 37 | pre = current; 38 | current = current.next; 39 | i++; 40 | } 41 | let n1 = pre; 42 | let n2 = current; 43 | i = 0; 44 | while (current && i < n - m + 1) { 45 | let temp = current.next; 46 | current.next = pre; 47 | pre = current; 48 | current = temp; 49 | i++; 50 | } 51 | if (n1) { 52 | n1.next = pre; 53 | } else { 54 | head = pre; 55 | } 56 | n2.next = current; 57 | return head; 58 | }; 59 | ``` 60 | 61 | 翻转链表中每个 K 的部分 62 | 63 | ```js 64 | var reverseKGroup = function (head, k) { 65 | let current = head; 66 | let count = 0; 67 | while (current && count !== k) { 68 | count++; 69 | current = current.next; 70 | } 71 | if (count === k) { 72 | current = reverseKGroup(current, k); 73 | while (count !== 0) { 74 | count--; 75 | let temp = head.next; 76 | head.next = current; 77 | current = head; 78 | head = temp; 79 | } 80 | head = current; 81 | } 82 | return head; 83 | }; 84 | ``` 85 | 86 | 旋转链表 87 | 88 | https://leetcode-cn.com/problems/rotate-list/ 89 | 90 | ```js 91 | /** 92 | * Definition for singly-linked list. 93 | * function ListNode(val) { 94 | * this.val = val; 95 | * this.next = null; 96 | * } 97 | */ 98 | /** 99 | * @param {ListNode} head 100 | * @param {number} k 101 | * @return {ListNode} 102 | */ 103 | var rotateRight = function (head, k) { 104 | if (!head || !k) return head; 105 | let fast = head; 106 | let slow = head; 107 | let length = 1; 108 | while (fast.next) { 109 | length++; 110 | fast = fast.next; 111 | } 112 | let step = length - (k % length); 113 | fast.next = head; 114 | while (step > 1) { 115 | step--; 116 | slow = slow.next; 117 | } 118 | let temp = slow.next; 119 | slow.next = null; 120 | return temp; 121 | }; 122 | ``` 123 | -------------------------------------------------------------------------------- /区间.md: -------------------------------------------------------------------------------- 1 | 合并区间 2 | 3 | https://leetcode-cn.com/problems/merge-intervals/submissions/ 4 | 5 | ```js 6 | var merge = function (intervals) { 7 | if (intervals.length < 2) return intervals; 8 | intervals.sort((a, b) => a[0] - b[0]); 9 | let start = intervals[0][0]; 10 | let end = intervals[0][1]; 11 | let result = []; 12 | for (let i = 1; i < intervals.length; i++) { 13 | const interval = intervals[i]; 14 | if (end >= interval[0]) { 15 | end = Math.max(end, interval[1]); 16 | } else { 17 | result.push([start, end]); 18 | end = interval[1]; 19 | start = interval[0]; 20 | } 21 | } 22 | result.push([start, end]); 23 | return result; 24 | }; 25 | ``` 26 | 27 | 插入区间 28 | 29 | https://leetcode-cn.com/problems/insert-interval/ 30 | 31 | ```js 32 | var insert = function (intervals, newInterval) { 33 | let result = []; 34 | let i = 0; 35 | // 不重叠,start > end 36 | while (i < intervals.length && newInterval[0] > intervals[i][1]) { 37 | result.push(intervals[i++]); 38 | } 39 | // 重叠,end < start 40 | while (i < intervals.length && newInterval[1] >= intervals[i][0]) { 41 | // 修改区间 42 | newInterval = [ 43 | Math.min(intervals[i][0], newInterval[0]), 44 | Math.max(intervals[i][1], newInterval[1]), 45 | ]; 46 | i++; 47 | } 48 | result.push(newInterval); 49 | // push 剩余不重叠的 50 | while (i < intervals.length) { 51 | result.push(intervals[i++]); 52 | } 53 | return result; 54 | }; 55 | ``` 56 | 57 | 交集 58 | 59 | https://leetcode-cn.com/problems/interval-list-intersections/ 60 | 61 | ```js 62 | var intervalIntersection = function (A, B) { 63 | let result = []; 64 | let i = 0; 65 | let j = 0; 66 | while (i < A.length && j < B.length) { 67 | let a = A[i]; 68 | let b = B[j]; 69 | // 寻找交集 70 | let start = Math.max(a[0], b[0]); 71 | let end = Math.min(a[1], b[1]); 72 | if (start <= end) { 73 | result.push([start, end]); 74 | } 75 | // 因为两数组都是排好序的,哪个 end 值小就挪动哪个向后 76 | if (a[1] < b[1]) { 77 | i++; 78 | } else { 79 | j++; 80 | } 81 | } 82 | return result; 83 | }; 84 | ``` 85 | 86 | 寻找需要最小多少会议室才满足开会需求 87 | 88 | https://leetcode-cn.com/problems/meeting-rooms-ii/ 89 | 90 | ```java 91 | class Solution { 92 | public int minMeetingRooms(int[][] intervals) { 93 | if (intervals.length == 0) return 0; 94 | Arrays.sort(intervals, (a,b) -> a[0] - b[0]); 95 | PriorityQueue q = new PriorityQueue<>(intervals.length, (a,b) -> a - b); 96 | q.offer(intervals[0][1]); 97 | for (int i = 1; i < intervals.length; i++) { 98 | if(intervals[i][0] >= q.peek()){ 99 | q.poll(); 100 | } 101 | q.offer(intervals[i][1]); 102 | } 103 | return q.size(); 104 | } 105 | } 106 | ``` 107 | -------------------------------------------------------------------------------- /圈排序.md: -------------------------------------------------------------------------------- 1 | 圈排序是给定一个数组及范围,在这种场景下进行排序。 2 | 3 | 比如说输入数组为 [1, 3, 2],给定范围为 1 ~ n,那么我们就需要将数组排序为 [1, 2, 3]。 4 | 5 | 排序过程为: 6 | 7 | input: [1, 3, 2],第一个索引值为 1,不需要排序 8 | input: [1, 3, 2],第二个索引值为 3,需要和索引为 2 的值交换 9 | input: [1, 2, 3],第二个索引值为 2,不需要排序 10 | input: [1, 2, 3],第三个索引值为 3,不需要排序 11 | 12 | 把过程写成代码如下: 13 | 14 | ```js 15 | const cyclic_sort = function (nums) { 16 | let i = 0; 17 | while (i < nums.length) { 18 | // -1 为给定范围的初始值 19 | let j = nums[i] - 1; 20 | if (nums[i] !== nums[j]) { 21 | [nums[i], nums[j]] = [nums[j], nums[i]]; 22 | } else { 23 | i++; 24 | } 25 | } 26 | return nums; 27 | }; 28 | ``` 29 | 30 | 以上就是圈排序的核心代码。 31 | 32 | 缺失数字 33 | 34 | https://leetcode-cn.com/problems/missing-number/ 35 | 36 | ```js 37 | /** 38 | * @param {number[]} nums 39 | * @return {number} 40 | */ 41 | var missingNumber = function (nums) { 42 | let i = 0; 43 | while (i < nums.length) { 44 | let j = nums[i]; 45 | if (nums[i] !== nums[j]) { 46 | [nums[i], nums[j]] = [nums[j], nums[i]]; 47 | } else { 48 | i++; 49 | } 50 | } 51 | for (let i = 0; i < nums.length; i++) { 52 | if (nums[i] === undefined) return i; 53 | } 54 | return nums.length; 55 | }; 56 | ``` 57 | 58 | 找到所有的缺失数字 59 | 60 | https://leetcode-cn.com/problems/find-all-numbers-disappeared-in-an-array/ 61 | 62 | ```js 63 | /** 64 | * @param {number[]} nums 65 | * @return {number[]} 66 | */ 67 | var findDisappearedNumbers = function (nums) { 68 | let result = []; 69 | let i = 0; 70 | while (i < nums.length) { 71 | const j = nums[i] - 1; 72 | if (nums[i] !== nums[j]) { 73 | [nums[i], nums[j]] = [nums[j], nums[i]]; 74 | } else { 75 | i++; 76 | } 77 | } 78 | for (let i = 0; i < nums.length; i++) { 79 | if (nums[i] !== i + 1) { 80 | result.push(i + 1); 81 | } 82 | } 83 | return result; 84 | }; 85 | ``` 86 | 87 | 找到所有的重复数字 88 | 89 | https://leetcode-cn.com/problems/find-all-duplicates-in-an-array/ 90 | 91 | ```js 92 | /** 93 | * @param {number[]} nums 94 | * @return {number[]} 95 | */ 96 | var findDuplicates = function (nums) { 97 | let i = 0; 98 | while (i < nums.length) { 99 | let j = nums[i] - 1; 100 | if (nums[i] !== nums[j]) { 101 | [nums[i], nums[j]] = [nums[j], nums[i]]; 102 | } else { 103 | i++; 104 | } 105 | } 106 | let result = []; 107 | for (let i = 0; i < nums.length; i++) { 108 | if (nums[i] !== i + 1) { 109 | result.push(nums[i]); 110 | } 111 | } 112 | return result; 113 | }; 114 | ``` 115 | 116 | 缺失的第一个整数 117 | 118 | https://leetcode-cn.com/problems/first-missing-positive/ 119 | 120 | ```js 121 | //** 122 | * @param {number[]} nums 123 | * @return {number} 124 | */ 125 | var firstMissingPositive = function(nums) { 126 | let i = 0 127 | let length = nums.length 128 | while (i < length) { 129 | let j = nums[i] - 1 130 | if (nums[i] > 0 && nums[i] <= length && nums[j] !== nums[i]) { 131 | [nums[i], nums[j]] = [nums[j], nums[i]] 132 | } else { 133 | i++ 134 | } 135 | } 136 | for (let i = 0; i < length; i++) { 137 | if (nums[i] !== i + 1) { 138 | return i + 1 139 | } 140 | } 141 | return length + 1 142 | }; 143 | ``` 144 | -------------------------------------------------------------------------------- /快慢指针.md: -------------------------------------------------------------------------------- 1 | 多用于处理环形链表及数组。 2 | 3 | ### **判断链表有环** 4 | 5 | ```JavaScript 6 | const has_cycle = function(head) { 7 | let fast, low 8 | fast = low = head 9 | while (fast && fast.next) { 10 | low = low.next 11 | fast = fast.next.next 12 | if (low === fast) return true 13 | } 14 | return false 15 | } 16 | ``` 17 | 18 | ![](https://yck-1254263422.cos.ap-shanghai.myqcloud.com/2020/09/16003310546323.png) 19 | 20 | [https://leetcode-cn.com/problems/linked-list-cycle/](https://leetcode-cn.com/problems/linked-list-cycle/) 21 | 22 | [https://leetcode-cn.com/problems/linked-list-cycle-ii/](https://leetcode-cn.com/problems/linked-list-cycle-ii/) 23 | 24 | 判断环 head 25 | 26 | ![](https://yck-1254263422.cos.ap-shanghai.myqcloud.com/2020/09/16003310546340.png) 27 | 28 | ```JavaScript 29 | var detectCycle = function(head) { 30 | if (!head || !head.next) return null 31 | let fast, low 32 | fast = low = head 33 | let hasCycle 34 | while (fast && fast.next) { 35 | low = low.next 36 | fast = fast.next.next 37 | if (fast === low) { 38 | hasCycle = true 39 | break 40 | } 41 | } 42 | low = head 43 | while(low !== fast && hasCycle) { 44 | fast = fast.next 45 | low = low.next 46 | } 47 | return hasCycle ? low : null 48 | }; 49 | ``` 50 | 51 | 寻找链表中点 52 | 53 | ```js 54 | var middleNode = function (head) { 55 | let slow, fast; 56 | slow = fast = head; 57 | while (fast && fast.next) { 58 | slow = slow.next; 59 | fast = fast.next.next; 60 | } 61 | return slow; 62 | }; 63 | ``` 64 | 65 | 通过终止条件 `fast && fast.next` 判断快指针是否还能继续下去: 66 | 67 | - 当链表长度为奇数,快指针最终为 `null`,此时慢指针在链表中间。 68 | - 当链表长度为偶数时,快指针的下一位为 `null`,触发终止条件,此时慢指针在链表中间偏右一位。 69 | 70 | 巧用快慢指针解决问题 71 | 72 | https://leetcode-cn.com/problems/happy-number/ 73 | 74 | ```js 75 | var isHappy = function (n) { 76 | let slow, fast; 77 | slow = fast = n; 78 | do { 79 | slow = sum(slow); 80 | fast = sum(sum(fast)); 81 | } while (fast !== slow); 82 | return slow === 1; 83 | }; 84 | 85 | const sum = (num) => { 86 | let sum = 0; 87 | while (num > 0) { 88 | let n = num % 10; 89 | sum += n * n; 90 | num = Math.floor(num / 10); 91 | } 92 | return sum; 93 | }; 94 | ``` 95 | 96 | 回文、反转链表 97 | 98 | https://leetcode-cn.com/problems/palindrome-linked-list/submissions/ 99 | 100 | ```js 101 | let fast, slow; 102 | let node, pre; 103 | fast = slow = head; 104 | pre = null; 105 | while (fast && fast.next) { 106 | node = slow; 107 | slow = slow.next; 108 | fast = fast.next.next; 109 | node.next = pre; 110 | pre = node; 111 | } 112 | if (fast) slow = slow.next; 113 | while (node) { 114 | if (node.val !== slow.val) return false; 115 | node = node.next; 116 | slow = slow.next; 117 | } 118 | return true; 119 | }; 120 | ``` 121 | 122 | 重排链表 123 | 124 | https://leetcode-cn.com/problems/reorder-list/ 125 | 126 | ```js 127 | var reorderList = function (head) { 128 | let fast, slow; 129 | fast = slow = head; 130 | while (fast && fast.next) { 131 | slow = slow.next; 132 | fast = fast.next.next; 133 | } 134 | let l1 = head; 135 | let l2 = reverse(slow); 136 | while (l1 && l2) { 137 | let temp = l1.next; 138 | l1.next = l2; 139 | l1 = temp; 140 | 141 | temp = l2.next; 142 | l2.next = l1; 143 | l2 = temp; 144 | } 145 | if (l1) l1.next = null; 146 | }; 147 | 148 | const reverse = (head) => { 149 | let prev = null; 150 | while (head) { 151 | let next = head.next; 152 | head.next = prev; 153 | prev = head; 154 | head = next; 155 | } 156 | return prev; 157 | }; 158 | ``` 159 | 160 | 环形数组 161 | 162 | https://leetcode-cn.com/problems/circular-array-loop/ 163 | 164 | ```js 165 | /** 166 | * @param {number[]} nums 167 | * @return {boolean} 168 | */ 169 | var circularArrayLoop = function (nums) { 170 | for (let i = 0; i < nums.length; i++) { 171 | if (nums[i] === 0) continue; 172 | let fast, slow; 173 | fast = slow = i; 174 | const isForward = nums[i] >= 0; 175 | while (1) { 176 | slow = getIndex(nums, isForward, slow); 177 | fast = getIndex(nums, isForward, fast); 178 | if (fast !== -1) { 179 | fast = getIndex(nums, isForward, fast); 180 | } 181 | if (slow === -1 || fast === -1 || slow === fast) { 182 | break; 183 | } 184 | } 185 | if (slow !== -1 && slow === fast) { 186 | return true; 187 | } 188 | slow = i; 189 | let sgn = nums[i]; 190 | while (sgn * nums[slow] > 0) { 191 | let tmp = getCurrentIndex(nums, slow); 192 | nums[slow] = 0; 193 | slow = tmp; 194 | } 195 | } 196 | return false; 197 | }; 198 | 199 | const getCurrentIndex = (nums, index) => { 200 | let next = (index + nums[index]) % nums.length; 201 | if (next < 0) { 202 | next += nums.length; 203 | } 204 | return next; 205 | }; 206 | 207 | const getIndex = (nums, isForward, index) => { 208 | let direction = nums[index] >= 0; 209 | if (direction !== isForward) return -1; 210 | let next = getCurrentIndex(nums, index); 211 | if (index === next) return -1; 212 | return next; 213 | }; 214 | ``` 215 | -------------------------------------------------------------------------------- /滑动窗口.md: -------------------------------------------------------------------------------- 1 | # 滑动窗口 2 | 3 | 大家对于滑动窗口应该不陌生,在 TCP 协议中就有这个概念的出现,用于控制网络流量,避免拥塞发生。 4 | 5 | 在算法中这个思想也是类似的,多用于解决在一段连续的区间中寻找满足条件的问题,比如说给定一个字符串,寻找出无重复字符的最长子串。该思路主要应用于数组及字符串的数据结构中。 6 | 7 | ## 示例 8 | 9 | ![截屏2020-11-04下午10.50.11](https://yck-1254263422.cos.ap-shanghai.myqcloud.com/2020/11/04/16045020528651.png) 10 | 11 | 滑动窗口主要思路是维护一对指针,在一定条件内右移右指针扩大窗口大小直到窗口内的解不满足题意,此时我们需要根据情况移动左指针,重复移动左右指针的操作并在区间内求解,直到双指针不能再移动。 12 | 13 | 以 [寻找出无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/) 题目为例,根据上述的思路解题就会很方便: 14 | 15 | ```js 16 | var lengthOfLongestSubstring = function(s) { 17 | // 用于存储指针移动过程中的值 18 | let map = {} 19 | // 双指针 20 | let left = 0 21 | let right = 0 22 | // 结果 23 | let count = 0 24 | // 指针移动终止条件 25 | while (right < s.length) { 26 | const char = s[right] 27 | // 根据题意我们需要寻找不重复的最长子串 28 | // 当 char 出现时我们需要移动左指针到重复字符的下一位 29 | if (char in map) { 30 | left = Math.max(left, map[char] + 1) 31 | } 32 | // 求解 33 | count = Math.max(count, right - left + 1) 34 | // 移动右指针并存下索引 35 | map[char] = right++ 36 | } 37 | return count 38 | }; 39 | ``` 40 | 41 | 此题为高频题,大家务必掌握 42 | 43 | ![截屏2020-11-05下午10.11.08](https://yck-1254263422.cos.ap-shanghai.myqcloud.com/2020/11/05/16045863477109.png) 44 | 45 | 46 | ## 框架 47 | 48 | 根据上题我们可以得出一个滑动窗口解题的大致框架的伪代码, 49 | 50 | ```js 51 | let left = 0 52 | let right = 0 53 | while (right < size) { 54 | 获取当前索引数据 55 | right++ 56 | 数据更新等操作 57 | while (窗口需要缩小) { 58 | left++ 59 | 数据移除等操作 60 | } 61 | } 62 | ``` 63 | 64 | 框架中需要变化的几点如下: 65 | 66 | - 右指针右移后数据的更新 67 | - 判断窗口何时需要缩小 68 | - 左指针右移后数据的更新 69 | - 根据题目求最优解 70 | 71 | 接下来我们根据这个框架代码来试着解决几道题目。 72 | 73 | ## 实战 74 | 75 | ### 209. 长度最小的子数组 76 | 77 | 解题思路: 78 | 1. 移动右指针并将移动后的值累加存储起来 79 | 2. 当累加值大于 `s` 时移动左指针缩小窗口,此时需要更新累加值及我们需要的解 80 | 81 | ```js 82 | var minSubArrayLen = function(s, nums) { 83 | // 定义双指针 84 | let left = 0 85 | let right = 0 86 | // 求解需要用到的变量 87 | let length = Infinity 88 | let sum = 0 89 | // 指针移动终止条件 90 | while (right < nums.length) { 91 | // 获取当前索引数据 92 | sum += nums[right] 93 | // 缩小窗口条件 94 | while (sum >= s) { 95 | // 求解 96 | length = Math.min(length, right - left + 1) 97 | // 缩小窗口 98 | sum -= nums[left++] 99 | } 100 | // 扩大窗口 101 | right++ 102 | } 103 | return length === Infinity ? 0 : length 104 | }; 105 | ``` 106 | 107 | 这道题目是 Leetcode 的第 [209](https://leetcode-cn.com/problems/minimum-size-subarray-sum/) 题,答案可以说除了小部分的微调之外,基本套用了框架代码。后续的题目大家可以继续跟着这个思路解题,快速掌握通过滑动窗口来解题的套路。 108 | 109 | ![出题频率](https://yck-1254263422.cos.ap-shanghai.myqcloud.com/2020/11/05/16045849179673.png) 110 | 111 | ### 438. 找到字符串中所有字母异位词 112 | 113 | 解题思路: 114 | 1. 通过哈希表存储 `p` 中的字符出现次数 115 | 2. 移动右指针判断当前字符是否还符合条件 116 | 3. 不符合条件时移动左指针缩小窗口,此时需要更新哈希表 117 | 4. 当前字符不存在哈希表时说明双指针可以直接跳到下一位 118 | 119 | ```js 120 | var findAnagrams = function(s, p) { 121 | if (!s.length || !p.length || s.length < p.length) return [] 122 | // 求解需要用到的变量 123 | const map = {} 124 | const result = [] 125 | // 定义双指针 126 | let left = 0, right = 0 127 | // 把字符串 p 中的字符通过 hash 存储起来 128 | for (let i = 0; i < p.length; i++) { 129 | const char = p[i] 130 | if (!(char in map)) { 131 | map[char] = 0 132 | } 133 | map[char] += 1 134 | } 135 | // 指针移动终止条件 136 | while (right < s.length) { 137 | const char = s[right] 138 | // map 中存在字符就移动右指针 139 | if (map[char] > 0) { 140 | map[char] -= 1 141 | right++ 142 | // 否则判断左指针所指向的字符是否存在 map 中 143 | } else if (map[s[left]] >= 0) { 144 | map[s[left]] += 1 145 | left++ 146 | // 不存在的话把左右指针全部挪到下一位 147 | } else { 148 | left = right += 1 149 | } 150 | // 存储正确解 151 | if (right - left === p.length) { 152 | result.push(left) 153 | } 154 | } 155 | return result 156 | }; 157 | ``` 158 | 159 | ![出题频率](https://yck-1254263422.cos.ap-shanghai.myqcloud.com/2020/11/08/16048348783681.png) 160 | 161 | ### 76. 最小覆盖子串 162 | ![出题频率](https://yck-1254263422.cos.ap-shanghai.myqcloud.com/2020/11/05/16045854473171.png) 163 | 164 | 这道题目和之前的 「找到字符串中所有字母异位词」思路很类似: 165 | 166 | 1. 通过哈希表存储 `t` 中的字符出现次数 167 | 2. 移动右指针判断当前字符是否还符合条件 168 | 3. 不符合条件时移动左指针缩小窗口,此时需要更新哈希表 169 | 170 | ```js 171 | var minWindow = function(s, t) { 172 | // 定义双指针 173 | let left = 0, right = 0 174 | // 求解需要用到的变量 175 | let length = Infinity 176 | let map = {} 177 | // 遇到 t 中存在的字符时更新 match,注意 t 中存在的字符可能在 s 中出现多次 178 | // 因此并不是每次都需要更新 match 179 | let match = 0 180 | // 记录最短子串开始位置,不能用 left 181 | let start = 0 182 | // 把字符串 t 中的字符通过 hash 存储起来 183 | for (let i = 0; i < t.length; i++) { 184 | const char = t[i] 185 | if (!(char in map)) { 186 | map[char] = 0 187 | } 188 | map[char] += 1 189 | } 190 | // 指针移动终止条件 191 | while (right < s.length) { 192 | const char = s[right] 193 | // 右指针移动时更新数据 194 | if (char in map) { 195 | map[char] -= 1 196 | if (map[char] >= 0) match += 1 197 | } 198 | // 缩小窗口条件 199 | while (match === t.length) { 200 | // 寻找到更佳解,保存数据 201 | if (length > right - left + 1) { 202 | length = right - left + 1 203 | start = left 204 | } 205 | // 移动左指针并且更新数据 206 | const char = s[left++] 207 | if (char in map) { 208 | if (map[char] === 0) match -= 1 209 | map[char] += 1 210 | } 211 | } 212 | // 移动右指针 213 | right++ 214 | } 215 | return length === Infinity ? '' : s.substring(start, start + length) 216 | }; 217 | ``` 218 | 219 | ## 总结 220 | 221 | 经过上面几道题目的练习,大家应该能看出滑动窗口的思路多用于解决数组及字符串中子元素的问题。 --------------------------------------------------------------------------------