常见的链表oj题:
目录
最后,以上就是全部内容了,希望不足的地方和方法有更好的地方可以在评论区一起讨论哦,欢迎大佬们进行讨论。
1.删除链表中等于给定值 val 的所有结点。
我们先进行思路分析,这道题我们可以先遍历,然后寻找链表中的数据域等于val的节点,然后对这个节点进行删除,也就是将上一个节点的指向下一个节点的的下一个节点。同时我们也要注意头节点这一个情况。
struct ListNode* removeElements(struct ListNode* head, int val)
{
//用两个节点
//当cur遇到val,prev直接跳到下一个
struct ListNode*prev=NULL,*cur=head;//prev表示的是cur节点的上一个节点
while(cur)
{
if(cur->val==val)
{
if(cur==head)//为头节点这一情况
{
head=cur->next;//将头节点移向下一个节点
free(cur);//删除当前节点
cur=head;//重新将cur节点置成头节点
}
else
{
prev->next=cur->next;//将prev节点指向cur的下一个节点,完成删除操作
free(cur);
cur=prev->next;
}
}
else
{
prev=cur;
cur=cur->next;//遍历整个链表
}
}
return head;
}
2.反转一个单链表
我们也进行简单的思路分析:我们可以建立一个新链表,将后面的节点不断移到新链表前面。
如果只定义两个节点,当n2指向n1时,n2的下一个结点就找不到了。因此要定义3个节点,n1和n2用于翻转,n3用于迭代:n1指向NULL,n2指向n1,n3用于记录n2的下一个结点。
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode*n1,*n2,*n3;
n1=NULL;
n2=head;
if(n2)
{
n3=n2->next;
}
while(n2)
{
n2->next=n1;//将后面的节点移向前面
n1=n2;
n2=n3;//翻转
if(n3)
{
n3=n3->next;
}
}
return n1;
}
我们观察,我们就发现了一个规律,他们是反着的,然后刚好满足头插法的效果。所以这道题我们可以用头插法。
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* cur = head;
struct ListNode* newhead = NULL;
while (cur)
{
struct ListNode* next = cur->next;
//头插
cur->next = newhead;
newhead = cur;
cur = next;
}
return newhead;
}
3.寻找链表的中间节点
思路分析:
这题我们可采用快慢节点的来进行解题,两者速度相差为1,到最后就相差一半,慢的刚好到中间位置
struct ListNode* middleNode(struct ListNode* head)
{
struct ListNode*fast=head,*slow=head;
while(fast&&fast->next)//两个错开,不同
{
slow=slow->next;
fast=fast->next->next;
}
return slow;
}
4.链表中倒数第k个结点
链表中倒数第k个结点_牛客题霸_牛客网 (nowcoder.com)
思路分析:
我们也可以同样设置两个快慢节点,一个节点先移动到第k个,然后两个节点同时移动,直到当fast下一个指向空时,slow就刚好指向倒数第k个节点。因为一开始slow和fast刚好相差k个,当fast下一个指向空时,slow就刚好指向倒数第k个节点。
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
//利用距离相差原理
struct ListNode*fast=pListHead,*slow=pListHead;
while(k--)//fast先走k个距离
{
if(fast==NULL)//fast大于k值
{
return NULL;
}
fast=fast->next;
}
while(fast)
{
fast=fast->next;
slow=slow->next;
}
return slow;
}
5.合并两个有序链表
思路分析
我们先建立一个新链表,再将两个链表分别进行比较,小的直接插入到新链表的后面。同时我们还要考虑一些问题其中一个为空时,比较完时,剩余部分的处理。
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
if(list1==NULL)//处理如果一个为空时
{
return list2;
}
if(list2==NULL)
{
return list1;
}
struct ListNode *head=NULL,*tail=NULL;//创建一个新链表
while(list1&&list2)//比较两者大小,插入到新链表后面。
{
if(list1->val < list2->val)
{
if(tail==NULL)
{
head=tail=list1;
}
else
{
tail->next=list1;
tail=tail->next;
}
list1=list1->next;
}
else
{
if(tail==NULL)
{
head=tail=list2;
}
else
{
tail->next=list2;
tail=tail->next;
}
list2=list2->next;
}
}
if(list1)//剩余部分的处理
tail->next=list1;
if(list2)
{
tail->next=list2;
}
return head;
}
6.链表分割问题
思路分析
我们先建立两个链表,一个比x大的链表,一个小于x的链表。我们需要不断进行比较,分别将数据保存在这两个链表中。最后链接这两个链表成一个链表也就是前面是小于x的链表后面是大于x的链表,返回这个链表的头节点。
class Partition {
public:
ListNode* partition(ListNode* pHead, int x) {
// write code here
struct ListNode*ghead,*gtail,*Lhead,*Ltail;
ghead=gtail=(struct ListNode*)malloc(sizeof(struct ListNode));//大于x的链表
Lhead=Ltail=(struct ListNode*)malloc(sizeof(struct ListNode));//小于x的链表
struct ListNode*cur=pHead;
//利用两个链表分别记录,在同时连接
while(cur)
{
if(cur->val<x)//分别保存
{
Ltail->next=cur;
Ltail=Ltail->next;
}
else
{
gtail->next=cur;
gtail=gtail->next;
}
cur=cur->next;
}
Ltail->next=ghead->next;//进行链接
gtail->next=NULL;//不空,出现损坏
struct ListNode*head=Lhead->next;
free(Lhead);//分别释放不然会内存泄漏
free(ghead);
return head;//返回连接后新头节点
}
};
7.链表回文
链表的回文结构_牛客题霸_牛客网 (nowcoder.com)
思路分析
我们先观察回文特点,我们不免发现回文中,后一半节点都是前一半的反转。所以我们应该先找到这这个链表中间节点。然后反转中间节点后一半的链表。与前一半链表进行比较,相同就是回文。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
struct ListNode* middleNode(struct ListNode* head)//中间节点函数
{
struct ListNode*fast=head,*slow=head;
while(fast&&fast->next)//两个错开,不同
{
slow=slow->next;
fast=fast->next->next;
}
return slow;
}
struct ListNode* reverseList(struct ListNode* head)//反转链表函数
{
struct ListNode*n1,*n2,*n3;
n1=NULL;
n2=head;
if(n2)
{
n3=n2->next;
}
while(n2)
{
n2->next=n1;
n1=n2;
n2=n3;
if(n3)
{
n3=n3->next;
}
}
return n1;
}
bool chkPalindrome(ListNode* A) {
//后半段逆置
struct ListNode *mid=middleNode(A);
struct ListNode*rmid=reverseList(mid);
while(rmid&&A)
{
if(rmid->val!=A->val)//判断两个对应是否相等
{
return false;
}
rmid=rmid->next;
A=A->next;
}
return true;
}
};
8.相交节点
思路分析
这道题我们可以用一下前面我们用到距离相差(快慢)的方法,先让他们能够在同一长度后同时比较,这样就可以知道他的每一个节点是否有相同的地方。
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
//计算a,b的长度
//长的链表走差距步abs(lenA-lenB)
//时间复杂度O(n)
struct ListNode *curA=headA,*curB=headB;
int lenA=1;
int lenB=1;
while(curA->next)
{
++lenA;
curA=curA->next;
}
while(curB->next)
{
++lenB;
curB=curB->next;
}
//不相交
if(curA!=curB)
{
return NULL;
}
int gap=abs(lenA-lenB);
struct ListNode *longlist=headA,*shortlist=headB;
if(lenA<lenB)
{
longlist=headB;
shortlist=headA;
}
//长的走差距步
while(gap--)
{
longlist=longlist->next;
}
//一起走
while(longlist!=shortlist)
{
longlist=longlist->next;
shortlist=shortlist->next;
}
return longlist;
}
9.环形链表
思路分析:
我们还是可以设置两个快慢节点,他们相差为1,如果在环中他们必定会相遇,相遇了就是环形链表。
bool hasCycle(struct ListNode *head) {
//理解为追及问题
struct ListNode*fast=head,*slow=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
return true;
}
}
return false;
}