众所周知,链表的应用还是比较广泛的,其在于数据的处理比较方便;本文主要讲述用C语言实现链表的增删改查等一些功能,环境是在Linux下。
注:下文案例是建立在整型数据基础上的,如果是其他类型的数据,请参考修改即可。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef int elem_type; //这个只是把int取了个外号,可以不需要这一步,但是记得下文的elem_type都要换成int (如果不需要这一步)
// 声明节点类型
typedef struct node_t
{
elem_type data; // 数据域,存放一个数据元素
struct node_t* next; // 指针域,存放后继节点的指针
} node;
// 创建一个空链表
node* create(void)
{
node* n = (node*)malloc(sizeof(node));
n->next = NULL;
return n;
}
// 销毁某个链表
void destroy(node* l)
{
node* p = NULL;
while(l != NULL)
{
p = l->next;
free(l);
l = p;
}
}
// 在链表尾部插入新的节点
void push_back(node* l, elem_type data)
{
// 创建一个新节点
node* n = (node*)malloc(sizeof(node));
n->data = data;
n->next = NULL;
// 找到链表的尾节点
while(l->next != NULL)
l = l->next;
// 在尾部插入新节点
l->next = n;
}
// 在链表头部插入节点
void push_front(node* l, elem_type data)
{
// 创建一个新节点
node* n = (node*)malloc(sizeof(node));
n->data = data;
n->next = l->next;
// 将新节点插入头节点之后
l->next = n;
}
// 在链表中任意位置插入节点
void insert(node* l, unsigned int pos, elem_type data)
{
// 创建一个新节点
node* n = (node*)malloc(sizeof(node));
n->data = data;
// 找到要插入新节点的位置
while(pos-- && l->next != NULL) l = l->next;
// 在上面找到的节点之后插入新节点
n->next = l->next;
l->next = n;
}
// 删除某个节点
int my_remove(node* l, elem_type data)
{
node* p = NULL;
// 找到待删除节点的前驱节点
// 如果待删除的节点存在,下面循环结束后 l 就是待删除节点的前驱节点的指针
while(l->next != NULL && l->next->data != data)
l = l->next;
// 目标节点不存在,删除失败返回 0
if(l->next == NULL) return 0;
// 将待删除节点的前驱和后继连接起来,否则链表会断为两截
// 释放待删除的节点
p = l->next;
l->next = p->next;
free(p);
// 删除成功就返回 1
return 1;
}
// 修改(更新)链表节点数据
int update(node* l, elem_type old_val, elem_type new_val)
{
// 遍历链表找到目标节点
l = l->next;
while(l != NULL && l->data != old_val)
l = l->next;
// 如果找不到要修改的数据,就返回 0,表示修改失败
if(l == NULL) return 0;
// 如果找到了要修改的数据,就修改它,然后返回 1,表示修改成功
l->data = new_val;
return 1;
}
// 清空,即删除所有存放有数据元素的节点,就是将所有数据元素删除
void clear(node* l)
{
node* p = l->next, *q = NULL;
while(p != NULL)
{
q = p->next;
free(p);
p = q;
}
l->next = NULL;
}
// 打印链表中所有节点的数据
void show(node* l)
{
// 遍历链表所有节点
l = l->next;
while(l != NULL)
{
printf("%d ", l->data);
l = l->next;
}
printf("\n");
}
// 逆序
void reverse(node* l)
{
node* p = NULL, *q = NULL;
// 如果链表为空(没有任何数据元素),直接返回
if(l->next == NULL) return;
p = l->next->next;
l->next->next = NULL; // 最初的第一个节点逆序后将变为尾节点
// 从第二个节点开始,依次插入到最前面(头节点之后)
while(p != NULL)
{
q = p->next;
p->next = l->next;
l->next = p;
p = q;
}
}
// 求长度
int size(node* l)
{
int cnt = 0;
l = l->next;
while(l != NULL)
{
cnt++;
l = l->next;
}
return cnt;
}
// 判空
int empty(node* l)
{
return !(l->next);
}
/* 使用案例 */
int main(int argc, char* argv[])
{
node* l1 = create();
show(l1);
if(empty(l1))
{
printf("链表 l1 为空!\n");
}
push_back(l1, 5); //尾部插入
push_back(l1, 3);
push_front(l1, 9); //头部插入
if(!empty(l1))
{
printf("链表 l1 不为空!\n");
}
push_back(l1, 4);
printf("链表 l1 的长度为 %d\n", size(l1)); // 4
push_front(l1, 6);
//任意位置插入
insert(l1, 3, 8); // 中插
insert(l1, 10000, 10); // 尾插
insert(l1, 0, 7); // 头插
my_remove(l1, 5); //删除
if(!my_remove(l1, 13))
{
printf("链表中不存在 13,删除失败!\n");
}
if(update(l1, 8, 88)) //更新
{
printf("8 -> 88 修改成功!\n");
}
if(!update(l1, 20, 30))
{
printf("20 -> 30 修改失败!\n");
}
printf("逆序前:\n");
show(l1); // 7 6 9 88 3 4 10
reverse(l1); //逆序
printf("逆序后:\n");
show(l1);
return 0;
}
上述运行结果如下:
/*
链表 l1 为空!
链表 l1 不为空!
链表 l1 的长度为 4
链表中不存在 13,删除失败!
8 -> 88 修改成功!
20 -> 30 修改失败!
逆序前:
7 6 9 88 3 4 10
逆序后:
10 4 3 88 9 6 7
*/