LeetCode 26:删除有序数组中的重复项
**题目链接:**https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/
**题目难度:**简单
**题目描述:**给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
1.暴力解法
类似数组 [1,2,2,3,4] 的数组。
- 通过对数组从第0个位置进行遍历,如果 第 i 个位置的元素与第1+ 1 个位置的元素相同则将第 i+1 后面的元素整体向前移动一位,同时将数组长度减一。
- 如果不相等则将指针向后移动。直到数组尾部为止。
代码实现如下:
public int removeDuplicates(int[] nums) {
/*
1. 对数组进行遍历,对 i 和 i + 1 进行比较
2. 如果两个数不相等则向后移动一位
3.如果两个数相等则将i+1后面的数向前移动一位,并且将数组长度减一
*/
int length = nums.length;
for (int i = 0; i < length-1; ) {
//1. 相邻两个数不相等则向后移动一位
if (nums[i] != nums[i+1]) {
i++;
} else {
//2.相邻的两个数相等,则进行移动数组
for (int j = i+1; j < length -1; j++) {
nums[j] = nums[j +1];
}
//数组长度减一
length--;
}
}
return length;
}
时间复杂度:O(n^2)
空间复杂度:O(1)
思考: 以上暴力解法是每次发现有重复元素后都进行移动数组后面的所有元素,但未必移动后的位置未必就是最终位置。那么是否一次性移动元素到目标位置?
2、双指针
分别记录移动元素和目标位置的索引
什么情况下使用双指针:
按照一定的规律同步发生变化,彼此影响的数据,可以采用双指针解法
步骤:
1.定义两个指针分别指向目标位和待移动元素为 : 初始目标位为0 ,待移动元素位为1
2.比较两个指针对应的数据
- 比较相等:目标位不变,待移动为加1
- 比较不等: 目标位加1 , 待移动元素复制到目标位;待移动位加1
细节问题:当目标位与待移动位相同,跳过赋值操作
//定义两个指针一个目标指针,一个待移动指针
int target = 0;
//i相当于移动指针
for (int i = 1; i < nums.length; i++) {
if (nums[target] != nums[i]) {
//如果目标位和移动位不相等则进行 数据交换 出现 情况 如 1,2,2,3 时会出现相等的情况
if (++target != i) {
nums[target] = nums[i];
}
}
}
return target +1;
3、类似题目
3.1、删除排序链表中的重复元素
题目连接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/
**题目难度:**简单
**题目描述:**存在一个按升序排列的链表,给你这个链表的头节点 head
,请你删除所有重复的元素,使每个元素 只出现一次 。
返回同样按升序排列的结果链表。
3.1.1、遍历
类似链表head = [1,1,2,3,3]
1、从链表头开始遍历,定义当前节点currNode 及 nextNode 如果二者不相等则统一向后移动一位,如果二者相等则将当前节点值的下一个指针指向 nextNode的下一个节点。
2、直nextNode为null为止
边界问题:
- head如果为空则直接返回
- 当currNode到最后一个节点时结束,即当nextNode为Null的时候停止遍历。
代码实现:
class Solution {
public ListNode deleteDuplicates(ListNode head) {
//如果为null直接返回
if(head == null) {
return head;
}
//定义两个指针,从头节点开始,间隔为1
ListNode currNode = head;
ListNode nextNode = head.next;
//判断边界
while (nextNode != null) {
//比较相邻的两个节点是否相等,如果相等则将currNode节点指向 NextNode 的下一个节点。将nextNode节点指向null
if (currNode.val == nextNode.val) {
ListNode tempNode = nextNode.next;
nextNode.next = null;
nextNode = tempNode;
currNode.next = nextNode;
} else {
//相邻两个节点不相等,分别向后移动一位
currNode = currNode.next;
nextNode = nextNode.next;
}
}
return head;
}
}
时间复杂度:O(n)
空间复杂度:O(1)
3.2、 删除排序链表中的重复元素 II
题目连接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/
**题目难度:**中等
题目描述
给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。
示例 1:
输入: 1->2->3->3->4->4->5
输出: 1->2->5
示例 2:
输入: 1->1->1->2->3
输出: 2->3
解题思路:
1.定义一个前置节点pre 指向 head防止头结点被删除
- 当前节点currNode = pre
- 判断currNode.next != null 并且 currNode.next.next != null进行遍历
- 如果 currNode.next的值和currNode.next.next 的值相等则记录该值
- 判断当 currNode.next 不为null 并且 currNode.next 的值与记录值相等则将指针向后移动
public ListNode deleteDuplicates(ListNode head) {
if (head == null){
return head;
}
ListNode pre = new ListNode(0,head);
ListNode curr = pre;
while (curr.next != null && curr.next.next != null) {
if (curr.next.val == curr.next.next.val) {
int value = curr.next.val;
while (curr.next != null && curr.next.val == value) {
curr.next = curr.next.next;
}
} else {
curr = curr.next;
}
}
return pre.next;
}
3.3、删除排序数组中的重复项 II
题目连接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array-ii/
**题目难度:**中等
**题目描述:**给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成
解题思路:
-
创建两个指针 初始位 slow 为2,待移动位为2
-
因为数组是连续的,所以可以比较 slow -2 与 待移动位是否相等,如果相等则说明slow位置的元素是被剔除的元素
两元素相等,所以待移动位向后移动一位。
两元素不相等,将待移动位 与 slow 位元素进行替换,初始化指针向后移动一位。
代码实现
public int removeDuplicates(int[] nums) {
int length = nums.length;
if (length <= 2) {
return length;
}
int slow = 2;
for (int i = 2; i < length; i++) {
if (nums[slow-2] != nums[i]) {
nums[slow++] = nums[i];
}
}
return slow;
}
3.4、移除元素
题目连接:https://leetcode-cn.com/problems/remove-element
**题目难度:**简单
**题目描述:**给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
解题思路:
- 双指针
定义两个指针 一个在数组头 i=0 ;一个在数组尾部 j= length -1
将与val相同的值放在数组尾部。不相等的放在数组头部。
即 判断 nums[i] 与 val是否相等,如果相等则 nums[i] = nums[j] 同时 j向 数组头移动,i保持不变
如果nums[i] 与val不相等,则i向后移动,知道 i >= j的时候结束遍历
最终数组 j 前面的数值为与目标值不相等数值,所以返回时返回长度为j +1
public int removeElement(int[] nums, int val) {
int j = nums.length - 1;
for (int i = 0; i <= j; i++) {
if (nums[i] == val) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
i--;
j--;
}
}
return j+1;
}
- 通用解法
定义下标idx = 0;
对数组进行遍历 取出值x,对x 与 val进行比较
如果x 与 val不相等,则将 x放到idx的位置上,并且 idx++
如果x 与 val相等,则 idx保持不变, 数组继续向后遍历。
该方法即为挑选出想要的元素。
class Solution {
public int removeElement(int[] nums, int val) {
int idx = 0;
for(int x : nums) {
if(x != val) {
nums[idx++] = x;
}
}
return idx;
}
}