目录
需要的基础:结构体,动态内存管理,指针,这些要很熟。
什么是链表?
链表是一种物理上不连续,逻辑上连续的存储结构,逻辑顺序是按指针来链接的。
从图知道它们物理是不连续的,一下块一小块的空间都是在堆区随机开辟的,这些空间有可能连续,也有可能不连续,通过指针连在一起。
链表分类
1.单向与双向
2.带头与不带头(哨兵位的头节点一般存储有效数据)
3.循环与不循环
不带头的单向不循环链表
不带头的单向不循环链表
如何掌握它呢?那就看看以下链表的增删查改吧
#pragma once
#include<stdio.h>
#include<assert.h>
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode ;
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SListNode* pos, SLTDateType x);
void SListInsertpre(SListNode* plist, SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
但这个是不是感觉很对,但是假如这个链表什么都没有呢?
讲解为什么要传二级指针
不要定性思维认为传址就可以了,而是想要改变什么就要有什么的地址
要改整型就要知道整型地址,要改结构体就要有结构体地址 ,要改一级指针就要一级指针的地址就是二级指针
void SListPushBack(SListNode** pplist, SLTDateType x)
{
assert(pplist);
SListNode* newnode = BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
SListNode* end = *pplist;
while (end->next)
{
end = end->next;
}
end->next = newnode;
}
}
自己想想为什么要断言?
// 单链表的尾删
void SListPopBack(SListNode** pplist);
假如只有一个呢?
void SListPopBack(SListNode** pplist)
{
assert(pplist);
assert(*pplist);
SListNode* end = *pplist;
if (end->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SListNode* end = *pplist;
while (end->next->next)
{
end = end->next;
}
free(end->next);
end->next = NULL;
}
}
为什么又要多一个断言呢?
总结:单链表一定考虑空指针问题,二级指针问题,断言问题
小伙伴们应该懂了吧
剩下的你们自己写了,跟上面差不多,理解好了就写的出,给我的给你们参考
void SListPrint(SListNode* plist)
{
SListNode* cur = plist;
while (cur)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
SListNode* BuySListNode(SLTDateType x)
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SListPushBack(SListNode** pplist, SLTDateType x)
{
assert(pplist);
SListNode* newnode = BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
SListNode* end = *pplist;
while (end->next)
{
end = end->next;
}
end->next = newnode;
}
}
void SListPopBack(SListNode** pplist)
{
assert(pplist);
assert(*pplist);
SListNode* end = *pplist;
if (end->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SListNode* end = *pplist;
while (end->next->next)
{
end = end->next;
}
free(end->next);
end->next = NULL;
}
}
void SListPushFront(SListNode** pplist, SLTDateType x)
{
assert(pplist);
SListNode* newnode = BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
newnode->next = *pplist;
*pplist = newnode;
}
}
void SListPopFront(SListNode** pplist)
{
assert(*pplist);
assert(pplist);
SListNode* star = *pplist;
*pplist = star->next;
free(star);
}
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
SListNode* cur = plist;
while (cur->next)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
assert(pos);
SListNode* newnode = BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
void SListInsertpre(SListNode** pplist, SListNode* pos, SLTDateType x)
{
assert(pos);
assert(pplist);
//assert(*pplist);
/*SListNode* cur = *pplist;*/
SListNode* newnode = BuySListNode(x);
if (*pplist ==pos)
{
newnode->next = pos;
*pplist = newnode;
}
else
{
SListNode* cur = *pplist;
while (cur->next != pos)
{
cur = cur->next;
}
cur->next = newnode;
newnode->next = pos;
}
}
void SListEraseAfter(SListNode* pos)
{
assert(pos);
assert(pos->next);
SListNode* cur=pos->next;
pos->next =cur-> next;
free(cur);
}
面试题
1
思路:头插,一个一个插进新的链表
struct ListNode* reverseList(struct ListNode* head)
{
if(head==NULL)
return NULL;
struct ListNode* rhead=NULL;
struct ListNode* cur=head;
struct ListNode* afcur=head->next;
while(cur)
{
cur->next=rhead;
rhead=cur;
cur=afcur;
if(afcur)
afcur=afcur->next;
}
return rhead;
}
思路:快慢指针问题,给一个快指针,一个慢指针,快指针先走k步,然后同时走,快指针是NULL 时停,你们可以试试写不写的出
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
struct ListNode* fast=pListHead;
struct ListNode* slow=pListHead;
for(int i=0;i<k;i++)
{
if(fast==NULL)
return NULL;
fast=fast->next;
}
while(fast)
{
slow=slow->next;
fast=fast->next;
}
return slow;
}
思路:快慢指针,快指针走2步,慢指针走一步,假如有环,快指针先进环,然后慢指针追赶,一定能追上,最后两个指针指向同一个位置。
bool hasCycle(struct ListNode* head) {
struct ListNode* fast=head;
struct ListNode* slow=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
return true;
}
}
return false;
}
解释:为什么快指针要走2步,三步行不行呢?
所以说fast走一步肯定能相遇
思路:一个指针从头开始走,一个指针从相遇点开始走,当两个指针相等时的位置就是环开始的位置。是有点难想到并且也难理解,后面有解释。
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode* fast=head;
struct ListNode* slow=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
struct ListNode* meet=fast;
struct ListNode* newnode=head;
while(meet!=newnode)
{
meet=meet->next;
newnode=newnode->next;
}
return meet;
}