您的位置:首页 > 编程语言 > Java开发

利用双指针技巧解决数组和字符串中的一些问题【Java】【leetcode】【双指针技巧】

2019-05-16 21:24 399 查看

双指针技巧 一

双指针技巧 —— 情景一
问题:反转数组中的元素。
分析:其思想是将第一个元素与末尾进行交换,再向前移动到下一个元素,并不断地交换,直到它到达中间位置。我们可以同时使用两个指针来完成迭代:一个从第一个元素开始,另一个从最后一个元素开始。持续交换它们所指向的元素,直到这两个指针相遇。
总结:总之,使用双指针技巧的典型场景之一是你想要从两端向中间迭代数组。
这时你可以使用双指针技巧:一个指针从始端开始,而另一个指针从末端开始。
值得注意的是,这种技巧经常在排序数组中使用。

反转字符串(344. Reverse String)

编写一个函数,其作用是将输入的字符串反转过来。
输入字符串以字符数组 char[] 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的
额外空间解决这一问题。
你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
示例 1:
输入:[“h”,“e”,“l”,“l”,“o”]
输出:[“o”,“l”,“l”,“e”,“h”]
示例 2:
输入:[“H”,“a”,“n”,“n”,“a”,“h”]
输出:[“h”,“a”,“n”,“n”,“a”,“H”]

class Solution {
public void reverseString(char[] s) {
int head = 0;
int tail = s.length - 1;
char temp;
while (head < tail) {
temp = s[head];
s[head] = s[tail];
s[tail] = temp;
head++;
tail--;
}
System.out.println(Arrays.toString(s));
}
}

数组拆分 I (561. Array Partition I)

给定长度为 2n 的数组, 你的任务是将这些数分成 n 对, 例如:
(a1, b1), (a2, b2), …, (an, bn) ,使得从1 到 n 的 min(ai, bi) 总和最大。
示例 1:
输入: [1,4,3,2]
输出: 4
解释: n 等于 2, 最大总和为 4 = min(1, 2) + min(3, 4).
提示:
n 是正整数,范围在 [1, 10000].
数组中的元素范围在 [-10000, 10000].

//分析:排序原数组然后对数组的奇数为求和并返回该值即可
//此处的排序方法选择了直接调用而非自己重写排序
class Solution {
public int arrayPairSum(int[] nums) {
Arrays.sort(nums);
int sum = 0;
for (int i = 0; i < nums.length; i += 2) {
sum += nums[i];
}
return sum;
}
}

两数之和 II - 输入有序数组(167. Two Sum II - Input array is sorted)

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:
输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

class Solution {
public int[] twoSum(int[] numbers, int target) {
int head = 0;
int tail = numbers.length - 1;

while (true) {
int sum = numbers[head] + numbers[tail];
if (sum == target) {
return new int[]{head + 1, tail + 1};
}
if (sum > target) {
tail--;
} else {
head++;
}
}
}
}

双指针技巧 二

双指针技巧 —— 情景二
有时,我们可以使用两个不同步的指针来解决问题
示例:给定一个数组和一个值,原地删除该值的所有实例并返回新的长度。
**分析:**如果我们没有空间复杂度上的限制,那就更容易了。我们可以初始化一个新的数组来存储答案。如果元素不等于给定的目标值,则迭代原始数组并将元素添加到新的数组中。
实际上,它相当于使用了两个指针,一个用于原始数组的迭代,另一个总是指向新数组的最后一个位置。
重新考虑空间限制
现在让我们重新考虑空间受到限制的情况。
我们可以采用类似的策略,我们继续使用两个指针:一个仍然用于迭代,而第二个指针总是指向下一次添加的位置。
总结:
这是你需要使用双指针技巧的一种非常常见的情况:
同时有一个慢指针和一个快指针。
解决这类问题的关键是
确定两个指针的移动策略。
与前一个场景类似,你有时可能需要在使用双指针技巧之前对数组进行排序,也可能需要运用贪心想法来决定你的运动策略。

移除元素 (27. Remove Element)

给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组< 22159 /strong>并在使用 O(1) 额外空间的条件下完成。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:

给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,1,2,2,3,0,4,2], val = 2,
函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
注意这五个元素可为任意顺序。
你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以**“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中
该长度范围内**的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}

class Solution {
public int removeElement(int[] nums, int val) {
if (nums.length == 0) {
return 0;
}
int fast;
int slow = 0;
for (fast = 0; fast < nums.length; fast++) {
if (nums[fast] != val) {
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}
}

最大连续1的个数 (485. Max Consecutive Ones)

给定一个二进制数组, 计算其中最大连续1的个数。
示例 1:
输入: [1,1,0,1,1,1]
输出: 3
解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3.
注意:
输入的数组只包含 0 和1。
输入数组的长度是正整数,且不超过 10,000。

class Solution {
public int findMaxConsecutiveOnes(int[] nums) {
//        新建一个数组记录连续一的个数,使用双指针技巧
int[] record = new int[nums.length];
int slow = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 1) {
record[slow] = 1;
slow++;
} else {
slow = 0;
}
}
//设置计数
int count = 0;
//便利原数组,遇到0停止,返回第一个0前的1的个数
for (int i = 0; i < record.length; i++) {
if (record[i] != 1) {
break;
} else {
count++;
}
}
return count;
}
}

长度最小的子数组 (209. Minimum Size Subarray Sum)

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。
示例:
输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。
进阶:
如果你已经完成了O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。

class Solution {
public int minSubArrayLen(int s, int[] nums) {
if (nums.length == 0) {
return 0;
}
if (nums[0] >= s) {
return 1;
}
//        当输入数组的长度大于等于二时,试着使用快慢指针计算最小的连续数组的长度
int fast = 1;
int slow = 0;
int max = nums.length - 1;
int res = max + 2;
int sum = nums[fast] + nums[slow];
while (fast < max) {
//            int sum = nums[fast] + nums[slow];
while (fast < max && sum < s) {
fast++;
sum += nums[fast];
}
while (sum >= s) {
sum -= nums[slow];
slow++;
}
res = res > (fast - slow + 2) ? (fast - slow + 2) : res;
}
return res == max + 2 ? 0 : res;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: