Linux自学day16-数据结构-单向链表2

1.函数 is_exist_linklist(判断给定的数据是否存在于链表中)

完整代码如下 :

int is_exist_linklist(list_node_t *phead, datatype tmpdata)
{
    list_node_t *ptmpnode = NULL;

    if (NULL == phead)
    {
        return -1;
    }

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

    return 0;
}

 1.1函数声明

int is_exist_linklist(list_node_t *phead, datatype tmpdata)

函数 is_exist_linklist 接受两个参数:

  • phead:指向链表头节点的指针。

  • tmpdata:要查找的数据。
    函数返回值为 int 类型,用于表示查找结果。

1.2局部变量声明

list_node_t *ptmpnode = NULL;

声明一个指向链表节点的指针 ptmpnode,并初始化为 NULL,用于遍历链表。

1.3检查链表头指针 

if (NULL == phead)
{
    return -1;
}

如果链表头指针 phead 为 NULL,表示链表为空,函数返回 -1 表示错误或无效情况。

1.4初始化遍历指针

ptmpnode = phead->pnext;

将 ptmpnode 指向链表的第一个实际节点(跳过头节点)。

 1.5遍历链表

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

使用 while 循环遍历链表,直到 ptmpnode 为 NULL(到达链表末尾)。在每次循环中:

  • 检查当前节点的数据 ptmpnode->data 是否等于要查找的数据 tmpdata

  • 如果相等,返回 1 表示找到了目标数据。

  • 如果不相等,将 ptmpnode 指向下一个节点。

1.6若未找到数据 

return 0;

