题目均来自于牛客或者力扣
一.移除链表元素
1. 题目描述

2.思路分析
题目中描述说明删除所有符合条件的节点,因此需要把整个链表都遍历一遍,然后再删除其中的元素,释放内存,但是删除之前,需要把删除位置前的节点指向改变一下。
3.代码实现
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode* cur=head;
struct ListNode* pre=NULL;
while(cur)
{
if(cur->val==val)
{
//若头节点的元素的值就符合条件
if(cur==head)
{
//需要更新头节点,头节点没有前一个
head=cur->next;
free(cur);
cur=head;
}
else
{
//更改指向
pre->next=cur->next;
free(cur);//释放内存
cur=pre->next;
}
}
else
{
//迭代,pre为cur前面一个节点
pre=cur;
cur=cur->next;
}
}
return head;
}
二.反转链表
1.题目描述

2.思路分析
反转链表可以通过改变链表节点的指向,因此从后往前改变指向,需要迭代到最后一个节点,注意链表为空的情况。
3.代码实现
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
if(head==NULL)
{
return NULL;
}
struct ListNode* pre=NULL;
struct ListNode* cur=head;
struct ListNode* next=head->next;
while(cur!=NULL)
{
cur->next =pre;//反转指向
pre=cur;//更新pre节点的地址
cur=next;//更新cur节点的地址
if(next!=NULL)
{
next=next->next; //迭代遍历
}
}
return pre;//返回反转完后的地址,该地址为链表的首地址
}
三.链表的中间结点
1.题目描述

要求只可以遍历链表一次。
2.思路分析
设置两个指针,一个走的快,一个走的慢,快的速度是慢的两倍,当快指针遍历到结束后,慢指针就遍历到链表中间。
需要讨论奇数个结点和偶数个结点的情况
若是奇数结点,当fast->next为NULL时,slow为中间结点
若是偶数结点,当fast为NULL时,slow为第二个中间结点

3.代码实现
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* middleNode(struct ListNode* head){
struct ListNode *fast,*slow;
fast=slow=head;
while(fast&&fast->next)
{
//迭代
fast=fast->next->next;
slow=slow->next;
}
return slow;
}
四.链表中倒数第k个结点
1.题目描述

2.思路分析
创建两个指针变量,fast先走k步,之后两个指针同时迭代,直到fast一直迭代到尾结点,此时slow就是倒数第k个结点。
注意k为0,则返回NULL

3.代码实现
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
*
* @param pListHead ListNode类
* @param k int整型
* @return ListNode类
*/
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
struct ListNode* fast;
struct ListNode* slow;
fast = slow = pListHead;
//讨论特殊情况
if(k==0)
return NULL;
//先走k步
while (k--) {
if (fast) {
fast = fast->next;
}
else {
return NULL;
}
}
//同时迭代
while (fast) {
fast = fast->next;
slow = slow->next;
}
return slow;
}
五.合并两个有序链表
1.题目描述
注意是升序链表

题目 要求只遍历一遍链表
2.思路分析
若在原链表中进行求解,两个循环遍历的暴力解法的时间复杂度为O(n*m)。
考虑到两个链表是升序链表,显然我们需要创建第三个链表,把两个链表的元素按照大小排序尾插到第三个链表中。
注意要考虑到链表为空的情况

