数据结构(3)—双向链表、循环链表

一、双向链表

1.定义

typedef int datatype;

typedef struct node
{
        datatype data;                 //存放数据空间
        struct node *ppre;           //存放前一个节点地址
        struct node *pnext;         //存放下一个节点地址
}linknode;

2.创建

申请节点空间,对pnext和ppre赋值为NULL,返回空白节点地址

linknode *create_empty_linklist(void)
{
        linknode *ptmpnode = NULL;
        ptmpnode = malloc(sizeof(linknode));
        if (NULL == ptmpnode)
        {
                perror("fail to malloc");
                return NULL;
        }
        ptmpnode->ppre = NULL;
        ptmpnode->pnext = NULL;
        return ptmpnode;
}

3.头插法

①申请节点

②存放数据

③pnext赋值为phead->pnext

④ppre赋值为phead的地址

⑤phead->pnext赋值为新申请节点地址

⑥如果有后一个节点,需要让后一个节点的ppre指向该节点

int insert_head_linklist(linknode *phead, datatype tmpdata)
{
        linknode *ptmpnode = NULL;

        ptmpnode = malloc(sizeof(linknode));
        if (NULL == ptmpnode)
        {
                perror("fail to malloc");
                return -1;
        }
        ptmpnode->data = tmpdata;
        ptmpnode->pnext = phead->pnext;
        ptmpnode->ppre = phead;
        phead->pnext = ptmpnode;
        if (ptmpnode->pnext != NULL)
        {
                ptmpnode->pnext->ppre = ptmpnode;
        }
        return 0;
}

4.遍历

 具体查看上次单向链表遍历

int show_linklist(linknode *phead)
{
        linknode *ptmpnode = NULL;
        ptmpnode = phead->pnext;
        while (ptmpnode != NULL)
        {
                printf("%d ", ptmpnode->data);
                ptmpnode = ptmpnode->pnext;
        }
        printf("\n");
        return 0;
}

5、查找

具体查看上次单向链表查找

 linknode *find_linklist(linknode *phead, datatype tmpdata)
{
        linknode *ptmpnode = NULL;
        ptmpnode = phead->pnex;

        while (ptmpnode != NULL)
        {
                if (ptmpnode->data == tmpdata)
                {
                        return ptmpnode;
                }
        ptmpnode = ptmpnode->pnext;
        }
        return NULL;
}

6. 修改

具体查看上次单向链表修改

int update_linklist(linknode *phead, datatype olddata, datatype newdata)
{
        linknode *ptmpnode = NULL;
        ptmpnode = phead->pnext;
        while (ptmpnode != NULL)
        {
                if (ptmpnode->data == olddata)
                {
                        ptmpnode->data = newdata;
                }
        ptmpnode = ptmpnode->pnext;
        }
        return 0;
}

7.删除

①找到要删除的节点

②让删除节点的前一个节点的pnext指向要删除节点的后一个节点

③让删除节点的后一个节点的ppre指向要删除节点的前一个节点

④将要删除的节点释放掉

int delete_linklist(linknode *phead, datatype tmpdata)
{
        linknode *ptmpnode = NULL;
        linknode *pfreenode = NULL;         //需要定义两个变量,因为如果只有一个变量被释放                                                                   后,无法继续遍历
        ptmpnode = phead->pnext;
        while (ptmpnode != NULL)
        {
                if (ptmpnode->data == tmpdata)
                {
                        ptmpnode->ppre->pnext = ptmpnode->pnext;
                        if (ptmpnode->pnext != NULL)           //判断要删除的节点是否为最后一个
                        {
                                ptmpnode->pnext->ppre = ptmpnode->ppre;
                        }
                pfreenode = ptmpnode;
                ptmpnode = ptmpnode->pnext;
                free(pfreenode);
                }
                else           //如果不加else会向后推进两个节点,导致无法全部遍历
                {
                        ptmpnode = ptmpnode->pnext;
                }
        }
        return 0;
}

8. 销毁

具体查看上次单向链表销毁

int destroy_linklist(linknode **pphead)
{
        linknode *ptmpnode = NULL;
        linknode *pfreenode = NULL;
        ptmpnode = *pphead;
        pfreenode = ptmpnode;
        while (ptmpnode != NULL)
        {
                ptmpnode = ptmpnode->pnext;
                free(pfreenode);
                pfreenode = ptmpnode;
        }
        *pphead = NULL;
        return 0;
}

9.尾插法

①申请节点

②将节点的pnext赋值为NULL

③找到链表最后一个节点

④将节点的ppre赋值为最后一个节点地址

⑤将最后一个节点的pnext赋值为新申请节点

int insert_tail_linklist(linknode *phead, datatype tmpdata)
{
        linknode *ptmpnode = NULL;
        linknode *plastnode = NULL;
        ptmpnode = malloc(sizeof(linknode));
        if (NULL == ptmpnode)
        {
                perror("fail to malloc");
                return -1;
        }
        plastnode = phead;
        while (plastnode->pnext != NULL)
        {
                plastnode = plastnode->pnext;
        }
        ptmpnode->data = tmpdata;
        ptmpnode->pnext = NULL;
        ptmpnode->ppre = plastnode;
        plastnode->pnext = ptmpnode;
        return 0;
}

二、循环列表

1. 头结点的ppre指向链表最后一个节点,最后节点的pnext指向第一个节点,可以通过头节点或尾节点定位到头节点和尾节点

2.定义

