其实链表很简单,跟着我的脚步走,基本是这篇博客看完,你也就能实现简单的链表操作了
数组、链表是最常见的重要的数据结构,所以掌握链表也是很重要的咯。
一般链表的操作无外乎增删改查。 今天就简单的实现一下双向循环链表的增删
1. 链表的数据结构,主要包含指针域和数据域
双向循环链表的数据结构包含至少两个指针和至少一个数据
//用结构体封装一下链表的数据结构
typedef struct _node_t{
struct _node_t *prev; //上一个节点
struct _node_t *next; //下一个节点
uint8_t data; //数据域
}Node,*pNode;
2.链表的初始化,即创建一个头结点
头结点的prev或next都是其本身,一般建议头节点只存指针。
//链表初始化,即创建一个头结点
pNode linklist_init(void){
pNode head = (pNode)malloc(sizeof(Node)); //申请内存
if(NULL == head){
perror("malloc Node failed!");
}
head->prev = head; //头节点的prev指向本身
head->next = head; //头节点的next指向本身
return head;
}
3.链表的插入
在前面插入,即在头结点之后插入:
来来来,排队了,,新来了一个童鞋tmp,个子比较矮的,老师让他站在排头的后面;
新来的tmp看了一下前面是排头(head),后面是某某(head->next),然后他记住了自己的位置,通过自己前面和后面的童鞋定位了自己的位置;
可是还不够啊,排头和之前在排头后面的童鞋不知道啊,所以之前排头后面的童鞋,重新定位了一下自己的位置(head->next->prev)我原来在排头后面,但是现在我前面是谁?排头后面的换了,排头也定位了一下自己的位置(head->next) 我后面新来的是谁呀。
pNode linklist_addhead(pNode head, uint8_t dat){
pNode tmp = (pNode)malloc(sizeof(Node)); //申请内存
if(tmp == head){
perror("malloc Node failed!");
}
tmp->prev = head; //tmp定位自己,看下前面是谁
tmp->next = head->next; //再看看后面是谁
head->next->prev = tmp; //原来在排头后面的童鞋,看看现在自己前面的是谁
head->next = tmp; //排头也看了下,现在自己后面的是谁
tmp->data = dat; //新来的童鞋的名字
return head; //排头的位置不能丢,不然找不到这一队人马了
}
在末尾插入
这里就不再赘述了,直接上图,看不懂的,看上图及描述
pNode linklist_addtail(pNode head, uint8_t dat){
pNode tmp = (pNode)malloc(sizeof(Node));
if(tmp == head){
perror("malloc Node failed!");
}
tmp->prev = head->prev; //tmp看下自己前面是谁,主要前面是排头ing,因为是环形的链表
tmp->next = head; //tmp看下自己后面的是谁
head->prev->next = tmp; //原来排头前面的童鞋的后面是谁,来新人tmp了
head->prev = tmp; //现在排头前面的换成了tmp了
tmp->data = dat;
return head;
}
还有其他插入方法,但是只要掌握了头插尾插,其他的都是浮云
3.链表的删除
从头删除,即将排头后面的tmp童鞋删除
先告诉tmp童鞋后面的童鞋(tmp->next),我走了之后你前面的是谁;然后告诉tmp童鞋前面的童鞋(这里head),我走了之后你后面的是谁;把前后两个童鞋定位告诉他们后就可以离开了(free);
pNode Linklist_delhead(pNode head){
pNode tmp = head->next;
if(tmp == head){
perror("linklist empty!");
return head;
}
tmp->next->prev = head; //tmp走了,tmp童鞋后面的童鞋的前面变成了排头
head->next = tmp->next; //tmp走了,排头后面的童鞋变成了原来排在tmp童鞋后面的童鞋
tmp->next = NULL;
tmp->prev = NULL;
free(tmp); //释放内存
}
从尾部删除
这里就不再赘述了,直接上图,看不懂的,看上图及描述
pNode Linklist_deltail(pNode head){
pNode tmp = head->prev;
if(tmp == head){
perror("linklist empty!");
return head;
}
tmp->prev->next = head;
head->prev = tmp->prev;
tmp->next = NULL;
tmp->prev = NULL;
free(tmp);
}
4. 遍历显示链表
从排头依次报数,轮流一圈,回到排头就完成了
void linklist_show(pNode head){
tmp = head->next; //从排头后面的童鞋开始遍历
while(tmp != head){ //没有遍历到排头
printf("%p:%d\n",tmp, tmp->data);
tmp = tmp->next; //指向下一个童鞋
}
}
5. 测试
int main(void){
pNode head = linklist_init();
printf("\nhead add Node!\n");
for(uint8_t i=0;i<5;i++){
linklist_addhead(head, i);
}
linklist_show(head);
printf("\nhead del!\n");
for(uint8_t i=0;i<2;i++){
Linklist_delhead(head);
}
linklist_show(head);
printf("\ntail_del!\n");
for(uint8_t i=0;i<2;i++){
Linklist_deltail(head);
}
linklist_show(head);
printf("\ntail add Node!\n");
for(uint8_t i=0;i<5;i++){
linklist_addtail(head, i);
}
linklist_show(head);
printf("\ntail del Node!\n");
Linklist_deltail(head);
linklist_show(head);
printf("\nhead del Node!\n");
Linklist_delhead(head);
linklist_show(head);
return 0;
}
运行结果