- 链表的分类
- 实现带有哨兵位的双向的循环链表
- **定义节点的结构**
- 初始化单个节点
- 初始化带有哨兵位的双向循环链表
- 打印链表
- 销毁链表
- 尾插
- 尾删
- 头插
- 头删
- find函数
- 在任意位置之前插入
- 任意位置的删除
- 全部代码
- list.h
- list.c
- test.c
- 链表和顺序表的区别
链表的分类
如下
根据上述的三种组合,一共分为8类
实现带有哨兵位的双向的循环链表
定义节点的结构
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType data;
}LTNode;
初始化单个节点
LTNode* BuyListNode(LTDataType x)
{
LTNode* newnode=(LTNode*)malloc(sizeof(LTNode));
if (newnode==NULL)
{
perror("malloc fail");
}
newnode->next = NULL;
newnode->prev = NULL;
newnode->data = x;
return newnode;
}
初始化带有哨兵位的双向循环链表
作用:使链表在无数据的情况下就具有循环的特点
LTNode* LTInit()
{
LTNode* phead = BuyListNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
打印链表
哨兵位phead一定不能为空,否则无法连接下面的节点
void LTPrint(LTNode* phead)
{
assert(phead);
printf("<=head=>");
LTNode* cur = phead->next;
while (cur!=phead)
{
printf("%d<=>",cur->data);
cur = cur->next;
}
printf("\n");
}
销毁链表
void LPDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur!=phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
}
尾插
需要三个节点即可布置好顺序
顺序为:tail newnode phead
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LTNode* tail = phead->prev;
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
尾删
增加一个函数用于判断除哨兵位以外是否有其它的节点
bool类型只有两个取值:true和false
需要三个节点,顺序为:tailprev,tail,phead
bool LTEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTNode* tail = phead->prev;
LTNode* tailprev = tail->prev;
tailprev->next = phead;
phead->prev = tailprev;
free(tail);
}
头插
需要三个节点即可布置好顺序
顺序为:phead newnode first
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LTNode* first = phead->next;
newnode->next = first;
first->prev = newnode;
phead->next = newnode;
newnode->prev = phead;
}
头删
增加一个函数用于判断除哨兵位以外是否有其它的节点
bool类型只有两个取值:true和false
需要三个节点,顺序为:phead,tail,tailnex
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTNode* tail = phead->next;
LTNode* tailnex = tail->next;
phead->next = tailnex;
tailnex->prev = phead;
free(tail);
}
find函数
从哨兵位的下一个节点开始遍历,直至找到该元素或者遍历到哨兵位结束
LTNode* LTFind(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur!=phead)
{
if (cur->data==x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
在任意位置之前插入
任意位置之前的插入需要搭配find函数来使用,需要三个节点,顺序为:posprev,newnode,pos
可以使用它来进行头插和尾插,分别可以表示为LTInsert(phead->next, x) LTInsert(phead, x)
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode= BuyListNode(x);
LTNode* posprev = pos->prev;
newnode->next = pos;
pos->prev = newnode;
posprev->next = newnode;
newnode->prev = posprev;
}
任意位置的删除
道理同任意位置的插入
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* posprev = pos->prev;
LTNode* posnex = pos->next;
posprev->next = posnex;
posnex->prev = posprev;
free(pos);
}
结果如下:
全部代码
list.h
#pragma once
#include <stdlib.h>
#include <stdio.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);
//初始化
LTNode* LTInit();
//
void LTPrint(LTNode* phead);
void LPDestroy(LTNode* phead);
//尾插
void LTPushBack(LTNode* phead, LTDataType x);
//尾删
void LTPopBack(LTNode* phead);
//头插
void LTPushFront(LTNode* phead, LTDataType x);
//头删
void LTPopFront(LTNode* phead);
LTNode* LTFind(LTNode* phead, LTDataType x);
//任意位置之前的插入
void LTInsert(LTNode* pos, LTDataType x);
//任意位置的删除
void LTErase(LTNode* pos);
list.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "list.h"
LTNode* BuyListNode(LTDataType x)
{
LTNode* newnode=(LTNode*)malloc(sizeof(LTNode));
if (newnode==NULL)
{
perror("malloc fail");
}
newnode->next = NULL;
newnode->prev = NULL;
newnode->data = x;
return newnode;
}
LTNode* LTInit()
{
LTNode* phead = BuyListNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
void LTPrint(LTNode* phead)
{
assert(phead);
printf("<=head=>");
LTNode* cur = phead->next;
while (cur!=phead)
{
printf("%d<=>",cur->data);
cur = cur->next;
}
printf("\n");
}
void LPDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur!=phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
}
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LTNode* tail = phead->prev;
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
bool LTEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTNode* tail = phead->prev;
LTNode* tailprev = tail->prev;
tailprev->next = phead;
phead->prev = tailprev;
free(tail);
}
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LTNode* first = phead->next;
newnode->next = first;
first->prev = newnode;
phead->next = newnode;
newnode->prev = phead;
}
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
LTNode* tail = phead->next;
LTNode* tailnex = tail->next;
phead->next = tailnex;
tailnex->prev = phead;
free(tail);
}
LTNode* LTFind(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur!=phead)
{
if (cur->data==x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode= BuyListNode(x);
LTNode* posprev = pos->prev;
newnode->next = pos;
pos->prev = newnode;
posprev->next = newnode;
newnode->prev = posprev;
}
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* posprev = pos->prev;
LTNode* posnex = pos->next;
posprev->next = posnex;
posnex->prev = posprev;
free(pos);
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "list.h"
void Test1()
{
LTNode* plist = LTInit();
LTPushBack(plist, 1);
LTPushBack(plist, 2);
LTPushBack(plist, 3);
LTPushBack(plist, 4);
LTPrint(plist);
LTPopBack(plist);
LTPopBack(plist);
LTPrint(plist);
}
void Test2()
{
LTNode* plist = LTInit();
LTPushBack(plist, 1);
LTPushBack(plist, 2);
LTPushBack(plist, 3);
LTPushBack(plist, 4);
LTPushFront(plist, 100);
LTPushFront(plist, 200);
LTPushFront(plist,300);
LTPrint(plist);
LTPopFront(plist);
LTPrint(plist);
}
void Test3()
{
LTNode* plist = LTInit();
LTPushBack(plist, 1);
LTPushBack(plist, 2);
LTPushBack(plist, 3);
LTNode* pos=LTFind(plist, 3);
if (pos==NULL)
{
perror("pos NULL");
}
LTInsert(pos, 100);
LTPrint(plist);
LTErase(pos);
LTPrint(plist);
}
int main()
{
//Test1();
//Test2();
Test3();
return 0;
}
链表和顺序表的区别
局部性原理:加载指定的数据,可能会把与其物理内存之后的数据加载上去。
根据局部性原理,顺序表在载入缓存中时,可能会把与之相邻的数据一同载入,但是,链表的地址并不是相邻的,在载入相应的链表的时候不会把相邻的链表载入缓存。
因此顺序表的缓存利用率比较高。