typedef int datatype;

typedef struct node
{
        datatype data;                 //存放数据空间
        struct node *ppre;           //存放前一个节点地址
        struct node *pnext;         //存放下一个节点地址
}linknode;

3.创建

申请节点,pnext和ppre都指向自己

linknode *create_empty_linklist(void)
{
        linknode *ptmpnode = NULL;
        ptmpnode = malloc(sizeof(linknode));
        if (NULL == ptmpnode)
        {
                perror("fail to malloc");
                return NULL;
        }
        ptmpnode->pnext = ptmpnode->ppre = ptmpnode;
        return ptmpnode;
}

4.头插法

①申请节点空间

②存放数据

③将pnext指向空白节点的pnext

④将ppre指向空白节点

⑤将空白节点的pnext指向新申请节点

⑥将后续节点的ppre指向新申请节点

int insert_head_linklist(linknode *phead, datatype tmpdata)
{
        linknode *ptmpnode = NULL;
        ptmpnode = malloc(sizeof(linknode));
        if (NULL == ptmpnode)
        {
                perror("fail to malloc");
                return -1;

        }
        ptmpnode->data = tmpdata;
        ptmpnode->pnext = phead->pnext;
        ptmpnode->ppre = phead;
        ptmpnode->pnext->ppre = ptmpnode;
        ptmpnode->ppre->pnext = ptmpnode;
        return 0;
}

 5.尾插法

①申请节点空间

②将数据存放到节点中

③将pnext赋值为空白节点

④将ppre赋值为之前的最后一个节点地址

⑤将之前最后一个节点的pnext指向新申请节点

⑥将空白节点的ppre指向新申请节点

int insert_tail_linklist(linknode *phead, datatype tmpdata)
{
        linknode *ptmpnode = NULL;
        ptmpnode = malloc(sizeof(linknode));
        if (NULL == ptmpnode)
        {
                perror("fail to malloc");
                return -1;
        }
        ptmpnode->data = tmpdata;
        ptmpnode->pnext = phead;
        ptmpnode->ppre = phead->ppre;
        ptmpnode->pnext->ppre = ptmpnode;
        ptmpnode->ppre->pnext = ptmpnode;
        return 0;
}

6.遍历

其他与双向链表无差别,唯一的差别是循环链表最终循环条件由 != NULL修改为 != phead

int show_linklist(linknode *phead)
{
        linknode *ptmpnode = NULL;
        ptmpnode = phead->pnext;
        while (ptmpnode != phead)
        {
                printf("%d ", ptmpnode->data);
                ptmpnode = ptmpnode->pnext;
        }
        printf("\n");
        return 0;
}

7.查找

具体与双向链表的查找方法一致

 linknode *find_linklist(linknode *phead, datatype tmpdata)
{
        linknode *ptmpnode = NULL;
        ptmpnode = phead->pnext;
        while (ptmpnode != phead)
        {
                if (ptmpnode->data == tmpdata)
                {
                        return ptmpnode;
                }
                ptmpnode = ptmpnode->pnext;
        }
        return NULL;
}

8. 修改

具体与双向链表的修改方法一致

int update_linklist(linknode *phead, datatype olddata, datatype newdata)
{
        linknode *ptmpnode = NULL;
        ptmpnode = phead->pnext;
        while (ptmpnode != phead)
        {
                if (ptmpnode->data == olddata)
                {
                        ptmpnode->data = newdata;
                }
        ptmpnode = ptmpnode->pnext;
        }
        return 0;
}

9.删除

具体与双向链表的删除方法一致,但是无需判断是否有pnext,因为循环链表永远有pnext

 int delete_linklist(linknode *phead, datatype tmpdata)
{
        linknode *ptmpnode = NULL;
        linknode *pfreenode = NULL;
        ptmpnode = phead->pnext;
        while (ptmpnode != phead)
        {
                if (ptmpnode->data == tmpdata)
                {
                        ptmpnode->ppre->pnext = ptmpnode->pnext;
                        ptmpnode->pnext->ppre = ptmpnode->ppre;
                        pfreenode = ptmpnode;
                        ptmpnode = ptmpnode->pnext;
                        free(pfreenode);
                }
                else
                {
                        ptmpnode = ptmpnode->pnext;
                }

        }
        return 0;
}

10.销毁

 具体与双向链表的销毁方法一致,但是需要从第一个非空节点开始销毁,因为判断条件是!=*pphead,如果销毁会导致无法进入循环;循环结束后要单独释放*pphead

int destroy_linklist(linknode **pphead)
{
        linknode *ptmpnode = NULL;
        linknode *pfreenode = NULL;
        ptmpnode = (*pphead)->pnext;
        pfreenode = ptmpnode;
        while (ptmpnode != *pphead)
        {
                ptmpnode = ptmpnode->pnext;
                free(pfreenode);
                pfreenode = ptmpnode;
        }
        free(*pphead);
        *pphead = NULL;
        return 0;
}

三、 内核链表

1.是Linux内核中所使用到的一种链表结构,使用同一种结构可以存储不同类型的链表;普通链表是链表节点中包含数据,而内核链表是数据中包含链表节点。

2.结构描述

 

创建:创建空白节点即可(只需给ppre和pnext初始化)

插入:申请节点,并按照之前学习的循环链表插入即可(插入的链表节点首地址即数据节点首地
址)

访问节点:通过遍历找到每个节点的首地址,强制类型转换即可获得对应数据空间首地

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值