1.双链表和单链表的区别就是在于,双向链表多了一个头结点,并且有一个哨兵位,带头链表⾥的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这⾥“放哨 ”,有了头结点就可以双向循环了。头结点存的上一个的地址,头的前面是谁呢,循环一圈不就是最后的尾结点嘛,尾结点存的下一个的地址,是不是正向反向都可以进行遍历了,那么我们使用它进行顺序表的功能是不是更方便呢
2 先来初始化一下我们的哨兵位
void init()
{
list* newnode = (list*)malloc(sizeof(list));
if (newnode == NULL)
{
perror("NULL");
exit(1);
}
newnode->data = -1;
newnode->head = newnode->next = newnode;
return newnode;
}
其实哨兵位和我们其他的结点一样,理论上它不存储任何数据,但是我们初始化时候,还是要给他初始化一个-1,具有辨别性,我们一开始只有一个哨兵位,我们的头结点存储尾吧,但是我们就这一个,我们自己存储自己就行了
3.尾插,尾插的话,我们是不是需要为插入的数据开辟一个空间呢,我们看到了初始化哨兵位,是不是可以也用来给插入的数据开辟新的空间呢,我们写一个给数据开辟的函数,然后复制粘贴就好了,因为哨兵位其实和我们正常的结点都一样。
list* chuang(innt x)
{
list* newnode = (list*)malloc(sizeof(list));
if (newnode == NULL)
{
perror("NULL");
exit(1);
}
newnode->data = x;
newnode->head = newnode->next = newnode;
return newnode;
}
尾插的话,我们如何找到末尾的那个结点呢,头结点的prev是不是就存储的末尾结点,并且,我们还要让新插的数据的prev链接上末尾结点,那我们应该先让新插的数据的prev和next指针先存刚才的链表的尾结点,存储完了,在改头哨兵位的prev的结点,让新插的数据成为新的尾结点。
void pushback(list* head, innt x)
{
assert(head);
list* ps = chuang(x);
ps->prev = head->prev;
ps->next = head;
head->prev->next = ps;//在图中我们就可以看到哨兵位头结点连着尾巴,
//所以我们通过这个找尾结点,再吧新的数据变成新的尾结点
head->prev = ps;
}
4.打印,我们既然初始化了,也进行了尾插,我们可以通过调试,也可以通过打印的方式,看一下我们的程序有没有问腿,打印就是遍历。值得提醒的就是我们不要打印哨兵位
void print(list* head)
{
assert(head);
list* ps = head->next;
while (ps != head)
{
printf("%d->", ps->data);
ps = ps->next;
}
}
5.头插,我们有了哨兵位,哨兵位不动,哨兵位的next结点,就是我们要头插的位置,这么来看,我们可以受尾插的启蒙,先让新插的数据存储原来链表的各个地址,再让链表存储它的地址
void pushfront(list* head, innt x)
{
list* ret = chuang(x);
ret->next = head->next;
ret->prev = head;
head->next = ret;
}
让我们用打印看一下程序是否正常运行了
6 尾删,尾删我们就要把头结点的prev指针指向尾结点的上一个结点,而新的尾结点next指针也得指向头结点
void popback(list* head)
{
list* ps = head;
list* ret = head->prev;//方便接下来释放空间
ps->prev->prev->next = ps;
ps->prev = ps->prev->prev;
free(ret);
ret = NULL;
}
7头删
头删就是把哨兵位的next指针改成第一个结点的下一个结点,然后把这个结点的prev存储哨兵位。
void popfront(list* head)
{
assert(head);
list* ps = head;
list* ret = head->next;
ret->next->prev = ps;
ps->next = ret->next;
free(ret);
ret = NULL;
}
8指定位置后插入 指定一个位置,我们只能通过 数据来判断一个位置,所以我们应该先写一个寻找函数,找一下我们所需要的位置
void insert(list* head, innt x,int a)
{
list* newnode = chuang(x);
list* ret = find(head,a);
newnode->next = ret->next;
ret->next->prev = newnode;
newnode->prev = ret;
ret->next = newnode;
}
9销毁链表
void destory(list* head)
{
list* ps = head->next;
list* ret = NULL;
while (ps != head)
{
ret = ps->next;
free(ps);
ps = ret;
}
free(head);
//head=NULL;没有用
}
销毁链表就和我们遍历存储一样,两个指针,一个负责往前并且被销毁,另一个负责记录位置
为什么head=NULL 没有用,我们这是一级指针,传的是一个指针,但是要想对指针改变,我们要传指针的地址,但是我们为了保持接口一致性,所以传了一级指针,head=NULL需要再主函数手动置空。
10 分享整个代码
#pragma once
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<assert.h>
typedef int innt;
typedef struct list
{
struct list* prev;
innt data;
struct list* next;
}list;
list* init();
void pushback(list* head,innt x);
void print(list* head);
void pushfront(list* head, innt x);
void popfront(list* head);
void popback(list* head);
void insert(list* head, innt x ,int a);//x要插入的数据 a是要寻找的数据
void destory(list* head);
#include"list.h"
list* init()
{
list* newnode = (list*)malloc(sizeof(list));
if (newnode == NULL)
{
perror("NULL");
exit(1);
}
newnode->data = -1;
newnode->prev = newnode->next = newnode;
return newnode;
}
list* chuang(innt x)
{
list* newnode = (list*)malloc(sizeof(list));
if (newnode == NULL)
{
perror("NULL");
exit(1);
}
newnode->data = x;
newnode->prev = newnode->next = newnode;
return newnode;
}
void pushback(list* head, innt x)
{
assert(head);
list* ps = chuang(x);
ps->prev = head->prev;
ps->next = head;
head->prev->next = ps;//在图中我们就可以看到哨兵位头结点连着尾巴,
//所以我们通过这个找尾结点,再吧新的数据变成新的尾结点
head->prev = ps;
}
void print(list* head)
{
assert(head);
list* ps = head->next;
while (ps != head)
{
printf("%d->", ps->data);
ps = ps->next;
}
}
void pushfront(list* head, innt x)
{
list* ret = chuang(x);
ret->next = head->next;
ret->prev = head;
head->next = ret;
}
void popback(list* head)
{
list* ps = head;
list* ret = head->prev;//方便接下来释放空间
ps->prev->prev->next = ps;
ps->prev = ps->prev->prev;
free(ret);
ret = NULL;
}
void popfront(list* head)
{
assert(head);
list* ps = head;
list* ret = head->next;
ret->next->prev = ps;
ps->next = ret->next;
free(ret);
ret = NULL;
}
list* find(list* head, innt x)
{
list* ps = head->next;//因为我们遍历链表,所以哨兵位直接忽略掉
while (ps != head)
{
if (ps->data == x)
{
return ps;
}
ps = ps->next;
}
return NULL;
}
void insert(list* head, innt x,int a)
{
list* newnode = chuang(x);
list* ret = find(head,a);
newnode->next = ret->next;
ret->next->prev = newnode;
newnode->prev = ret;
ret->next = newnode;
}
void destory(list* head)
{
list* ps = head->next;
list* ret = NULL;
while (ps != head)
{
ret = ps->next;
free(ps);
ps = ret;
}
free(head);
//head=NULL;没有用
}