文章目录
0.前言
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,同样我们也采用分文件的方式实现。
1. List.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int LTDataType;
typedef struct ListNode
{
LTDataType data;
struct ListNode* next;
struct ListNode* prev;
}LTNode;
void ListPrint(LTNode* phead);
LTNode* ListInit();
void ListDestroy(LTNode* phead);
LTNode* BuyLTNode(LTDataType x);
void ListPushBack(LTNode* phead, LTDataType x);
void ListPopBack(LTNode* phead);
void ListPushFront(LTNode* phead, LTDataType x);
void ListPopFront(LTNode* phead);
LTNode* ListFind(LTNode* phead, LTDataType x);
// 在pos之前插入
void ListInsert(LTNode* pos, LTDataType x);
//void ListInsert(int i, LTDataType x);
// 删除pos位置的节点
void ListErase(LTNode* pos);
2. List.c
2.1 开辟一个新节点
LTNode* BuyLTNode(LTDataType x)
{
LTNode *newnode = (LTNode*)malloc(sizeof(LTNode));
if(newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
newnode->data = x;
newnode->prev = NULL;
newnode->next = NULL;
return newnode;
}
2.2 初始化链表
LTNode* ListInit()
{
LTNode* phead = BuyLTNode(0);
phead->next = phead;
phead->prev = phead;
return phead;
}
2.3 摧毁链表
void ListDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while(cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
phead = NULL;
}
2.4 尾插
多个节点的情况:
这种方法也能解决空链表的情况
代码实现:
void ListPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode *tail = phead->prev;
LTNode *newnode = BuyLTNode(X);
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
2.5 和之前不带哨兵位的单链表传参的区别
带哨兵位的双向循环链表:
不带哨兵位的单链表:
2.6 尾删
多个节点:
这种方法也能解决一个节点的情况:
void ListPopBack(LTNode* phead)
{
assert(phead);
//链表为空
assert(phead !=phead->next);
LTNode* tail = phead->prev;
LTNode* tailPrev = tail->prev;
free(tail);
tail = NULL;
tailPrev->next = phead;
phead->prev = tailPrev;
}
2.7 打印链表
我们发现cur
指针再次回到phead
为结束条件。
void ListPrint(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while(cur != phead)
{
printf("%d ",cur->data);
cur = cur->next;
}
printf("\n\n");
}
2.8 头插
void ListPushFront(LTNode* phead, LTDateType x)
{
assert(phead);
LTNode* first = phead->next;
LTNode* newnode = BuyLTNode(x);
phead->next = newnode;
newnode->prev = phead;
newnode->next = first;
first->prev = newnode;
}
2.9 头删
void ListPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
LTNode* first = phead->next;
LTNode* second = first->next;
free(first);
phead->next = second;
second->prev = phead;
}
2.10 查找
LTNode* ListFind(LTNode* phead, LTDateType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
2.11 在pos之前插入
void ListInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = BuyLTNode(x);
LTNode* posPrev = pos->prev;
newnode->next = pos;
pos->prev = newnode;
posPrev->next = newnode;
newnode->prev = posPrev;
}
2.12 删除pos位置的节点
void ListErase(LTNode* pos)
{
assert(pos);
LTNode* prev = pos->prev;
LTNode* next = pos->next;
prev->next = next;
next->prev = prev;
free(pos);
}
2.13 10min实现双向循环链表
//尾插
void ListPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
ListInsert(phead,x);
}
//尾删
void ListPopBack(LTNode* phead)
{
assert(phead);
ListErase(phead->prev);
}
//头插
void ListPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
ListInsert(phead,x);
}
//头删
void ListPopFront(LTNode* phead)
{
assert(phead);
ListErase(phead->next);
}
2.14 List.c完整代码
#include"List.h"
LTNode* BuyLTNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
void ListPrint(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n\n");
}
void ListDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while(cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
phead = NULL;
}
LTNode* ListInit()
{
LTNode* phead = BuyLTNode(0);
phead->next = phead;
phead->prev = phead;
return phead;
}
void ListPushBack(LTNode* phead, LTDataType x)
{
//assert(phead);
//LTNode* tail = phead->prev;
//LTNode* newnode = BuyLTNode(x);
//tail->next = newnode;
//newnode->prev = tail;
//newnode->next = phead;
//phead->prev = newnode;
ListInsert(phead, x);
}
void ListPopBack(LTNode* phead)
{
assert(phead);
// Ϊ
assert(phead->next != phead);
LTNode* tail = phead->prev;
LTNode* tailPrev = tail->prev;
free(tail);
tail = NULL;
tailPrev->next = phead;
phead->prev = tailPrev;
}
void ListPushFront(LTNode* phead, LTDateType x)
{
assert(phead);
LTNode* first = phead->next;
LTNode* newnode = BuyLTNode(x);
phead->next = newnode;
newnode->prev = phead;
newnode->next = first;
first->prev = newnode;
}
void ListPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
LTNode* first = phead->next;
LTNode* second = first->next;
free(first);
phead->next = second;
second->prev = phead;
}
LTNode* ListFind(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 ListInsert(LTNode* pos, LTDataType x)
{
assert(pos);
/*LTNode* newnode = BuyLTNode(x);
pos->prev->next = newnode;
newnode->prev = pos->prev;
pos->prev = newnode;
newnode->next = pos;*/
LTNode* newnode = BuyLTNode(x);
LTNode* posPrev = pos->prev;
newnode->next = pos;
pos->prev = newnode;
posPrev->next = newnode;
newnode->prev = posPrev;
}
void ListErase(LTNode* pos)
{
assert(pos);
LTNode* prev = pos->prev;
LTNode* next = pos->next;
prev->next = next;
next->prev = prev;
free(pos);
}
3.Test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "List.h"
void TestList1()
{
LTNode* pList = ListInit();
ListPushBack(pList, 1);
ListPushBack(pList, 2);
ListPushBack(pList, 3);
ListPushBack(pList, 4);
ListPushBack(pList, 5);
ListPushBack(pList, 6);
ListPrint(pList);
ListPopBack(pList);
ListPopBack(pList);
ListPopBack(pList);
ListPopBack(pList);
ListPopBack(pList);
ListPopBack(pList);
//ListPopBack(pList);
ListPrint(pList);
ListDestroy(plist);
}
void TestList2()
{
//LTNode* pList = NULL;
//ListInit(&pList);
LTNode* pList = ListInit();
ListPushBack(pList, 1);
ListPushBack(pList, 2);
ListPushBack(pList, 3);
ListPushBack(pList, 4);
ListPrint(pList);
ListPushFront(pList, 0);
ListPushFront(pList, -1);
ListPrint(pList);
LTNode* pos = ListFind(pList, 3);
if (pos)
{
ListInsert(pos, 30);
}
ListPrint(pList);
}
int main()
{
TestList1();
return 0;
}
4. 运行结果
5. 顺序表和链表的区别
不同点 | 顺序表 | 链表 |
---|---|---|
存储空间上 | 物理上一定连续 | 逻辑上连续,但物理上不一定连续 |
随机访问 | 支持O(1) | 不支持:O(N) |
任意位置插入或者删除元素 | 可能需要搬移元素,效率低O(N) | 只需修改指针指向 |
插入 | 动态顺序表,空间不够时需要扩容 | 没有容量的概念 |
应用场景 | 元素高效存储+频繁访问 | 任意位置插入和删除频繁 |
缓存利用率 | 高 | 低 |
备注:缓存利用率参考存储体系结构以及局部原理性。
与程序员相关的cpu缓存知识