前言
题目链接
移除链表元素
链表的中间结点
反转链表
分割链表
环形链表的约瑟夫问题
欢迎关注个人主页:逸狼
创造不易,可以点点赞吗~
如有错误,欢迎指出~
移除链表元素
题述
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点
思路1:直接删除原链表中不符合的节点
遍历原链表,遇到val就执行删除操作
执行删除操作修改指针的指向有点麻烦,还有其他办法
思路2:满足要求的节点放在新链表上
定义新链表,利用pcur遍历原链表,找不为val的节点,尾插在新链表中
新链表:newHead newTail
要考虑链表是否为空
链表为空:插入进来的节点就是链表的头节点和尾节点
链表不为空,插入进来的节点就是链表的新的尾节点
代码实现思路2
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) {
//定义新链表的头尾指针
ListNode* newHead,* newTail;
newHead=newTail=NULL;
ListNode* pcur=head;
while(pcur)
{
//不是val,尾插到新链表
if(pcur->val!=val){
//链表为空
if(newHead==NULL)
{
newHead=newTail=pcur;
}
else{
//链表不为空
newTail->next=pcur;
newTail=newTail->next;//
}
}
//pcur继续往下走
pcur=pcur->next;
}
if(newTail)//要先判断newTail本来是否为空
{
newTail->next=NULL;
}
return newHead;
}
链表的中间结点
给你单链表的头结点 head ,请你找出并返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点
思路1:统计链表中节点的个数,通过除2找到中间节点
for(统计链表个数)
for(根据除2结果走到中间节点)
思路2:快慢指针
slow指针每次走1步,fast走2步
当fast->next或者fast走到为空,则slow刚好指向的就是中间节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) {
ListNode* fast,*slow;
fast=slow=head;
while(fast&&fast->next)//条件fast和fast->next不能相反,会造成短路
{
slow=slow->next;//slow走1步
fast=fast->next->next;//fast走2步
}
return slow;
}
反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
思路1
创建新链表,遍历原链表的节点将其插入到新链表中
思路2:创建3个指针
分别记录前驱节点,当前节点,后继节点,改变原链表的指向1
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {
//要先处理空链表
if(head==NULL)
{
return head;
}
ListNode *n1,*n2,*n3;
n1=NULL;
n2=head;
n3=head->next;
while(n2)
{
//修改n2的指向
n2->next=n1;
//n1,n2,n3往下走
n1=n2;
n2=n3;
if(n3)//要先判断n3不为空,才能往下走
{
n3=n3->next;
}
}
return n1;
}
合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
代码实现
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
if (list1 == NULL)
return list2;
if (list2 == NULL)
return list1;
ListNode *l1, *l2;
l1 = list1, l2 = list2;
//创建头节点
ListNode *newHead, *newTail;
newHead = newTail = (ListNode*)malloc(sizeof(ListNode));
while (l1 && l2) {
if (l1->val < l2->val) {
// l1小,l1上,但要先判断l1不为空指针
// if (newHead == NULL)
// newHead = newTail = l1;
// else {
// newTail->next = l1;
// newTail = newTail->next;
// }
// l1 = l1->next;
//代码优化
newTail->next=l1;
newTail=newTail->next;
l1=l1->next;
} else {
// l2小
// if (newHead == NULL)
// newHead = newTail = l2;
// else {
// newTail->next = l2;
// newTail = newTail->next;
// }
// l2 = l2->next;
//代码优化
newTail->next=l2;
newTail=newTail->next;
l2=l2->next;
}
}
if (l1) {
newTail->next = l1;
}
if (l2) {
newTail->next = l2;
}
return newHead->next;
}
分割链表
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你不需要 保留 每个分区中各节点的初始相对位置。
思路
定义2个链表:大链表和小链表,遍历原链表的节点将其放到对应的新链表中,最将大小链表首尾相连
创建大小链表都要先创建一个哨兵位
利用pcur遍历链表
代码实现
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode* partition(struct ListNode* head, int x){
if(head==NULL)
return head;
//创建带头的大小链表
ListNode* lessHead ,*lessTail;
ListNode* greaterHead,*greaterTail;
lessHead=lessTail=(ListNode*)malloc(sizeof(ListNode));
greaterHead=greaterTail=(ListNode*)malloc(sizeof(ListNode));
//遍历原链表,将节点放在对应的新链表中
ListNode*pcur=head;
while(pcur)
{
if(pcur->val < x)
{
//放在小链表中
lessTail->next=pcur;
lessTail=lessTail->next;
}
else
{
//放在大链表中
greaterTail->next=pcur;
greaterTail=greaterTail->next;
}
pcur=pcur->next;
}
//大链表尾要置为空
greaterTail->next=NULL;
//大小链表首尾相连
lessTail->next=greaterHead->next;
ListNode*ret=lessHead->next;
free(greaterHead);
free(lessHead);
return ret;
}
环形链表的约瑟夫问题
编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数,报到 m 的人离开。
下一个人继续从 1 开始报数。
n-1 轮结束以后,只剩下一个人,问最后留下的这个人编号是多少?、
代码实现
//这道OJ题已经创建好了结构体结点,只是没有展示出来
typedef struct ListNode ListNode;
//申请新节点
ListNode* BuyNode(int x)
{
ListNode* newNode=(ListNode*)malloc(sizeof(ListNode));
//可写可不写,一般不会申请失败
if(newNode==NULL)
{
exit(1);
}
newNode->val=x;
newNode->next=NULL;
return newNode;
}
//创建循环链表
ListNode*createList(int n)
{
//创建头节点
ListNode* phead=BuyNode(1);
ListNode* ptail=phead;
//从2开始,共创建了n个节点
for(int i=2;i<=n;i++)
{
ptail->next=BuyNode(i);
ptail=ptail->next;
}
//链表要首尾相连,循环起来
ptail->next=phead;
return phead;
}
int ysf(int n, int m ) {
//创建不带头单向循环链表
//逢m删除节点
ListNode*head=createList(n);
ListNode*pcur=head;
ListNode*prev=NULL;//用于记录pcur走过的位置,是pcur的前驱节点
int count=1;
//逢m删除节点
while(pcur->next!=pcur)//表示删除节点到只剩下自己跳出循环
{
if(count==m)
{
//删除当前节点pcur
prev->next=pcur->next;
free(pcur);
//删除pcur节点后,要让pcur走到新的位置,count置为初始值
pcur=prev->next;
count=1;
}
else
{
//pcur往后走
prev=pcur;
pcur=pcur->next;
count++;
}
}
//此时pcur节点就是幸存下来的节点
return pcur->val;
}