循环链表:循环链表的特点是可以通过尾结点的指向找到头结点的地址;
结构体设计与单链表相同:
typedef int ELEM_TYPE;
typedef struct Clist
{
ELEM_TYPE data; //数据域
struct Clist* next; //指针域
} Clist, * PClist;
主要功能实现如下:
1.初始化操作
循环列表初始化操作中需要将首节点的next域指向它本身的地址
void Init_clist(struct Clist* plist)
{
assert(plist != NULL);
if (plist == NULL)
{
return;
}
plist->next = plist;
}
2.尾插函数
首先申请一个新节点pnewnode,然后将其next域指向首节点的地址,然后再申请一个临时节点p将其指向尾结点的地址,然后让p的next域指向pnewnode的地址。
bool Insert_tail(PClist plist, ELEMTYPE val)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
//1.购买新节点
struct Clist *pnewnode = (struct Clist*)malloc(1 * sizeof(struct Clist));
assert(pnewnode != NULL);
pnewnode->data = val;//购买的新节点 处理完毕
//2.找到插入位置(通过带前驱的for循环)
struct Clist *p = plist;
for(p; p->next!=plist; p=p->next);
//此时 for循环执行结束 p指向尾结点
//3.插入
pnewnode->next = p->next;
p->next = pnewnode;
return true;
}
循环链表完整代码
clist.h
//循环链表结构体设计
typedef int ELEMTYPE;
typedef struct Clist
{
ELEMTYPE data; //数据域 存放数据
struct Clist* next;//指针域 存放下一个节点的地址(尾结点的next保存头结点的地址)
}Clist, *PClist;
//循环链表拥有的 可执行函数声明:
//初始化
void InitClist(struct Clist* plist);
//头插
bool Insert_head(PClist plist, ELEMTYPE val);
//尾插
bool Insert_tail(PClist plist, ELEMTYPE val);
//按位置插
bool Insert_pos(PClist plist, int pos, ELEMTYPE val);
//头删
bool Del_head(PClist plist);
//尾删
bool Del_tail(PClist plist);
//按位置删
bool Del_pos(PClist plist, int pos);
//按值删除
bool Del_val(PClist plist, ELEMTYPE val);
//查找(如果查找到,需要返回找到的这个节点的地址)
struct Clist* Search(struct Clist *plist, ELEMTYPE val);
//判空
bool IsEmpty(PClist plist);
//判满(循环链表不需要这个操作)
//获取长度
int Get_length(PClist plist);
//清空
void Clear(PClist plist);
//销毁1
void Destroy1(PClist plist);
//销毁2
void Destroy2(PClist plist);
//打印
void Show(struct Clist *plist);
clist.cpp
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
#include "clist.h"
//循环链表里边很少出现NULL, nullptr
//初始化
void InitClist(struct Clist* plist)
{
assert(plist != NULL);
if (plist == NULL)
{
return;
}
//plist->data; // 数据域不处理
plist->next = plist;
}
//头插
bool Insert_head(PClist plist, ELEMTYPE val)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
//1.购买新节点
struct Clist *pnewnode = (struct Clist*)malloc(1 * sizeof(struct Clist));
assert(pnewnode != NULL);
pnewnode->data = val;//购买的新节点 处理完毕
//2.找到插入位置 (头插 不用找)
//3.插入
pnewnode->next = plist->next;
plist->next = pnewnode;
return true;
}
//尾插
bool Insert_tail(PClist plist, ELEMTYPE val)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
//1.购买新节点
struct Clist *pnewnode = (struct Clist*)malloc(1 * sizeof(struct Clist));
assert(pnewnode != NULL);
pnewnode->data = val;//购买的新节点 处理完毕
//2.找到插入位置(通过带前驱的for循环)
struct Clist *p = plist;
for(p; p->next!=plist; p=p->next);
//此时 for循环执行结束 p指向尾结点
//3.插入
pnewnode->next = p->next;
p->next = pnewnode;
return true;
}
//按位置插
bool Insert_pos(PClist plist, int pos, ELEMTYPE val)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
assert(pos>=0 && pos<=Get_length(plist));
//1.购买新节点
struct Clist *pnewnode = (struct Clist*)malloc(1 * sizeof(struct Clist));
assert(pnewnode != NULL);
pnewnode->data = val;//购买的新节点 处理完毕
//2.找到插入位置(通过带前驱的for循环)
struct Clist *p = plist;
for(int i=0; i<pos; i++)
{
p = p->next;
}
//此时 for循环执行结束 p指向待插入的合适位置
//3.插入
pnewnode->next = p->next;
p->next = pnewnode;
return true;
}
//头删
bool Del_head(PClist plist)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
if(IsEmpty(plist))//不空 则至少存在一个有效值
{
return false;
}
//1.指针p指向待删除节点
struct Clist *p = plist->next;
//2.指针q指向待删除节点的前一个节点
//q 就是 头结点 这里就不再额外处理
//3.跨越指向
plist->next = p->next;
free(p);
return true;
}
//尾删
bool Del_tail(PClist plist)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
if(IsEmpty(plist))//不空 则至少存在一个有效值
{
return false;
}
//1.指针p指向待删除节点(尾删的话,这里指向尾结点)
struct Clist *p = plist;
for(p; p->next!=plist; p=p->next);
//此时 for指向结束 代表着p指向尾结点
//2.指针q指向倒数第二个节点
struct Clist *q = plist;
for(q; q->next!=p; q=q->next);
//此时 for指向结束 代表着q指向p的上一个节点
//3.跨越指向
q->next = p->next;
free(p);
return true;
}
//按位置删
bool Del_pos(PClist plist, int pos)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
assert(pos>=0 && pos<Get_length(plist));
if(IsEmpty(plist))
{
return false;
}
//1.指针p指向待删除节点
//2.指针q指向待删除节点的上一个节点
//这里采用第二种方案找pq,先找q再找p
struct Clist *q = plist;
for(int i=0; i<pos; i++)
{
q = q->next;
}
struct Clist *p = q->next;
//3.跨越指向
q->next = p->next;
free(p);
return true;
}
//按值删除
bool Del_val(PClist plist, ELEMTYPE val)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
struct Clist* p = Search(plist, val);
if(p == NULL)
{
return false;
}
struct Clist *q = plist;
for(q; q->next!=p; q=q->next);
q->next = p->next;
free(p);
return true;
}
//查找(如果查找到,需要返回找到的这个节点的地址)
struct Clist* Search(struct Clist *plist, ELEMTYPE val)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
for(struct Clist *p=plist->next; p!=plist; p=p->next)
{
if(p->data == val)
{
return p;
}
}
return NULL;
}
//判空
bool IsEmpty(PClist plist)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
return plist->next == plist;
}
//判满(循环链表不需要这个操作)
//获取长度
/*指针p从头结点的下一个节点开始向后跑,如果p再次遇到了头结点,
证明p走了一圈回来了,这是有效节点肯定已经遍历结束*/
int Get_length(PClist plist)
{
//不带前驱的for循环 跑一遍就好
int count = 0;
for(struct Clist *p=plist->next; p!=plist; p=p->next)
{
count++;
}
return count;
}
//清空
void Clear(PClist plist)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
Destroy1(plist);
}
//销毁1(无限头删)
void Destroy1(PClist plist)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
while(plist->next != plist)
{
struct Clist *p = plist->next;
plist->next = p->next;
free(p);
}
plist->next = plist;
}
//销毁2(要用到两个指针变量)
void Destroy2(PClist plist)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
struct Clist *p = plist->next;
struct Clist *q = NULL;
plist->next = plist;
while (p!=plist)
{
q = p->next;
free(p);
p = q;
}
}
//打印
void Show(struct Clist *plist)
{
assert(plist != NULL);
if (plist == NULL)
{
return false;
}
for(struct Clist *p=plist->next; p!=plist; p=p->next)
{
printf("%d ", p->data);
}
printf("\n");
}