双指针算法是我们在学习中常见的一种算法,指的是在遍历元素的过程中,不是使用单个指针进行访问,而是使用两个指针进行访问,从而达到相应的目的。按照分类可以分为对撞指针、快慢指针和分离双指针。我们利用双指针,可以改善时间复杂度,暴力算法的时间复杂度往往是 O(n*n)
。而双指针利用了区间「单调性」的性质,可以将时间复杂度降到 O(n)
。
对撞指针:指的是两个指针 left
、right
分别指向序列第一个元素和最后一个元素,然后 left
指针不断递增,right
不断递减,直到两个指针的值相撞(即 left == right
),或者满足其他要求的特殊条件为止。
left
,right
。left
指向序列第一个元素,即:left = 0
,right
指向序列最后一个元素,即:right = len(nums) - 1
。left += 1
。当满足另外一定条件时,将右指针左移,right -= 1
。left == right
),或者满足其他要求的特殊条件时,跳出循环体。
int left = 0;
int right = nums.length - 1;
while (left < right) {
if (满足要求的特殊条件) {
return 符合条件的值;
} else if (一定条件 1) {
left++;
} else if (一定条件 2) {
right--;
}
}
return 没找到 或 找到对应值;
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
示例 2:
输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]
提示:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums
已按 非递减顺序 排序进阶:
O(n)
的算法解决本问题思路解析:数组为非递减顺序排列,那么代表可能会存在相同的元素。我们可以直接暴力破解,直接将所有的元素进行平方,然后进行排序。代码如下。
import java.util.Arrays;
class Solution {
public int[] sortedSquares(int[] nums) {
for(int i = 0 ; i < nums.length ;i++){
nums[i] = nums[i] * nums[i];
}
Arrays.sort(nums);
return nums;
}
}
进阶:对于这道题,我们可以直接使用对撞指针来求解,一个指针从前面遍历,一个指针从后面遍历,然后进行比较,如果前面的元素平方大于后面元素的平方那就交换位置。时间复杂度为时间复杂度为 O(n)
class Solution {
public int[] sortedSquares(int[] nums) {
int len = nums.length;
int[] result = new int[len];
int left = 0, right = len - 1;
int i = n - 1;
while (left <= right) {
int leftNum = nums[left] * nums[left];
int rightNum = nums[right] * nums[right];
if (leftNum > rightNum) {
result[i--] = leftNum;
left++;
} else {
result[i--] = rightNum;
right--;
}
}
return result;
}
}
给你一个下标从 1 开始的整数数组 numbers
,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target
的两个数。如果设这两个数分别是 numbers[index1]
和 numbers[index2]
,则 1 <= index1 < index2 <= numbers.length
。
以长度为 2 的整数数组 [index1, index2]
的形式返回这两个整数的下标 index1
和 index2
。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
你所设计的解决方案必须只使用常量级的额外空间。
示例 1:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
示例 2:
输入:numbers = [2,3,4], target = 6
输出:[1,3]
解释:2 与 4 之和等于目标数 6 。因此 index1 = 1, index2 = 3 。返回 [1, 3] 。
示例 3:
输入:numbers = [-1,0], target = -1
输出:[1,2]
解释:-1 与 0 之和等于目标数 -1 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
提示:
2 <= numbers.length <= 3 * 104
-1000 <= numbers[i] <= 1000
numbers
按 非递减顺序 排列-1000 <= target <= 1000
思路解析:给出的数组还是非递减顺序排列,要找到满足相加之和等于目标数 target
的两个数,我们可以从两边分别开始遍历,如果得到的结果小于目标值,那么left++,否则right++,注意返回的为数组形式。
class Solution {
public int[] twoSum(int[] numbers, int target) {
int left = 0;
int right = numbers.length - 1;
while (left < right) {
int sum = numbers[left] + numbers[right];
if (sum == target) {
return new int[]{left+1, right+1};
} else if (sum < target) {
left++;
} else {
right--;
}
}
return new int[0]; // 返回一个空数组
}
}
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串 s
,如果它是 回文串 ,返回 true
;否则,返回 false
。
示例 1:
输入: s = "A man, a plan, a canal: Panama"
输出:true
解释:"amanaplanacanalpanama" 是回文串。
示例 2:
输入:s = "race a car"
输出:false
解释:"raceacar" 不是回文串。
示例 3:
输入:s = " "
输出:true
解释:在移除非字母数字字符之后,s 是一个空字符串 "" 。
由于空字符串正着反着读都一样,所以是回文串。
提示:
1 <= s.length <= 2 * 105
s
仅由可打印的 ASCII 字符组成思路解析:我们首先要把输入的字符串转化为只含有小写字母的字符串,然后进行左右指针开始遍历,主要是对于字符串库函数的应用。
class Solution {
public boolean isPalindrome(String s) {
StringBuffer sgood = new StringBuffer();
int length = s.length();
for (int i = 0; i < length; i++) {
char ch = s.charAt(i);
if (Character.isLetterOrDigit(ch)) {
sgood.append(Character.toLowerCase(ch));
}
}
int n = sgood.length();
int left = 0, right = n - 1;
while (left < right) {
if (Character.toLowerCase(sgood.charAt(left)) != Character.toLowerCase(sgood.charAt(right))) {
return false;
}
++left;
--right;
}
return true;
}
}
快慢指针:指的是两个指针从同一侧开始遍历序列,且移动的步长一个快一个慢。移动快的指针被称为 「快指针(fast)」,移动慢的指针被称为「慢指针(slow)」。两个指针以不同速度、不同策略移动,直到快指针移动到数组尾端,或者两指针相交,或者满足其他特殊条件时为止。
slow
、fast
。slow
一般指向序列第一个元素,即:slow = 0
,fast
一般指向序列第二个元素,即:fast = 1
。slow += 1
。当满足另外一定条件时(也可能不需要满足条件),将快指针右移,即 fast += 1
。fast == len(nums) - 1
),或者两指针相交,或者满足其他特殊条件时跳出循环体。int slow = 0;
int fast = 1;
while (fast < arr.length) {
if (/* 满足要求的特殊条件 */) {
slow++;
}
fast++;
}
return //返回合适的值
给你一个 非严格递增排列 的数组 nums
,请你** 原地** 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums
中唯一元素的个数。
考虑 nums
的唯一元素的数量为 k
,你需要做以下事情确保你的题解可以被通过:
nums
,使 nums
的前 k
个元素包含唯一元素,并按照它们最初在 nums
中出现的顺序排列。nums
的其余元素与 nums
的大小不重要。k
。判题标准:
系统会用下面的代码来测试你的题解:
int[] nums = [...]; // 输入数组
int[] expectedNums = [...]; // 长度正确的期望答案
int k = removeDuplicates(nums); // 调用
assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
assert nums[i] == expectedNums[i];
}
如果所有断言都通过,那么您的题解将被 通过。
示例 1:
输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
示例 2:
输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。
提示:
1 <= nums.length <= 3 * 104
-104 <= nums[i] <= 104
nums
已按 非严格递增 排列思路解析:题目要求我们原地删除出现的重复元素,定义两个快慢指针。然后通过循环遍历整个数组,比较两个指针所指向的元素是否相等。如果不相等,将第二个指针所指向的元素赋值给第一个指针的下一个元素,并将第一个指针向后移动一位。如果相等,则只将第二个指针向后移动一位。最后返回第一个指针所指向的索引加一,即为去重后的数组长度。
class Solution {
public int removeDuplicates(int[] nums) {
if (nums.length == 0) {
return 0;
}
int show = 0;
int fast = 1;
while (fast < nums.length) {
if (nums[show] != nums[fast]) {
show++;
nums[show] = nums[fast];
}
fast++;
}
return show + 1;
}
}