• 指针应用-----链表三


    今天接着对链表进行进一步完善,对于上节中已经实现了对节点的删除操作,这次对这个操作进行升级,符合条件的都可以被删除(通过函数指针来实现),具体实现如下:

    list.c:

    #ifndef _LIST_H_
    #define _LIST_H_
    
    #include <assert.h>
    
    typedef struct node
    {
        int data;
        struct node* next;
    } node_t;
    
    typedef void (*FUNC)(node_t*);
    typedef int (*FUNC2)(node_t*);//这个是用来判断是否节点符合条件的
    
    node_t* list_insert_front(node_t* head, int data);
    
    void list_for_each(node_t* head, FUNC f);
    
    node_t* list_free(node_t* head);
    
    node_t* list_get_node(node_t* head, int index);
    
    node_t* list_insert_at(node_t* head, int data, int index);
    
    node_t* list_remove_at(node_t* head, int index);
    
    node_t* list_find(node_t* head, int data, int* ret);
    
    node_t* list_remove_if(node_t* head, FUNC2 f);//根据函数指针实现的符合条件的指针进行删除
    
    #endif /* _LIST_H_ */

    list.c:

    #include "list.h"
    #include <stdlib.h>
    #include <stdio.h>
    
    node_t* list_insert_front(node_t* head, int data)
    {
        node_t* n = (node_t*)malloc(sizeof(node_t));
        assert(n != NULL);
        n->data = data;
        n->next = NULL;
    
        if (head == NULL)
            head = n;
        else
        {
            n->next = head;
            head = n;
        }
    
        return head;
    }
    
    void list_for_each(node_t* head, FUNC f)
    {
        while (head)
        {
            f(head);
            head = head->next;
        }
    }
    
    node_t* list_free(node_t* head)
    {
        node_t* tmp;
        while (head)
        {
            tmp = head;
            head = head->next;
            free(tmp);
        }
        return head;
    }
    
    node_t* list_get_node(node_t* head, int index)
    {
        assert(index >= 0);
    
        int j = 0;
        while (head && j < index)
        {
            head = head->next;
            j++;
        }
            
        return head;
    
    }
    
    node_t* list_insert_at(node_t* head, int data, int index)
    {
        assert(index >= 0);
    
        if (index == 0)
            return list_insert_front(head, data);
    
        node_t* p;
        p = list_get_node(head, index - 1);
        if (p == NULL)
        {
            fprintf(stderr, "error insert pos
    ");
            exit(EXIT_FAILURE);
        }
    
        node_t* n = (node_t*)malloc(sizeof(node_t));
        assert(n != NULL);
        n->data = data;
        n->next = NULL;
    
        n->next = p->next;
        p->next = n;
    
        return head;
    }
    
    node_t* list_remove_at(node_t* head, int index)
    {
        assert(index >= 0);
        node_t* n;
        if (index == 0)
        {
            n = head;
            head = head->next;
            free(n);
        }
        else
        {
            node_t* p = list_get_node(head, index - 1);
            if (p == NULL || p->next == NULL)
            {
                fprintf(stderr, "error remove pos
    ");
                exit(EXIT_FAILURE);
            }
            n = p->next;
            p->next = n->next;
            free(n);
        }
    
        return head;
    }
    
    node_t* list_find(node_t* head, int data, int* ret)
    {
        *ret = -1;
        int i = 0;
        while (head)
        {
            if (head->data == data)
            {
                *ret = i;
                break;
            }
            head = head->next;
            i++;
        }
    
        return head;
    }
    
    node_t* list_remove_if(node_t* head, FUNC2 f)
    {
        node_t* prev = NULL;
        node_t* curr = head;
        while (curr)
        {
            node_t* next = curr->next;
            if (f(curr))//符合条件的,则进行删除
            {
                if (prev == NULL)
                    head = next;
                else
                    prev->next = next;
                free(curr);
            }
            else//不符合条件
                prev = curr;
            curr = next;
        }
        return head;
    }

    main.c:

    #include "list.h"
    #include <stdio.h>
    
    void print_node(node_t* n)
    {
        printf("data=%d ", n->data);
    }
    
    int greater_than_10(node_t* n)//如果结点大小10则为满足条件
    {
        return n->data > 10;
    }
    
    int main(void)
    {
        node_t* head = NULL;
        head = list_insert_front(head, 30);
        head = list_insert_front(head, 20);
        head = list_insert_front(head, 10);
    
        list_for_each(head, print_node);
        putchar('
    ');
    
        node_t* n = NULL;
        n = list_get_node(head, 1);
        if (n != NULL)
            printf("data = %d
    ", n->data);
        else
            printf("not found
    ");
    
        head = list_insert_at(head, 15, 1);
        list_for_each(head, print_node);
        putchar('
    ');
    
        head = list_remove_at(head, 1);
        list_for_each(head, print_node);
        putchar('
    ');
    
        int ret;
        n = list_find(head, 20, &ret);
        if (n != NULL)
            printf("data = %d index = %d
    ", n->data, ret);
        else
            printf("not found
    ");
    
        list_remove_if(&head, greater_than_10);
        list_for_each(head, print_node);
        putchar('
    ');
    
        head = list_free(head);
        assert(head == NULL);
    
    
        return 0;
    }

    编译运行:

    对于上面这种实现方式,其实我们可以进行优化,也就是一直有提到过的----指针的指针

    下面用它来进行程序的优化:

    list.h:

    #ifndef _LIST_H_
    #define _LIST_H_
    
    #include <assert.h>
    
    typedef struct node
    {
        int data;
        struct node* next;
    } node_t;
    
    typedef void (*FUNC)(node_t*);
    typedef int (*FUNC2)(node_t*);
    
    node_t* list_insert_front(node_t* head, int data);
    
    void list_for_each(node_t* head, FUNC f);
    
    node_t* list_free(node_t* head);
    
    node_t* list_get_node(node_t* head, int index);
    
    node_t* list_insert_at(node_t* head, int data, int index);
    
    node_t* list_remove_at(node_t* head, int index);
    
    node_t* list_find(node_t* head, int data, int* ret);
    
    void list_remove_if(node_t** head, FUNC2 f);//此时不用返回值了,因为二级指针能改变实参指针的指向
    
    #endif /* _LIST_H_ */

    list.c:

    void list_remove_if(node_t** head, FUNC2 f)
    {
        node_t** curr = head;
        while (*curr)
        {
            node_t* entry = *curr;
            if (f(entry))
            {
                *curr = entry->next;
                free(entry);
            }
            else
                curr = &entry->next;
        }
    }

    从实现角度来看,这种方式实现起来要简单多了,但是理解起来可能要难一些,但这是比较好的做法,所以接下来,对它进行分解,体现一下二级指针的好处:

    下面开始遍历结点,我们假设第一个结点满足条件第二个结点不满足条件第三个结点满足条件,来一一来分析删除的流程,这样分析完之后,就对其二级指针的这种实现就会比较清楚了:

    删除node1的流程:

     

    跳过不满足删除条件node2的流程:

    删除node3的流程:

    这时再次循环,由于*curr已经指向NULL了,所以循环终止,最后node1和node3就被删除了。通过上面的实现可以发现,利用指针的指针来实现删除时,就不用去判断要删除的节点的前驱是否为NULL了,回顾下之前的实现:

    因为指针的指针可以虚拟出一个前驱来,虽说不是真正的前驱,这样就省去了判断,代码也更加精练,好了,理解上面的实现流程之后,下面对其进行测试一下:

    main.c:

    #include "list.h"
    #include <stdio.h>
    
    void print_node(node_t* n)
    {
        printf("data=%d ", n->data);
    }
    
    int greater_than_10(node_t* n)
    {
        return n->data > 10;
    }
    
    int main(void)
    {
        node_t* head = NULL;
        head = list_insert_front(head, 30);
        head = list_insert_front(head, 20);
        head = list_insert_front(head, 10);
    
        list_for_each(head, print_node);
        putchar('
    ');
    
        node_t* n = NULL;
        n = list_get_node(head, 1);
        if (n != NULL)
            printf("data = %d
    ", n->data);
        else
            printf("not found
    ");
    
        head = list_insert_at(head, 15, 1);
        list_for_each(head, print_node);
        putchar('
    ');
    
        head = list_remove_at(head, 1);
        list_for_each(head, print_node);
        putchar('
    ');
    
        int ret;
        n = list_find(head, 20, &ret);
        if (n != NULL)
            printf("data = %d index = %d
    ", n->data, ret);
        else
            printf("not found
    ");
    
        list_remove_if(&head, greater_than_10);//这时就不用再次对head进行赋值了
        list_for_each(head, print_node);
        putchar('
    ');
    
        head = list_free(head);
        assert(head == NULL);
    
    
        return 0;
    }

    编译运行:

    接着,将链表的所有能用二级指针的实现都进行统一一下,最终我们的链表程序代码如下:

    list.h:

    #ifndef _LIST_H_
    #define _LIST_H_
    
    #include <assert.h>
    
    typedef struct node
    {
        int data;
        struct node* next;
    } node_t;
    
    typedef void (*FUNC)(node_t*);
    typedef int (*FUNC2)(node_t*);
    
    void list_insert_front(node_t** head, int data);
    void list_for_each(node_t* head, FUNC f);
    void list_free(node_t** head);
    
    node_t* list_get_node(node_t* head, int index);
    void list_insert_at(node_t** head, int data, int index);
    void list_remove_at(node_t** head, int index);
    node_t* list_find(node_t* head, int data, int* ret);
    
    //node_t* list_remove_if(node_t* head, FUNC2 f);
    void list_remove_if(node_t** head, FUNC2 f);
    
    #endif /* _LIST_H_ */

    list.c:

    #include "list.h"
    #include <stdlib.h>
    #include <stdio.h>
    
    
    void list_insert_front(node_t** head, int data)
    {
        node_t* n = (node_t*)malloc(sizeof(node_t));
        assert(n != NULL);
        n->data = data;
        n->next = NULL;
    
        if (*head == NULL)
            *head = n;
        else
        {
            n->next = *head;
            *head = n;
        }
    }
    
    void list_for_each(node_t* head, FUNC f)
    {
        while (head)
        {
            f(head);
            head = head->next;
        }
    }
    
    void list_free(node_t** head)
    {
        node_t* tmp;
        while (*head)
        {
            tmp = *head;
            *head = (*head)->next;
            free(tmp);
        }
    }
    
    node_t* list_get_node(node_t* head, int index)
    {
        assert(index >= 0);
    
        int j = 0;
        while (head && j < index)
        {
            head = head->next;
            j++;
        }
    
    /*
        if (j == index)
            return head;
            */
    
        return head;
    
    }
    
    void list_insert_at(node_t** head, int data, int index)
    {
        assert(index >= 0);
    
        if (index == 0)
            return list_insert_front(head, data);
    
        node_t* p;
        p = list_get_node(*head, index - 1);
        if (p == NULL)
        {
            fprintf(stderr, "error insert pos
    ");
            exit(EXIT_FAILURE);
        }
    
        node_t* n = (node_t*)malloc(sizeof(node_t));
        assert(n != NULL);
        n->data = data;
        n->next = NULL;
    
        n->next = p->next;
        p->next = n;
    }
    
    void list_remove_at(node_t** head, int index)
    {
        assert(index >= 0);
        node_t* n;
        if (index == 0)
        {
            n = *head;
            free(n);
            *head = (*head)->next;
        }
        else
        {
            node_t* p = list_get_node(*head, index - 1);
            if (p == NULL || p->next == NULL)
            {
                fprintf(stderr, "error remove pos
    ");
                exit(EXIT_FAILURE);
            }
            n = p->next;
            p->next = n->next;
            free(n);
        }
    }
    
    node_t* list_find(node_t* head, int data, int* ret)
    {
        *ret = -1;
        int i = 0;
        while (head)
        {
            if (head->data == data)
            {
                *ret = i;
                break;
            }
            head = head->next;
            i++;
        }
    
        return head;
    }
    
    /*
    node_t* list_remove_if(node_t* head, FUNC2 f)
    {
        node_t* prev = NULL;
        node_t* curr = head;
        while (curr)
        {
            node_t* next = curr->next;
            if (f(curr))
            {
                if (prev == NULL)
                    head = next;
                else
                    prev->next = next;
                free(curr);
            }
            else
                prev = curr;
            curr = next;
        }
        return head;
    }
    */
    
    void list_remove_if(node_t** head, FUNC2 f)
    {
        node_t** curr = head;
        while (*curr)
        {
            node_t* entry = *curr;
            if (f(entry))
            {
                *curr = entry->next;
                free(entry);
            }
            else
                curr = &entry->next;
        }
    }

    main.c:

    #include "list.h"
    #include <stdio.h>
    
    void print_node(node_t* n)
    {
        printf("data=%d ", n->data);
    }
    
    int greater_than_10(node_t* n)
    {
        return n->data > 10;
    }
    
    int main(void)
    {
        node_t* head = NULL;
        list_insert_front(&head, 30);
        list_insert_front(&head, 20);
        list_insert_front(&head, 10);
    
        list_for_each(head, print_node);
        putchar('
    ');
    
    
        node_t* n = NULL;
        n = list_get_node(head, 1);
        if (n != NULL)
            printf("data = %d
    ", n->data);
        else
            printf("not found
    ");
    
        list_insert_at(&head, 15, 1);
        list_for_each(head, print_node);
        putchar('
    ');
    
        list_remove_at(&head, 1);
        list_for_each(head, print_node);
        putchar('
    ');
    
        int ret;
        n = list_find(head, 20, &ret);
        if (n != NULL)
            printf("data = %d index = %d
    ", n->data, ret);
        else
            printf("not found
    ");
    
        list_remove_if(&head, greater_than_10);
        list_for_each(head, print_node);
        putchar('
    ');
    
        list_free(&head);
        assert(head == NULL);
        
    
    
        return 0;
    }

    好了,关于链表的实现到此结束,当谈到链表时,如果第一时间你想到的是用一级指针去实现,说明你对C语言的指针理解不是太熟,建议学会用二级指针去解决实际问题,关于C语言相关的探讨,下节再见!

  • 相关阅读:
    基于.Net Core的Redis:实现查询附近的地理信息
    基于.Net Core的Redis:基本数据类型及其应用场景与命令行操作
    C# WebClient几种常用方法的用法
    const学习(续)
    C++ const学习
    Unicode
    android studio下使用HAXM android模拟器(x86)加速器
    使用efinance包获取股票数据
    Linux初识
    UWSGI
  • 原文地址:https://www.cnblogs.com/webor2006/p/3485566.html
Copyright © 2020-2023  润新知