双指针是一种技巧, 不等同于C,C++中的指针, 其是一种算法思想, 没有具体的算法, 通常用在二分查找中。
常见题型
一般分以下三种类型
- 快慢指针(两个指针步长不同, 多用于滑动窗口)
- 左右端点指针(两个指针分别指向头尾, 并向中间移动, 多用以二分查找)
- 固定间距指针(两个指针步长不同, 间距相同, 多用于滑动窗口)
伪代码框架
快慢指针
l = 0
r = 0
while 没有遍历完
if 一定条件
l += 1
r += 1
return
左右端点指针
l = 0
r = n - 1
while l < r
if 找到了
return 找到的值
if 一定条件1
l ++
else if 一定条件2
r --
return 没找到
固定间距指针
l = 0
r = k
while 没有遍历完
自定义逻辑
l += 1
r += 1
return 合适的值
快慢指针常见题--快慢指针
环形链表
给定一个链表,判断链表中是否有环。
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
来源: leetcode-cn.com/problems/li…
var hasCycle = function(head) {
if(!head || !head.next) return false;
let slow = head, fast = head.next;
while(slow != fast) {
if(!fast || !fast.next) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
};
环形链表 II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
来源: leetcode-cn.com/problems/li…
var detectCycle = function(head) {
let slow = head, fast = head;
while(fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
if(slow === fast) {
fast = head;
while(slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
return null;
};
最大连续1的个数 III
给定一个由若干 0 和 1 组成的数组 A,我们最多可以将 K 个值从 0 变成 1 。返回仅包含 1 的最长(连续)子数组的长度。
输入:A = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:
[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。
来源: leetcode-cn.com/problems/ma…
var longestOnes = function(A, K) {
let left = K, i = 0, j = 0, res = 0;
while(j < A.length) {
if(A[j] === 0) {
if(left === 0) {
if(A[i] === 0) {
left++;
}
i++;
} else {
left--;
j++;
}
} else {
j++;
}
res = Math.max(res, j - i)
}
return res;
};
和相同的二元子数组
在由若干 0 和 1 组成的数组 A 中,有多少个和为 S 的非空子数组。
输入:A = [1,0,1,0,1], S = 2
输出:4
解释:
如下面黑体所示,有 4 个满足题目要求的子数组:
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]
[1,0,1,0,1]
来源: leetcode-cn.com/problems/bi…
var numSubarraysWithSum = function(A, S) {
let l = r = 0, sum = 0, count = 0;
while(r < A.length) {
let c = A[r];
sum += c;
while(sum > S) {
let d = A[l];
sum -= d;
l++;
}
if(sum === S) {
let tmp = sum, j = l;
while(j <= r && tmp === S) {
count++;
tmp -= A[j];
j++
}
}
r++;
}
return count;
};
两个数组的交集
给定两个数组,编写一个函数来计算它们的交集。
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
來源: leetcode-cn.com/problems/in…
var intersection = function(nums1, nums2) { nums1.sort((a, b) => a - b); nums2.sort((a, b) => a - b); let i = 0, j = 0; let res = new Set(); while(i < nums1.length && j < nums2.length) { if(nums1[i] < nums2[j]) { i++; } else if(nums1[i] > nums2[j]) { j++; } else { res.add(nums1[i]); i++; j++; } } return [...res];};
两个数组的交集 II
给定两个数组,编写一个函数来计算它们的交集。
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]
來源: leetcode-cn.com/problems/in…
var intersect = function(nums1, nums2) {
nums1 = nums1.sort((a, b) => a-b);
nums2 = nums2.sort((a, b) => a - b);
let i = 0, j = 0;
let res = [];
while(i < nums1.length && j < nums2.length) {
if(nums1[i] < nums2[j]) {
i++;
} else if(nums1[i] > nums2[j]) {
j++;
} else {
res.push(nums1[i]);
i++;
j++;
}
}
return res;
};
数组中的 k-diff 数对
给定一个整数数组和一个整数 **k**,你需要在数组里找到不同的 k-diff 数对,并返回不同的 k-diff 数对 的数目。
输入:nums = [3, 1, 4, 1, 5], k = 2
输出:2
解释:数组中有两个 2-diff 数对, (1, 3) 和 (3, 5)。
尽管数组中有两个1,但我们只应返回不同的数对的数量。
來源: leetcode-cn.com/problems/k-…
var findPairs = function(nums, k) {
let count = 0;
nums = nums.sort((a, b) => a-b);
let left = 0, right = 1, len = nums.length;
while(right < len) {
let diff = nums[right] - nums[left];
if(diff == k) {
count++;
while(nums[right + 1] == nums[right]) {
right++;
}
while(nums[left + 1] == nums[left]) {
left++;
}
left++;
right = left + 1
} else if(diff > k) {
left++;
if(left == right) {
right = left + 1
}
} else {
right++;
}
}
return count;
};
快慢指针常见题--左右端点指针
左右断点指针多用于二分查找, 详情请见我的另一篇文章- 二分查找详解- juejin.cn/post/688746…
三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。 注意:答案中不可以包含重复的三元组。
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
来源: leetcode-cn.com/problems/3s…
var threeSum = function(nums) {
if(nums.length < 3) return [];
const list = [];
nums.sort((a, b) => a - b);
for (let i = 0; i < nums.length; i++) {
if(nums[i] > 0) break;
if(i > 0 && nums[i] === nums[i - 1]) continue;
let left = i + 1, right = nums.length - 1;
while(left < right) {
if(nums[left] + nums[right] + nums[i] === 0) {
list.push([nums[left], nums[right], nums[i]])
while(nums[left] === nums[left + 1]) {
left++;
}
left++;
while(nums[right] === nums[right - 1]) {
right--;
}
right--;
} else if(nums[left] + nums[right] + nums[i] > 0) {
right--;
} else {
left++;
}
}
}
return list;
};
三数之和的多种可能
给定一个整数数组 A,以及一个整数 target 作为目标值,返回满足 i < j < k 且 A[i] + A[j] + A[k] == target 的元组 i, j, k 的数量。 由于结果会非常大,请返回 结果除以 10^9 + 7 的余数。
输入:A = [1,1,2,2,3,3,4,4,5,5], target = 8
输出:20
解释:
按值枚举(A[i],A[j],A[k]):
(1, 2, 5) 出现 8 次;
(1, 3, 4) 出现 8 次;
(2, 2, 4) 出现 2 次;
(2, 3, 3) 出现 2 次。
来源: leetcode-cn.com/problems/3s…
var threeSumMulti = function(A, target) {
A.sort((a, b) => a - b)
let res = 0;
let len = A.length;
for (let i = 0; i < len - 2; i++) {
let l = i + 1, r = len - 1;
while(l < r) {
let total = A[i] + A[l] + A[r];
if(total === target) {
if(A[l] === A[r]) {
res += Math.floor((r - l + 1) * (r - l) / 2);
break;
} else {
let tmp = 1;
while(A[l] === A[++l]) tmp++;
res += tmp;
while(A[r] === A[--r]) res += tmp;
}
} else if(total > target) {
r--;
} else {
l++;
}
}
}
return res % (1e9 + 7)
};
最接近的三数之和
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
来源: leetcode-cn.com/problems/3s…
var threeSumClosest = function(nums, target) {
nums.sort((a, b) => a - b);
let ans = nums[0] + nums[1] + nums[2];
for (let i = 0; i < nums.length; i++) {
let l = i + 1, r = nums.length - 1;
while(l < r) {
let sum = nums[i] + nums[l] + nums[r];
if(Math.abs(target - sum) < Math.abs(target - ans)) {
ans = sum;
}
if(sum > target) {
r--;
} else if(sum < target) {
l++;
} else {
return ans;
}
}
}
return ans;
};
四数之和
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。 注意: 答案中不可以包含重复的四元组。
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
来源: leetcode-cn.com/problems/4s…
var fourSum = function(nums, target) {
let res = [];
if(nums.length < 4) return res;
nums.sort((a, b) => a - b);
for (let i = 0; i < nums.length; i++) {
if(i > 0 && nums[i] === nums[i - 1]) {
continue;
}
for (let j = i + 1; j < nums.length; j++) {
if(j > i + 1 && nums[j] === nums[j - 1]) {
continue;
}
let l = j + 1, r = nums.length - 1;
while(l < r) {
let sum = nums[i] + nums[j] + nums[l] + nums[r];
if(sum === target) {
res.push([nums[i], nums[j], nums[l], nums[r]]);
while(l < r && nums[l] == nums[l + 1]) {
l++;
}
while(l < r && nums[r] === nums[r - 1]) {
r--;
}
l++;
r--;
} else if (sum < target) {
l++;
} else {
r--;
}
}
}
}
return res;
};
有序数组的平方
给定一个按非递减顺序排序的整数数组 A,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。
输入:[-4,-1,0,3,10]
输出:[0,1,9,16,100]
来源: leetcode-cn.com/problems/sq…
var sortedSquares = function(A) {
let i = 0, arr = [], j = A.length - 1, z = A.length - 1;
while(z >= 0) {
let l = A[i] * A[i];
let r = A[j] * A[j];
if(l > r) {
arr[z] = l;
i++;
} else {
arr[z] = r;
j--;
}
z--;
}
return arr;
};
救生艇
第 i 个人的体重为 people[i],每艘船可以承载的最大重量为 limit。每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit。返回载到每一个人所需的最小船数。(保证每个人都能被船载)。
输入:people = [1,2], limit = 3
输出:1
解释:1 艘船载 (1, 2)
输入:people = [3,5,3,4], limit = 5
输出:4
解释:4 艘船分别载 (3), (3), (4), (5)
来源: leetcode-cn.com/problems/bo…
var numRescueBoats = function(people, limit) {
let res = 0;
let right = people.length - 1, left = 0;
people.sort((a, b) => a - b)
while(left <= right) {
if(left === right) {
res++; // 只剩下最后一个, 直接一个走,结束
break;
} if(people[left] + people[right] > limit) {
res++;
right--; // 先载最重的,而且最小的也无法一起,那么最重的要单独走
} else {
res++;
right--; // 最重的和最轻的一起走
left++;
}
}
return res;
};
验证回文串
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
输入: "A man, a plan, a canal: Panama"
输出: true
来源: leetcode-cn.com/problems/va…
var isPalindrome = function(s) {
s = s.replace(/[^0-9a-zA-Z]/g, '').toLowerCase();
let left = 0, right = s.length - 1;
while(left < right) {
if(s[left] !== s[right]) {
return false;
}
left++;
right--;
}
return true;
};
接雨水
给定n个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
来源: leetcode-cn.com/problems/tr…
var trap = function(height) {
let n = height.length, l = 0, r = n -1;
let ans = 0;
let l_max = height[0], r_max = height[n - 1];
while(l <= r) {
l_max = Math.max(l_max, height[l]);
r_max = Math.max(r_max, height[r]);
if(l_max < r_max) {
ans += l_max - height[l];
l++;
} else {
ans += r_max - height[r];
r--;
}
}
return ans;
};
反转字符串中的元音字母
编写一个函数,以字符串作为输入,反转该字符串中的元音字母。
输入:"hello"
输出:"holle"
来源: leetcode-cn.com/problems/re…
var reverseVowels = function(s) {
let set = new Set(['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']);
let arr = s.split('');
let i = 0, j = arr.length;
while(i < j) {
if(set.has(arr[i])) {
if(set.has(arr[j])) {
[arr[i], arr[j]] = [arr[j], arr[i]];
i++;
}
j--;
} else {
i++;
}
}
return arr.join('');
};
盛最多水的容器
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
输入:[1,8,6,2,5,4,8,3,7]
输出:49
來源: leetcode-cn.com/problems/co…
var maxArea = function(height) {
let i = 0, j = height.length - 1, max = 0, tmp;
while(i < j) {
if(height[i] < height[j]) {
tmp = (j - i) * height[i];
i++;
max = Math.max(max, tmp);
} else {
tmp = (j - i) * height[j];
j--;
max = Math.max(max, tmp);
}
}
return max;
};
二维数组中的查找
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
來源: leetcode-cn.com/problems/er…
var findNumberIn2DArray = function(matrix, target) {
if(matrix.length === 0) return false;
let row = matrix.length, col = matrix[0].length;
for (let i = 0, j = col - 1; i >= 0 && i < row && j >= 0 && j < col;) {
if(matrix[i][j] > target) {
j--;
} else if(matrix[i][j] < target) {
i++;
} else {
return true;
}
}
return false;
};
颜色分类
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
来源:leetcode-cn.com/problems/so…
var sortColors = function(nums) {
let i = 0, l = 0, r = nums.length - 1;
while(i <= r) {
if(nums[i] === 0) {
swap(nums, i++, l++)
} else if (nums[i] === 1) {
i++;
} else {
swap(nums, i, r--);
}
}
function swap(nums, i, j) {
let tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
};
部分排序
给定一个整数数组,编写一个函数,找出索引m和n,只要将索引区间[m,n]的元素排好序,整个数组就是有序的。注意:n-m尽量最小,也就是说,找出符合条件的最短序列。函数返回值为[m,n],若不存在这样的m和n(例如整个数组是有序的),请返回[-1,-1]。
输入: [1,2,4,7,10,11,7,12,6,7,16,18,19]
输出: [3,9]
来源:leetcode-cn.com/problems/su…
var subSort = function(array) {
if(array.length === 0) return [-1, -1];
let max = array[0];
let r = -1;
for (let i = 1; i < array.length; i++) {
if(array[i] >= max) {
max = array[i]
} else {
r = i;
}
}
if(r === -1) return [-1, -1];
let min = array[array.length - 1];
let l = -1;
for (let i = array.length - 2; i >= 0; i--) {
if(array[i] <= min) {
min = array[i]
} else {
l = i;
}
}
return [l, r]
};
快慢指针常见题--固定间距指针
定长子串中元音的最大数目
给你字符串 s 和整数 k 。 请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。 英文中的 元音字母 为(a, e, i, o, u)。
输入:s = "abciiidef", k = 3
输出:3
解释:子字符串 "iii" 包含 3 个元音字母。
来源: leetcode-cn.com/problems/ma…
var maxVowels = function(s, k) {
const strs = new Set(['a', 'e', 'i', 'o', 'u'])
let count = 0,
l = 0,
r = 0
while (r < k) {
strs.has(s[r]) && count++
r++
}
let max = count
while (r < s.length) {
strs.has(s[r]) && count++
strs.has(s[l]) && count--
l++
r++
max = Math.max(max, count)
}
return max;
};
乘积小于K的子数组
给定一个正整数数组 nums。找出该数组内乘积小于 k 的连续的子数组的个数。
输入: nums = [10,5,2,6], k = 100
输出: 8
解释: 8个乘积小于100的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于100的子数组。
来源: leetcode-cn.com/problems/su…
var numSubarrayProductLessThanK = function(nums, k) {
if(k <= 1) return 0;
let prod = 1, ans = left = right = 0;
while(right < nums.length) {
prod *= nums[right];
while(prod >= k) {
prod = prod / nums[left++]
}
ans += right - left + 1;
right++;
}
return ans
};
最小差
给定两个整数数组a和b,计算具有最小差绝对值的一对数值(每个数组中取一个值),并返回该对数值的差
输入:{1, 3, 15, 11, 2}, {23, 127, 235, 19, 8}
输出: 3,即数值对(11, 8)
来源: leetcode-cn.com/problems/sm…
var smallestDifference = function(a, b) {
a = a.sort((a, b) => a-b);
b = b.sort((a, b) => a-b);
let m = a.length, n = b.length;
let i = 0, j = 0;
let ans = Infinity;
while(i < m && j < n) {
if(a[i] === b[j]) {
return 0;
} else if(a[i] < b[j]) {
ans = Math.min(ans, b[j] - a[i]);
i++;
} else {
ans = Math.min(ans, a[i] - b[j]);
j++;
}
}
return ans;
};