今天接着对链表进行进一步完善,对于上节中已经实现了对节点的删除操作,这次对这个操作进行升级,符合条件的都可以被删除(通过函数指针来实现),具体实现如下:
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语言相关的探讨,下节再见!