在数据结构的学习中,不仅要理解底层代码实现,还要多做算法题来巩固。
下面我给大家列出几道用单链表解决的算法题。
如果对单链表的知识有疑问的话,我上次博客就是对单链表的讲解。欢迎大家收看 OvO
1.移除链表元素
https://leetcode.cn/problems/remove-linked-list-elements/description/%01
这道题目的是删除链表中值为val的值,并返回头节点。
其实思路很简单,删除每一个值为val的结点即可。但是实现过程有一些需要注意的地方。
1.当我们在遍历过程中遇到值为val的结点,就需要将上一个结点的next指针指向下一个结点。下一个结点好表示,但如何获取上一个结点呢?
我们可以在遍历之前就创建两个结点(prev和pcur),都指向头结点,但是在每次循环的最后先让prev=pcur,再让pcur=pcur->next。
这样就实现了获取前一个结点的目的。
struct ListNode* removeElements(struct ListNode* head, int val) {
struct ListNode*pcur=head;
struct ListNode*prev=head;
while(pcur)
{
if(pcur->val==val)
{
prev->next=pcur->next;
pcur=pcur->next;
continue;
}
prev=pcur;
pcur=pcur->next;
}
return head;
}
当我们要删除某一个结点时,prev指针的next指针已经指向了pcur的下一个节点,为了保持prev和pcur始终相差一个结点的距离,我们需要将pcur指向下一个结点,并直接进行下一次循环。
我们提交代码,发现这个样例不通过
因为这串代码不能将头结点进行处理,所以我们可以这样写
struct ListNode* removeElements(struct ListNode* head, int val) {
struct ListNode*pcur=head;
struct ListNode*prev=head;
while(pcur)
{
if(pcur->val==val)
{
prev->next=pcur->next;
pcur=pcur->next;
continue;
}
prev=pcur;
pcur=pcur->next;
}
if(head!=NULL&&head->val==val)
{
return head->next;
}
return head;
}
这样子我们就完美解决了这道题。
2.反转链表
https://leetcode.cn/problems/middle-of-the-linked-list/description/
顾名思义,这道题让我们将链表反转。并返回头结点。
我们一定会遍历一遍原链表,那么我们可以先创建一个新链表,并在遍历原链表每一个结点的时候头插到新链表中,那么这道题就解决了。
struct ListNode*NewNode(int x)
{
struct ListNode*newnode=(struct ListNode*)malloc(sizeof(struct ListNode));
if(newnode==NULL)
{
perror("malloc fail");
exit(1);
}
newnode->val=x;
newnode->next=NULL;
return newnode;
}
//链表的头插
void SLTPushFront(struct ListNode**pphead,int x)
{
struct ListNode* newnode1=NewNode(x);
if(*pphead==NULL)
{
(*pphead)=newnode1;
return;
}
newnode1->next=(*pphead);
(*pphead)=newnode1;
return;
}
struct ListNode* reverseList(struct ListNode* head) {
struct ListNode*pcur=head;
if(head==NULL)
{
return NULL;
}
struct ListNode*prev=NewNode(pcur->val);
pcur=pcur->next;
while(pcur)
{
int data=pcur->val;
SLTPushFront(&prev,data);
pcur=pcur->next;
}
return prev;
}
3.链表的中间结点
https://leetcode.cn/problems/middle-of-the-linked-list/description/
思路一:
遍历两遍链表,第一遍得出结点个数,算出中间结点位置,当第二遍遍历到中间位置时返回。
但是这种方法我们要遍历两遍,虽然时间复杂度也不高,但终归不是最简方法。
思路二:
快慢指针法,创建一快一慢指针,快指针每次走两步,慢指针每次走一步,当快指针走完整个链表时,慢指针所指的就是中间结点。
推导:1.设链表的长度为2n,那么快指针走完时慢指针走了n步,对应的就是中间位置。
2.设链表的长度为2n+1,那么当快指针走2n步后没走完,再走一次,即走了(2n+2)步,对应慢指针走了n+1步,也成立。
struct ListNode* middleNode(struct ListNode* head) {
struct ListNode*fast=head;
struct ListNode*slow=head;
while(fast)
{
if(fast->next==NULL)
{
return slow;
}
fast=fast->next->next;
slow=slow->next;
}
return slow;
}
完
如发现错误,欢迎打在评论区。
主页还有更多优质内容OvO