链表刷题集合
160. 相交链表
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
//思路:1.找到两个链表中较大的那一个,然后让长链表先走他们的差距部,等到他们位于同一起点,然后两者
// 一起走,直到两者到达同一节点处和
//特殊情况的处理:
//1.两个链表的只想都为null,返回true,两个链表中只有一个指向为null,返回false
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
//1.两个头指针都为null
if(headA==NULL&&headB==NULL)
{
return headA;
}
//2.两个结点其中一个为空
if(headA==NULL||headB==NULL)
{
return NULL;
}
//3.排除特殊情况之后,我们就要开始处理正常情况
int LenA=0,LenB=0;
struct ListNode*curA=headA;
struct ListNode*curB=headB;
while(curA!=NULL)
{
LenA++;
curA=curA->next;
}
while(curB!=NULL)
{
LenB++;
curB=curB->next;
}
int n=abs(LenA-LenB);
if(LenA>LenB)
{
while(n--)
{
headA=headA->next;
}
}
else
{
while(n--)
{
headB=headB->next;
}
}
curA=headA;
curB=headB;
while(curA!=NULL&&curB!=NULL)
{
if(curA==curB)
{
return curA;
}
curA=curA->next;
curB=curB->next;
}
return NULL;
}
203. 移除链表元素
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
实例1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
实例2:
输入:head = [], val = 1
输出:[]
实例3:
输入:head = [7,7,7,7], val = 7
输出:[]
**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val){
//创建一个哨兵位的头节点
struct ListNode*pHead=(struct ListNode*)malloc(sizeof(struct ListNode));
//连接头节点和这个链表的元素
pHead->next=head;
//cur指向现在的头节点
struct ListNode*cur=pHead;
//prev作为前驱指针
struct ListNode*prev=NULL;
while(cur!=NULL)
{
//如果cur->val==val,执行删除操作,其中prev指向前驱指针,Next指向要删除元素的下一个地方
if(cur->val==val)
{
struct ListNode*Next=cur->next;
prev->next=Next;
free(cur);
cur=Next;
}
else
{
此时prev总是比cur少一个
prev=cur;
cur=cur->next;
}
}
return pHead->next;
}
/** * Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val){
//本题特殊情况:当头结点为val时
struct ListNode*cur=head;
struct ListNode*prev=NULL;
while(cur!=NULL)
{
struct ListNode*next=cur->next;
if(cur->val==val)
{
//当第一个节点为val时,代表prev为空,那我们直接释放这个节点,然后让head指针指向下一个元素
//然后让将下一个节点赋给cur
if(prev==NULL)
{
head=next;
free(cur);
cur=next;
}
//当第一个节点不为空时,我们使prev直接指向cur的下一个元素,释放cur,将next赋给cur
else
{
prev->next=next;
free(cur);
cur=next;
}
}
//如果该节点不是val,那么将cur赋给prev,然后cur指向下一个元素
else
{
prev=cur;
cur=cur->next;
}
}
return head;
剑指 Offer II 024. 反转链表
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
输入:head = [1,2]
输出:[2,1]
输入:head = []
输出:[]
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//判断头结点是否为空
if(head==nullptr)
return nullptr;
ListNode*cur=head;
//创建新的头结点
ListNode*NewHead=nullptr;
while(cur)
{
ListNode*next=cur->next;
//使当前节点的下一个元素指向新的头结点
cur->next=NewHead;
//使得cur成为新的头节点
NewHead=cur;
cur=next;
}
return NewHead;
}
};
148. 排序链表
输入:head = [4,2,1,3]
输出:[1,2,3,4]
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]
输入:head = []
输出:[]
class Solution {
public:
//链表的排序:归并排序
//1.找到中间节点
//2.将链表分为两部分
//3.实现有序链表的排序
ListNode*Sort(ListNode*&l,ListNode*&r)
{
//到这里就是排序的思想了,排序思想和合并两个有序链表的思想差不多
//创建新的节点,然后按照插入排序的思想,谁大谁就插入
ListNode*dummyHead=new ListNode(0);
ListNode*cur=dummyHead;
while(l!=nullptr&&r!=nullptr)
{
if(l->val<=r->val)
{
cur->next=l;
cur=cur->next;
l=l->next;
}
else
{
cur->next=r;
cur=cur->next;
r=r->next;
}
}
if(l!=nullptr)
{
cur->next=l;
}
if(r!=nullptr)
{
cur->next=r;
}
return dummyHead->next;
}
//传递参数时记得引用传递
ListNode*MergSort(ListNode*&head)
{
//判断头节点的下一个节点是否为空,如果是,直接返回头节点
if(head->next==nullptr)
return head;
//定义快慢指针,寻找中间节点
ListNode*slow=head;
ListNode*fast=head;
ListNode*prev=nullptr;
while(fast&&fast->next)
{
prev=slow;
slow=slow->next;
fast=fast->next->next;
}
//此时slow所在的位置就是中间节点所在的位置,prev指向slow的前一个节点
//我们人为的让链表从slow这个地方断开,head->prev是链表的前半段,slow到完是链表的右半部分
prev->next=nullptr;
//递归在左、右两端继续找中间节点
ListNode*l=MergSort(head);
ListNode*r=MergSort(slow);
//当分割的不能分割时,进行排序,最开始两两排序,到后来四四排序
return Sort(l,r);
}
ListNode* sortList(ListNode* head) {
//1.判断节点是否为空
if(head==nullptr)
return head;
//2.进入归并排序,寻找中间节点
return MergSort(head);
}
};
725. 分隔链表
给你一个头结点为 head 的单链表和一个整数 k ,请你设计一个算法将链表分隔为 k 个连续的部分。
每部分的长度应该尽可能的相等:任意两部分的长度差距不能超过 1 。这可能会导致有些部分为 null 。
这 k 个部分应该按照在链表中出现的顺序排列,并且排在前面的部分的长度应该大于或等于排在后面的长度。
返回一个由上述 k 部分组成的数组。
输入:head = [1,2,3], k = 5
输出:[[1],[2],[3],[],[]]
解释:
第一个元素 output[0] 为 output[0].val = 1 ,output[0].next = null 。
最后一个元素 output[4] 为 null ,但它作为 ListNode 的字符串表示是 [] 。
输入:head = [1,2,3,4,5,6,7,8,9,10], k = 3
输出:[[1,2,3,4],[5,6,7],[8,9,10]]
解释:
输入被分成了几个连续的部分,并且每部分的长度相差不超过 1 。前面部分的长度大于等于后面部分的长度。
思路:
1.求链表得节点个数
2.如果节点个数<K,说明节点个数不够分割,那么就让前面几个节点单独为一个,后面的都为NULL,凑齐k个
3.链表节点>K并且有剩余,那么我们就k个节点为一组,如果有剩余,那么剩余的节点每次多添加一个给前面的节点
class Solution {
public:
vector<ListNode*> splitListToParts(ListNode* head, int k) {
vector<ListNode*>ret;
//首先遍历得到链表的长度
//每k个一组进行分割,分别进入链表
ListNode*cur=head;
int sum=0;
while(cur)
{
sum++;
cur=cur->next;
}
int temp=sum/k;//判断每组有多少个节点
cur=head;
//temp==0说明链表节点少于K个,那么就将链表单独分割,不足的用nullptr代替
if(temp==0)
{
//此时说明k>sum,说明不够分
while(k--)
{
if(cur)
{
ListNode*next=cur->next;
cur->next=nullptr;
ret.push_back(cur);
cur=next;
}
else
{
ret.push_back(nullptr);
}
}
return ret;
}
//让cur重新指向头
cur=head;
//next用来记录cur得下一个位置
ListNode*next=cur->next;
//phead指向cur的起始位置,以便于后续将该节点加入到数组中
ListNode*phead=cur;
//判断有没有余数,如果有余数则一次让前面几个组的temp值+1
int remind=sum%k;
//k用来控制外层循环,即用来表示要分的组数
while(k--)
{
//里面即表示每组的成员
int val=temp;
if(remind!=0)
val+=1;
//从cur位置开始往后走val步即到达每个组的数量
while(--val)
{
cur=cur->next;
}
next=cur->next;
if(cur!=nullptr)
cur->next=nullptr;
//入数组
ret.push_back(phead);
//重新使cur指向下一个位置
cur=next;
//phead重新指向cur的起始位置
phead=cur;
//余数--
if(remind)
remind--;
}
return ret;
}
};
430. 扁平化多级双向链表
思路:将该图旋转一下,发现它非常像一颗二叉树,child相当于左孩子,next相当于右孩子,那么我们只要前序遍历这颗二叉树,就能得到正确答案
/*
// Definition for a Node.
class Node {
public:
int val;
Node* prev;
Node* next;
Node* child;
};
*/
class Solution {
public:
//创建全局变量,设置一个哨兵位置的头节点
Node*dummy=new Node();
Node*p=dummy;
void dfs(Node*&head)
{
//将head连接到p->next
p->next=new Node();
p->next->val=head->val;
p->next->prev=p;
p=p->next;
//递归遍历左子树
if(head->child)
{
dfs(head->child);
}
//递归遍历右子树
if(head->next)
{
dfs(head->next);
}
}
Node* flatten(Node* head) {
//判断头节点是否为空
if(head==nullptr)
return nullptr;
dfs(head);
//将最后一个节点的下一个位置置为nullptr
p->next=nullptr;
//将第一个节点与前面哨兵位置断开
dummy->next->prev=nullptr;
return dummy->next;
}
};
删除有序链表中重复的元素-I
删除给出链表中的重复元素(链表中元素从小到大有序),使链表中的所有元素都只出现一次
例如:
给出的链表为1->1->-2,返回1->2.
给出的链表为1->1->2->3->3,返回1→2→3.
数据范围:链表长度满足 0 \le n \le 1000≤n≤100,链表中任意节点的值满足 |val| \le 100∣val∣≤100
进阶:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
输入:
{1,1,2}
复制
返回值:
{1,2}
代码如下所示
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
class Solution {
public:
/**
*
* @param head ListNode类
* @return ListNode类
*/
ListNode* deleteDuplicates(ListNode* head) {
// write code here
if(head==nullptr||head->next==nullptr)
{
return head;
}
ListNode*cur=head;
while(cur&&cur->next)
{
ListNode*next=cur->next;
//前后双指针进行判断
if(cur->val==next->val)
{
cur->next=next->next;
delete next;
}
else
{
cur=cur->next;
}
}
return head;
}
};
删除有序链表中重复的元素-II
给出一个升序排序的链表,删除链表中的所有重复出现的元素,只保留原链表中只出现一次的元素。
例如:
给出的链表为1→2→3→3→4→4→5, 返回1→2→5.
给出的链表为1→1→1→2→3, 返回2→3.
数据范围:链表长度 0 \le n \le 100000≤*n*≤10000,链表中的值满足 |val| \le 1000∣*v**a**l*∣≤1000
要求:空间复杂度 O(n)*O*(*n*),时间复杂度 O(n)*O*(*n*)
进阶:空间复杂度 O(1)*O*(1),时间复杂度 O(n)*O*(*n*)
示例1
输入:
{1,2,2}
复制
返回值:
{1}
代码示例
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
class Solution {
public:
/**
*
* @param head ListNode类
* @return ListNode类
*/
ListNode* deleteDuplicates(ListNode* head) {
// write code here
//哈希映射,判断是否有重复值
//文中题目结点的val范围为|val|<=1000
//也就是-1000<=val<=1000
//我们人为映射统计每个节点出现的次数,然后判断是不是只出现了一次
//我们可以创建一个大小为2000的数组,为什么是2000呢,因为数组范围-1000,1000
//我们无法直接映射负数到数组中,每次让val+1000即可
//即-1000+1000<=val+1000<=1000+1000;
//一次遍历映射,判断是否出现一次,这样也符合题目要求的时间复杂度为O(N),空间负责都为O(1)
// write code here
//哈希映射,判断是否有重复值
int*arr=new int[2000];
ListNode*cur=head;
while(cur)
{
arr[cur->val+1000]++;
cur=cur->next;
}
cur=head;
//建立头结点
ListNode*NewHead=new ListNode(0);
ListNode*tail=NewHead;
while(cur)
{
if(arr[cur->val+1000]==1)
{
tail->next=cur;
tail=tail->next;
}
cur=cur->next;
}
tail->next=nullptr;
return NewHead->next;
}
};
//我们无法直接映射负数到数组中,每次让val+1000即可
//即-1000+1000<=val+1000<=1000+1000;
//一次遍历映射,判断是否出现一次,这样也符合题目要求的时间复杂度为O(N),空间负责都为O(1)
// write code here
//哈希映射,判断是否有重复值
int*arr=new int[2000];
ListNode*cur=head;
while(cur)
{
arr[cur->val+1000]++;
cur=cur->next;
}
cur=head;
//建立头结点
ListNode*NewHead=new ListNode(0);
ListNode*tail=NewHead;
while(cur)
{
if(arr[cur->val+1000]==1)
{
tail->next=cur;
tail=tail->next;
}
cur=cur->next;
}
tail->next=nullptr;
return NewHead->next;
}
};