【学习嵌入式day-17-数据结构-单向链表/双向链表】

单向链表

链表的查找

在链表中找到指定的第一个元素

沿用遍历思想,每次访问一个节点元素判断是否是要找的节点

符合条件,返回该节点地址

到最后依旧没有找到符合条件的节点,返回NULL;

cs 复制代码
//返回第一个指定元素节点的地址
linknode *find_linklist(linknode *phead, datatype tmpdata)
{
    linknode *ptmpnode = NULL;

    ptmpnode = phead->pnext;
    
    while(ptmpnode != NULL)
    {
         if(ptmpnode->data == tmpdata)
        {
            return ptmpnode;
        }
        ptmpnode = ptmpnode->pnext;
    }
    return NULL;
}
cs 复制代码
//main.c中调用    
    pret = find_linklist(plinklist,3);//找出链表中第一个3数据的地址并返回
    printf("&plinklist = %p\n", pret);
    printf("%d\n", pret->data);

链表的修改

使用遍历,找到符合条件的元素修改为新的值

cs 复制代码
//更新链表中指定元素的值
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;//没有找到olddata,就继续下一个节点
    }
    return 0;
}
cs 复制代码
//main.c中调用
    update_linklist(plinklist, 4, 5);
    show_linklist(plinklist);

链表的尾插法

在链表末尾插入一个元素

1、申请一个节点空间

2、存数据

3、存地址,赋值为NULL;

4、遍历找到最后一个节点

5、最后一个节点的pnext赋值为新申请的节点

cs 复制代码
//尾插
int insert_tail_linklist(linknode *phead, datatype tmpdata)
{
    linknode *ptmpnode = NULL;
    linknode *pnewnode = NULL;

    ptmpnode = phead;//指向头节点
    //或者ptmpnode = phead->pnext;指向第一个有效节点
    //但是如果只有头节点情况下不能使用

    //申请空间
    pnewnode = malloc(sizeof(linknode));//分配新节点
    if(NULL == pnewnode)
    {
        perror("fail to malloc");
        return -1;
    }
    pnewnode->data = tmpdata;//数据存放到节点中
    pnewnode->pnext = NULL;//将节点地址赋值为NULL

    while(ptmpnode->pnext != NULL)//找到最后一个节点
    {
        ptmpnode = ptmpnode->pnext;
    }
    ptmpnode->pnext = pnewnode;//将最后一个节点的pnext指向新申请的节点
   
    return 0;
}
cs 复制代码
//main.c中调用

    insert_tail_linklist(plinklist, 6);
    show_linklist(plinklist);

链表的销毁

将所有链表节点空间都释放掉,使用二级指针

1、定义两个指针pfreenode和ptmpnode都指向头节点

2、使ptmpnode向后走

3、再释放pfreenode指向的节点

4、再将pfreenode指向ptmpnode指向的空间

cs 复制代码
//销毁,使用二级指针
int destory_linklist(linknode **pphead)
{
    linknode *pfreenode = NULL;
    linknode *ptmpnode = NULL;
    ptmpnode = *pphead;
    pfreenode = *pphead;

    while(ptmpnode != NULL)
    {
        ptmpnode = ptmpnode->pnext;
        free(pfreenode);
        pfreenode = ptmpnode;
    }
    *pphead = NULL;
    return 0;
}
cs 复制代码
//main.c中调用   
    destory_linklist(&plinklist);
    printf("plinklist = %p\n", plinklist);
    show_linklist(plinklist);

查找链表中间节点

定义两个指针,一个快指针,一个慢指针

1、快指针每次走 2步,慢指针每次都1步

2、快指针走到末尾,慢指针走到中间

cs 复制代码
//找中间节点
linknode *fine_midnode(linknode *phead)
{
    linknode *pfast = NULL;
    linknode *pslow = NULL;
    pfast = phead->pnext;
    pslow = phead->pnext;
    while(pfast != NULL)
    {
        pfast = pfast->pnext;//pfast走一步
        if(NULL == pfast)
        {
            break;
        }
        pfast = pfast->pnext;//pfast走第二步
        if(NULL == pfast)
        {
            break;
        }
        pslow = pslow->pnext;//pslow走一步
    }
    return pslow;
}
cs 复制代码
//main.c中调用
    pret = fine_midnode(plinklist);
    show_linklist(plinklist);
    printf("midnode = %d\n", pret->data);

查找链表倒数第k个节点

1、快指针先走k步

2、慢指针和快指针每次走一步

3、快指针走到末尾,慢指针少走k步,即倒数第k个元素

