• Leetcode hot 100之双指针(快慢指针、滑动窗口)


    目录

    数组

    有序的平方仍有序

    删除/覆盖元素

    移动零:交换slow和fast

    滑动窗口:最短的连续子串(r++可行解->l--最短解)

    最小长度的子数组

    求和:sort、l = i + 1, r = len - 1

    三数之和a+b+c=target

    四数之和a+b+c+d=target

    颜色分类(荷兰国旗):start=0、i、end=len-1

    盛水最多:start=0、end=len-1 (水=哪边,则哪边往内走)

    重复数:[1, n] 

    链表

    相交点:长的链表先走len=long-short

    倒数第n个:slow+1,fast+n

    中点/回文/环:slow+1,fast+2

    环入口:相遇点+1、头结点+1

    归并排序

    自底向上

    自顶向下

    双指针

    数组

    数组

    有序的平方仍有序

    删除/覆盖元素

    1. if(nums[i] != val){
    2. nums[k++] = nums[i]
    3. }

    移动零:交换slow和fast

    滑动窗口

    初始化left = right = 0把索引左闭右开区间[left, right)称为一个「窗口」

    1. int left = 0, right = 0;
    2. while (right < s.size()) {
    3. // 增大窗口
    4. window.add(s[right]);
    5. right++;
    6. while (window needs shrink) {
    7. // 缩小窗口
    8. window.remove(s[left]);
    9. left++;
    10. }
    11. }

    最小覆盖子串

    1. function minWindow(s, t) {
    2. const need = new Map();
    3. const window = new Map();
    4. for (const c of t) {
    5. need.set(c, (need.get(c) || 0) + 1);
    6. }
    7. let left = 0;
    8. let right = 0;
    9. let valid = 0;
    10. let start = 0;
    11. let len = Infinity;
    12. while (right < s.length) {
    13. const c = s[right];
    14. right++;
    15. if (need.has(c)) {
    16. window.set(c, (window.get(c) || 0) + 1);
    17. if (window.get(c) === need.get(c)) {
    18. valid++;
    19. }
    20. }
    21. while (valid === need.size) {
    22. if (right - left < len) {
    23. start = left;
    24. len = right - left;
    25. }
    26. const d = s[left];
    27. left++;
    28. if (need.has(d)) {
    29. if (window.get(d) === need.get(d)) {
    30. valid--;
    31. }
    32. window.set(d, window.get(d) - 1);
    33. }
    34. }
    35. }
    36. return len === Infinity ? "" : s.substr(start, len);
    37. }

    字符串排列/异位词

    1. function checkInclusion(t, s) {
    2. const need = new Map();
    3. const window = new Map();
    4. for (const c of t) {
    5. need.set(c, (need.get(c) || 0) + 1);
    6. }
    7. let left = 0;
    8. let right = 0;
    9. let valid = 0;
    10. while (right < s.length) {
    11. const c = s[right];
    12. right++;
    13. if (need.has(c)) {
    14. window.set(c, (window.get(c) || 0) + 1);
    15. if (window.get(c) === need.get(c)) {
    16. valid++;
    17. }
    18. }
    19. //与最小覆盖串的区别
    20. while (right - left >= t.length) {
    21. if (valid === need.size) {
    22. return true;
    23. }
    24. //与最小覆盖串的区别
    25. const d = s[left];
    26. left++;
    27. if (need.has(d)) {
    28. if (window.get(d) === need.get(d)) {
    29. valid--;
    30. }
    31. window.set(d, window.get(d) - 1);
    32. }
    33. }
    34. }
    35. return false;
    36. }

    最长无重复子串

    1. function lengthOfLongestSubstring(s) {
    2. const window = new Map();
    3. let left = 0;
    4. let right = 0;
    5. let res = 0; // 记录结果
    6. while (right < s.length) {
    7. const c = s[right];
    8. right++;
    9. // 进行窗口内数据的一系列更新
    10. window.set(c, (window.get(c) || 0) + 1);
    11. // 判断左侧窗口是否要收缩
    12. while (window.get(c) > 1) {
    13. const d = s[left];
    14. left++;
    15. // 进行窗口内数据的一系列更新
    16. window.set(d, window.get(d) - 1);
    17. }
    18. // 在这里更新答案
    19. res = Math.max(res, right - left);
    20. }
    21. return res;
    22. }

    最小长度的子数组

    给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s长度最小连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

    类似于前缀和:区间求和

    1. let ans = Infinity
    2. while(end < len){
    3. sum += nums[end];
    4. while (sum >= target) {
    5. ans = Math.min(ans, end - start + 1);
    6. sum -= nums[start];
    7. start++;
    8. }
    9. end++;
    10. }

    求和:sort、l = i + 1, r = len - 1

    三数之和a+b+c=target

    1. arr.sort()
    2. let l = i + 1, r = len - 1, iNum = nums[i]
    3. // 数组排过序,如果第一个数大于0直接返回res
    4. if (iNum > 0) return res
    5. // 去重
    6. if (iNum == nums[i - 1]) continue
    7. while(l < r) {
    8. if (threeSum < 0) l++
    9. else if (threeSum > 0) r--
    10. else {
    11. res.push([iNum, lNum, rNum])
    12. // 去重
    13. while(l < r && nums[l] == nums[l + 1]){
    14. l++
    15. }
    16. while(l < r && nums[r] == nums[r - 1]) {
    17. r--
    18. }
    19. l++
    20. r--
    21. }
    22. }

    四数之和a+b+c+d=target

    1. for(let i = 0; i < len - 3; i++) {
    2. // 去重i
    3. if(i > 0 && nums[i] === nums[i - 1]) continue;

    颜色分类(荷兰国旗):start=0、i、end=len-1

    盛水最多:start=0、end=len-1 (水=哪边,则哪边往内走)

    重复数:[1, n] 

    T(n):O(n)。「Floyd 判圈算法」时间复杂度为线性的时间复杂度。

    S(n):O(1)。只需要常数空间存放若干变量。

    对 nums数组建图,每个位置 i连一条 i→nums[i] 的边。由于存在的重复的数字 target,因此 targe这个位置一定有起码两条指向它的边,因此整张图一定存在环,且我们要找到的 target就是这个环的入口

    1. var findDuplicate = function(nums) {
    2. let slow = 0, fast = 0;
    3. do {
    4. slow = nums[slow];
    5. fast = nums[nums[fast]];
    6. } while (slow != fast);
    7. slow = 0;
    8. while (slow != fast) {
    9. slow = nums[slow];
    10. fast = nums[fast];
    11. }
    12. return slow;
    13. };

    链表

    相交点:长的链表先走len=long-short

    倒数第n个:slow+1,fast+n

    中点/回文/:slow+1,fast+2

    环入口:相遇点+1、头结点+1

    相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A

    (x + y) * 2 = x + y + n (y + z)

    x = (n - 1) (y + z) + z

    虽然实际中的n>1,当 n为1的时候,公式就化解为 x = z

    从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点

    归并排序

    自底向上

     T(n):O(nlogn)

    S(n):O(1)

    空间复杂度不是累计的,而是计算使用空间的峰值,

    C/C++ 没有回收资源(new完后需要delete,不然内存泄漏照样是O(logn)),

    但是像 java ,js这类语言会自动回收资源的

    每次将链表拆分成若干个长度为 subLength 的子链表(最后一个子链表的长度可以小于 subLength)

    1. /**
    2. * Definition for singly-linked list.
    3. * function ListNode(val, next) {
    4. * this.val = (val? 0 : val)
    5. * this.next = (next? null : next)
    6. * }
    7. */
    8. const merge = (head1, head2) => {
    9. let temp = new ListNode(0), temp1 = head1, temp2 = head2;
    10. while (temp1&& temp2) {
    11. if (temp1.val <= temp2.val) {
    12. temp.next = temp1;
    13. temp1 = temp1.next;
    14. } else {
    15. temp.next = temp2;
    16. temp2 = temp2.next;
    17. }
    18. temp = temp.next;
    19. }
    20. if (temp1 !== null) {
    21. temp.next = temp1;
    22. } else if (temp2 !== null) {
    23. temp.next = temp2;
    24. }
    25. return dummyHead.next;
    26. }
    27. var sortList = function(head) {
    28. if (head === null) {
    29. return head;
    30. }
    31. //获取长度
    32. let length = 0;
    33. let node = head;
    34. while (node !== null) {
    35. length++;
    36. node = node.next;
    37. }
    38. const dummyHead = new ListNode(0, head);
    39. for (let subLength = 1; subLength < length; subLength <<= 1) {
    40. let prev = dummyHead, curr = dummyHead.next;
    41. while (curr !== null) {
    42. let head1 = curr;
    43. for (let i = 1; i < subLength && curr.next; i++) {
    44. curr = curr.next;
    45. }
    46. let head2 = curr.next;
    47. curr.next = null;
    48. curr = head2;
    49. for (let i = 1; i < subLength && curr&& curr.next; i++)
    50. curr = curr.next;
    51. }
    52. let next = null;
    53. if (curr) {
    54. next = curr.next;
    55. curr.next = null;
    56. }
    57. const merged = merge(head1, head2);
    58. //通过 prev 指针将已排序的子链表连接到一起
    59. prev.next = merged;
    60. while (prev.next) {
    61. prev = prev.next;
    62. }
    63. //用 curr 指针继续遍历未排序的部分
    64. curr = next;
    65. }
    66. }
    67. return dummyHead.next;
    68. };

    自顶向下

    操作

    内部排序

    思想

    稳定

    平均

    S(n)

    T(n)

    平均

    最坏

    最好

    2-路归并

    分治;分组排序,两两合并 相邻 有序序列

    n

    nlog2n

    nlog2n逆序

    nlog2n顺序

    双指针
    1. const merge = (head1, head2) => {
    2. const dummyHead = new ListNode(0);
    3. let temp = dummyHead, temp1 = head1, temp2 = head2;
    4. while (temp1 !== null && temp2 !== null) {
    5. if (temp1.val <= temp2.val) {
    6. temp.next = temp1;
    7. temp1 = temp1.next;
    8. } else {
    9. temp.next = temp2;
    10. temp2 = temp2.next;
    11. }
    12. temp = temp.next;
    13. }
    14. if (temp1 !== null) {
    15. temp.next = temp1;
    16. } else if (temp2 !== null) {
    17. temp.next = temp2;
    18. }
    19. return dummyHead.next;
    20. }
    21. const toSortList = (head, tail) => {
    22. if (head === null) {
    23. return head;
    24. }
    25. if (head.next === tail) {
    26. head.next = null;
    27. return head;
    28. }
    29. let slow = head, fast = head;
    30. while (fast !== tail) {
    31. slow = slow.next;
    32. fast = fast.next;
    33. if (fast !== tail) {
    34. fast = fast.next;
    35. }
    36. }
    37. const mid = slow;
    38. return merge(toSortList(head, mid), toSortList(mid, tail));
    39. }
    40. var sortList = function(head) {
    41. return toSortList(head, null);
    42. };
    数组
    • key:
    1. left=arr.slice(0,mid)
    2. mergeLeft=mergeSort(left)
    3. res.push(leftArr.shift())
    4. res=res.concat(leftArr)
    1. function mergesort(arr){
    2. if(arr.length<2)return arr
    3. let len=arr.length
    4. let mid=parseInt(len/2)
    5. let l1=arr.slice(0,mid)
    6. let r1=arr.slice(mid,len)
    7. let mergeleft=mergesort(l1)
    8. let mergeright=mergesort(r1)
    9. return merge(mergeleft,mergeright)
    10. function merge(left,right){
    11. let res=[]
    12. while(left.length&&right.length){
    13. if(left[0]<=right[0]){
    14. res.push(left.shift())
    15. }else{
    16. res.push((right.shift()))
    17. }
    18. }
    19. if(left.length){
    20. res=res.concat(left)
    21. }
    22. if(right.length){
    23. res=res.concat(right)
    24. }
    25. return res
    26. }
    27. }
  • 相关阅读:
    1011 A + B 和 C【PAT (Basic Level) Practice (中文)】
    WPF调用webapi并展示数据(二):类库实体类的构建
    vulnhub之Nagini
    C++——命名空间
    Spring中的<lookup-method>标签的功能简介说明
    Hadoop3教程(二十六):(生产调优篇)NameNode核心参数配置与回收站的启用
    C++ STL之stack
    AI艺术的背后:详解文本生成图像模型【基于GAN】
    Python杂谈--关于iter迭代器的一些讨论
    代码随想录笔记_动态规划_474一和零
  • 原文地址:https://blog.csdn.net/qq_28838891/article/details/133610298