一般来说, 链表我们用的最多的是不带头单向链表 和 带头双向循环链表。
所谓带头与不带头的区别在于头结点是否存储数据。
1、带头意味着头结点不会真正存放结点信息, 一般存放一些记账信息(例如链表多长等)
2、不带头意味着会真正存放结点信息。
(但是请注意实现的时候我们还是会创建一个结点用来管理 – 为了方便实现)
在这里我讲一个小技巧, 用来区分带头与不带头。
在init初始化的时候, 带头的会开辟空间, 不带头会置为nullptr。 (置为nullptr的话,方便插入元素的时候判断头结点是否为空。)
双向循环链表的优势?
由于结点信息中多包含了一个指向上一个结点的指针。 这样操作起来就特别方便。它弥补了单链表的缺点。 使得链表更加灵活、实用。 (举个例子:在之前做的判断回文链表, 对于双向循环链表就更加简单了。 不需要反转和求中间结点。 直接一个循环就搞定了!)
缺点呢? 要说缺点也只能是实现需要注意连接吧!
下面是代码。
contact.h
#pragma once
/*
project: 双向循环链表(带头)
time: 2020-6-12
name: HMW
*/
#include
#include
//#include
typedef int LTDataType; //重定义int
//结点信息
typedef struct ListNode
{
LTDataType data; //data
struct ListNode *_prev; //前指针
struct ListNode *_next; //后指针
}ListNode;
//头节点的结构
typedef struct List
{
ListNode *_head; //头结点
}List;
void Init_List(List *pList); //初始化头节点
void Destroy_List(List *pList); //销毁节点,并释放堆内存空间
ListNode *Create_Node(List *pList, LTDataType x); //创建节点
void Print_List(List *pList); //打印链表
void ListPushBack(List *pList, LTDataType x); //尾插操作
void ListPushFront(List *pList, LTDataType x); //头插操作
void ListPopBack(List *plist); //尾删操作
void ListPopFront(List *plist); //头删操作
ListNode* ListFind(List *plist, LTDataType x); //寻找x值的节点,并返回
void ListInsert(List *plist, ListNode* pos, LTDataType x); //插入任意位置的节点
void ListErase(List *plist, ListNode* pos); //删除pos节点的值
void ListRemove(List *plist, LTDataType x); //删除x值的节点
contact.c
#include"contact.h"
//初始化头节点
void Init_List(List *pList)
{
if (!pList)
return;
pList->_head = (ListNode *)malloc(sizeof(ListNode)); //头结点开辟空间
if (!pList)
exit(0);
pList->_head->data = 0; //data初始化为0
pList->_head->_next = pList->_head->_prev = pList->_head; //设置为自环
}
//销毁节点,并释放堆内存空间
void Destroy_List(List *pList)
{
if (!pList)
return;
ListNode *prev = pList->_head->_next; //头结点下一个结点
ListNode *cur = NULL;
while (prev != pList->_head)
{
cur = prev->_next;
free(prev);
prev = cur;
}
free(prev); //头节点最后释放
prev = NULL;
}
ListNode *Create_Node(List *pList, LTDataType value)
{
if (!pList)
return NULL;
ListNode *p_temp = (ListNode *)malloc(sizeof(ListNode));
if (!p_temp)
exit(0);
p_temp->data = value;
return p_temp;
}
//尾插操作
void ListPushBack(List* pList, LTDataType x)
{
if (!pList)
return;
ListNode *prev = pList->_head->_prev;
ListNode *new_Node = Create_Node(pList, x);
if (!new_Node)
exit(0);
prev->_next = new_Node;
new_Node->_prev = prev;
new_Node->_next = pList->_head;
pList->_head->_prev = new_Node;
}
//打印链表
void Print_List(List *pList)
{
if(!pList)
return;
ListNode *p_temp = pList->_head->_next;
while (p_temp != pList->_head)
{
printf("%d ", p_temp->data);
p_temp = p_temp->_next;
}
printf("\n");
}
//头插操作
void ListPushFront(List* pList, LTDataType x)
{
if (!pList)
return;
ListNode *next = pList->_head->_next;
ListNode *new_Node = Create_Node(pList, x);
if (!new_Node)
exit(0);
next->_prev = new_Node;
new_Node->_next = next;
pList->_head->_next = new_Node;
new_Node->_prev = pList->_head;
}
//尾删操作
void ListPopBack(List* plist)
{
if (!plist)
return;
ListNode *prev = plist->_head->_prev->_prev;
ListNode *ToBeDelete_Node = plist->_head->_prev;
prev->_next = plist->_head;
plist->_head->_prev = prev;
free(ToBeDelete_Node);
ToBeDelete_Node = NULL;
}
//头删操作
void ListPopFront(List* plist)
{
if (!plist)
return;
ListNode *next = plist->_head->_next->_next;
ListNode *ToBeDelete_Node = plist->_head->_next;
next->_prev = plist->_head;
plist->_head->_next = next;
free(ToBeDelete_Node);
ToBeDelete_Node = NULL;
}
//查找结点
ListNode* ListFind(List* plist, LTDataType x)
{
if (!plist)
return NULL;
ListNode *p_temp = plist->_head->_next;
while (p_temp != plist->_head)
{
if (p_temp->data == x)
{
return p_temp;
}
p_temp = p_temp->_next;
}
return NULL;
}
//任意位置插入操作
void ListInsert(List* plist, ListNode* pos, LTDataType x)
{
if (!pos)
return;
ListNode *new_Node = Create_Node(plist, x);
if (!new_Node)
exit(0);
ListNode *prev = pos->_prev;
prev->_next = new_Node;
new_Node->_prev = prev;
new_Node->_next = pos;
pos->_prev = new_Node;
}
//删除pos节点
void ListErase(List* plist, ListNode* pos)
{
if (!plist)
return;
ListNode *prev = pos->_prev;
ListNode *next = pos->_next;
prev->_next = next;
next->_prev = prev;
free(pos);
}
test.c
#include"contact.h"
//今天学习循环链表--循环双向带头链表
void test_LTlist()
{
List pList;
//初始化
Init_List(&pList);
printf("尾插1,2,3,4,5,6,7 ; 头插8,9,10,11,12\n");
ListPushBack(&pList, 1);
ListPushBack(&pList, 2);
ListPushBack(&pList, 3);
ListPushBack(&pList, 4);
ListPushBack(&pList, 5);
ListPushBack(&pList, 6);
ListPushBack(&pList, 7);
ListPushFront(&pList, 8);
ListPushFront(&pList, 9);
ListPushFront(&pList, 10);
ListPushFront(&pList, 11);
ListPushFront(&pList, 12);
Print_List(&pList);
printf("头删、尾删一次!\n");
ListPopBack(&pList);
ListPopFront(&pList);
Print_List(&pList);
ListNode *p = ListFind(&pList, 1);
if (!p)
{
printf("val为1的结点没有找到!\n");
}
else {
printf("val为1的结点找到了!\n");
}
printf("插入val为13\n");
ListInsert(&pList, p, 13);
Print_List(&pList);
printf("删除val为1的结点\n");
ListErase(&pList, p);
Print_List(&pList);
Destroy_List(&pList);
}
int main()
{
test_LTlist();
system("pause");
return 0;
}