3.代码实现
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
//创建新链表的头结点
struct ListNode* head=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* tail;//更新尾插
head=tail=NULL;//初始化
//空链表合并
if(list1==NULL)
{
return list2;
}
if(list2==NULL)
{
return list1;
}
//当其中一个链表遍历走到尾时结束
while(list1&&list2)
{
//比较两个链表元素的大小
if(list1->val>list2->val)
{
if(head==NULL)
{
head=tail=list2;
}
else
{
//尾插,迭代
tail->next=list2;
tail=list2;
}
list2=list2->next;
}
else
{
if(head==NULL)
{
tail=head=list1;
}
else
{
//尾插,迭代
tail->next=list1;
tail=list1;
}
list1=list1->next;
}
}
//如果其中一个链表先遍历完了,则直接尾插另外一个链表中的所有元素
if(list1)
{
tail->next=list1;
}
if(list2)
{
tail->next=list2;
}
return head;
}
六.链表分割
1.题目描述
现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。
2.思路分析
将链表分割为两部分,一部分比x高,一部分比x低。
需要创建两个新链表,遍历原链表,一个存储比x高的,一个存储比x低的,链表中元素的相对位置因此不变,之后再拼接两个链表
3.代码实现
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {
public:
ListNode* partition(ListNode* pHead, int x) {
struct ListNode* lesstail, *greattail, *greathead, *lesshead;
//创建两个新链表
lesshead = greattail = (struct ListNode*)malloc(sizeof(struct ListNode));
greathead = lesstail = (struct ListNode*)malloc(sizeof(struct ListNode));
//初始化
lesshead = lesstail;
greathead = greattail;
lesstail->next = nullptr;
greattail->next = nullptr;
struct ListNode* cur = pHead;
while (cur) {
if (cur->val < x) {
lesstail->next = cur;
lesstail = cur;
} else {
greattail->next = cur;
greattail = cur;
}
//遍历 迭代
cur = cur->next;
}
//拼接两个链表
greattail->next = nullptr;
lesstail->next = greathead->next;
//拼接后的链表的新表头
struct ListNode* newhead = lesshead->next;
//删除旧表头,释放内存
free(lesshead);
free(greathead);
return newhead;
}
};
七.链表的回文结构
1.题目描述

2.思路分析
创建两个指针,第一个指针从头开始遍历到中间结点,第二个指针从中间开始遍历到尾部结点,在第二个指针开始遍历前,需要把第二部分的链表反转一下。之后再遍历。
反转链表函数和取中间结点的链表函数
注意,取中间结点链表,若结点为偶数个,取的是第二个中间结点
**特别地,当结点为奇数个时,两边当遍历到其倒数第二个结点时,遍历的最后一个结点是一样的,之后就遍历到NULL了,因为第一个中间结点的指向并没有改变。
3.代码实现
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
//反转链表
struct ListNode* reverseList(struct ListNode* head) {
//链表为空 返回NULL
if (head == NULL) {
return NULL;
}
struct ListNode* pre = NULL;
struct ListNode* cur = head;
struct ListNode* next = head->next;
while (cur != NULL) {
cur->next = pre;
pre = cur;
cur = next1;
if (next != NULL) {
next = next->next;
}
}
return pre;
}
//取中间结点链表
struct ListNode* middleNode(struct ListNode* head) {
struct ListNode* fast, *slow;
fast = slow = head;
while (fast && fast->next) {
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
class PalindromeList {
public:
bool chkPalindrome(ListNode* A) {
struct ListNode* mid = middleNode(A);
struct ListNode* rhead = reverseList(mid);
struct ListNode* cur1 = A;
struct ListNode* cur2 = rhead;
//同时遍历,直到cur1遍历到头结束
while (cur1 && cur2) {
//若不同,则不回文返回false
if (cur1->val != cur2->val) {
return false;
} else {
cur1 = cur1->next;
cur2 = cur2->next;
}
}
return true;
}
};
八.相交链表
1.题目描述

2.思路分析
第一步,判断两个链表是否是相交的
若相交,则返回相交结点
不相交,返回null
判断相交,分别创建两个链表的起始指针—————两个指针遍历到最后的结点是否相同,若相同,则相交。
第二步,如何找到相交结点
假设相交的前提下,记录两个指针走到最后走过的步数,假设A指针走了a步,B指针走了b步,若a>b
则让longlist先走a-b步,之后longlist和shortlist同时走,直到longlist==shortlist为止,此时的longlist或者shortlist就是相交结点。
3.代码实现
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode* tailA;
struct ListNode* tailB;
tailA=headA;
tailB=headB;
//创建两个变量记录步数
int a=0;
int b=0;
while(tailA->next)
{
tailA=tailA->next;
a++;
}
while(tailB->next)
{
tailB=tailB->next;
b++;
}
//判断是否相交
if(tailA!=tailB)
{
return NULL;
}
struct ListNode* shortlist=headA;
struct ListNode* longlist=headB;
//判断是headA长,还是headB长
if(a>b)
{
shortlist=headB;
longlist=headA;
}
int gap=abs(a-b);
//长链表先走gap步
while(gap--)
{
longlist=longlist->next;
}
//同时走
while(shortlist!=longlist)
{
shortlist=shortlist->next;
longlist=longlist->next;
}
return shortlist;
}
九.环型链表
1.题目描述

二.思路分析
若存在环,则走的快的指针一定比走的慢的指针在环中相遇
则创建fast和slow指针,fast每次走两步,slow每次走一步
当fast与slow在环中相遇则返回true,若fast走到NULL,则不存在环
三.代码实现
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head) {
struct ListNode* fast;
struct ListNode* slow;
fast=slow=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
return true;
}
}
return false;
}
十.环形链表进阶
1.题目描述

