目录
1. 带头双向循环链表
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向 循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而 简单了,后面我们代码实现了就知道了。
2. 实现带头双向循环链表:
2.1创建头文件“LTlist.h”
#pragma once #include<stdio.h> #include<stdlib.h> #include<assert.h> #include<string.h> #include<stdbool.h> typedef int LTDataType; typedef struct LTlist { LTDataType data; struct LTlist* prev; struct LTlist* next; }LTNode; LTNode* BuyListNode(LTDataType x);//申请结点空间 LTNode* LTInit();//初始化 void LTPrint(LTNode* phead); void LTPushfront(LTNode* phead, LTDataType x); void LTPopfront(LTNode* phead); void LTPushBack(LTNode* phead, LTDataType x); void LTPopBack(LTNode* phead); LTNode* LTFind(LTNode* phead, LTDataType x); void LTInsert(LTNode* pos, LTDataType x); void LTErase(LTNode* pos); bool LTEmpty(LTNode* phead); size_t LTSize(LTNode* phead);//链表长度 void LTDestroy(LTNode* phead);
这里我们用来结构体的嵌套来定义双链表的节点结构体的嵌套定义
2.2 创建具体接口实现文件LTlist.c
先引用#include "LTlist.h"
2.2.1打印
void LTPrint(LTNode* phead) { assert(phead); LTNode* cur = phead->next; while (cur != phead) { printf("%d ", cur->data); cur = cur->next; } printf("\n"); }
2.2.2申请链表结点
LTNode* BuyListNode(LTDataType x) { LTNode* node = (LTNode*)malloc(sizeof(LTNode));//先申请结点 if (node == NULL) { perror("malloc fail"); exit(-1); } node->data = x; node->next = NULL; node->prev = NULL; return node; }//申请结点空间
在这里我们用了扩容,但我们使用的是malloc,和顺序表有所不同的是我们并不需要异地扩容,对于链表来说存储的位置本就是随机的,不需要整块连续的空间。
2.2.3初始化
LTNode* LTInit() { LTNode* phead = BuyListNode(-1); phead->next = phead; phead->prev = phead; return phead; }//初始化
2.2.4尾插尾删
void LTPushBack(LTNode* phead, LTDataType x) { assert(phead); LTNode* newnode = BuyListNode(x); phead->prev->next = newnode; newnode->prev = phead->prev; newnode->next = phead; phead->prev = newnode; } void LTPopBack(LTNode* phead) { assert(phead); assert(phead->next != phead); LTNode* cur = phead->prev; cur->prev->next = phead; phead->prev = cur->prev; free(cur); }
2.2.5头插头删
void LTPushfront(LTNode* phead, LTDataType x) { assert(phead); LTNode* newnode = BuyListNode(x); newnode->next = phead->next; phead->next->prev = newnode; phead->next = newnode; newnode->prev = phead; }//注意理解头插的一个过程 void LTPopfront(LTNode* phead) { assert(phead); assert(phead->next); LTNode*first = phead->next; LTNode* second = first->next; free(first); phead->next = second; second->prev = phead; }
2.2.6寻找x元素,返回pos
LTNode* LTFind(LTNode* phead, LTDataType x) { assert(phead); LTNode* cur = phead->next; while (cur!=phead)//特别注意这里的循环失效条件是不是NULL; { if (cur->data == x) return cur; cur = cur->next; } return NULL; }
2.2.7统计链表长度和判空
bool LTEmpty(LTNode* phead) { assert(phead); if (phead->next == phead) { return true; } else { return false; } } size_t LTSize(LTNode* phead) { LTNode* cur = phead->next; size_t size = 0; while (cur!=phead) { size++; cur = cur->next; } }//链表长度
2.2.8插入pos之前的位置和删除pos位置
void LTInsert(LTNode* pos, LTDataType x) { assert(pos); LTNode* newnode = BuyListNode(x); newnode->next = pos; pos->prev->next = newnode; newnode->prev = pos->prev; pos->prev = newnode; //插入的是pos之前的位置 } void LTErase(LTNode* pos) { assert(pos); LTNode* cur = pos; cur->prev->next = cur->next; cur->next->prev = cur ->prev; free(cur); }
2.2.9 销毁链表
void LTDestroy(LTNode* phead) { //assert(phead); //LTNode* cur = phead->prev; //LTNode* cprior = cur->prev; //while (cur!= phead) //{ // free(cur); // cur = cprior; // cprior = cprior->prev; //} //free(cur); assert(phead); LTNode* cur = phead->next; while (cur != phead) { LTNode* next = cur->next; free(cur); cur = next; } free(phead); }
3.主函数的实现
#include "LTlist.h"
void Test1()
{
LTNode* phead = LTInit();
LTPushfront(phead, 1);
LTPushfront(phead, 2);
LTPushfront(phead, 3);
LTPushfront(phead, 4);
LTPushfront(phead, 5);
LTPrint(phead);
LTPopfront(phead);
LTPopfront(phead);
LTPopfront(phead);
LTPrint(phead);
//尾插测试
LTPushBack(phead, 100);
LTPushBack(phead, 200);
LTPushBack(phead, 300);
LTPrint(phead);
//尾删测试
LTPopBack(phead);
LTPopBack(phead);
LTPrint(phead);
LTNode*pos=LTFind(phead, 1);
//插入pos测试
LTInsert(pos, 999);
LTPrint(phead);
//删除pos
LTErase(pos);
LTPrint(phead);
}
int main()
{
Test1();
}