目录
一. 链表的概念
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的 。
在这里介绍链表中的两种结构
无头单向非循环列表:结构简单,一般不会单独用来存放数据。实际中更多是作为其他数据结构的子结构,比如说哈希桶等等。
带头双向循环链表:结构最复杂,一般单独存储数据。实际中经常使用的链表数据结构,都是带头双向循环链表。这个结构虽然复杂,但是使用代码实现后会发现这个结构会带来很多优势,实现反而简单了。
二. 单链表的增删查改
1.单链表的定义
在C语言中我们一般创建一个结构体来作为链表的结点
typedef int SLDataType;
typedef struct SListNode
{
SLDataType data;
struct SListNode* next;
}SListNode;
创建初始化一个节点:
void SListInitNode(SListNode** plist, SLDataType x)
{
assert(plist);
*plist = (SListNode*)malloc(sizeof(SListNode));
assert(*plist);
(*plist)->data = x;
(*plist)->next = NULL;
}
2.单链表的头插与头删
1.头插
void SListPushFront(SListNode** plist, SLDataType x)
{
assert(plist);
SListNode* ptr = *plist;
SListInitNode(plist, x);
(*plist)->next = ptr;
}
2.头删
void SListPopFront(SListNode** plist)
{
assert(*plist);
assert(plist);
SListNode* ptr = (*plist)->next;
free(*plist);
*plist = ptr;
}
3.单链表的尾插与尾删
1.尾插
void SListPushBack(SListNode** plist, SLDataType x)
{
assert(plist);
if (*plist == NULL)
{
SListInitNode(plist, x);
}
else
{
SListNode* ptr = *plist;
while (ptr->next)
{
ptr = ptr->next;
}
SListInitNode(&(ptr->next), x);
}
}
2.尾删
void SListPopBack(SListNode** plist)
{
assert(plist);
assert(*plist);
if ((*plist)->next == NULL)
{
free(*plist);
*plist = NULL;
}
else
{
SListNode* ptr = *plist;
while (ptr->next->next)
{
ptr = ptr->next;
}
free(ptr->next);
ptr->next = NULL;
}
}
4.单链表的中间插入删除
1.在pos位置之后插入
为什么不在pos位置之前插入?
在pos位置之前插入需要传单链表的二级指针,
1.用来解决pos指向单链表的头部出现的头插情况
2.用来寻找插入位置的前一个位置,以改变其指向
void SListInsertAfter(SListNode* pos, SLDataType x)
{
assert(pos);
SListNode* ptr = NULL;
ptr = (SListNode*)malloc(sizeof(SListNode));
assert(ptr);
ptr->data = x;
ptr->next = pos->next;
pos->next = ptr;
}
2.在pos位置之后删除
为什么不删除pos位置?
删除pos位置同样需要传入单链表的二级指针,1.用来解决pos指向单链表的头部出现的单链表指针指向的改变
2.用来寻找pos位置的前一个位置,以改变其指向
void SListEraseAfter(SListNode* pos)
{
assert(pos);
assert(pos->next);
SListNode* ptr = pos->next;
pos->next = pos->next->next;
free(ptr);
ptr = NULL;
}
5.单链表的查找
SListNode* SListFind(SListNode* plist, SLDataType x)
{
assert(plist);
while (plist)
{
if (plist->data == x)
return plist;
plist = plist->next;
}
return NULL;
}
三. 带头循环双向链表的增删查改
1.带头循环双向链表的定义
在C语言中我们一般创建一个结构体来作为链表的结点
typedef int LDataType;
typedef struct ListNode
{
LDataType data;
struct ListNode* next;
struct ListNode* prev;
}ListNode;
创建一个节点:
ListNode* BuyListNode()
{
ListNode* tmp = (ListNode*)malloc(sizeof(ListNode));
if (tmp == NULL)
{
perror("malloc");
}
return tmp;
}
2.带头循环双向链表的头插与头删
1.头插
void ListPushfront(ListNode* phead, LDataType x)
{
assert(phead);
ListNode* newnode = BuyListNode();
ListNode* tail = phead->next;
phead->next = newnode;
newnode->next = tail;
newnode->prev = phead;
tail->prev = newnode;
newnode->data = x;
//ListInsert(phead->next, x);
}
2.头删
void ListPopFront(ListNode* phead)
{
assert(phead);
ListNode* tail = phead->next;
tail->next->prev = phead;
phead->next = tail->next;
free(tail);
//ListErase(phead->next);
}
3.带头循环双向链表的尾插与尾删
1.尾插
void ListPushBack(ListNode* phead, LDataType x)
{
ListNode* plist = BuyListNode();
ListNode* tail = phead->prev;
tail->next = plist;
plist->prev = tail;
plist->next = phead;
phead->prev = plist;
plist->data = x;
//ListInsert(phead, x);
}
2.尾删
void ListPopBack(ListNode* phead)
{
assert(phead);
ListNode* tail = phead->prev;
tail->prev->next = phead;
phead->prev = tail->prev;
free(tail);
//ListErase(phead->prev);
}
4.带头循环双向链表的中间插入删除
1.插入
void ListInsert(ListNode* pos, LDataType x)
{
assert(pos);
ListNode* newnode = BuyListNode();
pos->prev->next = newnode;
newnode->prev = pos->prev;
newnode->next = pos;
pos->prev = newnode;
newnode->data = x;
}
2.删除
void ListErase(ListNode* pos)
{
assert(pos);
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
free(pos);
}
5.带头循环双向链表的查找
ListNode* ListFind(ListNode* phead, LDataType x)
{
assert(phead);
ListNode* cur = phead->next;
while (phead != cur)
{
if (cur->data == x)
return cur;
cur = cur->next;
}
return NULL;
}