题目
删除有序数组中的重复项
题目入口
题目内容
给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:
更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
返回 k 。
节选部分内容,建议看原题
思路
这种思路我之前也讲过讲过类似的双指针问题,这里指针不是真指针,而是表示数组下标的一种形象的说法
一般来说,我们要先分成两个数组模拟来思考思路的,但是本题要是先分两个数组来考虑,要用到三指针,所以再试着思考一下下一步的步骤复杂度,下一步我们是要在原数组进行思考,我们还是设立两个指针,一个叫big,另一个叫small(本人习惯,big是大哥,small是小弟).
big负责遍历数组,不对原数组进行干涉
small负责修改数组
不想好一个遍历的指针在后面判断跳出循环容易搞混的
过程就是在big指针遍历的过程中,判断small与big各自指向的元素是否相同,因为我们要找不重复的元素,所以当small和big指向的元素不相同的时候,我们要先++small,因为你不加加,small最开始指向的是第一个元素会被覆盖,所以要先++small覆盖数组第二个位置(原题的nums[1])
代码
big指针用来遍历,在代码中初始值应该是0,但是因为small和big都是第一个位置肯定相等,big++为1,这里就直接上来赋值1,也可以在思路上从第二个位置考虑,都行,这里提醒一下
c版本
int removeDuplicates(int* nums, int numsSize)
{
int big = 1;
int small = 0;
while(big<numsSize)
{
if(nums[big]!=nums[small])
nums[++small] = nums[big];
big++;
}
return small+1;
}
c嘎嘎版本
class Solution {
public:
int removeDuplicates(vector<int>& nums)
{
int big = 1;
int small = 0;
while(big<nums.size())
{
if(nums[big]!=nums[small])
{
nums[++small] = nums[big];
}
big++;
}
return small+1;
}
};
合并两个有序数组
题目链接
题目内容
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
思路
最终排序的有序数组要在nums1中进行,本题我们采用从后往前的对两个数组进行遍历挑选,采用从前往后的思路是要挪动数据的,时间复杂度就上来了,而且麻烦,所有我们不妨换种思路,从后往前来考虑
我们从后往前考虑要注意对两个数组边界进行考虑,无非是nums1数组有效元素遍历完或者nums2有效元素遍历完
第一种情况:nums1有效元素遍历完,nums2仍然有剩余元素,所以我们继续从后往前在nums1数组进行覆盖即可
第一个数组指向的动态下标我们叫end1,第二个叫end2,在end1后面是我们处理过的元素,end1前面是未处理的元素,又因为本身数组的有序,所以在我们遍历完后,即便end2的数据覆盖完,end1没走到0位置,也是非递减排序
第二种情况:nums2遍历完,我刚才说过两个数组都是有序数组,所以在nums2遍历之后nums1已经是非递减状态,可以自己尝试画图看一下
代码
c版本(c嘎嘎版本与c版本内容一样)
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
int end1 = m-1;
int end2 = n-1;
int num = m+n-1;
while(end1>=0&&end2>=0)
{
if(nums2[end2]>nums1[end1])
{
nums1[num] = nums2[end2];
num--;
end2--;
}
else
{
nums1[num] = nums1[end1];
num--;
end1--;
}
}
while(end2>=0)
{
nums1[num] = nums2[end2];
num--;
end2--;
}
}
移除链表元素
题目链接
题目内容
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
思路1
第一种思路就是最直接的思路删除,而单链表的删除要记得站在本节点的上一个才能对本节点进行删除,所以我们要处理两个问题,第一个要在针对节点时分情况讨论头节点和其他节点的问题,第二个我前面提过要站在节点前面才能对本节点进行修改所以我们要使用两个动态指针来标记前一个节点和该节点
代码1
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* big=head,*small=NULL;
//big还是本人习惯的方式,大哥指针,用来
//遍历链表,small就是小弟指针,用来记录前一个
//节点的地址
while(big)
{
//第一层判断就是判断big指针里的数值
//是不是所要删除的数值
//如果是的就进行删除操作
//不是的话就跳进下一个
if(big->val==val)
{
//第二层循环判断头节点等于删除数值的情况
//如果是头节点是删除数值会直接跳入这个
//循环,不会让small小弟指针记录
//当然,头节点也没有前节点,所以就要
//特殊考虑
if(small==NULL)
{
//head头节点是删除数据的情况下
//你还要更新头节点,让头节点
//指向下一个节点
//此处可以解决一直出现删除数据的
//情况,比如你要删除1,然后数据给的
//是1 1 1 2,这个代码可以一直把头节点
//更新到第一个头节点非删除值得情况
big = head->next;
free(head);
head=big;
}
else
{
//删除操作
small->next = big->next;
free(big);
big=small->next;
}
}
else
{
small=big;
big=big->next;
}
}
return head;
}
思路2
链表删除
使用tail指针来遍历链表
tail判断是不是删除的对应数值,如果是就跳过,如果不是就把next指针指向他,并跳到下一个节点
newnode作为新链表(删除后的链表)
newnode赋值的是head的地址,刚开始newnode指向的就是原链表,为了方便理解,所以我把在录视频的时候分成两个部分来更好的理解这个思路
代码2
struct ListNode* removeElements(struct ListNode* head, int val)
{
struct ListNode* newnode=NULL,* tail=NULL;
while(head!=NULL)
{
if(head->val==val)
{
/*
当节点指向的是被删除的对象时,就要跳过这个节点,指向下一个节点来达到删除的目的
*/
head=head->next;
}
else
{
/*
在链表不是空链表时,对newnode初始化,指向原链表
*/
if(newnode==NULL)
{
newnode=head;
tail=newnode;
}
else
{
/*
在head指向不是删除元素的时候,
用tail进行连接该节点,并跳到下一个
节点
*/
tail->next=head;
tail=tail->next;
}
/*
head指向下一个节点
tail的next指针要置为空,因为这个思路是让newnode指向新链表新的头节点,利用tail不断向后遍历链表来连接非删除值得节点,而你tail最后一个不置为NULL,那么在调用该函数得时候,无法判断该链表的结束节点,tail->next最后一次变成野指针
*/
head=head->next;
tail->next=NULL;
}
}
head=newnode;
return head;
}
思路3
第三种思路与第一种思路类似,是先对头节点的情况来讨论,然后让头节点是一个非删除值的节点,然后进行正常的删除操作
代码3
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val)
{
//判断是不是空链表
if(head==NULL) return head;
//非空链表解决一连串的删除值头节点的情况
while(head->next!=NULL&&head->val==val)
{
head=head->next;
}
//看看在删除完一连串删除值头节点的情况下,
//是否是空链表
if(head->next==NULL&&head->val==val)
return NULL;
//头节点非删除值的节点,开始正常的删除操作
struct ListNode* p = head;
while(p->next)
{
if(p->next->val==val)
{
struct ListNode* tem = p->next;
p->next = tem->next;
free(tem);
}
else
{
p = p->next;
}
}
return head;
}