cs 复制代码
//找倒数第k个节点
linknode *find_last_kth_node(linknode *phead, int k)
{
    int i = 0;
    linknode *pfast = NULL;
    linknode *pslow = NULL;
    pfast = phead->pnext;

    for(i = 0; i < k && pfast != NULL; i++)
    {
        pfast = pfast->pnext;
    }
    if(NULL == pfast)
    {
        return NULL;
    }

    pslow = phead->pnext;
    while(pfast != NULL)
    {
        pfast = pfast->pnext;
        pslow = pslow->pnext;
    }
    return pslow;
}
cs 复制代码
//main.c中调用

    pret = find_last_kth_node(plinklist, 3);
    show_linklist(plinklist);
    printf("last_kth_node = %d\n", pret->data);

不知道头节点,删除指定节点

将指针指向的下一个节点的值覆盖当前节点的值

再去删除下一个节点

下一个节点的值和地址都赋给当前节点ptmpnode,删除下一个节点

cs 复制代码
/* 删除指定节点 */
int delete_linknode(linknode *ptmpnode)
{
    linknode *pnextnode = NULL;
    pnextnode = ptmpnode->pnext;
    ptmpnode->data = pnextnode->data;
    ptmpnode->pnext = pnextnode->pnext;
    free(pnextnode);
    return 0;
}

倒置

先将原链表断开

将所有元素依次使用头插法插入

cs 复制代码
//链表倒置
int reverse_linklist(linknode *phead)
{
    linknode *pinsertnode = NULL;
    linknode *ptmpnode = NULL;

    //将链表从头节点处断开
    ptmpnode = phead->pnext;    //ptm指针指向第一个有效节点
    phead->pnext = NULL;        //头结点的pnext指向NULL,断开 头节点
    //依次将所有元素使用头插法插入链表中
    while(ptmpnode != NULL)
    {
        pinsertnode = ptmpnode;             //使插入指针pin走到指针ptm的位置
        ptmpnode = ptmpnode->pnext;         //ptmp指针向后走
        pinsertnode->pnext = phead->pnext;  //插入节点指向第一个有效节点,存入下一个节点地址
        phead->pnext = pinsertnode;         //头结点指向插入节点
    }
    return 0;
}

链表冒泡:

定义三个指针,ptmpnode1和ptmpnode2,还有pend(初始为NULL)

两个指针相邻两个元素比较

指针循环向后走,知道ptmp2为NULL,即等于pend,循环停止

每次循环,pend赋值为ptmp1的节点地址

循环将所有元素都比完,剩余一个小的元素

cs 复制代码
//冒泡排序
int bubble_sort_linklist(linknode *phead)
{
    linknode *ptmpnode1 = NULL;
    linknode *ptmpnode2 = NULL;
    linknode *pend = NULL;
    datatype tmpdata;
    //少于两个节点直接结束
    if(NULL == phead->pnext || NULL == phead->pnext->pnext)
    {
        return 0;
    }

    while(1)
    {
        ptmpnode1 = phead->pnext;       //ptmp1指向第一个有效节点
        ptmpnode2 = phead->pnext->pnext;//ptmp2指向第一个有效节点的pnext,也就是第二个有效节点
        //每一轮开始前判断是否已经排序完成
        if(pend == ptmpnode2)
        {
            break;
        }

        while(ptmpnode2 != pend)                    //ptm2不等于pend时,进入循环
        {
            if(ptmpnode1->data > ptmpnode2->data)   //指针1的元素是否比指针2的大
            {
                tmpdata = ptmpnode1->data;          //如果1的值大于2的值,则交换
                ptmpnode1->data = ptmpnode2->data;
                ptmpnode2->data = tmpdata;
            }
            ptmpnode1 = ptmpnode1->pnext;           //如果不满足交换的条件,指针1向后走
            ptmpnode2 = ptmpnode2->pnext;           //指针2向后走
        }
        pend = ptmpnode1;                           //让pend指针走到指针1的位置上,开始新一轮循环
    }
    return 0;
}

选择排序

pswapnode指向要交换的节点

pminnode指向假设的最小值

ptmpnode和后续节点比较

cs 复制代码
//选择排序
int select_sort_linklist(linknode *phead)
{
    linknode *ptmpnode = NULL;
    linknode *pminnode = NULL;
    linknode *pswapnode = NULL;
    datatype tmpdata;
    //少于两个节点直接结束
    if(NULL == phead->pnext || NULL == phead->pnext->pnext)
    {
        return 0;
    }

    pswapnode = phead->pnext;       //交换指针指向第一个有效节点
    while(pswapnode->pnext !=NULL)  //遍历到最后一个节点结束
    {
        pminnode = pswapnode;       //最小值指针指向交换指针的节点
        ptmpnode = pswapnode->pnext;//ptmp指针指向交换指针的下一个节点
        while(ptmpnode != NULL)     //当ptmp指针不等于NULL,进入循环
        {
            if(ptmpnode->data < pminnode->data) //判断ptmp的元素是否 < 最小值的元素
            {
                pminnode = ptmpnode;            //满足交换条件,让最小值的指针指向ptmp(当前的最小值)
            }
            ptmpnode = ptmpnode->pnext;         //指针ptmp向后走
        }
        if(pswapnode != pminnode)               //判断交换指针是否与最小值指针元素不相等
        {
            tmpdata = pswapnode->data;          //不相等就交换,交换指针的data和最小值指针的data相互交换
            pswapnode->data = pminnode->data;   
            pminnode->data = tmpdata;           
        }
        pswapnode = pswapnode->pnext;           //交换指针向后走
    }
    return 0;
}

