0.前言
之前我讲解了循序表以及单链表,接下来我会在介绍几个不同的链表,并举例相关习题使大家能够更加深入的理解。
前期内容如下:
链接: 顺序表(动态顺序表增删查改的代码实现)
链接: 单链表(无头单向不循环)增删查改的代码实现
链接: [双向链表增删查改的代码实现] 紧张制作中
文章目录
- 0.前言
- 1.带哨兵位头节点的链表
- 2.双向链表
- 3.链表习题一,反转链表(不带哨兵位)
- 4.链表习题二,链表分割(带哨兵位)
- 5.链表习题三,带环链表(多个题目环环相扣)
- 5.1 判断是否有环?
- 5.2 判断入环节点的位置
- 5.3 附加问题(如果慢指针一次一步,快指针一次三步,会相遇嘛?)
1.带哨兵位头节点的链表
哨兵位头节点有什么作用?
当一个链表为空的时候,如果没有哨兵位,那么是直接指向NULL的。如果有哨兵位,那么先指向head,再指向NULL。如图所示:
2.双向链表
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都
是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带 来很多优势,实现反而简单了。
除此之外还有带环链表,下面的习题中我会进行讲解。
还有双向链表的增删查改,后续我也会放在我的gitee中供大家学习。
3.链表习题一,反转链表(不带哨兵位)
思路:
struct ListNode* reverseList(struct ListNode* head)
{
if(head==NULL)
{
return NULL;
}
struct ListNode* pre,*mid,*last;
pre=NULL;
mid=head;
last=head->next;
while(mid!=NULL)
{
mid->next=pre;
pre=mid;
mid=last;
if(last!=NULL)
last=last->next;
}
return pre;
}
4.链表习题二,链表分割(带哨兵位)
这个题是什么意思呢?
思路:
但是如果是极端情况,比如val的值为1,tail1的next指向head2,就需要单独处理,不仅仅需要处理tail1还需要处理head1,因为默认情况下两个链表都不为空,返回head1;如果所有值都比val小,没有比它大的,那么第二个链表就会为空。
尾插后还需要一定的链接,这就是这题的复杂所在,那么头节点可以帮我们省去一定的麻烦。如果这里我们使用带哨兵位的头节点,那么就会省去很多麻烦。使用了哨兵位,那么我们的head1和head2始终不会为空。如图所示:
ListNode* partition(ListNode* pHead, int x)
{
struct ListNode* head1,*tail1,*head2,*tail2;
head1=tail1=(struct ListNode*)malloc(sizeof(struct ListNode));//
head2=tail2=(struct ListNode*)malloc(sizeof(struct ListNode));//malloc出哨兵位的头节点
struct ListNode* cur=pHead;//原链表的头
while(cur!=NULL)
{
if(cur->val<x)//小于x的尾插到链表1
{
tail1->next=cur;
tail1=tail1->next;
}
else //大于等于x的尾插到链表2
{
tail2->next=cur;
tail2=tail2->next;
}
cur=cur->next;//下一个循环
}
tail1->next=head2->next;//体现了哨兵位的方便
tail2->next=NULL;
pHead=head1->next;
free(head1);
free(head2);//释放malloc的空间
return pHead;
}
5.链表习题三,带环链表(多个题目环环相扣)
5.1 判断是否有环?
思路:
bool hasCycle(struct ListNode *head)
{
struct ListNode* fast=head,*slow=head;
while((fast!=NULL)&&((fast->next)!=NULL))
{
fast=fast->next->next;
slow=slow->next;
if(slow==fast)
{
return true;
}
}
return false;
}
5.2 判断入环节点的位置
思路:
struct ListNode *detectCycle(struct ListNode *head)
{
struct ListNode* slow,* fast;
slow=fast=head;
while((fast!=NULL)&&((fast->next)!=NULL))
{
//慢指针走一步,快指针走两步
slow=slow->next;
fast=fast->next->next;
if (slow==fast)
{
struct ListNode* meet=fast;//快慢指针相遇的话,让meet为相遇点
while(head!=meet)
{
head=head->next;
meet=meet->next;
}
return meet;
}
}
return NULL;
}