LeetCode 热题 100 | 双指针(上)

目录

1  283. 移动零

2  11. 盛最多水的容器

3  15. 三数之和


菜鸟做题第一周,语言是 C++

1  283. 移动零

解题思路:

  1. 两个指针一前一后遍历数组
  2. 前者永远指向 0,后者永远在寻找非 0 数的路上
  3. 后者找到一个非 0 数就和前者进行一个数值交换

思路说明图:

  • 上图并没有画出每一步,请自行脑补
  • 由上图可见,i 始终指向 0,j 的作用就是寻找非 0 数
  • 一旦找到就进行交换

思考过程:

本菜鸟一开始交换两数还用的是最传统的 temp 三步法,结果被 swap 函数一把子秒了!对于双指针,既然 i 和 j 必须是一前一后地移动(毕竟自己没有和自己交换的必要),那为什么初始化的时候要令 j 等于 i 呢?这是因为 nums 的长度可能为 1,你初始化 j = 1 就越界了。

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int i = 0, j = 0;
        while (j < nums.size()) {
            if (nums[j] != 0) {
                swap(nums[i], nums[j]);
                ++i;
            }
            ++j;
        }
    }
};

每完成一次交换,i 就要向右移动一格,毕竟之前的序列已经处理好了。无论交不交换,j 都要向右移动一格。若交换,则代表 j 当前指向 0;若没交换,则还是代表 j 当前指向 0 。而 j 是用来寻找非 0 数的,因此必须向右移动。

2  11. 盛最多水的容器

解题思路:

  1. 两个指针一头一尾遍历数组
  2. 若左侧更低则左边的指针移动,若右侧更低则右边的指针移动
  3. 每次移动完就计算新的面积,并和历史最大面积做比较

思路说明图:

这里我们的移动标准是 “哪侧的边矮,我们就移动哪侧”。为什么这样做呢?难道不会遗漏更好的组合吗?举个例子,对于初始组合 1 和 9,面积的高度等于 1 这个矮边,又因为 9 离 1 最远,所以面积的宽度已经取到了极值,这就是针对 1 这个矮边的最大面积了!我们再怎么移动右侧的边也不能使面积增大。

同样地,当左侧的边移动到 2 时,9 成了矮边。在保证 9 是矮边的条件下,2 又是离 9 最远的,所以面积的宽度已经取到了极值,这就是针对 9 这个矮边的最大面积了!我们再怎么移动左侧的边也不能使面积增大。

class Solution {
public:
    int maxArea(vector<int>& height) {
        int i = 0, j = height.size() - 1;
        int area = min(height[i], height[j]) * (j-i);
        while (i != j) {
            if (height[i] < height[j]) {
                ++i;
            } else if (height[i] > height[j]) {
                --j;
            } else {
                ++i;
            }
            area = max(area, min(height[i], height[j]) * (j-i));
        }
        return area;
    }
};

3  15. 三数之和

解题思路:

  1. 为数组排序
  2. 进行三层循环,每层循环针对三数中的一个
  3. 第二层和第三层体现了双指针,一个从头开始,一个从尾开始

思路说明图:

1)双指针

传统的三层循环如 method1 所示,即 i、j 和 k 都是从左至右遍历数组。但是因为排序后的数字是按从小到大的顺序排序的,并且要求 nums[i] + nums[j] + nums[k] == 0 。又由于 i 和 j 是从左至右遍历的,nums[i] + nums[j] 会变得越来越大,因此 nums[k] 应该越来越小!这就要求 k 从右至左进行遍历,如 method2 所示。

2)避免重复

if (i > 0 && nums[i] == nums[i - 1]) continue;

这行代码是为了避免三元组出现重复。如上图所示,我们只看第一层循环。

当 i 等于第一个 -4 的时候,j 和 k 屁颠屁颠地给 i 找满足要求的组合,它们考虑的数字包含 {-4, -1, -1, 0, 1, 2, 2} 。当 i 等于第二个 -4 的时候,j 和 k 还是屁颠屁颠地给 i 找满足要求的组合,它们考虑的数字包含 {-1, -1, 0, 1, 2, 2} 。

这里少考虑了一个 -4 会影响结果吗?答案是并不会。只能说为第二个 -4 找的三元组可能比为第一个 -4 找的少一个罢了(如果数组里还有个 8 的话,就会少一个 {-4, -4, 8})但这并不影响啊,题目要求的就是不能重复!所以我们索性跳过第二个 -4,反正为它找的三元组已经存在了。

类似地,针对 j 也有这样的 if 语句。

if (j > i + 1 && nums[j] == nums[j - 1]) continue;

