一、链表的种类
单向 | 双向 |
单向带头循环链表 | 双向带头循环链表 |
单向带头不循环链表 | 双向带头不循环链表 |
单向不带头循环链表 | 双向不带头循环链表 |
单向不带头不循环链表 | 双向不带头不循环链表 |
二、双向带头循环链表
其中单向不带头不循环链表和双向循环链表最为常见,而双向循环链表看起来好像很复杂,但是由于它是带头的又是双向循环的,反而给操作上带来了很多方便。下面主要主要实现双向带头循环链表。
双向循环链表和单链表都是由结点组成的,单链表包含一个数据域和一个指针域构成,而双向循环链表不同,它是由一个数据域和两个指针域组成,其中指针包含前驱指针(prev)和后继指针(next)。
三、代码实现
(1)List.h文件
typedef int LTDataType;
typedef struct ListNode
{
LTDataType Data;
struct ListNode* next;
struct ListNode* prev;
}LTNode;
LTNode* ListInit();
//双向带头循环链表的初始化
void ListPrint(LTNode* phead);
//打印
LTNode* BuyListNode(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);
//查找
void ListInsert(LTNode* pos,LTDataType x);
//在pos之前插入
void ListErase(LTNode* pos);
//在pos位置删除
void ListDestroy(LTNode* phead);
//销毁
(2)List.c文件
LTNode* ListInit()
{
LTNode* phead=(LTNode*)malloc(sizeof(LTNode));
//创建头节点
phead->next=phead;//后继指针指向头
phead->prev=phead;//前驱指针指向头
return phead;
}
void ListPrint(LTNode* phead)
{
assert(phead);
LTNode* cur=phead->next;
while(cur!=phead)
{
printf("%d->",cur->Data);
cur=cur->next;
}
printf("\n");
}
LTNode* BuyListNode(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 ListPushBack(LTNode* phead,LTDataType x)
{
assert(phead);
LTNode* tail=phead->prev;
LTNode* newnode=BuyListNode(x);
tail->next=newnode;
newnode->prev=tail;
newnode->next=phead;
phead->prev=newnode;
}
void ListPopBack(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 ListPushFront(LTNode* phead,LTDataType x)
{
assert(phead);
LTNode* newnode=BuyListNode(x);
LTNode* next=phead->next;
phead->next=newnode;
newnode->prev=phead;
newnode->next=next;
next->prev=newnode;
}
void ListPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next!=phead);
LTNode* next=phead->next;
LTNode* nextNext=next->next;
phead->next=next->next;
nextNext->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* posPrev=pos->prev;
LTNode* newnode=BuyListNode(x);
posPrev->next=newnode;
newnode->prev=posPrev;
newnode->next=pos;
pos->prev=newnode;
}
void ListErase(LTNode* pos)
{
assert(pos);
LTNode* posPrev=pos->prev;
LTNode* posNext=pos->next;
posPrev->next=posNext;
posNext->next=posPrev;
free(pos);
pos=NULL;
}
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;
}
(3)test.c文件
void menu()
{
printf("*******************带头双循环链表************************\n");
printf("************* 1.尾插 *************\n");
printf("************* 2.头插 *************\n");
printf("************* 3.尾删 *************\n");
printf("************* 4.头删 *************\n");
printf("************* 5.查询n位置 *************\n");
printf("************* 6.在n位置位前插入元素x *************\n");
printf("************* 7.删除n位置的元素 *************\n");
printf("************* 8.打印链表 *************\n");
printf("************* -1.退出链表 *************\n");
printf("***********************************************************\n");
}
int main()
{
LTNode* pList = ListInit();
menu();
int option = -1;
do
{
printf("请输入选项:> ");
scanf("%d", &option);
if (option == 1)
{
int x = 0;
printf("请输入你要尾插的元素:>");
scanf("%d",&x);
ListPushBack(pList, x);
}
else if (option == 2)
{
int x = 0;
printf("请输入你要头插的数字:>");
scanf("%d", &x);
ListPushFront(pList, x);
}
else if (option == 3)
{
//尾删
ListPopBack(pList);
}
else if (option == 4)
{
//头删
ListPopFront(pList);
}
else if (option == 5)
{
int x = 0;
printf("请输入你要查找的值x:>");
scanf("%d", &x);
LTNode* pos = ListFind(pList, x);
if (pos != NULL)
{
printf("存在数字%d\n", x);
}
else
{
printf("未找到数字%d\n", x);
}
}
else if (option == 6)
{
int x = 0;
int y = 0;
printf("请分别输入n位置值x和n位置前所插入的值y:>");
scanf("%d %d", &x, &y);
LTNode* n = ListFind(pList, x);
if (n != NULL)
{
ListInsert(n, y);
}
else
{
printf("该链表不存在%d\n", x);
}
}
else if (option == 7)
{
int x = 0;
printf("请输入要删除第n个位置的值:>");
scanf("%d", &x);
LTNode* n= ListFind(pList, x);
ListErase(n);
}
else if (option == 8)
{
ListPrint(pList);
}
} while (option != -1);
ListDestroy(pList);
return 0;
}
(4)makefile文件
src=$(wildcard *.c)
obj=$(patsubst %.c,%.o,$(src))
ALL:test
test:$(obj)
gcc $^ -pthread -o $@
clean:
-rm -rf $(obj) test
%.o:%.c
gcc -c $< -o $@
.PHONY:clean ALL