#include <iostream>
using namespace std;
int main()
{
typedef int ListType;
typedef struct ListNode
{
ListType data;//无需写成指针类型,因为这是 每一个单个结点空间通过指针链接而成,而不是连续的内存空间
struct ListNode *prev;
struct ListNode *next;
}ListNode;
//创建一个新的结点:
ListNode *BuyListNode(ListType x)
{
ListNode *newnode = (ListNode *)malloc(sizeof (ListNode));//需要在 malloc 前面声明出结点的类型
/*ListNode *prev = NULL; //已经malloc出了结点,无需再次重复创建
ListNode *next = NULL;*/
newnode->prev = NULL;
newnode->next = NULL;
newnode->data = x;
return newnode;
}
//双向循环带头链表的初始化函数:(这里的初始化是对于每一个结点单独的初始化,此时需要单独的创建构成链表中的每一个结点)
/*因为有哨兵位(头结点也是结点)的存在,*/
ListNode *ListInit()
{
ListNode *head = BuySListNode(0);//这里的0属于链表结点数据域的初始值
head->prev = head;//先将值初始化为自己本身
head->next = head;
return head;//初始化+创建了结点之后就需要返回这个结点的地址,不然无法与下一个结点进行链接
}
//双向带头循环链表的尾插:
void ListPushBack(ListNode *head,ListType x)
{
ListNode *newnode = BuySListNode(x);
newnode->prev = head->prev;
head->prev->next = newnode;
//不要忘了插入最后一个结点后还要与整个链表的头结点重新进行链接
newnode->next = head;
head->prev = newnode;
}
//双向带头循环链表的头插
/*特别注意:
是在哨兵位之后第一个结点之前进行头插,而不是在哨兵位之前进行头插*/
void ListPushHead(ListNode *head, ListType x)
{
ListNode *newnode = BuyListNode(x);
newnode->next = head->next;
newnode->prev = head;
head->next->prev = newnode;
head->next = newnode;
}
//双向带头循环链表的尾删:
void ListPopBack(ListNode *head)
{
ListNode *tmp = head->prev->prev;
free(head->prev);
head->prev = tmp;
tmp->next = head;
}
//双向带头循环链表的头删:
/*同样需要注意:
头删的不是哨兵位,而是哨兵位之后的第一个结点*/
void ListPopHead(ListNode *head)
{
assert(head);
ListNode *tmp = head->next->next;//将哨兵位之后的第二个结点的地址进行存储
free(head->next);
head->next = tmp;
tmp->prev = head;
}
//查找 带头双向循环 链表中的某一个结点(重要,易错)
ListNode *FindListNode(ListNode *head, ListType x)
{
assert(head);
ListNode *cur = head->next;//跳过哨兵位从第一个结点开始遍历,因为哨兵位的数据域不存放值
while (cur != head)/*遍历的条件应该以第一个结点是否到达最后一个结点,
然后在循环体内部判断每一次遍历到的结点中的数据域
是否满足条件;
而不是直接在while的条件判断中以每一个结点的数据域
是否等于给定值,因为这样只能在结束循环体时才能拿到
对应的结点,而循环的结束也有可能是因为没有找到结点,
而遍历结束的原因;虽然也可通过中间变量记录,但不太整洁*/
{
if (cur->data == x)
{
return cur;
}
else
{
cur = ccur->next;
}
}
return NULL;//while循环结束后还没有找到
}
//对 带头双向循环 链表的其中一个结点(给定函数该结点的地址)进行删除的函数:
/*注意:
因为是对链表中任意一个结点的删除,
所以头删,与尾删都可以复用该函数。*/
void ListErase(ListNode *pos)
{
assert(pos);
ListNode *tmp = pos->prev;
tmp->next = pos->next;
pos->next->prev = tmp;
free(pos);
pos = NULL;
}
//将带头循环双向链表的其中一个结点地址传入实现“任意结点前插入”的函数:
/*同理:
有了任意位置插入元素函数的实现,我们的头插、尾插函数便可以复用这个函数来实现*/
void ListInsert(ListNode *pos,ListType x)
{
assert(pos);
ListNode *tmp = pos->prev;
ListNode *newnode = BuyListNode(x);
tmp->next = newnode;
pos->prev = newnode;
newnode->next = pos;
newnode->prev = tmp;
}
//判断 带头双向循环链表 是否为空的函数:
bool ListEmpty(ListNode *head)
{
if (head->next == NULL)
{
return false;
}
else
{
return true;
}
}
//统计 带头双向循环链表 中有效结点的个数
int ListNodeSize(ListNode *head)
{
assert(head);
ListNode *cur = head->next;
int num = 0;
while (cur != head)
{
num++;
cur = cur->next;
}
return num;
}
//对整个链表进行销毁操作的函数
/*不要忘了最后也要将头结点释放*/
void ListDestroy(ListNode *head)
{
assert(head);
ListNode *cur = head->next;
while (cur != head)
{
ListNode *tmp = cur->next;//释放当前结点时要将下一个结点提前存储用于下一个结点的遍历和释放
free(cur);
cur = tmp;
}
free(head);
head = NULL;
}
}
【数据结构】带头+双向+循环链表的 增,删,查,改 的实现
于 2022-10-19 15:12:36 首次发布