双指针
从下面序列中删除重复元素[1,2,2,2,3,3,3,5,5,7,8],重复元素只保留个。删除之后的结果应该为[1,2,3,5,7,8]。
我们如果每一次都是通过整体移动的话,那样效率实在是太低了。我们可以尝试使用 双指针 ------ 简单且高效
的方式
这样之后 slow 之前的值就满足条件啦!
删除元素专题
原地移除所有数值等于val的元素
快慢双指针
public int removeElement(int[] nums, int val) {
int slow = 0;
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != val) {
// 这里的代码相当于
// nums[slow] = nums[fast];
// slow++
nums[slow++] = nums[fast];
}
}
return slow;
}
对撞双指针
核心思想是: 使用右边的不是 val 的值和左边是 val 的值互换
class Solution {
public int removeElement(int[] nums, int val) {
// 初始化两个指针
int left = 0;
// 索引是从 0 开始的,所以需要在 数组长度减去 1
int right = nums.length - 1;
while (left <= right) {
if ((nums[left] == val) && (nums[right] != val)) {
// 使用中间变量进行交换
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
// 如果这个索引位置的值不是 val ,那么就需要继续向后遍历
if (nums[left] != val) {
left++;
}
// 如果这个索引位置的值是 val 那么也不行需要向前面遍历
if (nums[right] == val) {
right--;
}
}
return left;
}
}
对撞双指针+覆盖
结合上面两种方式
public static int removeElement3(int[] nums, int val) {
int right = nums.length - 1;
for (int left = 0; left <= right; ) {
// 这里注意一点,那就是尽管 right 的值可能就是 val ,但是是也没问题,因为如果是的话那么 left 这个指针是不移动的!
if (nums[left] == val) {
nums[left] = nums[right];
right--;
} else {
left++;
}
}
return right + 1;
}
删除有序数组中的重复项
普通双指针
public int removeDuplicates(int[] nums) {
// 定义一个慢指针
int slow = 0;
// 快指针在 for 循环之中,所以所以一定要注意 里面不要有 ++fast 的操作
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != nums[slow]) {
nums[++slow] = nums[fast];
}
}
return slow + 1;
}
元素奇偶移动专题
题目 LeetCode 905
图解
具体代码
class Solution {
public int[] sortArrayByParity(int[] nums) {
int left = 0, right = nums.length - 1;
while (left < right) {
if ((nums[left] % 2 != 0) && (nums[right] % 2 == 0)) {
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
if (nums[left] % 2 == 0) {
left++;
}
if (nums[right] % 2 != 0) {
right--;
}
}
return nums;
}
}
数组轮转问题
题目:189. 轮转数组
解决方案
- 一个一个的移动 ❌
- 整体反转 ✔️
举个具体的例子 比如:nums = [1,2,3,4,5,6,7], k = 3
第一次整体翻转
结果是 [7, 6, 5, 4, 3, 2, 1]
第二次反转 从索引位置
的 0
开始到k - 1
结果是 [5, 6, 7, 4 ,3, 2, 1]
第三次反转 从 索引位置
的 k
开始到nums.length - 1
结果是 [5, 6, 7, 1, 2, 3, 4]
这里面有一个 bug, 如果这个 k 的值比 nums.length 还大怎么办 ?
其实当 k > nums.length
class Solution {
public void rotate(int[] nums, int k) {
int len = nums.length - 1;
// 这里多出的几次和数组长度相同,其实没有实际的意义
// 所以直接取余数就好了
k %= len + 1;
reverse(nums, 0, len);
reverse(nums, 0, k - 1);
reverse(nums, k , len);
}
/**
反转数组
*/
public void reverse(int[] nums, int start, int end) {
while (start < end) {
int temp = nums[start];
nums[start] = nums[end];
nums[end] = temp;
start++;
end--;
}
}
}
数组的区间专题
题目:
图示解析:
具体代码:
public List<String> summaryRanges(int[] nums) {
List<String> res = new ArrayList<>();
// slow 初始指向第 1 个区间的起始位置
int slow = 0;
for (int fast = 0; fast < nums.length; fast++) {
// fast 向后遍历,直到不满足连续递增(即 nums[fast] + 1 != nums[fast + 1])
if (fast + 1 == nums.length || nums[fast] + 1 != nums[fast + 1]) {
// 将当前区间 [slow, fast] 写入结果列表
StringBuilder sb = new StringBuilder();
sb.append(nums[slow]);
if (slow != fast) {
sb.append("->").append(nums[fast]);
}
res.add(sb.toString());
// 将 slow 指向更新为 fast + 1,作为下一个区间的起始位置
slow = fast + 1;
}
}
return res;
}
字符串替换空格问题
- 方式一
转化为 char 数组
class Solution {
public String replaceSpace(String s) {
// 传统方式
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == ' ') {
sb.append("%20");
} else {
sb.append(c);
}
}
return sb.toString();
}
}
- 方式二
双指针
class Solution {
public String replaceSpace(String s) {
StringBuilder str = new StringBuilder(s);
// 双指针
// 记录有多少空格
int nullNum = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == ' ') {
nullNum++;
}
}
// 需要在数组上修改,长度
str.setLength(s.length() + 2 * nullNum);
// 定义两个快慢指针
int slow = s.length() + 2 * nullNum - 1;
int fast = s.length() - 1;
while (fast >= 0 && (slow > fast)) {
char c = str.charAt(fast);
if (c == ' ') {
// 这个相当于
// str.setCharAt(slow, '0');
// slow--;
str.setCharAt(slow--, '0');
str.setCharAt(slow--, '2');
str.setCharAt(slow--, '%');
fast--;
} else {
str.setCharAt(slow, s.charAt(fast));
slow--;
fast--;
}
}
return str.toString();
}
}