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

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

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\n");
        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\n");
            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('\n');

    node_t* n = NULL;
    n = list_get_node(head, 1);
    if (n != NULL)
        printf("data = %d\n", n->data);
    else
        printf("not found\n");

    head = list_insert_at(head, 15, 1);
    list_for_each(head, print_node);
    putchar('\n');

    head = list_remove_at(head, 1);
    list_for_each(head, print_node);
    putchar('\n');

    int ret;
    n = list_find(head, 20, &ret);
    if (n != NULL)
        printf("data = %d index = %d\n", n->data, ret);
    else
        printf("not found\n");

    list_remove_if(&head, greater_than_10);
    list_for_each(head, print_node);
    putchar('\n');

    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('\n');

    node_t* n = NULL;
    n = list_get_node(head, 1);
    if (n != NULL)
        printf("data = %d\n", n->data);
    else
        printf("not found\n");

    head = list_insert_at(head, 15, 1);
    list_for_each(head, print_node);
    putchar('\n');

    head = list_remove_at(head, 1);
    list_for_each(head, print_node);
    putchar('\n');

    int ret;
    n = list_find(head, 20, &ret);
    if (n != NULL)
        printf("data = %d index = %d\n", n->data, ret);
    else
        printf("not found\n");

    list_remove_if(&head, greater_than_10);//这时就不用再次对head进行赋值了
    list_for_each(head, print_node);
    putchar('\n');

    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\n");
        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\n");
            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('\n');


    node_t* n = NULL;
    n = list_get_node(head, 1);
    if (n != NULL)
        printf("data = %d\n", n->data);
    else
        printf("not found\n");

    list_insert_at(&head, 15, 1);
    list_for_each(head, print_node);
    putchar('\n');

    list_remove_at(&head, 1);
    list_for_each(head, print_node);
    putchar('\n');

    int ret;
    n = list_find(head, 20, &ret);
    if (n != NULL)
        printf("data = %d index = %d\n", n->data, ret);
    else
        printf("not found\n");

    list_remove_if(&head, greater_than_10);
    list_for_each(head, print_node);
    putchar('\n');

    list_free(&head);
    assert(head == NULL);
    


    return 0;
}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

webor2006

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值