2.思路分析
假设存在环,环的长度c,入环的第一个结点与环的初始结点长度为l,相遇结点与入环第一个结点相距为a
fast先于slow走到环内,因此当slow与fast相遇后,fast实际上走的是nc+l+a,slow为则为l+a。
可知fast=2slow 推导出l=nc-a=(n-1)c+c-a

可以知道,相交结点再走c-a步就回到入环的第一个节点了
故让slow与fast相交结点meet与链表头节点cur=head同时走,他们两个的相交结点就是入环的第一个结点
3.代码实现
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode* fast;
struct ListNode* slow;
fast=slow=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
//找fast与slow的相交结点meet
if(fast==slow)
{
struct ListNode* meet=slow;
//同时迭代
while(meet!=head)
{
head= head->next;
meet=meet->next;
}
return meet;
}
}
return NULL;
}
十一.复制带随机指针的链表
1.题目描述

二.思路分析
难点在于如何复制原链表中的random值,因为我们在复制结点时是无法获得复制链表中的结点的random值,所以要让复制的链表和原链表中存在位置关系。

1.复制下来的结点插入到原结点与下一个结点中间
2.根据原结点random,处理复制结点的random
3.将复制结点解开,之后形成新链表。
三.代码实现
/**
* Definition for a Node.
* struct Node {
* int val;
* struct Node *next;
* struct Node *random;
* };
*/
struct Node* copyRandomList(struct Node* head) {
struct Node* cur;
cur=head;
//复制下来的结点插入到原结点与下一个结点中间
while(cur)
{
struct Node* copy=(struct Node*)malloc(sizeof(struct Node));
//copy原结点
copy->val=cur->val;
//插入copy结点
copy->next=cur->next;
cur->next=copy;
//迭代
cur=copy->next;
}
cur=head;
//根据原结点random,处理复制结点的random
while(cur)
{
//复制原结点的random值
struct Node* copy=cur->next;
if(cur->random==NULL)
{
copy->random=NULL;
}
else
{
copy->random=cur->random->next;
}
//迭代
cur=copy->next;
}
struct Node* copyhead=NULL,*copytail=NULL;
cur=head;
//将复制结点解开,之后形成新链表。
while(cur)
{
struct Node* copy=cur->next;
struct Node* next=copy->next;
//断开原结点与copy结点的连接
if(copytail==NULL)
{
copyhead=copytail=copy;
}
else
{
copytail->next=copy;
copytail=copy;
}
//迭代
cur->next=next;
cur=next;
}
return copyhead;
}
本文介绍了链表的各种操作,包括删除指定元素,反转链表,找到链表的中间结点,寻找倒数第k个节点,合并两个有序链表,分割链表,判断链表的回文结构,找出相交链表的交点,以及检测和处理环形链表问题。每个操作都提供了详细的思路分析和C语言实现代码。
429

被折叠的 条评论
为什么被折叠?