判断有环

判断链表是否有环:

定义两个指针:快指针(每次走2步)和慢指针(每次走一步)

当快指针 - 慢指针 == 环长时,即相遇,快指针和慢指针相等即为链表有环

计算环长:

定义一个指针从相遇点开始走一圈,直到走到该节点为止

每走一个节点计数,最终可得到环长

获得环的入口位置:

a = c + (n-1) * l; n为圈数

定义一个指针从相遇点开始每次走一步,定义一个指针从开头每次走一步

两个指针相遇的位置即为环入口的位置

cs 复制代码
//判断链表是否有环      计算环长        找到环的入口位置
int circle_linklist(linknode *phead, int *pis_circle, int *pcirlen, linknode **ppnode)
{
    linknode *pfast = NULL;
    linknode *pslow = NULL;
    linknode *ptmpnode = NULL;
    linknode *pstartnode = NULL;
    int count = 1;

    //判断是否有环
    pfast = phead->pnext;
    pslow = phead->pnext;
    while(1)
    {
        pfast = pfast->pnext;
        if(NULL == pfast)
        {
            break;
        }
        pfast = pfast->pnext;
        if(NULL == pfast)
        {
            break;
        }
        pslow = pslow->pnext;
        if(pfast == pslow)
        {
            break;
        }
    }

    if(NULL == pfast)
    {
        *pis_circle = 0;
        return 0;
    }
    else
    {
        *pis_circle = 1;
    }
    //统计环长
    ptmpnode = pslow->pnext;
    while(ptmpnode != pslow)
    {
        count++;
        ptmpnode = ptmpnode->pnext;
    }
    *pcirlen = count;

    //找到环入口
    pstartnode = phead->pnext;
    ptmpnode = pslow;
    while(pstartnode != ptmpnode)
    {
        pstartnode = pstartnode->pnext;
        ptmpnode = ptmpnode->pnext;
    }
    *ppnode = ptmpnode;
    return 0;
}

双向链表

节点定义

cs 复制代码
typedef int datatype;//存放数据的类型

typedef struct node{
    datatype data;      //存放数据
    struct  node *ppre; //指向前一个节点的指针
    struct node *pnext; //指向后一个节点的指针
}linknode;

创建空白节点

1、申请空白空间

2、对pnext和ppre赋值为NULL

3、返回头节点地址

cs 复制代码
//创建一个空链表
linknode *create_empty_linklist(void)
{
    linknode *ptmpnode = NULL;

    ptmpnode = malloc(sizeof(linknode));//分配一个节点空间当做头结点(不存数据)
    if(NULL == ptmpnode)
    {
        perror("fail to malloc");
        return NULL;
    }
    ptmpnode->pnext = NULL;//把它的 pnext 指针设为 NULL,代表链表为空。
    ptmpnode->ppre = NULL;
    return ptmpnode;
}

头插法

1、申请节点

2、存数据

3、申请的pnext赋值为phead->pnext ptmpnode->pnext = phead->pnext

4、申请的ppre赋值为phead的地址 ptmpnode->ppre = phead

5、将phead->pnext赋值为新申请节点地址 phead->pnext = ptmpnode

6、如果有后一个节点,需要让后一个节点的ppre指向该节点ptmpnode->pnext->ppre = ptmpnode

cs 复制代码
//头插法
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; //头节点的pnext赋值给当前节点
    ptmpnode->ppre = phead;         //ppre指向头节点
    phead->pnext = ptmpnode;        //头结点的pnext指向ptmp节点
    if(ptmpnode->pnext != NULL)     //判断是否有下一个节点
    {
        ptmpnode->pnext->ppre = ptmpnode;
    }
    return 0;
}

遍历(和单向一样)

cs 复制代码
//遍历(和单向一样)
void show_linklist(linknode *phead)
{
    linknode *ptmpnode = NULL;
    ptmpnode = phead->pnext;    //指向第一个有效节点
    while(ptmpnode != NULL)     //遍历每个节点元素
    {
        printf("%d ", ptmpnode->data);
        ptmpnode = ptmpnode->pnext;
    }
    printf("\n");
    return;
}

