今天接着对链表进行进一步完善,对于上节中已经实现了对节点的删除操作,这次对这个操作进行升级,符合条件的都可以被删除(通过函数指针来实现),具体实现如下:
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;
}
编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/21d4a5386ad60692aa51b325fe76d9e1.png)
对于上面这种实现方式,其实我们可以进行优化,也就是一直有提到过的----指针的指针:
![](https://i-blog.csdnimg.cn/blog_migrate/d8505a796b62a6fb873ae3d4106c335f.png)
下面用它来进行程序的优化:
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;
}
}
从实现角度来看,这种方式实现起来要简单多了,但是理解起来可能要难一些,但这是比较好的做法,所以接下来,对它进行分解,体现一下二级指针的好处:
![](https://i-blog.csdnimg.cn/blog_migrate/7d286996c2d9d2b2ed88190165975fd3.png)
![](https://i-blog.csdnimg.cn/blog_migrate/826899a0a62b386a82ddb399c469450c.png)
下面开始遍历结点,我们假设第一个结点满足条件,第二个结点不满足条件,第三个结点满足条件,来一一来分析删除的流程,这样分析完之后,就对其二级指针的这种实现就会比较清楚了:
删除node1的流程:
![](https://i-blog.csdnimg.cn/blog_migrate/07a31f719b62f3198645ec215aab9770.png)
跳过不满足删除条件node2的流程:
![](https://i-blog.csdnimg.cn/blog_migrate/acdb94bd1fdcec0d69f20ed1f10f7805.png)
删除node3的流程:
![](https://i-blog.csdnimg.cn/blog_migrate/8c1c74147dffedc10e1e0bd223064db2.png)
这时再次循环,由于*curr已经指向NULL了,所以循环终止,最后node1和node3就被删除了。通过上面的实现可以发现,利用指针的指针来实现删除时,就不用去判断要删除的节点的前驱是否为NULL了,回顾下之前的实现:
![](https://i-blog.csdnimg.cn/blog_migrate/727a16cefda427a85d75f981d43de53a.png)
因为指针的指针可以虚拟出一个前驱来,虽说不是真正的前驱,这样就省去了判断,代码也更加精练,好了,理解上面的实现流程之后,下面对其进行测试一下:
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;
}
编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/87c1d120a6dc293edca56acdca7785c8.png)
接着,将链表的所有能用二级指针的实现都进行统一一下,最终我们的链表程序代码如下:
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语言相关的探讨,下节再见!