3)第三层循环

按理说,第三层循环也拿 for 做不就好了吗,条件为 j < k,只要 --k 就好了呀,但会超时……

还是那个思路 “能少循环就少循环”。

while (j < k && nums[i] + nums[j] + nums[k] > 0) --k;

这里有两个条件。一个是 j 不能遇上 k,它避免了 nums[k] 和 nums[i]、nums[j] 取到同一个位置的值;另一个是 nums[i] + nums[j] + nums[k] > 0,它避免了 nums[i] + nums[j] + nums[k] <= 0 以后,k 还在一个劲儿地往左移。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());

        // 第一层循环
        for (int i = 0; i < nums.size(); ++i) {
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            int k = nums.size() - 1;
            // 第二层循环
            for (int j = i + 1; j < nums.size(); ++j) {
                if (j > i + 1 && nums[j] == nums[j - 1]) continue;
                // 第三层循环
                while (j < k && nums[i] + nums[j] + nums[k] > 0) --k;
                // 处理循环结果
                if (j == k) break;
                if (nums[i] + nums[j] + nums[k] == 0) {
                    result.push_back({nums[i], nums[j], nums[k]});
                }
            }
        }

        return result;
    }
};

这道题最离谱的是把 int k = nums.size() - 1; 改写到第二层循环里也会超时啊!?

1. Two Sum 2. Add Two Numbers 3. Longest Substring Without Repeating Characters 4. Median of Two Sorted Arrays 5. Longest Palindromic Substring 6. ZigZag Conversion 7. Reverse Integer 8. String to Integer (atoi) 9. Palindrome Number 10. Regular Expression Matching 11. Container With Most Water 12. Integer to Roman 13. Roman to Integer 14. Longest Common Prefix 15. 3Sum 16. 3Sum Closest 17. Letter Combinations of a Phone Number 18. 4Sum 19. Remove Nth Node From End of List 20. Valid Parentheses 21. Merge Two Sorted Lists 22. Generate Parentheses 23. Swap Nodes in Pairs 24. Reverse Nodes in k-Group 25. Remove Duplicates from Sorted Array 26. Remove Element 27. Implement strStr() 28. Divide Two Integers 29. Substring with Concatenation of All Words 30. Next Permutation 31. Longest Valid Parentheses 32. Search in Rotated Sorted Array 33. Search for a Range 34. Find First and Last Position of Element in Sorted Array 35. Valid Sudoku 36. Sudoku Solver 37. Count and Say 38. Combination Sum 39. Combination Sum II 40. First Missing Positive 41. Trapping Rain Water 42. Jump Game 43. Merge Intervals 44. Insert Interval 45. Unique Paths 46. Minimum Path Sum 47. Climbing Stairs 48. Permutations 49. Permutations II 50. Rotate Image 51. Group Anagrams 52. Pow(x, n) 53. Maximum Subarray 54. Spiral Matrix 55. Jump Game II 56. Merge k Sorted Lists 57. Insertion Sort List 58. Sort List 59. Largest Rectangle in Histogram 60. Valid Number 61. Word Search 62. Minimum Window Substring 63. Unique Binary Search Trees 64. Unique Binary Search Trees II 65. Interleaving String 66. Maximum Product Subarray 67. Binary Tree Inorder Traversal 68. Binary Tree Preorder Traversal 69. Binary Tree Postorder Traversal 70. Flatten Binary Tree to Linked List 71. Construct Binary Tree from Preorder and Inorder Traversal 72. Construct Binary Tree from Inorder and Postorder Traversal 73. Binary Tree Level Order Traversal 74. Binary Tree Zigzag Level Order Traversal 75. Convert Sorted Array to Binary Search Tree 76. Convert Sorted List to Binary Search Tree 77. Recover Binary Search Tree 78. Sum Root to Leaf Numbers 79. Path Sum 80. Path Sum II 81. Binary Tree Maximum Path Sum 82. Populating Next Right Pointers in Each Node 83. Populating Next Right Pointers in Each Node II 84. Reverse Linked List 85. Reverse Linked List II 86. Partition List 87. Rotate List 88. Remove Duplicates from Sorted List 89. Remove Duplicates from Sorted List II 90. Intersection of Two Linked Lists 91. Linked List Cycle 92. Linked List Cycle II 93. Reorder List 94. Binary Tree Upside Down 95. Binary Tree Right Side View 96. Palindrome Linked List 97. Convert Binary Search Tree to Sorted Doubly Linked List 98. Lowest Common Ancestor of a Binary Tree 99. Lowest Common Ancestor of a Binary Search Tree 100. Binary Tree Level Order Traversal II
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值