如果循环结束后仍未找到目标数据,函数返回 0 表示数据不存在于链表中。

 2.函数replace_linklist(链表中查找并替换指定的数据

完整代码如下: 

int replace_linklist(list_node_t *phead, datatype olddata, datatype newdata)
{
    list_node_t *ptmpnode = NULL;

    if (NULL == phead)
    {
        return -1;
    }

    ptmpnode = phead->pnext;
    while (ptmpnode != NULL)
    {
        if (ptmpnode->data == olddata)
        {
            ptmpnode->data = newdata;
        }
        ptmpnode = ptmpnode->pnext;
    }

    return 0;
}

 2.1函数定义

int replace_linklist(list_node_t *phead, datatype olddata, datatype newdata)
  • replace_linklist是函数名,返回值类型为int

  • phead是指向链表头节点的指针,类型为list_node_t *

  • olddatanewdata是要查找和替换的数据,类型为datatype。

2.2 局部变量声明

list_node_t *ptmpnode = NULL;
  • 声明一个指向链表节点的指针ptmpnode,并初始化为NULL,用于遍历链表。

 2.3链表头节点检查

if (NULL == phead)
{
    return -1;
}

 如果链表头指针pheadNULL,表示链表为空,函数返回 - 1

2.4 链表遍历和数据替换

ptmpnode = phead->pnext;
while (ptmpnode != NULL)
{
    if (ptmpnode->data == olddata)
    {
        ptmpnode->data = newdata;
    }
    ptmpnode = ptmpnode->pnext;
}
  • ptmpnode = phead->pnext;:将ptmpnode指向链表的第一个实际节点(头节点之后的节点)。

  • while (ptmpnode != NULL):当ptmpnode不为NULL时,继续遍历链表。

  • if (ptmpnode->data == olddata):检查当前节点的数据是否等于要查找的olddata

  • 如果相等,ptmpnode->data = newdata;将当前节点的数据替换为newdata

  • ptmpnode = ptmpnode->pnext;:将ptmpnode移动到下一个节点。

 2.5函数返回

return 0;

当链表遍历完成后,函数返回 0,表示操作成功。

总体来说,这个函数的作用是在给定的链表中查找所有等于olddata的数据,并将其替换为newdata,如果链表为空则返回 - 1,否则返回 0 表示操作成功。

3. 函数append_linklist(单向链表尾插法)

完整代码如下:

int append_linklist(list_node_t *phead, datatype tmpdata)
{
    list_node_t *pnewnode = NULL;
    list_node_t *plastnode = NULL;

    //参数合法性检测
    if (NULL == phead)
    {
        return -1;
    }

    //申请新的节点
    pnewnode = malloc(sizeof(list_node_t));
    if (NULL == pnewnode)
    {
        return -1;
    }

    //对新节点内容初始化
    pnewnode->data = tmpdata;
    pnewnode->pnext = NULL;

    //找到最后一个节点
    plastnode = phead;
    while (plastnode->pnext != NULL)
    {
        plastnode = plastnode->pnext;
    }

    //插入元素
    plastnode->pnext = pnewnode;

    return 0;
}

3.1函数声明和参数

int append_linklist(list_node_t *phead, datatype tmpdata);
  • append_linklist是函数名。

  • list_node_t *phead是指向链表头节点的指针,函数通过这个指针来操作链表。

  • datatype tmpdata是要插入到链表中的数据,datatype在这里是一个自定义的数据类型。

3.2局部变量声明

list_node_t *pnewnode = NULL;
list_node_t *plastnode = NULL;
  • pnewnode用于指向新创建的节点。

  • plastnode用于在链表中找到最后一个节点。

3.3参数合法性检测

if (NULL == phead)
{
    return -1;
}
  • 检查传入的链表头指针是否为NULL,如果是,则说明链表不存在,函数返回 - 1 表示操作失败。

3.4申请新的节点

pnewnode = malloc(sizeof(list_node_t));
if (NULL == pnewnode)
{
    return -1;
}
  • 使用malloc函数动态分配一个list_node_t类型的内存空间,用于存储新节点。

  • 如果malloc分配内存失败(返回NULL),函数返回 - 1 表示操作失败。

3.5对新节点内容初始化

pnewnode->data = tmpdata;
pnewnode->pnext = NULL;
  • 将传入的tmpdata赋值给新节点的数据域data

  • 将新节点的指针域pnext设置为NULL,表示它是链表的最后一个节点。

3.6找到最后一个节点

plastnode = phead;
while (plastnode->pnext != NULL)
{
    plastnode = plastnode->pnext;
}
  • 从链表头节点开始,通过遍历链表找到最后一个节点,将plastnode指向该节点。

3.7插入元素

plastnode->pnext = pnewnode;
  • 将最后一个节点的pnext指针指向新创建的节点,从而将新节点添加到链表末尾。

3.8返回值

return 0;
  • 如果整个操作成功,函数返回 0 表示操作完成。

4 .函数delete_linklist(从链表中删除指定数据节点

完整代码如下: 

int delete_linklist(list_node_t *phead, datatype tmpdata)
{
    list_node_t *pprenode = NULL;
    list_node_t *ptmpnode = NULL;

    //参数合法性检测
    if (NULL == phead)
    {
        return -1;
    }

    ptmpnode = phead->pnext;
    pprenode = phead;
    while (ptmpnode != NULL)
    {
        if (ptmpnode->data == tmpdata)
        {
            pprenode->pnext = ptmpnode->pnext;
            free(ptmpnode);
            ptmpnode = pprenode->pnext;
        }
        else 
        {
            ptmpnode = ptmpnode->pnext;
            pprenode = pprenode->pnext;
        }
    }

    return 0;
}

4.1函数定义和参数

int delete_linklist(list_node_t *pphead, datatype tmpdata)

函数名为delete_linklist,返回值类型为int。它接受两个参数:

  • pphead:指向链表头节点指针的指针,用于在函数中修改链表头指针(如果删除的是头节点)。

  • tmpdata:要删除的节点中的数据。

4.2局部变量声明

list_node_t *pprenode = NULL;
list_node_t *ptmpnode = NULL;

声明了两个指向链表节点的指针:

  • pprenode:用于指向当前节点的前一个节点。

  • ptmpnode:用于遍历链表的当前节点。

4.3参数合法性检测

if (NULL == phead)
{
    return -1;
}

 检查传入的链表头指针是否为NULL,如果是,说明链表不存在,函数返回 - 1 表示操作失败。

4.4初始化指针

ptmpnode = phead->next;
pprenode = phead;

 将ptmpnode指向链表的第一个有效节点(头节点后面的节点),pprenode指向头节点。

4.5遍历链表并删除节点

while (ptmpnode != NULL)
{
    if (ptmpnode->data == tmpdata)
    {
        pprenode->next = ptmpnode->next;
        free(ptmpnode);
        ptmpnode = pprenode->next;
    }
    else
    {
        ptmpnode = ptmpnode->next;
        pprenode = pprenode->next;
    }
}

使用while循环遍历链表,直到ptmpnodeNULL(即到达链表末尾)。在每次循环中:

  • 如果当前节点ptmpnode的数据等于要删除的数据tmpdata,则将当前节点从链表中删除,通过修改前一个节点pprenodenext指针跳过当前节点,并使用free函数释放当前节点的内存。然后将ptmpnode指向下一个节点。

  • 如果当前节点的数据不等于要删除的数据,则将ptmpnodepprenode都指向下一个节点,继续遍历链表。

4.6返回结果

return 0;

函数返回 0 表示操作成功。

5.函数destroy_linklist(销毁链表)

完整代码如下:

int destroy_linklist(list_node_t **pphead)
{
    list_node_t *pprenode = NULL;
    list_node_t *ptmpnode = NULL;

    //参数合法性检测
    if (NULL == pphead || NULL == *pphead)
    {
        return -1;
    }

    pprenode = *pphead;
    ptmpnode = *pphead;

    while (ptmpnode != NULL)
    {
        ptmpnode = ptmpnode->pnext;
        free(pprenode);
        pprenode = ptmpnode;
    }

    *pphead = NULL;

    return 0;
}

5.1函数定义

int destroy_linklist(list_node_t **pphead)
  • 函数名为destroy_linklist,返回值类型为int

  • 函数接受一个指向list_node_t类型指针的指针pphead,这意味着它可以修改传入的链表头指针。

5.2局部变量声明

list_node_t *pprenode = NULL;
list_node_t *ptmpnode = NULL;
  • 声明了两个指向list_node_t类型的指针pprenodeptmpnode,并初始化为NULL。它们将用于遍历链表和释放节点内存。

5.3参数合法性检测

if (NULL == phead || NULL == *pphead)
{
    return -1;
}
  • 检查传入的指针pphead是否为NULL,以及pphead所指向的链表头指针是否为NULL。如果其中任何一个为NULL,说明链表不存在或传入参数无效,函数返回-1表示错误。

5.4初始化指针

pprenode = *pphead;
ptmpnode = *pphead;
  • pprenodeptmpnode都初始化为链表的头节点。

5.5遍历并释放节点内存

while (ptmpnode != NULL)
{
    ptmpnode = ptmpnode->pnext;
    free(pprenode);
    pprenode = ptmpnode;
}
  • 使用while循环遍历链表。

  • ptmpnode = ptmpnode->pnext;:将ptmpnode指向下一个节点,以便在释放当前节点后仍能访问后续节点。

  • free(pprenode);:释放当前节点的内存。

  • pprenode = ptmpnode;:将pprenode更新为ptmpnode,以便继续遍历和释放后续节点。

5.6设置头指针为NULL

*pphead = NULL;

 将链表的头指针设置为NULL,表示链表已被销毁

5.7返回值

return 0;

函数执行成功,返回0

这个函数通过遍历链表并释放每个节点的内存,最终将链表头指针设置为NULL,完成链表的销毁操作。

6.函数 find_mid_node_linklist(在链表中找到中间节点

完整代码如下:

list_node_t *find_mid_node_linklist(list_node_t *phead)
{
    list_node_t *pfast = NULL;
    list_node_t *pslow = NULL;

    pslow = phead->pnext;
    pfast = phead->pnext;

    while (1)
    {
        pfast = pfast->pnext;
        if (NULL == pfast)
        {
            break;
        }
        pfast = pfast->pnext;
        if (NULL == pfast)
        {
            break;
        }
        pslow = pslow->pnext;
    }
    
    return pslow;
}

6.1 函数定义

list_node_t *find_mid_node_linklist(list_node_t *phead)

函数名为 find_mid_node_linklist,它接受一个指向 list_node_t 类型的指针 phead(链表头指针)作为参数,返回值是一个指向 list_node_t 类型的指针,即链表的中间节点指针。

6.2变量声明

list_node_t *pfast = NULL;
list_node_t *pslow = NULL;

声明了两个指向 list_node_t 类型的指针 pfast 和 pslow,并初始化为 NULL。它们将用于遍历链表,其中 pfast 每次移动两个节点,pslow 每次移动一个节点。

6.3初始化指针

pslow = phead->pnext;
pfast = phead->pnext;

将 pslow 和 pfast 都初始化为链表头节点的下一个节点。这意味着链表至少需要有一个节点(头节点之后的节点)才能进行后续操作。 

6.4遍历链表

while (1)
{
    pfast = pfast->pnext;
    if (NULL == pfast)
    {
        break;
    }
    pfast = pfast->pnext;
    if (NULL == pfast)
    {
        break;
    }
    pslow = pslow->pnext;
}

这是一个无限循环,用于遍历链表。在循环中:

  1. pfast 先移动一个节点。

  2. 检查 pfast 是否为 NULL,如果是则说明链表节点数为奇数且 pfast 到达链表末尾,退出循环。

  3. pfast 再移动一个节点。

  4. 再次检查 pfast 是否为 NULL,如果是则说明链表节点数为偶数且 pfast 到达链表末尾,退出循环。

  5. 如果 pfast 两次移动后都不为 NULL,则 pslow 移动一个节点。

6.5返回结果

return pslow;

循环结束后,pslow 指向的就是链表的中间节点,将其返回。

该函数使用快慢指针(pfast 和 pslow)的方法,快指针每次移动两个节点,慢指针每次移动一个节点,当快指针到达链表末尾时,慢指针正好指向链表的中间节点,从而实现了找到链表中间节点的功能。

7.函数 find_last_kth_node(在链表中找到倒数第 k 个节点)

完整代码如下:

list_node_t *find_last_kth_node(list_node_t *phead, int k)
{
    list_node_t *pfast = NULL;
    list_node_t *pslow = NULL;
    int i = 0;

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

    while (pfast != NULL)
    {
        pfast = pfast->pnext;
        pslow = pslow->pnext;
    }

    return pslow;
}

7.1函数定义

list_node_t *find_last_kth_node(list_node_t *phead, int k)

函数 find_last_kth_node 接受两个参数,第一个参数 phead 是链表的头指针,类型为 list_node_t *;第二个参数 k 是一个整数,表示要找的倒数第 k 个节点。函数的返回值是一个指向 list_node_t 类型的指针,即找到的倒数第 k 个节点的指针,如果找不到则返回 NULL

7.2变量声明

list_node_t *pfast = NULL;
list_node_t *pslow = NULL;
int i = 0;

声明了两个指向 list_node_t 类型的指针 pfast 和 pslow,并初始化为 NULL,还声明了一个整数变量 i 并初始化为 0

7.3初始化指针

pfast = phead->pnext;
pslow = phead->pnext;

 将 pfast 和 pslow 都初始化为链表头节点的下一个节点(即第一个实际数据节点)。

7.4移动 pfast 指针

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

使用 for 循环将 pfast 指针向前移动 k 步。如果在移动过程中 pfast 指向了 NULL,说明链表长度小于 k,无法找到倒数第 k 个节点,直接返回 NULL

7.5同步移动 pfast 和 pslow 指针

while (pfast != NULL)
{
    pfast = pfast->pnext;
    pslow = pslow->pnext;
}

当 pfast 指针没有到达链表末尾(即 pfast != NULL)时,同时将 pfast 和 pslow 指针向前移动一步。当 pfast 到达链表末尾时,pslow 就指向了链表的倒数第 k 个节点。

7.6返回结果

return pslow;

最后返回 pslow 指针,它指向的就是链表中倒数第 k 个节点。

8. 函数delete_mid_node(删除链表中指定节点的下一个节点,并将下一个节点的数据复制到当前节点)

这段代码通过复制数据和调整指针的方式,实现了在链表中删除指定节点的下一个节点的功能。完整代码如下 :

int delete_mid_node(list_node_t *ptmpnode)
{
    list_node_t *pnextnode = NULL;

    pnextnode = ptmpnode->pnext;
    ptmpnode->data = pnextnode->data;
    ptmpnode->pnext = pnextnode->pnext;
    free(pnextnode);

    return 0;
}

8.1函数定义

int delete_mid_node(list_node_t *ptmpnode)
  • int:表示函数的返回值类型为整数。这里返回0,通常用于表示操作成功。

  • delete_mid_node:函数名。

  • list_node_t *ptmpnode:函数的参数,是一个指向链表节点的指针。这个指针指向的是链表中需要操作的当前节点。

8.2局部变量声明

list_node_t *pnextnode = NULL;
  • 声明了一个指向链表节点的指针pnextnode,并初始化为NULL。这个指针将用于指向当前节点ptmpnode的下一个节点。

8.3获取下一个节点的指针

pnextnode = ptmpnode->pnext;

 将ptmpnode的下一个节点的地址赋值给pnextnode

8.4复制数据

ptmpnode->data = pnextnode->data;

 将下一个节点pnextnode的数据复制到当前节点ptmpnode

8.5调整指针

ptmpnode->pnext = pnextnode->pnext;
  • 将当前节点ptmpnodepnext指针指向pnextnode的下一个节点,跳过了pnextnode,从逻辑上删除了pnextnode

8.6释放内存

free(pnextnode);
  • 使用free函数释放pnextnode所指向的内存,防止内存泄漏。

8.7返回值

return 0;
  • 函数返回0,表示操作成功完成。

9. 函数invert_linklist(链表倒置)

完整代码如下:

int invert_linklist(list_node_t *phead)
{
    list_node_t *ptmpnode = NULL;
    list_node_t *pinsertnode = NULL;

    //参数合法性检测
    if (NULL == phead)
    {
        return -1;
    }

    //让头结点与后序节点断开
    ptmpnode = phead->pnext;
    pinsertnode = ptmpnode;
    phead->pnext = NULL;

    while (ptmpnode != NULL)
    {
        ptmpnode = ptmpnode->pnext;
        pinsertnode->pnext = phead->pnext;
        phead->pnext = pinsertnode;
        pinsertnode = ptmpnode;
    }

    return 0;
}

9.1函数定义

int invert_linklist(list_node_t *phead)

该函数接受一个指向链表头节点的指针 phead 作为参数,返回值为 int 类型。

9.2局部变量声明

list_node_t *ptmpnode = NULL;
list_node_t *pinsertnode = NULL;

声明了两个指向 list_node_t 类型的指针变量 ptmpnode 和 pinsertnode,并初始化为 NULL。这两个指针将用于链表倒置过程中的临时操作。

9.3参数合法性检测

if (NULL == phead)
{
    return -1;
}

检查传入的链表头指针 phead 是否为 NULL。如果是,则说明链表为空,函数返回 -1 表示操作失败。

9.4断开头节点与后续节点

ptmpnode = phead->pnext;
pinsertnode = ptmpnode;
phead->pnext = NULL;

将 ptmpnode 指向头节点的下一个节点,pinsertnode 也指向该节点,然后将头节点的 pnext 指针设为 NULL,使头节点成为新链表的尾节点。

假设我们有一个链表,头指针为 phead,链表结构为 phead -> node1 -> node2 -> node3 ->...

  • ptmpnode = phead->pnext;:这一步将 ptmpnode 指向头节点 phead 的下一个节点,也就是 node1。此时 ptmpnode 就指向了原链表除头节点外的部分。

  • pinsertnode = ptmpnode;:让 pinsertnode 也指向 node1,后续会用它来处理节点插入操作。

  • phead->pnext = NULL;:将头节点 phead 的 pnext 指针设置为 NULL,这就使得头节点与后面的节点断开了连接,此时头节点变成了一个独立的节点,并且将成为反转后链表的尾节点。链表状态变为 phead(独立,尾节点) 和 node1 -> node2 -> node3 ->... 两部分。

9.5链表反转循环

while (ptmpnode != NULL)
{
    ptmpnode = ptmpnode->pnext;
    pinsertnode->pnext = phead->pnext;
    phead->pnext = pinsertnode;
    pinsertnode = ptmpnode;
}

循环遍历原链表的剩余节点,将每个节点插入到头节点之后,实现链表的反转。具体步骤如下:

  • ptmpnode = ptmpnode->pnext;:将 ptmpnode 指向下一个节点,以便后续处理。

  • pinsertnode->pnext = phead->pnext;:将当前要插入的节点的 pnext 指针指向原头节点的下一个节点。

  • phead->pnext = pinsertnode;:将原头节点的 pnext 指针指向当前要插入的节点。

  • pinsertnode = ptmpnode;:更新 pinsertnode 为下一个要处理的节点。

假设当前链表状态是 phead(独立,尾节点) 和 node1 -> node2 -> node3 ->... 。

  • 第一次循环:

    • ptmpnode = ptmpnode->pnext;ptmpnode 原本指向 node1,执行后它指向 node2。此时 pinsertnode 仍指向 node1

    • pinsertnode->pnext = phead->pnext;:因为 phead->pnext 是 NULL(前面断开头节点时设置的),所以 node1 的 pnext 被设置为 NULL

    • phead->pnext = pinsertnode;:将 phead 的 pnext 指针指向 node1,此时链表变为 phead -> node1(反转后的一小段),剩下未处理的是 node2 -> node3 ->...

    • pinsertnode = ptmpnode;:让 pinsertnode 指向 node2,为下一次循环做准备。

  • 第二次循环及后续:

    • 重复上述过程,每次循环都将 pinsertnode 指向的节点(当前未处理链表的第一个节点)插入到 phead 后面,不断改变指针指向关系,逐步将剩余未处理的链表节点反转到 phead 之后。随着循环的进行,phead 后面的节点越来越多,最终原链表所有节点都被反转到 phead 之后,完成整个链表的反转。

9.6返回结果

return 0;

如果链表反转成功,函数返回 0

10.单向链表的冒泡排序(bubble_sort_linklist)

完整代码如下:

int bubble_sort_linklist(list_node_t *phead)
{
    list_node_t *ptmpnode1 = NULL;
    list_node_t *ptmpnode2 = NULL;
    list_node_t *pend = NULL;
    datatype tmpdata;

    if (NULL == phead)
    {
        return -1;
    }

    if (NULL == phead->pnext || NULL == phead->pnext->pnext)
    {
        return 0;
    }

    while (1)
    {
        ptmpnode1 = phead->pnext;
        ptmpnode2 = phead->pnext->pnext;

        if (pend == ptmpnode2)
        {
            break;
        }

        while (ptmpnode2 != pend)
        {
            if (ptmpnode1->data > ptmpnode2->data)
            {
                tmpdata = ptmpnode1->data;
                ptmpnode1->data = ptmpnode2->data;
                ptmpnode2->data = tmpdata;
            }
            ptmpnode1 = ptmpnode1->pnext;
            ptmpnode2 = ptmpnode2->pnext;        
        }
        pend = ptmpnode1;
    }
    
    return 0;
}

10.1函数定义

int bubble_sort_linklist(list_node_t *phead)

这是函数bubble_sort_linklist的声明,它接受一个指向链表头节点的指针phead,并返回一个整数。函数的返回值在不同情况下有不同的含义:

  • 如果链表为空(pheadNULL),返回-1

  • 如果链表只有一个节点或没有节点,返回0

  • 如果排序成功,返回0

10.2局部变量声明

list_node_t *ptmpnode1 = NULL;
list_node_t *ptmpnode2 = NULL;
list_node_t *pend = NULL;
datatype tmpdata;
  • ptmpnode1ptmpnode2是用于遍历链表的临时指针。

  • pend是一个指针,用于标记已经排序好的部分的末尾。

  • tmpdata是一个临时变量,用于交换节点的数据。

10.3链表为空的检查

if (NULL == phead)
{
    return -1;
}

 如果链表头指针pheadNULL,说明链表为空,函数返回-1。

10.4链表节点数小于 2 的检查

if (NULL == phead->pnext || NULL == phead->pnext->pnext)
{
    return 0;
}

 如果链表只有一个节点或没有节点,不需要排序,函数返回0。

10.5冒泡排序的主循环

while (1)
{
    ptmpnode1 = phead->pnext;
    ptmpnode2 = phead->pnext->pnext;
    if (pend == ptmpnode2)
    {
        break;
    }
    while (ptmpnode2 != pend)
    {
        if (ptmpnode1->data > ptmpnode2->data)
        {
            tmpdata = ptmpnode1->data;
            ptmpnode1->data = ptmpnode2->data;
            ptmpnode2->data = tmpdata;
        }
        ptmpnode1 = ptmpnode1->pnext;
        ptmpnode2 = ptmpnode2->pnext;
    }
    pend = ptmpnode1;
}
  • 外层while (1)是一个无限循环,直到排序完成。

  • ptmpnode1ptmpnode2分别指向链表的前两个节点。

  • 如果ptmpnode2等于pend,说明已经到达已排序部分的末尾,退出外层循环。

  • 内层while循环用于比较相邻节点,并在需要时交换它们的数据。

  • 每次内层循环结束后,pend指针移动到当前已排序部分的末尾。

循环初始化:while(1) 是一个无限循环,只有在满足特定条件时才会退出。每次循环开始时,ptmpnode1 指向链表头节点的下一个节点(即第一个有效节点),ptmpnode2 指向 ptmpnode1 的下一个节点。

终止条件判断:if (pend == ptmpnode2) 用于检查是否已经完成了所有需要的排序轮次。pend 指针标记了已经排序好的部分的末尾,当 ptmpnode2 到达 pend 的位置时,说明当前轮次已经检查完了未排序部分的所有元素,此时退出外层循环。

内层 while 循环:

  • 循环目的:内层 while 循环用于在未排序的链表部分中进行一次完整的冒泡操作,即比较相邻节点并交换顺序不正确的节点。
  • 比较和交换:在循环内部,if (ptmpnode1->data > ptmpnode2->data) 检查相邻的两个节点 ptmpnode1 和 ptmpnode2 的数据。如果 ptmpnode1 的数据大于 ptmpnode2 的数据,就通过临时变量 tmpdata 交换这两个节点的数据。
  • 指针移动:每次比较和交换操作后,ptmpnode1 和 ptmpnode2 都向后移动一个节点,以便继续比较下一对相邻节点,直到 ptmpnode2 到达 pend 所标记的位置,即未排序部分的末尾。

pend 指针更新:

  • 在内层循环结束后,pend = ptmpnode1; 将 pend 指针更新为当前轮次中最后一个进行比较的节点 ptmpnode1。这意味着 pend 之后的节点已经是有序的,在下一轮排序中不需要再检查这些节点。

通过外层循环不断重复上述过程,每一轮都会将未排序部分中最大(或最小)的元素移动到未排序部分的末尾,直到整个链表都被排序完成。

10.6函数返回

return 0;

排序成功后,函数返回0。 

 11.单向链表的选择排序(select_sort_linklist)

这段代码实现了对链表的选择排序,通过两层循环找到最小值节点并与当前节点交换数据,从而逐步将链表排序。完整代码如下: 

int select_sort_linklist(list_node_t *phead)
{
    list_node_t *ptmpnode1 = NULL;
    list_node_t *ptmpnode2 = NULL;
    list_node_t *pminnode = NULL;
    datatype tmpdata;

    if (NULL == phead)
    {
        return -1;
    }

    if (NULL == phead->pnext || NULL == phead->pnext->pnext)
    {
        return 0;
    }

    ptmpnode1 = phead->pnext;
    while (ptmpnode1->pnext != NULL)
    {
        pminnode = ptmpnode1;
        ptmpnode2 = ptmpnode1->pnext;
        while (ptmpnode2 != NULL)
        {
            if (ptmpnode2->data < pminnode->data)
            {
                pminnode = ptmpnode2;
            }
            ptmpnode2 = ptmpnode2->pnext;
        }
        if (pminnode != ptmpnode1)
        {
            tmpdata = pminnode->data;
            pminnode->data = ptmpnode1->data;
            ptmpnode1->data = tmpdata;
        }
        ptmpnode1 = ptmpnode1->pnext;
    }

    return 0;
}

11.1函数声明

int select_sort_linklist(list_node_t *phead)

函数 select_sort_linklist 接受一个指向链表头节点的指针 phead 作为参数,返回值为 int 类型。函数的目的是对传入的链表进行选择排序。

11.2局部变量声明

list_node_t *ptmpnode1 = NULL;
list_node_t *ptmpnode2 = NULL;
list_node_t *pminnode = NULL;
datatype tmpdata;

 声明了三个指向链表节点的指针 ptmpnode1ptmpnode2 和 pminnode,以及一个 datatype 类型的变量 tmpdataptmpnode1 用于遍历链表,ptmpnode2 用于在每次 ptmpnode1 移动时从 ptmpnode1 的下一个节点开始遍历寻找最小值节点,pminnode 用于记录当前找到的最小值节点,tmpdata 用于临时存储数据以便交换。

11.3边界条件检查

if (NULL == phead)
{
    return -1;
}
if (NULL == phead->pnext || NULL == phead->pnext->pnext)
{
    return 0;
}

第一个 if 语句检查链表头指针是否为空,如果为空,说明链表不存在,返回 -1 表示错误。

第二个 if 语句检查链表是否只有一个节点或者没有节点(即头节点的下一个节点为空,或者头节点的下一个节点的下一个节点为空),如果是这种情况,链表已经是有序的,返回 0 表示排序成功(这里排序成功但实际上无需排序)。

11.4选择排序逻辑

ptmpnode1 = phead->pnext;
while (ptmpnode1->pnext!= NULL)
{
    pminnode = ptmpnode1;
    ptmpnode2 = ptmpnode1->pnext;
    while (ptmpnode2!= NULL)
    {
        if (ptmpnode2->data < pminnode->data)
        {
            pminnode = ptmpnode2;
        }
        ptmpnode2 = ptmpnode2->pnext;
    }
    if (pminnode!= ptmpnode1)
    {
        tmpdata = pminnode->data;
        pminnode->data = ptmpnode1->data;
        ptmpnode1->data = tmpdata;
    }
    ptmpnode1 = ptmpnode1->pnext;
}
  1. ptmpnode1 = phead->pnext;:将 ptmpnode1 指向链表的第一个实际节点(跳过头节点)。
  2. 外层 while 循环:当 ptmpnode1 的下一个节点不为空时,继续循环。这是为了确保 ptmpnode1 可以遍历到链表的倒数第二个节点。
  3. 在每次外层循环开始时,将 pminnode 初始化为 ptmpnode1,即假设当前节点是最小值节点。
  4. 内层 while 循环:从 ptmpnode1 的下一个节点开始遍历链表,寻找最小值节点。如果找到比当前 pminnode 更小的节点,更新 pminnode
  5. 内层循环结束后,如果找到的最小值节点 pminnode 不是最初假设的 ptmpnode1,则交换它们的数据。
  6. 外层循环结束后,链表中的节点已经按照升序排列。

 

11.5返回值

return 0;

函数最后返回 0,表示链表排序成功。 

你可能感兴趣的:(数据结构,链表,算法,linux)