上一篇我们已经实现了无头单向非循环链表,下面我们来实现带头双向循环链表:
带头双向循环链表的实现:
List.h 函数声明
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDateTyper;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDateTyper val;
}LTNode;
//初始化
LTNode* LTInit();
//尾插
void LTPushBack(LTNode* phead, LTDateTyper x);
//尾删
void LTPopBack(LTNode* phead);
//头插
void LTPushFront(LTNode* phead, LTDateTyper x);
//头删
void LTPopFront(LTNode* phead);
//双向链表查找
LTNode* LTFind(LTNode*phead,LTDateTyper x);
//双向链表再pos位置前进行插入
void LTInsert(LTNode*pos, LTDateTyper x);
//双向链表删除pos位置的节点
void LTErease(LTNode* pos);
//打印
void LTPrint(LTNode* phaed);
list.c 函数定义
#include"List.h"
//创建节点
LTNode* CreateNode(LTDateTyper x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("mallo fail");
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
//初始化
//头节点
LTNode* LTInit()
{
LTNode* phead = CreateNode(-1);
phead->next = phead;
phead ->prev = phead;
return phead;
}
//尾插
void LTPushBack(LTNode* phead, LTDateTyper x)
{
//链表带头可以处理空链表
LTNode* tail = phead->prev;
LTNode* newnode = CreateNode(x);
//phead tail newnode
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
//尾删
void LTPopBack(LTNode* phead)
{
assert(phead);
//真就过了,假就报错
assert(phead->next != phead);
LTNode* tail = phead->prev;
LTNode* tailprev = tail->prev;
free(tail);
tailprev->next = phead;
phead->prev = tailprev;
}
//头插
void LTPushFront(LTNode* phead, LTDateTyper x)
{
assert(phead);
LTNode* tail = phead->next;
LTNode* newnode = CreateNode(x);
phead->next = newnode;
newnode->prev = phead;
newnode->next = tail;
tail->prev = newnode;
}
//头删
void LTPopFront(LTNode* phead)
{
//无死角,不会出现空,但会出现野
assert(phead);
assert(phead->next != phead);
LTNode* first = phead->next;
LTNode* second = first->next;
phead->next = second;
second->prev = phead;
free(first);
first = NULL;
}
//双向链表查找
LTNode* LTFind(LTNode* phead,LTDateTyper x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->val == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
//双向链表再pos位置前进行插入
void LTInsert(LTNode* pos, LTDateTyper x)
{
assert(pos);
LTNode* newnode = CreateNode(x);
newnode->prev = pos->prev;
pos->prev ->next = newnode;
newnode->next = pos;
pos->prev = newnode;
}
//双向链表删除pos位置的节点
void LTErease(LTNode* pos)
{
assert(pos);
LTNode* cur = pos->prev;
LTNode* tail = pos->next;
cur->next = tail;
tail->prev = cur;
free(pos);
}
//打印
void LTPrint(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
printf("哨兵位");
while (cur != phead)
{
printf("%d<=>", cur->val);
cur = cur->next;
}
printf("\n");
}
test.c
//带头双向循环链表
#include"List.h"
int main()
{
//尾插
LTNode* plist = LTInit();
LTPushBack(plist, 2);
LTPushBack(plist, 3);
LTPushBack(plist, 7);
LTPushBack(plist, 8);
LTPrint(plist);
//尾删
LTPopBack(plist);
LTPrint(plist);
//头插
LTPushFront(plist, 6);
LTPushFront(plist, 5);
LTPrint(plist);
//头删
LTPopFront(plist);
LTPrint(plist);
LTNode*pos=LTFind(plist, 2);
if (pos)
{
pos->val *= 10;
}
LTPrint(plist);
//在pos位置进行插入
LTInsert(pos,3000);
LTPrint(plist);
LTErease(pos);
LTPrint(plist);
return 0;
}
顺序表和链表的区别
顺序表的问题:
1.头插,中间插入,删除效率低,需要挪动数据O(N)
2.空间不够扩容,扩容有一定的消耗,且可能存在一定的空间浪费
优势:
物理连续,支持下标的随机访问,适合尾插,尾删,元素高效存储+频繁访问。
缓存利用率:高
链表:
1.任意位置的插入删除都是O(1)。
2.按需申请释放,合理利用空间,不存在浪费。
问题:
随机访问不方便O(N)。
缓存利用率:低