图解双向链表(含完整C代码)+剖析STL顺序容器List(C++)
双向链表动图解析,对比双向链表在STL中的操作,内含完整代码
·
目录
🌟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头插法
在头结点后面插入一个新的结点,特别注意指针的移动。
- 先给插入结点的两指针找好位置
- 再改变后一结点的prior(通过head->next找到后一结点)
- 最后改变头结点的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尾插法
- 同头插法,先找到node的next与prior
- 然后再改变end前一个结点的next为node(通过end->prior找到)
- 最后改变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删除
- 用delnode保存需要删除的结点
- 将delnode的后继结点的prior指向temp
- 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;
}
}
更多推荐
已为社区贡献1条内容
所有评论(0)