—— 引子
今天给大家带来的是一个新的数据结构,链表!
上一次的顺序表,大家一定觉得非常的容易,没错,作为一个入门级别的数据结构,要是让大家感觉到吃力,那还了得!
一起来回顾一下我们上次的顺序表,作为一个比较简单的数据结构,顺序表利用自身的管理结构,完成了对于整个结构的控制,利用结构体将三个参数封装起来,size用来表示当前顺序表中一共有多少元素,而capacity表示最大可以储存多少元素,最后,还有这一个指针,用来指向一片堆区中的连续空间,用来真正的保存我们的数据。
相信现在大家听到我的介绍,恐怕陌生感要减少很多了吧,所以,不要放弃,要相信,熟能生巧,总有一天,这区区数据结构,你一定是能将他拿下的!
什么是链表?
对于链表这个数据结构,相信很多小伙伴还是比较头疼的,放心,等会,就让你完全弄懂它!
首先,链表的关键,在窝瓜看来,就是这个“链”字!
我想佛珠大家一定都见过,窝瓜觉得,佛珠的整体形态就和我们的链表有着异曲同工之妙!
首先,我们可以将每一颗珠子看成是我们的一个节点,而这珠子与珠子之间的线,我们就将其想象为一个看的见、摸得到的指针,这样,每一颗佛珠,就可以通过这条线,找到下一个佛珠,这不正是我们的链表吗!
现在我们来模拟一下链表的实现!
首先,桌面上什么都没有,当我们插入第一个元素的时候,桌面上出现了一个佛珠(也就是一个节点),这时,我们又需要第二颗佛珠,首先,将线从第一颗佛珠上穿过,然后拿出第二颗佛珠,用线将其串在第一颗佛珠的后面,以此类推,这种用线将佛珠一颗颗串在一起的过程,在链表中,就是我们的插入操作!
此时我们发现其中一颗佛珠的品相不好,不想要它了,我们要怎么将它去掉呢?
很多小伙伴一定会说,这也太简单了,直接把线间断,然后把珠子取出来,然后将断了的线连接上,不就好了!
没错,就是这么简单!其实在我们链表的世界中,也是这样的,上面的情况,就完美的模拟了我们删除节点的情况,在删除节点的时候,我们只需要将要删除节点的前一个节点找到,然后切断这个节点与要删除节点之间的线(指针),然后将这个节点直接指向要删除节点的后一个节点,这不就完成了所谓的删除操作。
所以说,链表其实也不难!
接下来,就让我们看看怎样用代码模拟上面的过程,从而实现我们的链表!
链表的实现
上次介绍了顺序表的特点,那么,链表有着什么特点呢?让我们一起来看看!
链表的特点:
- 空间不固定,随用随建,避免浪费
- 逻辑上每个节点相邻,物理上不一定
- 插入和删除比较简单,不用遍历整个结构
- 查询因为没有索引的缘故,性能不如顺序表,时间复杂度为O(n)
定义头节点和节点
typedef struct N{
int data;
struct N * next;
}Node;
//节点
typedef struct{
Node * head;
Node * tail;
int size;
}List;
//头节点
初始化链表
List * init_list(List * p){
p->head = (void *)0;
p->tail = (void *)0;
p->size = 0;
return p;
}//初始化链表
清空链表
List * clear_list(List * p){
while(p->head){
Node * del = p->head;
p->head = del->next;
free(del);
}
p->tail = (void *)0;
p->size = 0;
return p;
}//清空链表
销毁链表
void * destroy_list(List * p){
clear_list(p);
}//销毁链表
尾插法
List * push_back_list(List * p, int data){
Node * new_node = make_node(data);
if(!new_node){
return (void *)0;
}
p->tail->next = new_node;
p->tail = new_node;
p->size++;
return p;
}//尾部插入元素
头插法
List * push_front_list(List * p, int data){
Node * new_node = make_node(data);
if(!new_node){
return (void *)0;
}
new_node->next = p->head;
p->head = new_node;
p->size++;
if(p->size == 1){
p->tail = new_node;
}
return p;
}//头部插入元素
头删法
List * pop_front_list(List * p){
if(p->size == 0){
return (void *)0;
}
Node * del = p->head;
p->head = p->head->next;
free(del);
p->size--;
if(p->size == 0){
p->tail = (void *)0;
}
return p;
}//头部删除元素
尾删法
List * pop_back_list(List * p){
if(p->size == 0){
return (void *)0;
}
Node * del = p->tail;
Node * tem = p->head;
if(p->size == 1){
p->head = p->tail = (void *)0;
}else{
while(tem->next != p->tail){
tem = tem->next;
}
tem->next = (void *)0;
p->tail = tem;
}
free(del);
p->size--;
return p;
}//尾部删除元素
打印链表
void print(List * p){
Node * tem = p->head;
if(tem){
printf("现在有%d个元素,分别是:%d",p->size, tem->data);
tem = tem->next;
while(tem){
printf("、%d",tem->data);
tem = tem->next;
}
printf("\n");
}else{
printf("链表为空!\n");
}
}//打印链表
查找指定元素位置
Node * find_list(List * p, int data){
if(p->size == 0){
return (void *)0;
}
Node * tem = p->head;
while(tem && tem->data == data){
tem = tem->next;
}
return tem;
}//查找指定元素
删除指定位置元素
List * erase_list(List * p, Node * pos){
if(p->size == 0 || pos == p->tail){
return (void *)0;
}
if(pos == (void *)0){
return pop_back_list(p);
}
Node * del = pos->next;
pos->next = del->next;
p->size--;
if(del == p->tail){
p->tail = pos;
}
free(del);
return p;
}//删除指定
在指定位置插入元素
List * insert_list(List * p, Node * pos, int data){
if(p->size == 0 || pos == (void *)0){
return push_front_list(p , data);
}
if(pos == p->tail){
return push_back_list(p , data);
}
Node * new_node = make_node(data);
if(!new_node){
return (void *)0;
}
new_node->next = pos->next;
pos->next = new_node;
p->size++;
return p;
}//在指定位置插入元素
测试代码
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
int main()
{
int i;
List list;
init_list(&list);
push_front_list(&list, 1);
push_front_list(&list, 2);
print(&list);
push_back_list(&list, 3);
push_back_list(&list, 4);
print(&list);
pop_front_list(&list);
pop_back_list(&list);
print(&list);
insert_list(&list,find_list(&list, 3) , 5);
print(&list);
erase_list(&list, find_list(&list, 5));
print(&list);
clear_list(&list);
print(&list);
destroy_list(&list);
return 0;
}
结果展示
这就是这次给大家带来的关于链表的实现,下次,将会给大家带来顺序栈和链式栈的实现!谢谢大家~~~~~~~~~