一、链表的概念及结构
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表的结构可以形象的看作是一辆火车,每块空间独立存在并且通过指针链接在一起。
在数据结构中:

注意:1、从上图可以看出,链式结构在逻辑上是连续的,但在物理上不一定连续。
2、现实的节点一般都是从堆上申请出来的。
3、在堆上申请的空间是按一定策略来分配的,两次申请的空间可能连续也可能不连续。
二、链表的分类
实际上链表的结构非常多样,以下情况组合起来就有八种链表结构
1、单向或者双向

2、带头(哨兵位)或者不带头

3、循环或者非循环

虽然链表中存在这么多结构,但是在实际中最常用的就只有两种结构:

1、无头单向非循环链表:结构简单,一般不会单独用来存储数据,实际中更多的是作为其他数据结构的子结构,如哈希桶、图的邻接表等。
2、带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表,这个结构虽然结构较为复杂,但是使用代码实现反而因为结构的原因更加简单了,这一点在后续代码实现过程中会体现出来。
三、链表的实现
在代码实现过程中,我们依然为两个链表分别建立三个文件
无头单向非循环链表:SList.h(函数声明)、SList.c(函数定义)、4.c(测试函数功能)
带头双向循环链表:List.h(函数声明)、List.c(函数定义)、5.c(测试函数功能)
3.1 无头单向非循环链表
1)创建结构体
首先我们需要创建一个结构体,其中一个存放数据,另一个指向下一个空间。
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode;
2)尾插/头插
尾插需要先判断链表是否为空,为空则直接建立一个节点就行,不为空则需要遍历到最后一个节点后插入
// 单链表的尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
assert(pplist);
assert(*pplist);
SListNode* newnode = BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
SListNode* tail = *pplist;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
头插的实现较为简单,只需要在链表头插入即可
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
assert(pplist);
SListNode* newnode = BuySListNode(x);
newnode->next = *pplist;
*pplist = newnode;
}
测试结果

3)尾删/头删
尾删和尾插的思路刚好相反,注意需要判空
// 单链表的尾删
void SListPopBack(SListNode** pplist)
{
assert(pplist);
assert(*pplist);
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SListNode* tail = *pplist;
while (tail->next->next)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
// 单链表的头删
void SListPopFront(SListNode** pplist)
{
assert(pplist);
assert(*pplist);
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SListNode* del = *pplist;
*pplist = (*pplist)->next;
free(del);
}
}
测试结果


4)查找
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
SListNode* cur = plist;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
3.2 带头双向循环链表
1)创建结构体
这里我们依然创建一个结构体,其中一个存放数据,另外两个分别指向前一片空间和后一片空间
typedef int LTDataType;
typedef struct ListNode
{
LTDataType _data;
struct ListNode* _next;
struct ListNode* _prev;
}ListNode;
2)创建头结点(哨兵位)
创建哨兵位时只有这一个元素,该元素的前后指针都指向自己,已到达循环的目的
// 创建头结点(哨兵位)
ListNode* ListCreate()
{
ListNode* pHead = BuyNode(-1);
pHead->_next = pHead;
pHead->_prev = pHead;
return pHead;
}
3)尾插/头插
// 双链表的尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* tail = pHead->_prev;
ListNode* newnode = BuyNode(x);
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = pHead;
pHead->_prev = newnode;
}
// 双链表的头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* newnode = BuyNode(x);
ListNode* first = pHead->_next;
pHead->_next = newnode;
newnode->_prev = pHead;
newnode->_next = first;
first->_prev = newnode;
}
测试结果

4)尾删/头删
// 双链表的尾删
void ListPopBack(ListNode* pHead)
{
assert(pHead);
ListNode* tail = pHead->_prev;
ListNode* tailprev = tail->_prev;
free(tail);
tailprev->_next = pHead;
pHead->_prev = tailprev;
}
// 双链表的头删
void ListPopFront(ListNode* pHead)
{
assert(pHead);
ListNode* first = pHead->_next;
ListNode* second = first->_next;
pHead->_next = second;
second->_prev = pHead;
free(first);
}
测试结果

