目录
问题1:移除链表元素
题目链接:OJ链接
题目描述:
方法1:递归删除
情况1:不存在值的情况,返回一个空链表
情况2:头节点要删除,删除完继续返回新的头节点进行判断
情况3:不是情况1,2,不动原来的值,继续下一个节点的判断
最后返回头节点
struct ListNode* removeElements(struct ListNode* head, int val)
{
// 不存在值的情况,链表为空
if(head == NULL)
{
return NULL;
}
// 头节点的值是要删除的值的情况
if(head->val == val)
{
// 新头节点指向下一个节点
struct ListNode* newhead = head->next;
// 释放原头节点的内存
free(head);
// 头节点赋值为新的头节点
head = newhead;
// 递归调用函数,去除剩余节点中的值为val的节点
head = removeElements(head, val);
}
else
{
// 递归调用函数,去除剩余节点中的值为val的节点
head->next = removeElements(head->next, val);
}
return head;
}
方法2:连续尾插
先头尾节点置为空,然后传入pcur值,没有pcur值说明停止循环
当不是删除值时
链表为空,头尾节点都指向当前的节点。
链表不为空时,尾插pcur。
是删除值时,pcur走向下一个节点
当尾节点是要删除的值时(且要存在),pcur就不要往下走了,newtail->next=NULL.
typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val)
{
// 新链表的头节点和尾节点
ListNode* newhead, * newtail;
newhead = newtail = NULL;
// 当前节点指针
ListNode* pcur = head;
// 遍历原链表
while(pcur)
{
// 如果当前节点的值不等于要删除的值
if(pcur->val != val)
{
// 如果新链表的尾节点为空,表示新链表为空
if(newtail == NULL)
{
// 新链表的头尾节点都指向当前节点
newhead = newtail = pcur;
}
else
{
// 将当前节点添加到新链表的尾部
newtail->next = pcur;
newtail = newtail->next;
}
}
// 指针指向下一个节点
pcur = pcur->next;
}
// 如果新链表的尾节点不为空,将尾节点的next指针置为NULL
if(newtail)
{
newtail->next = NULL;
}
// 返回新链表的头节点
return newhead;
}
问题2:链表的中间节点
题目链接:OJ链接
方法:快慢指针
慢指针每次走一步,快指针每次走两步
奇数时,快指针的下一个节点指向NULL时停止,返回慢指针此时位置
偶数时,快指针的节点指向NULL时停止,返回慢指针此时的位置
typedef struct ListNode ListNode; // 定义结构体指针类型 ListNode
struct ListNode* middleNode(struct ListNode* head)
{
ListNode* slow, * fast; // 定义慢指针 slow 和快指针 fast
slow = fast = head; // 将慢指针和快指针都指向链表头部
while (fast && fast->next) // 当快指针和快指针的下一个节点都不为空时
{
slow = slow->next; // 慢指针向后移动一步
fast = fast->next->next; // 快指针向后移动两步
}
return slow; // 返回慢指针,即链表的中间节点
}
注意:不要写成while(fast->next&&fast),因为如果fast为空指针,fast->next便不存在了。会报错
问题3:反转链表
题目链接:OJ链接
方法: 三指针解法
创建三个指针
n1指向为空(NULL),n2指向第一个节点,n3指向下一个节点
如果n2不为空,n2节点的指针由指向n3改为指向n1
n1=n2,n2=n3,n3=n3->next
以此类推,直到n2不为空。
typedef struct ListNode ListNode; // 定义一个别名为ListNode的结构体
struct ListNode* reverseList(struct ListNode* head)
{
if(head==NULL) // 如果头节点为空,则直接返回头节点
return head;
ListNode* n1, *n2, *n3; // 定义三个指针n1、n2、n3
n1 = NULL; // n1指向NULL,表示反转链表的最后一个节点
n2 = head; // n2指向头节点
n3 = head->next; // n3指向头节点的下一个节点
while(n2)
{
n2->next = n1; // 将n2的next指针指向n1,实现反转
n1 = n2; // 将n2赋值给n1,更新n1的位置
n2 = n3; // 将n3赋值给n2,更新n2的位置
if(n3)
n3 = n3->next; // 如果n3不为空,则将n3指向n3的下一个节点
}
return n1; // 返回反转后的链表的头节点
}
注意,n3为空就不用往下走了。
问题4:合并两个有序链表
题目链接:OJ链接
方法:比较大小,插入新链表,注意空指针等细节。
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
if(list1==NULL)
return list2;
if(list2==NULL)
return list1;
ListNode*n1,*n2;
n1=list1,n2=list2;
ListNode*newhead,*newtail;
newhead=newtail=NULL;
while(n1&&n2)
{
if(n1->val<n2->val)
{
if(newhead==NULL)
{
// 如果新链表头为空,则将当前节点设为新链表头和新链表尾
newhead=newtail=n1;
}
else
{
// 如果新链表头不为空,则将当前节点接在新链表尾后,并更新新链表尾
newtail->next=n1;
newtail=newtail->next;
}
n1=n1->next;
}
else
{
if(newhead==NULL)
{
// 如果新链表头为空,则将当前节点设为新链表头和新链表尾
newhead=newtail=n2;
}
else
{
// 如果新链表头不为空,则将当前节点接在新链表尾后,并更新新链表尾
newtail->next=n2;
newtail=newtail->next;
}
n2=n2->next;
}
}
if(n1)
{
// 如果list1还有剩余节点,则将剩余节点接在新链表尾后,并更新新链表尾
newtail->next=n1;
newtail=newtail->next;
}
if(n2)
{
// 如果list2还有剩余节点,则将剩余节点接在新链表尾后,并更新新链表尾
newtail->next=n2;
newtail=newtail->next;
}
return newhead;
}
问题5:环形链表的约瑟夫问题
题目链接:环形链表的约瑟夫问题_牛客题霸_牛客网
方法:环形链表
创建一个环形链表,每个人对应一个节点,将对应的离开人数的节点释放掉,并且改边释放节点前后指针的指向,直到剩一人为止
typedef struct ListNode ListNode; // 定义一个 ListNode 结构体的别名为 ListNode
ListNode* BuyNode(int x) // 定义一个函数 BuyNode,用于创建一个新的节点并返回指向该节点的指针
{
ListNode*newnode=(ListNode*)malloc(sizeof(ListNode)); // 分配内存空间
newnode->val=x; // 设置节点的值为 x
newnode->next=NULL; // 下一个节点指针为空
return newnode; // 返回指向新节点的指针
}
// 创建不带头环形链表
ListNode*ctreatelist(int n)
{
ListNode*phead=BuyNode(1); // 创建头节点
ListNode*ptail=phead; // 尾节点指向头节点
for(int i=2;i<=n;i++) // 循环创建 n 个节点
{
ptail->next=BuyNode(i); // 创建新节点并连接到尾节点的下一个
ptail=ptail->next; // 尾节点指向新创建的节点
}
ptail->next=phead; // 尾节点指向头节点,形成环形链表
return phead; // 返回头节点指针
}
// 约瑟夫环问题
int ysf(int n, int m ) {
ListNode*head=ctreatelist(n); // 创建包含 n 个节点的环形链表
ListNode*pcur=head; // 当前节点指针指向头节点
ListNode*prev=NULL; // 前一个节点指针初始化为空
int count=1; // 计数器初始化为 1
while(pcur->next!=pcur) // 当链表中还有多于一个节点时循环
{
if(count==m) // 当计数器等于 m 时
{
// 删除当前节点
prev->next=pcur->next; // 前一个节点的指针指向当前节点的下一个节点
free(pcur); // 释放当前节点的内存空间
pcur=prev->next; // 当前节点指针指向下一个节点
count=1; // 计数器重置为 1
}
else
{
prev=pcur; // 前一个节点指针指向当前节点
pcur=pcur->next; // 当前节点指针指向下一个节点
count++; // 计数器加 1
}
}
return pcur->val; // 返回最后剩下的节点的值
}