目录

🌟1.什么是双向链表

💓2.双向链表的作用

🌵3.什么是双向循环链表

⚽4.双向链表的操作

4.1初始化

4.2头插法

 4.3尾插法

4.4删除

❄️5.STL-list

5.1 设计目的与代价

5.2头文件

5.3操作

📌6.完整代码(C语言)


🌟1.什么是双向链表

单链表的每个结点包含2个指针,分别指向结点的直接前驱直接后继

  • data:存放数据元素的值
  • next:存放直接后继的地址
  • prior:存放直接前驱的地址

💓2.双向链表的作用

相比于单链表,双向链表多了哪些东西呢?

咱们先上图:我们依然习惯采用有头结点的链表,并把链表中元素个数存放在头结点数据域中。

发现1:每个结点都多了一个prior指针

发现2:多了一个尾结点

 prior与尾结点有啥作用呢?

  • 不仅可以从头部进行操作,也可以从尾部进行操作
  • 在任何位置进行插入和删除的效率都很高
  • 尾结点与头结点的作用相同

🌵3.什么是双向循环链表

单链表有循环单链表,那双向链表也可以循环。

特点:

尾结点的next指向头结点,头结点的prior指向尾结点

⚽4.双向链表的操作

4.1初始化

相比单链表,多了两个操作

end->prior=head;

head->prior=NULL;

若是双向循环链表呢

head->prior =NULL;改为head->prior =end;

end->next = NULL;改为end->next = prior;

Linklist* InitList()
{
	//给head和end开辟一个空间
	head = (Linklist*)malloc(sizeof(Linklist));
	end = (Linklist*)malloc(sizeof(Linklist));
	head->data = 0;

	head->next = end;
	end->prior = head;

	head->prior = NULL;
	end->next = NULL;

	//若是双向循环,则head->prior =end,end->next = prior即可
}

4.2头插法

在头结点后面插入一个新的结点,特别注意指针的移动

  1. 先给插入结点的两指针找好位置
  2. 再改变后一结点的prior(通过head->next找到后一结点)
  3. 最后改变头结点的next
void HeadInsert(int n)
{
	Linklist* node = (Linklist*)malloc(sizeof(Linklist));
	node->data = n;
	head->data++;
	
	node->next = head->next;//1
	node->prior = head;//2
	head->next->prior = node;//3
	head->next = node;//4
	//1、2与3、4顺序不可颠倒
	//3、4顺序不可颠倒
	//即只有1、2顺序可以颠倒
}

4.3尾插法

  1.  同头插法,先找到node的next与prior
  2.  然后再改变end前一个结点的next为node(通过end->prior找到)
  3.  最后改变end->prior
void EndInsert(int n)
{
	Linklist* node = (Linklist*)malloc(sizeof(Linklist));
	node->data = n;
	head->data++;
	
	node->prior = end->prior;//1
	node->next = end;//2
	end->prior->next = node;//3
	end->prior = node;//4
	//1、2与3、4顺序不可颠倒
	//3、4顺序不可颠倒
	//即只有1、2顺序可以颠倒
}

4.4删除

  1. 用delnode保存需要删除的结点
  2. 将delnode的后继结点的prior指向temp
  3. temp的next指向delnode的后继结点 
void del(int n)
{
	int i = find(n);
	if (i == -1)
	{
		printf("没有此元素,删除失败\n");
	}
	else
	{
		Linklist* temp = head->next;
		int j = 1;
		while (j < i - 1)
		{
			j++;
			temp = temp->next;
		}
		
		Linklist* delnode = temp->next;
		delnode->next->prior = temp;
		temp->next = delnode->next;
		//也可以temp->next-next-prior=temp; temp->next=temp->next->next;
		//还可以temp->next=temp->next->next; temp->next->prior=temp
		free(delnode);
		head->data--;
	}
}

❄️5.STL-list

5.1 设计目的与代价

目的:

容器任何位置的删除插入操作都很快

代价:

不支持随机访问,为了访问一个元素,我们需要遍历容器

5.2头文件

#include<list>

5.3操作

访问

c.back()返回c中尾元素的引用
c.front()返回c中首元素的引用

不支持c[n]、c.at(n)操作

删除

c.pop_back()删除c中尾元素

c.pop_front()

删除c中首元素
c.erase(p)删除迭代器p指向的元素,返回一个被删除元素后一个元素的迭代器
c.erase(b,e)删除b、e范围内的元素,返回一个最后一个被删除元素后一个元素的迭代器
c.clear()删除c中所有元素,返回void

插入