查找(和单向一样)

cs 复制代码
//查找(和单向一样)
linknode *find_linklist(linknode *phead, datatype tmpdata)
{
    linknode *ptmpnode = NULL;
    ptmpnode = phead->pnext;    //指向第一个有效节点

    while(ptmpnode != NULL)     //遍历
    {
        if(ptmpnode->data == tmpdata) //判断是否是要找的tmpdata
        {
            return ptmpnode;          //找到了,返回当前节点指针
        }
        ptmpnode = ptmpnode->pnext;
    }
    return NULL;            //循环结束依旧找不到,返回NULL
}

修改(和单向一样)

cs 复制代码
//修改(和单向一样)
int update_linklist(linknode *phead, datatype olddata, datatype newdata)
{
    linknode *ptmpnode = NULL;
    ptmpnode = phead->pnext;            //指向第一个有效节点

    while(ptmpnode != NULL)
    {
        if(ptmpnode->data == olddata)   //判断是否是要修改的olddata
        {
            ptmpnode->data = newdata;
        }
        ptmpnode = ptmpnode->pnext;     //指针向后走
    }
    return 0;
}

删除指定元素

找到要删除的节点

如果前一个节点存在,则让前一个节点的pnext赋值为 要删除节点的 后一个节点

如果后一个节点存在,则让后一个节点的ppre赋值为 要删除节点的 前一个节点

让pfree指针指向要删除的节点

ptmp指针向后走

释放pfree指向的节点空间

cs 复制代码
//删除指定元素
int delete_linklist(linknode *phead, datatype tmpdata)
{
    linknode *ptmpnode = NULL;
    linknode *pfreenode = NULL;

    ptmpnode = phead->pnext;
    while(ptmpnode != NULL)
    {
        if(ptmpnode->data == tmpdata)   //判断是否找到指定元素
        {
            //如果前一个节点存在,则修改他的pnext
            if(ptmpnode->ppre != NULL)
            {
                ptmpnode->ppre->pnext = ptmpnode->pnext;//前一个节点的pnext赋值为后一个节点
            }
            //如果后一个节点存在,则修改它的ppre
            if(ptmpnode->pnext != NULL)
            {
                ptmpnode->pnext->ppre = ptmpnode->ppre;//后一个节点的ppre赋值为前一个节点
            }
            pfreenode = ptmpnode;       //释放指针赋值为ptmp指针
            ptmpnode = ptmpnode->pnext; //ptmp指针向后走
            free(pfreenode);            //释放pfree指针指向的节点空间
            return 0;//删除成功
        }
        else        
        {
            ptmpnode = ptmpnode->pnext;     //没有找到指定元素,ptmp指针向后走
        }
    }
    return -1;//未找到指定数据
}

销毁所有元素(和单向一样)

cs 复制代码
//销毁(和单向一样)
int destory_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;
}

尾插法

1、申请节点

2、将新申请节点的pnext赋值为NULL

3、找到链表的最后一个节点

4、将新申请节点的ppre赋值为最后一个节点地址

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

cs 复制代码
//尾插法
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;                  //指针plast先指向头节点
    while(plastnode->pnext != NULL)     //找到最后一个节点
    {
        plastnode = plastnode->pnext;
    }

    ptmpnode->data = tmpdata;       //新申请的节点存数据
    ptmpnode->pnext = NULL;         //新申请节点的pnext指向NULL
    ptmpnode->ppre = plastnode;     //新申请节点的ppre指向最后一个节点
    plastnode->pnext = ptmpnode;    //最后一个节点的pnext指向新申请节点
    return 0;
}
相关推荐
Entropy-Lee2 分钟前
JavaScript 执行上下文与作用域
开发语言·javascript·ecmascript
泡岩浆的child23 分钟前
朋友:你平常都用什么软件取色?我:QQ截图啊。朋友:牛X!
前端
老周聊大模型29 分钟前
AI工程化必学:Spring AI与Redis的黄金组合实践
javascript·人工智能·程序员
志如32 分钟前
【校招面试官说】什么样的技术人更容易被大厂校招选中?
前端·后端·面试
古夕40 分钟前
TS 导出 PDF:解决表头乱码,实现表格内添加可点击链接
前端·typescript
小白马丶41 分钟前
Jetpack Compose开发框架搭建
android·前端·android jetpack
天神下凡_42 分钟前
前端解析markdown语法
前端·vue.js
圆心角1 小时前
什么是CDN, 它为什么更快
前端·cdn
曲意已决1 小时前
《前端安全攻防》
前端·javascript
二闹1 小时前
实时数据触手可及!前端开发者必看的连接指南
前端·websocket