主要两种结构
- 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结
构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。 - 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向
循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而
简单了,后面我们代码实现了就知道了。
链表和顺序表的比较![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/3eb71d1b0d272ceaf9fd5a1b6de58729.png)
cpu补充知识点
带头双向链表的总代码
代码分析
Test.c
#include"List.h"
void test1()
{
LTNode* pList = NULL;
ListInit(&pList);
//尾插
ListPushBack(pList,1);
ListPushBack(pList,2);
ListPushBack(pList, 3);
ListPushBack(pList, 4);
//打印
Listprint(pList);
//尾删
ListPopBack(pList);
Listprint(pList);
ListPopBack(pList);
Listprint(pList);
//ListPopBack(pList);
//Listprint(pList);
//ListPopBack(pList);
//Listprint(pList);
//在1之前插入30
LTNode*pos = Listfine(pList, 1);
if (pos)
{
ListInsert(pos, 30);
}
Listprint(pList);
pos = Listfine(pList, 1);
if (pos)
{
ListErase(pos);
}
Listprint(pList);
ListDestroy(pList);//不销毁会内存泄露
pList = NULL;//野指针,phead销毁了因为调用的是一级指针所以plist还存在
//若为二级指针(*phead)=NULL就不用管pList了
}
int main()
{
test1();
return 0;
}
List.c
#include"List.h"
LTNode* BuyListNode(LTDataType x)
{
LTNode* node = (LTNode *)malloc(sizeof(LTNode));
if (node == NULL)
{
printf("malloc fail\n");
exit(-1);
}
node->data = x;
node->next = NULL;
node->prev = NULL;
}
//初始化
void ListInit(LTNode** pphead)
{
*pphead = BuyListNode(-1);
(*pphead)->next = *pphead;
(*pphead)->prev = *pphead;
}
//打印
void Listprint(LTNode* phead)
{
assert(phead);
LTNode *cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
//删除判空函数
bool ListEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;//自己指向自己就为空
}
//size
size_t ListSize(LTNode* phead)
{
assert(phead);
size_t n = 0;
LTNode* cur = phead->next;
while (cur != phead)
{
++n;
cur = cur->next;
}
return n;
}
//销毁
void ListDestroy(LTNode* phead)
{
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
phead = NULL;
}
//尾插
void ListPushBack(LTNode *phead, LTDataType x)
{
assert(phead);//检查传过来的phead,假设未初始化将会出现错误
LTNode *newnode = BuyListNode(x);
LTNode *tail = phead->prev;
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
//assert(phead);
//ListInsert(phead,x);//pos就是phead
}
//尾删
void ListPopBack(LTNode *phead)
{
assert(phead);
assert(!ListEmpty(phead));//尾删导致链表变空要报警报
LTNode* tail = phead->prev;
LTNode* tailprev = tail->prev;
free(tail);
tailprev->next = phead;
phead->prev = tailprev;
//ListErase(phead->prev);
}
//头插
void ListPushFront(LTNode *phead, LTDataType x)
{
assert(phead);
ListInsert(phead->next, x);
}
//头删
void ListPopFront(LTNode *phead)
{
assert(phead);
assert(!ListEmpty(phead));
ListErase(phead->next);
}
//查找
LTNode* Listfine(LTNode *phead, LTDataType x)
{
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
//在pos之前去插入
//考虑pos是头或者尾的情况是否符合
void ListInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = BuyListNode(x);
LTNode* prev = pos->prev;
newnode->next = pos;
pos->prev = newnode;
prev->next = newnode;
newnode->prev = prev;
}
//删除pos位置
void ListErase(LTNode* pos)
{
assert(pos);
LTNode* prev = pos->prev;
LTNode* next = pos->next;
free(pos);
prev->next = next;
next->prev = prev;
}
List.h
#pragma once
//带头双向链表
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType data;
}LTNode;
// 可以用一级指针,也可以用二级指针
// 用一级指针,外面用了以后,存在野指针的问题。但是他保持了接口的一致性
// 用二级指针,解决了野指针问题。从接口设计的角度,有点混乱
//开辟节点
LTNode* BuyListNode(LTDataType x);
void ListInit(LTNode** pphead);
void ListDestroy(LTNode* phead);
void Listprint(LTNode* phead);
bool ListEmpty(LTNode* phead);
size_t ListSize(LTNode* phead);
//尾插尾删
void ListPushBack(LTNode *phead, LTDataType x);
void ListPopBack(LTNode *phead);
//头插头删
void ListPushFront(LTNode *phead, LTDataType x);
void ListPopFront(LTNode *phead, LTDataType x);
//找pos位置
LTNode* Listfine(LTNode *phead, LTDataType x);
//删除pos位置
void ListErase(LTNode* pos);
//在pos之前去插入
void ListInsert(LTNode* pos, LTDataType x);