c.push_back(t)在c的尾部创建一个值为t的元素,返回void
c.push_front(t)在c的头部创建一个值为t的元素,返回void
c.insert(p,t)在迭代器p指向的元素之前插入t,返回新添加元素的迭代器
c.insert(p,n,t)在迭代器p指向的元素之前插入n个t,返回新添加的第一个元素的迭代器
c.insert(p,b,e)将迭代器b和e指定范围内的元素插入到迭代器p指向元素之前,返回新添加的第一个元素的迭代器

📌6.完整代码(C语言)

复制粘贴后可直接运行嗷~

#include<stdio.h>
#include<stdlib.h>

typedef struct DoubleCircularLinklist
{
	int data;
	struct DoubleCircularLinklist* next;//指向下一个指针
	struct DoubleCircularLinklist* prior;//指向前一个指针
}Linklist;

//全局变量定义一个头结点和尾结点
Linklist* head;
Linklist* end;
Linklist* InitList();
void HeadInsert(int n);
void i_insert(int i, int n);
void EndInsert(int n);
int find(int n);
void del(int n);
void show();

int main()
{
	Linklist* node = InitList();
	HeadInsert(1);
	HeadInsert(2);
	HeadInsert(3);
	HeadInsert(4);
	HeadInsert(5);
	show();//54321
	printf("\n");

	EndInsert(6);
	EndInsert(7);
	EndInsert(8);
	show();//54321678
	printf("\n");

	i_insert(4, 9);
	show();//543291678
	printf("\n");

	del(7);
	del(3);
	show();//5429168
	return 0;
}

Linklist* InitList()
{
	//给head和end开辟一个空间
	head = (Linklist*)malloc(sizeof(Linklist));
	end = (Linklist*)malloc(sizeof(Linklist));
	head->data = 0;

	head->next = end;
	end->prior = head;

	head->prior = NULL;
	end->next = NULL;

	//若是双向循环,则head->prior =end,end->next = prior即可
}

/*头插法*/
//把head,end定义为全局变量,就不需要传入参数了
void HeadInsert(int n)
{
	Linklist* node = (Linklist*)malloc(sizeof(Linklist));
	node->data = n;
	head->data++;
	//先给插入结点的两指针找好位置
	//再改变后一结点的prior(通过head->next找到后一结点)
	//最后改变头结点的next
	node->next = head->next;//1
	node->prior = head;//2
	head->next->prior = node;//3
	head->next = node;//4
	//1、2与3、4顺序不可颠倒
	//3、4顺序不可颠倒
	//即只有1、2顺序可以颠倒
}

/*按下标插入*/
//在第i个元素处插入n
void i_insert(int i, int n)
{
	Linklist* node = (Linklist*)malloc(sizeof(Linklist));
	Linklist* temp = head->next;
	int j = 1;//首元结点下标
	if (i > head->data)
	{
		printf("插入位置超出链表,插入失败\n");
	}
	else
	{
		while (j < i)
		{
			temp = temp->next;
			j++;
		}
		node->data = n;
		head->data++;
		//对比头插法,不同在于将head改为temp
		node->next = temp->next;
		node->prior = temp;
		temp->next->prior = node;
		temp->next = node;
	}
}

/*尾插法*/
void EndInsert(int n)
{
	Linklist* node = (Linklist*)malloc(sizeof(Linklist));
	node->data = n;
	head->data++;
	//同头插法,先找到node的next与prior
	//然后再改变end前一个结点的next为node(通过end->prior找到)
	//最后改变end->prior
	node->prior = end->prior;//1
	node->next = end;//2
	end->prior->next = node;//3
	end->prior = node;//4
	//1、2与3、4顺序不可颠倒
	//3、4顺序不可颠倒
	//即只有1、2顺序可以颠倒
}

/*查找元素*/
int find(int n)
{
	Linklist* temp = head->next;
	int i = 1;
	//也可以是temp!=end
	while (temp->next != NULL)
	{
		if (temp->data == n)
			return i;
		else
		{
			i++;
			temp = temp->next;
		}
	}
	return -1;
}

/*删除元素*/
void del(int n)
{
	int i = find(n);
	if (i == -1)
	{
		printf("没有此元素,删除失败\n");
	}
	else
	{
		Linklist* temp = head->next;
		int j = 1;
		while (j < i - 1)
		{
			j++;
			temp = temp->next;
		}
		//用delnode保存需要删除的结点
		//将delnode的后继结点的prior指向temp
		//temp的next指向delnode的后继结点
		Linklist* delnode = temp->next;
		delnode->next->prior = temp;
		temp->next = delnode->next;
		//也可以temp->next-next-prior=temp; temp->next=temp->next->next;
		//还可以temp->next=temp->next->next; temp->next->prior=temp
		free(delnode);
		head->data--;
	}
}

/*打印元素*/
//循环结束条件为temp指向尾结点
void show()
{
	Linklist* temp = head->next;
	while (temp != end)
	{
		printf("%d", temp->data);
		temp = temp->next;
	}
}
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