Day05循环链表

博客介绍了单循环链表,其与单链表的不同在于尾结点的next域保存头结点地址,形成循环,可从任意点遍历所有节点。同时提到单循环链表的结构体和单链表设计基本无改变。

单循环链表:

就一个点和单链表不同,尾结点的next域不再保存NULL,而是保存头结点的地址

即尾结点的地址是头结点的地址,这样就形成一个循环

可以从循环链表任何一个点出发,就可以遍历整个链表的所有节点

单循环链表的结构体和单链表的结构体设计基本无任何改变

结构体设计:

//带头结点单循环链表结构体设计:
typedef int ELEM_TYPE;
​
typedef struct CNode
{
    ELEM_TYPE data;//数据域
    struct CNode *next;//指针域
}CNode, *PCNode;

​
实现功能
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "clist.h"
​
//可实现的操作:
//初始化
void Init_clist(struct CNode *pclist)
{
    //0.安全性处理
    assert(pclist != NULL);
​
    //1.对pclist指向的头结点里面的成员进行赋值:
    //头结点的数据域不使用,只需要对指针域赋值即可,赋值为自身地址
    pclist->next = pclist;
​
}
​
//头插
bool Insert_head(PCNode pclist, ELEM_TYPE val)
{
    assert(pclist != NULL);//保证pclist这个指针 指向的单循环链表的头结点 确确实实存在
​
    struct CNode *pnewnode = (struct CNode *)malloc(1 * sizeof(struct CNode));
    assert(pnewnode != NULL);
    pnewnode->data = val;  //-> ==(*).
​
    //合适的插入位置不用找,因为头插就是在头结点后面插,所以使用pclist即可
​
    pnewnode->next = pclist->next;
    pclist->next = pnewnode;
​
    return true;
}
​
//尾插
bool Insert_tail(PCNode pclist, ELEM_TYPE val)
{
    //0.安全性处理
    assert(pclist != NULL);
​
    //1.购买新节点
    struct CNode *pnewnode = (struct CNode *)malloc(1 * sizeof(struct CNode));
    assert(pnewnode != NULL);
    pnewnode->data = val;
​
    //2.找到合适的插入位置(也就是说找到在哪一个节点后面插入)
    //尾插,找尾结点,用指针p指向
    //通过判断,确实使用需要前驱的for循环,也就是说,申请一个临时指针p,执行头结点
    struct CNode *p = pclist;
    for(; p->next!=pclist; p=p->next);
    //此时,for循环,跑完,p指向尾结点
​
    //3.插入即可
    pnewnode->next = p->next;
    p->next = pnewnode;
​
    return true;
}
​
//按位置插
bool Insert_pos(PCNode pclist, int pos, ELEM_TYPE val)
{
    //0.安全性处理
    assert(pclist != NULL);
    assert(pos>=0 && pos<=Get_length(pclist));
​
    //1.购买新节点
    struct CNode *pnewnode = (struct CNode *)malloc(1 * sizeof(struct CNode));
    assert(pnewnode != NULL);
    pnewnode->data = val;
​
    //2.找到合适的插入位置
    struct CNode * p = pclist; //判断这个是插入函数,需要使用带前驱的for循环
    for(int i=0; i<pos; i++)
    {
        p=p->next;
    }
​
    //3.插入即可
    pnewnode->next = p->next;
    p->next = pnewnode;
​
    return true;
}
​
//头删
bool Del_head(PCNode pclist)
{
    //0:安全性处理
    assert(pclist != NULL);
    if(IsEmpty(pclist))
    {
        return false;
    }//确保至少存在一个有效节点
    
    //1.找到待删除节点,用指针p指向(头删的话,待删除节点就是第一个有效节点)
    struct CNode *p = pclist->next;
​
    //2.找到待删除节点的前驱,用指针q指向
    //这里不用处理,因为这里pclist就可以代替q
​
    //3.跨越指向+释放
    pclist->next = p->next;
    free(p);
​
    return true;
}
​
​
//尾删
bool Del_tail(PCNode pclist)
{
    assert(pclist != NULL);//保证pclist这个指针 指向的单循环链表的头结点 确确实实存在
    if(IsEmpty(pclist))//保证pclist指向的这个头结点 后边存在有效节点
    {
        return false;
    }
​
    struct CNode *p = pclist;
    for( ; p->next!=pclist; p=p->next);
​
    struct CNode *q = pclist;
    for(; q->next!=p; q=q->next);
​
    q->next = p->next;
    free(p);
​
    return true;
}
​
//按位置删
bool Del_pos(PCNode pclist, int pos)
{
    //0.assert pclist
    assert(pos >=0 && pos<Get_length(pclist));
    if(IsEmpty(pclist))//保证pclist指向的这个头结点 后边存在有效节点
    {
        return false;
    }
​
    //1.先找q  从头结点开始出发,向后走pos步
    struct CNode *q = pclist;
    for(int i=0; i<pos; ++i)
    {
        q = q->next;
    }
    //这时q就为
​
    //2.再找p
    struct CNode *p = q->next;
​
    //3.跨越指向+释放
    q->next = p->next;
    free(p);
​
    return true;
}
​
//按值删
bool Del_val(PCNode pclist, ELEM_TYPE val)
{
    //0.安全性处理
​
    //1.先需要判断val值,是否存在于单循环链表中
    struct CNode*p = Search(pclist, val);
​
    //2.若存在,则删除,若不存在,则return false
    if(p == NULL)
    {
        return false;
    }
​
    //反之,找到val这个待删除节点了,且现在由指针p指针
​
​
    //3.找带删除节点的上一个节点,用指针q指向
    struct CNode *q = pclist;
    for(; q->next!=p; q=q->next);
​
    //4.跨越指向+释放
    q->next = p->next;
    free(p);
​
    return true;
}
​
//查找 //查找到,返回的是查找到的这个节点的地址
struct CNode *Search(PCNode pclist, ELEM_TYPE val)
{
    //assert pclist
    //将单循环链表遍历一遍即可,对每一个有效节点都判断一次
​
    struct CNode *p = pclist->next;
    for(; p!=pclist; p=p->next)
    {
        if(p->data == val)
        {
            return p;
        }
    }
​
    return NULL;
}
​
//获取有效值个数
int Get_length(PCNode pclist)
{
    //assert pclist
    //将单循环链表遍历一遍即可,对每一个有效节点都判断一次
​
    int count = 0;//有效值个数
    struct CNode *p = pclist->next;
    for(; p!=pclist; p=p->next)
    {
        count++;
    }
​
    return count;;
}
​
//判空
bool IsEmpty(PCNode pclist)
{
    return pclist->next == pclist;
}
​
//清空
void Clear(PCNode pclist)
{
    return Destroy1(pclist);
}
​
//销毁1 无限头删
void Destroy1(PCNode pclist)
{
    while(pclist->next != pclist)
    {
        struct CNode *p = pclist->next;
        pclist->next = p->next;
        free(p);
    }
}
​
//销毁2 不借助头结点,有两个辅助指针
void Destroy2(PCNode pclist)
{
    assert(pclist != NULL);
    if(IsEmpty(pclist)) 
    {
        return;
    }
    struct CNode *p = pclist->next;
    struct CNode *q = p->next;
​
    pclist->next = pclist;
​
    while(p!=pclist)
    {
        q = p->next;
        free(p);
        p = q;
    }
​
​
}
​
//打印
void Show(PCNode pclist)
{
 
​
    struct CNode *p = pclist->next;
    for(; p!=pclist; p=p->next)
    {
        printf("%d ", p->data);
    }
    printf("\n");
}
如果有宝子不是看的不是很懂,小白请大家回看Day04单链表学习,因为循环链表和单链表差的不是很大,小白在上一节对单链表已经做了很详细的个人见解。
最后希望看过此篇的宝子能给小白一个小小的赞,更希望宝子能有所收获
​

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值