5)查找
// 双链表的查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* cur = pHead->_next;
while (cur != pHead)
{
if (cur->_data == x)
{
return cur;
}
cur = cur->_next;
}
return NULL;
}
6)在指定位置前插入
// 在pos位置的前面插入
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* prev = pos->_prev;
ListNode* newnode = BuyNode(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = pos;
pos->_prev = newnode;
}
7)删除指定位置的节点
// 删除pos位置的节点
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* posprev = pos->_prev;
ListNode* posnext = pos->_next;
posprev->_next = posnext;
posnext->_prev = posprev;
free(pos);
}
四、源码
4.1 无头单向非循环链表
SList.h(函数声明)
#include<stdio.h>
#include<stdlib.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);
SList.c(函数定义)
#include"SList.h"
SListNode* BuySListNode(SLTDateType x)
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SListPrint(SListNode* plist)
{
SListNode* cur = plist;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
void SListPushBack(SListNode** pplist, SLTDateType x)
{
assert(pplist);
assert(*pplist);
SListNode* newnode = BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
SListNode* tail = *pplist;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
void SListPushFront(SListNode** pplist, SLTDateType x)
{
assert(pplist);
SListNode* newnode = BuySListNode(x);
newnode->next = *pplist;
*pplist = newnode;
}
void SListPopBack(SListNode** pplist)
{
assert(pplist);
assert(*pplist);
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SListNode* tail = *pplist;
while (tail->next->next)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
void SListPopFront(SListNode** pplist)
{
assert(pplist);
assert(*pplist);
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SListNode* del = *pplist;
*pplist = (*pplist)->next;
free(del);
}
}
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
SListNode* cur = plist;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
4.c(测试函数功能)
#include"SList.h"
void TestSList1()
{
SListNode* plist = NULL;
SListPushFront(&plist, 1);
SListPushFront(&plist, 2);
SListPushFront(&plist, 3);
SListPushFront(&plist, 4);
SListPrint(plist);
SListPushBack(&plist, 5);
SListPushBack(&plist, 6);
SListPushBack(&plist, 7);
SListPushBack(&plist, 8);
SListPrint(plist);
SListPopBack(&plist);
SListPopBack(&plist);
SListPopBack(&plist);
SListPrint(plist);
SListPopBack(&plist);
SListPrint(plist);
SListPopBack(&plist);
SListPrint(plist);
SListPopBack(&plist);
SListPrint(plist);
SListPopBack(&plist);
SListPrint(plist);
SListPopBack(&plist);
SListPrint(plist);
/*SListPopBack(&plist);
SListPrint(plist);*/
/*SListPopFront(&plist);
SListPrint(plist);
SListPopFront(&plist);
SListPrint(plist);
SListPopFront(&plist);
SListPrint(plist);
SListPopFront(&plist);
SListPrint(plist);
SListPopFront(&plist);
SListPrint(plist);*/
}
int main()
{
TestSList1();
return 0;
}
4.2 带头双向循环链表
List.h(函数声明)
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int LTDataType;
typedef struct ListNode
{
LTDataType _data;
struct ListNode* _next;
struct ListNode* _prev;
}ListNode;
// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);
List.c(函数定义)
#include"List.h"
ListNode* BuyNode(LTDataType x)
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->_data = x;
newnode->_next = NULL;
newnode->_prev = NULL;
}
ListNode* ListCreate()
{
ListNode* pHead = BuyNode(-1);
pHead->_next = pHead;
pHead->_prev = pHead;
return pHead;
}
void ListPrint(ListNode* pHead)
{
assert(pHead);
printf("sentinel<==>");
ListNode* cur = pHead->_next;
while (cur != pHead)
{
printf("%d<==>", cur->_data);
cur = cur->_next;
}
printf("\n");
}
bool LTEmpty(ListNode* pHead)
{
assert(pHead);
return pHead->_next == pHead;
}
void ListPushBack(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* tail = pHead->_prev;
ListNode* newnode = BuyNode(x);
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = pHead;
pHead->_prev = newnode;
}
void ListPopBack(ListNode* pHead)
{
assert(pHead);
ListNode* tail = pHead->_prev;
ListNode* tailprev = tail->_prev;
free(tail);
tailprev->_next = pHead;
pHead->_prev = tailprev;
}
void ListPushFront(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* newnode = BuyNode(x);
ListNode* first = pHead->_next;
pHead->_next = newnode;
newnode->_prev = pHead;
newnode->_next = first;
first->_prev = newnode;
}
void ListPopFront(ListNode* pHead)
{
assert(pHead);
ListNode* first = pHead->_next;
ListNode* second = first->_next;
pHead->_next = second;
second->_prev = pHead;
free(first);
}
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* cur = pHead->_next;
while (cur != pHead)
{
if (cur->_data == x)
{
return cur;
}
cur = cur->_next;
}
return NULL;
}
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* prev = pos->_prev;
ListNode* newnode = BuyNode(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = pos;
pos->_prev = newnode;
}
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* posprev = pos->_prev;
ListNode* posnext = pos->_next;
posprev->_next = posnext;
posnext->_prev = posprev;
free(pos);
}
void ListDestory(ListNode* pHead)
{
assert(pHead);
ListNode* cur = pHead->_next;
while (cur != pHead)
{
ListNode* next = cur->_next;
free(cur);
cur = next;
}
free(pHead);
}
5.c(测试函数功能)
#include"List.h"
TestList()
{
ListNode* plist = ListCreate();
ListPushBack(plist, 1);
ListPushBack(plist, 2);
ListPushBack(plist, 3);
ListPushBack(plist, 4);
ListPrint(plist);
ListPushFront(plist, 5);
ListPushFront(plist, 6);
ListPushFront(plist, 7);
ListPushFront(plist, 8);
ListPrint(plist);
ListPopBack(plist);
ListPopBack(plist);
ListPopBack(plist);
ListPrint(plist);
ListPopFront(plist);
ListPopFront(plist);
ListPopFront(plist);
ListPrint(plist);
}
int main()
{
TestList();
return 0;
}
5380

被折叠的 条评论
为什么被折叠?



