学习双链表

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;没有用
}

  • 11
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值