数据结构(完成)

This commit is contained in:
yinkanglong_lab
2021-03-13 17:12:31 +08:00
parent ef21032620
commit c2ecd338fd
59 changed files with 3968 additions and 525 deletions

View File

@@ -48,6 +48,12 @@
> 与数组完全一致,只是定义方式不同。
> 数组不能copy赋值但是array可以copy赋值。
### 定义
```
array<int, 5> arr = {1, 2, 3, 4, 5};
```
## 2 vector
## 3 deque

View File

@@ -0,0 +1,20 @@
# 临时对象
### 定义
* C++直接调用构造函数。会创建临时对象。
* 临时对象没有对象名,它的生命周期只有一条语句。
* 如果直接调用构造函数,赋值给一个具体的名字,则会使用复制初始化。则不是临时对象。
### 使用
* 如果做为函数参数使用。调用的是复制噶偶早函数。
```
vector<vector<int> > vec;
vec.push_back(vector<int>(5));//直接调用构造函数,会创建临时对象。
```
* 如果作为返回值使用。调用的是复制构造函数。
```
vector<int> hello(){
return vector<int>();
}
```

View File

@@ -3,7 +3,7 @@
### 基础
可以把主存看成一本空白bai的作业本,你现在要在笔记本上记录一些内容,他的页码排序是
可以把主存看成一本空白的作业本,你现在要在笔记本上记录一些内容,他的页码排序是
```
第一页 : 0x0000001
@@ -13,12 +13,12 @@
```
1. 如果你选择`从前向后记录`(用完第一页,用第二页,类推)这就是先使用低地址,后使用高地址.业内表述:动态分配内存时堆空间向高地址增长,说的就是这种情况.这个向高地址增长就是先使用低地址,后使用高地址的意思.
1. 如果你选择`从前向后记录`(用完第一页,用第二页,类推)这就是先使用低地址,后使用高地址.业内表述:动态分配内存时**堆空间向高地址增长**,说的就是这种情况.这个向高地址增长就是先使用低地址,后使用高地址的意思.
```
0x0000001 -> 0x0000002-> ... -> 0x0000092
```
1. 如果你选择`从后往前记录`(先用笔记本的最后一页,用完后使用倒数第二页,类推) 这就是先使用高地址,后使用低地址.业内表述:`0xbfac 5000-0xbfad a000`是栈空间,其中高地址的部分保存着进程的环境变量和命令行参数,低地址的部分保存函数栈帧,**栈空间是向低地址增长的**.这个向低地址增长就是先使用高地址,后使用低地址的意思.
2. 如果你选择`从后往前记录`(先用笔记本的最后一页,用完后使用倒数第二页,类推) 这就是先使用高地址,后使用低地址.业内表述:`0xbfac 5000-0xbfad a000`是栈空间,其中高地址的部分保存着进程的环境变量和命令行参数,低地址的部分保存函数栈帧,**栈空间是向低地址增长的**.这个向低地址增长就是先使用高地址,后使用低地址的意思.
```
0x0000092 -> ... ->0x0000002 -> 0x0000001
```

View File

@@ -1,36 +1,27 @@
# 数组
## 简介
## 1 数组的简介
### 数组定义
* 数组是存储在连续内存位置的相似类型数据项的集合。
* 数组是最简单的线性数据结构,它的每个数据元素都可以使用索引号随机访问。
* 数组可以有一个或多个维度。
* 每个元素具有相同的数据类型并且具有相同的大小即int = 4个字节。
### 定义
* 数组是存储在**连续内存位置**的**相同类型数据项**的集合,是最简单的**线性数据结构**
* 数组的每个数据元素都可以使用下表索引运算符,进行**随机访问**
* 数组可以有一个或**多个维度**
* 每个元素具有**相同的数据类型**并且具有相同的大小即int = 4个字节。
```
int arr[10]; char arr[10];
int arr[10];
char arr[10];
float arr[5];
```
### 数组的优点
### 优点
* 数组为同一类型的变量组提供单一名称,因此很容易记住数组中所有元素的名称。
* 遍历数组是一个非常简单的过程,只需要递增数组的基址,就可以逐个访问每个元素。
* 可以使用索引直接访问数组中的任何元素。
### 一维数组
* 一维(或单维)数组是一种线性数组,其中元素的访问是以行或列索引的单一下标表示。
* C++ 将高维维数组存储为一维数组。因此,如果我们将 A 定义为也包含 M * N 个元素的二维数组,那么实际上 A[i][j] 就等于 A[i * N + j]。
### 多维数组
* 普通数组采用一个整数来作下标。多维数组(高维数组)的概念特别是在数值计算和图形应用方面非常有用。我们在多维数组之中采用一系列有序的整数来标注,如在[ 3,1,5 ] 。这种整数列表之中整数的个数始终相同,且被称为数组的“维度”。关于每个数组维度的边界称为“维”。维度为 k 的数组通常被称为 k 维。
* 多维数组的数组名字,在表达式中自动转换为数组首元素地址值,但这个首元素实际上是去除数组下标第一维之后的数组剩余部分。
### 时间复杂性
@@ -45,30 +36,115 @@ float arr[5];
* 在数组中最坏情况下的空间复杂度是O(n)。
## 2 数组的类型
### 一维数组
### 数组的存储
* 一维(或单维)数组是一种线性数组,其中元素的访问是以行或列索引的单一下标表示。
* C++ 将高维维数组存储为一维数组。因此,如果我们将 A 定义为也包含 M * N 个元素的二维数组,那么实际上 A[i][j] 就等于 A[i * N + j]。
数组的所有数据元素都存储在主存储器中的连续位置。 数组名称表示主存储器中的基地址或第一个元素的地址。 数组的每个元素都由适当的索引表示。可以用三种方式定义数组的索引。
* 0(从零开始索引)数组的第一个元素是arr[0]。
* 1(基于一个索引)数组的第一个元素是arr [1]。
* n(基于n的索引):基于数组的第一个元素,可以定位任何随机索引值。
### 多维数组
* 普通数组采用一个整数来作下标。多维数组(高维数组)的概念特别是在数值计算和图形应用方面非常有用。我们在多维数组之中采用一系列有序的整数来标注,如在[ 3,1,5 ] 。这种整数列表之中整数的个数始终相同,且被称为数组的“维度”。关于每个数组维度的边界称为“维”。维度为 k 的数组通常被称为 k 维。
* 多维数组的数组名字,在表达式中自动转换为数组首元素地址值,但这个首元素实际上是去除数组下标第一维之后的数组剩余部分。
## 3 数组的存储和实现
### 数组存储
* 数组的所有数据元素都存储在主存储器中的连续位置。
* 数组名称表示主存储器中的基地址或第一个元素的地址。
* 数组的每个元素都由适当的索引表示。可以用三种方式定义数组的索引。
* 0(从零开始索引)数组的第一个元素是arr[0]。
* 1(基于一个索引)数组的第一个元素是arr [1]。
* n(基于n的索引):基于数组的第一个元素,可以定位任何随机索引值。
![](2021-03-12-20-47-17.png)
### 数组实现
* C++内置数组数据类型。
```
int a[4];
double b[3][9];//内置数组
```
* STL提供两种灵活的数组结构。
## 2 二维数组(矩阵)
```
array<int, 5> arr = {1, 2, 3, 4, 5};//STL模板数组
vector<int> vec ={3,4,3};//STL模板向量可变长度的数组
```
## 4 数组的操作
### 基础操作
* 创建
* 遍历
* 插入
* 删除
### 创建
```
// 定义数组
int size =20;//容量
int numbers =10;//数据量
int a[20]={1,3,4,5,6,7,4,2,4,5};
```
### 遍历
```C++
// 数组遍历
for(int i=0;i<10;i++){
cout<<a[i];
}
```
### 插入
```C++
//数组插入,尾插入
int value=9;
if(numbers<size){
a[numbers]=value;
numbers++;
}
else{
cout<<"overflow"<<endl;
}
// 数组插入,头插入
int value=10;
if(numbers<size){
for(int i=1;i<=numbers,i++){
a[numbers]=a[numbers-i];
}
a[0]=value;
}
else{
cout<<"overflow"<<endl;
}
```
### 删除
```C++
// 删除
int aim=8;
if(aim>=numbers){
cout<<"overbound"<<endl;
}
else{
for(int i=aim;i<numbers-1;i++){
a[i]=a[i+1];
}
}
```
## 5 二维数组(矩阵)
### 二维数组的表示
* 创建二维数组和实现关系数据库表看起来有相似的数据结构,在计算机存储器中,二维数组存储技术类似于一维数组的存储技术
* 二维数组的大小等于行数和数组中存在的列数的乘积。 确实需要将二维数组映射到一维数组,以便将它们存储在内存中。
* 一个3 X 3的二维数组如下图所示。 但是,需要将此数组映射到一维数组,以便将其存储到内存中。
* 二维数组的大小等于行数和数组中存在的列数的乘积
* 一般将二维数组映射到一维数组,存储在内存中。
* 一个3 X 3的二维数组如下图所示。需要将此数组映射到一维数组存储到内存中。
![](2021-03-12-20-55-32.png)
### 二维数组的存储——行主顺序
* 在行主排序中,二维数组的所有行连续地存储在存储器中。 考虑一下上图中所示数组,它按行主顺序的存储器分配如下所示
* 在行主排序中,二维数组的所有行连续地存储在存储器中。上图中所示数组,它按行主顺序的存储器分配如下所示
![](2021-03-12-20-57-19.png)
![](2021-03-12-20-58-14.png)
@@ -82,14 +158,12 @@ float arr[5];
### 计算二维数组随机元素的地址
由于存在两种不同的将二维数组存储到存储器中的技术,因此也有两种不同的公式来计算二维数组的随机元素的地址。
* 按行主顺序如果数组由a[m][n]声明其中m是行数而n是列数则以行主顺序存储的数组的元素a[i][j]的地址计算为
* 由于存在两种不同的将二维数组存储到存储器中的技术,因此也有两种不同的公式来计算二维数组的随机元素的地址。
* 按行主顺序如果数组由a[m][n]声明其中m是行数而n是列数则以行主顺序存储的数组的元素a[i][j]的地址计算为
```
Address(a[i][j]) = B. A. + (i * n + j) * size
```
* 按列主顺序如果数组由a[m][n]声明其中m是行数而n是列数则以列主顺序存储的数组的元素a[i][j]的地址计算为,
* 按列主顺序如果数组由a[m][n]声明其中m是行数而n是列数则以列主顺序存储的数组的元素a[i][j]的地址计算为,
```
Address(a[i][j]) = ((j*m)+i)*Size + BA
```

47
数据结构/1.cpp Normal file
View File

@@ -0,0 +1,47 @@
#include<iostream>
using namespace std;
int main(){
// 定义数组
int size =20;//容量
int numbers =10;//数据量
int a[20]={1,3,4,5,6,7,4,2,4,5};
// 数组遍历
for(int i=0;i<10;i++){
cout<<a[i];
}
//数组插入,尾插入
int value=9;
if(numbers<size){
a[numbers]=value;
numbers++;
}
else{
cout<<"overflow"<<endl;
}
// 数组插入,头插入
int value2=10;
if(numbers<size){
for(int i=1;i<=numbers;i++){
a[numbers]=a[numbers-i];
}
a[0]=value2;
}
else{
cout<<"overflow"<<endl;
}
// 删除
int aim=8;
if(aim>=numbers){
cout<<"overbound"<<endl;
}
else{
for(int i=aim;i<numbers-1;i++){
a[i]=a[i+1];
}
}
}

View File

@@ -1,17 +1,19 @@
# 链表
## 0 简介
## 1 简介
### 链表概念
* 链表是一种随机存储在内存中的叫做节点对象集合。节点包含两个字段即存储在该地址的数据和包含下一个节点地址的指针。链表的最后一个节点包含指向null的指针。
* 链表是一种随机存储在内存中的节点对象集合。
* 节点包含两个字段,即存储在该地址的数据和包含下一个节点地址的指针。
* 链表的最后一个节点包含指向null的指针。
![](2021-03-12-21-00-33.png)
![](2021-03-12-21-01-53.png)
### 链表用途
### 链表特点
* 链表不需要连续存在于存储器中。节点可以是存储器中的任何位置并链接在一起以形成链表。这实现了空间的优化利用。
* 链表大小仅限于内存大小,不需要提前声明。
* 空节点不能出现在链表中。
@@ -29,139 +31,11 @@
* 它动态分配内存。链表的所有节点都是非连续存储在存储器中,并使用指针链接在一起。
* 大小调整不再是问题,因为不需要在声明时定义大小。链表根据程序的需求增长,并且仅限于可用的内存空间。
## 2 链表的类型
## 1 单链表
### 分类
### 概念
* 链表是有序元素集的集合。元素的数量可以根据程序的需要而变化。 单链表中的节点由两部分组成:数据部分和链接部分。
* 节点的数据部分存储将由节点表示的实际信息,而节点的链接部分存储其直接后继的地址。
* 单向链或单链表可以仅在一个方向上遍历。也就是说每个节点只包含下一个节点的指针,因此不能反向遍历链表。
![](2021-03-12-21-00-33.png)
![](2021-03-12-21-08-07.png)
### 链表复杂度
| 操作 | 平均复杂度 | 最坏复杂度 |
|----|-------|-------|
| 访问 | θ(n) | θ(n) |
| 搜索 | θ(n) | θ(n) |
| 插入 | θ(1) | θ(1) |
| 删除 | θ(1) | θ(1) |
### 数据结构
```C
struct node
{
int data;
struct node *next;
};
struct node *head, *ptr;
ptr = (struct node *)malloc(sizeof(struct node *));
```
### 基本操作
* 创建
* 遍历、搜索、查找(同一类操作)
* 插入(尾插入、头插入、中间插入)
* 删除
### 实现
```
```
## 2 双链表
### 概念
双向链表是一种复杂类型的链表,它的节点包含指向序列中前一个节点和下一个节点的指针。 因此,在双向链表中,节点由三部分组成:节点数据,指向下一个节点的指针(next指针),指向前一个节点的指针(prev指针)。
![](2021-03-12-21-14-57.png)
![](2021-03-12-21-14-50.png)
### 双链表的数据存储
* 双向链表的内存表示如下图所示。 通常,双向链表为每个节点消耗更多空间,因此它有更广泛的基本操作,例如:插入和删除。 但是,可以轻松地操作链表中的元素,因为链表在两个方向(向前和向后)都保存指针。
![](双链表的数据存储.png)
### 数据结构
```C
struct node
{
struct node *prev;
int data;
struct node *next;
}
```
### 基本操作
* 创建
* 遍历、搜索
* 插入
* 删除
### 实现
```
```
## 3 循环单向链表
### 概念
* 在循环单链表中,链表的最后一个节点包含指向链表的第一个节点的指针。可以有循环单向链表以及循环双链表。
* 遍历一个循环单链表直到到达开始的同一个节点。循环单链表类似于链表但它没有开始也没有结束。任何节点的下一部分都不存在NULL值。
![](循环单向链表.png)
### 数据存储
链表的最后一个节点包含链表的第一个节点的地址。
![](2021-03-12-21-21-07.png)
### 基本操作
* 创建
* 遍历、搜索
* 插入
* 删除
### 实现
```
```
## 4 循环双向链表
### 概念
* 循环双向链表是一种更复杂的数据结构类型,它的节点包含指向其前一节点以及下一节点的指针。
* 循环双向链表在任何节点中都不包含NULL。
* 链表的最后一个节点包含列表的第一个节点的地址。
* 链表的第一个节点还包含的前一个指针是指向最后一个节点的地址。
![](2021-03-12-21-22-43.png)
### 数据存储
* 起始节点包含最后一个(也是前一个)节点的地址即8和下一个节点即4。链表的最后一个节点存储在地址8并包含数据6包含链表的第一个节点的地址
![](循环双向链表内存.png)
### 基本操作
* 创建
* 遍历、搜索
* 插入
* 删除
### 实现
```
```
* 单链表
* 链表
* 循环单链表
* 循环双链表

View File

@@ -0,0 +1,341 @@
# 单链表
## 1 简介
### 概念
* 单链表是有序元素集的集合。元素的数量可以根据程序的需要而变化。 单链表中的节点由两部分组成:数据部分和链接部分。
* 节点的数据部分存储将由节点表示的实际信息,而节点的链接部分存储其直接后继的地址。
* 单向链或单链表可以仅在一个方向上遍历。也就是说每个节点只包含下一个节点的指针,因此不能反向遍历链表。
![](2021-03-12-21-00-33.png)
![](2021-03-12-21-08-07.png)
### 链表复杂度
| 操作 | 平均复杂度 | 最坏复杂度 |
|----|-------|-------|
| 访问 | θ(n) | θ(n) |
| 搜索 | θ(n) | θ(n) |
| 插入 | θ(1) | θ(1) |
| 删除 | θ(1) | θ(1) |
## 2 链表的存储和实现
### 链表的存储
* 链表不需要连续存在于存储器中。节点可以是存储器中的任何位置并链接在一起以形成链表。这实现了空间的优化利用。
### 链表的实现
* 链表通过结构体和指针实现
```C++
struct node
{
int data;
struct node *next;
};
struct node *head, *ptr;
ptr = (struct node *)malloc(sizeof(struct node *));
```
* C++ STL提供了链表的实现
```
#include<list>
list<int> li;
forward_list<int> li;
```
## 3 链表的操作
### 基本操作
* 创建
* 遍历、搜索、查找(同一类操作)
* 插入(尾插入、头插入、中间插入)
* 删除
### 实现
```C++
#include<stdio.h>
#include<stdlib.h>
struct node
{
int data;
struct node *next;
};
struct node *head;
void beginsert();
void lastinsert();
void randominsert();
void begin_delete();
void last_delete();
void random_delete();
void display();
void search();
int main()
{
int choice = 0;
while (choice != 9)
{
printf("\n\n********* 主菜单 *********\n");
printf("从以下菜单列表中选择一个选项操作 ...\n");
printf("===============================================\n");
printf("1.插入到开头\n");
printf("2.插入到结尾\n");
printf("3.插入任何随机位置\n");
printf("4.从头部删除\n");
printf("5.从尾部删除\n");
printf("6.删除指定位置后的节点\n");
printf("7.搜索元素\n");
printf("8.显示链表中的数据\n");
printf("9.退出\n\n");
printf("===============================================\n");
printf("请输入您的选择:");
scanf("%d", &choice);
switch (choice)
{
case 1:
beginsert();
break;
case 2:
lastinsert();
break;
case 3:
randominsert();
break;
case 4:
begin_delete();
break;
case 5:
last_delete();
break;
case 6:
random_delete();
break;
case 7:
search();
break;
case 8:
display();
break;
case 9:
exit(0);
break;
default:
printf("请输入有效的选项...");
}
}
return 0;
}
void beginsert()
{
struct node *ptr;
int item;
ptr = (struct node *) malloc(sizeof(struct node *));
if (ptr == NULL)
{
printf("内存不够!\n");
}
else
{
printf("请输入一个整数值:");
scanf("%d", &item);
ptr->data = item;
ptr->next = head;
head = ptr;
printf("节点已经成功插入\n");
}
}
void lastinsert()
{
struct node *ptr, *temp;
int item;
ptr = (struct node*)malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("内存不够!\n");
}
else
{
printf("请输入一个整数值:");
scanf("%d", &item);
ptr->data = item;
if (head == NULL)
{
ptr->next = NULL;
head = ptr;
printf("节点已经成功插入\n");
}
else
{
temp = head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = ptr;
ptr->next = NULL;
printf("节点已经成功插入\n");
}
}
}
void randominsert()
{
int i, loc, item;
struct node *ptr, *temp;
ptr = (struct node *) malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("内存不够!\n");
}
else
{
printf("请输入一个整数值:");
scanf("%d", &item);
ptr->data = item;
printf("输入要插入的位置:");
scanf("%d", &loc);
temp = head;
for (i = 0;i < loc;i++)
{
temp = temp->next;
if (temp == NULL)
{
printf("此处不能插入节点\n");
return;
}
}
ptr->next = temp->next;
temp->next = ptr;
printf("节点已经成功插入\n");
}
}
void begin_delete()
{
struct node *ptr;
if (head == NULL)
{
printf("链表为空,没有什么可以删除!\n");
}
else
{
ptr = head;
head = ptr->next;
free(ptr);
printf("已经删除头部节点 ...\n");
}
}
void last_delete()
{
struct node *ptr, *ptr1;
if (head == NULL)
{
printf("链表为空,没有什么可以删除!\n");
}
else if (head->next == NULL)
{
head = NULL;
free(head);
printf("唯一的节点已经被删除了...\n");
}
else
{
ptr = head;
while (ptr->next != NULL)
{
ptr1 = ptr;
ptr = ptr->next;
}
ptr1->next = NULL;
free(ptr);
printf("已删除最后一个节点...\n");
}
}
void random_delete()
{
struct node *ptr, *ptr1;
int loc, i;
printf("输入要在此节点之后执行删除的节点的位置:");
scanf("%d", &loc);
ptr = head;
for (i = 0;i < loc;i++)
{
ptr1 = ptr;
ptr = ptr->next;
if (ptr == NULL)
{
printf("不能删除\n");
return;
}
}
ptr1->next = ptr->next;
free(ptr);
printf("\n第 %d 个节点已经被删除了", loc + 1);
}
void search()
{
struct node *ptr;
int item, i = 0, flag;
ptr = head;
if (ptr == NULL)
{
printf("链表为空!\n");
}
else
{
printf("请输入要搜索的项目:");
scanf("%d", &item);
while (ptr != NULL)
{
if (ptr->data == item)
{
printf("在 %d 位置找到数据项\n", i + 1);
flag = 0;
}
else
{
flag = 1;
}
i++;
ptr = ptr->next;
}
if (flag == 1)
{
printf("数据项未找到\n");
}
}
}
/**
* 显示链表中的数据
*/
void display()
{
struct node *ptr;
ptr = head;
if (ptr == NULL)
{
printf("链表为空,没有数据可以显示。");
}
else
{
printf("链表中的数据值如下所示:\n");
printf("--------------------------------------------------\n");
while (ptr != NULL)
{
printf("\n%d", ptr->data);
ptr = ptr->next;
}
}
printf("\n\n\n");
}
```

282
数据结构/2.1.cpp Normal file
View File

@@ -0,0 +1,282 @@
#include<stdio.h>
#include<stdlib.h>
struct node
{
int data;
struct node *next;
};
struct node *head;
void beginsert();
void lastinsert();
void randominsert();
void begin_delete();
void last_delete();
void random_delete();
void display();
void search();
int main()
{
int choice = 0;
while (choice != 9)
{
printf("\n\n********* 主菜单 *********\n");
printf("从以下菜单列表中选择一个选项操作 ...\n");
printf("===============================================\n");
printf("1.插入到开头\n");
printf("2.插入到结尾\n");
printf("3.插入任何随机位置\n");
printf("4.从头部删除\n");
printf("5.从尾部删除\n");
printf("6.删除指定位置后的节点\n");
printf("7.搜索元素\n");
printf("8.显示链表中的数据\n");
printf("9.退出\n\n");
printf("===============================================\n");
printf("请输入您的选择:");
scanf("%d", &choice);
switch (choice)
{
case 1:
beginsert();
break;
case 2:
lastinsert();
break;
case 3:
randominsert();
break;
case 4:
begin_delete();
break;
case 5:
last_delete();
break;
case 6:
random_delete();
break;
case 7:
search();
break;
case 8:
display();
break;
case 9:
exit(0);
break;
default:
printf("请输入有效的选项...");
}
}
return 0;
}
void beginsert()
{
struct node *ptr;
int item;
ptr = (struct node *) malloc(sizeof(struct node *));
if (ptr == NULL)
{
printf("内存不够!\n");
}
else
{
printf("请输入一个整数值:");
scanf("%d", &item);
ptr->data = item;
ptr->next = head;
head = ptr;
printf("节点已经成功插入\n");
}
}
void lastinsert()
{
struct node *ptr, *temp;
int item;
ptr = (struct node*)malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("内存不够!\n");
}
else
{
printf("请输入一个整数值:");
scanf("%d", &item);
ptr->data = item;
if (head == NULL)
{
ptr->next = NULL;
head = ptr;
printf("节点已经成功插入\n");
}
else
{
temp = head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = ptr;
ptr->next = NULL;
printf("节点已经成功插入\n");
}
}
}
void randominsert()
{
int i, loc, item;
struct node *ptr, *temp;
ptr = (struct node *) malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("内存不够!\n");
}
else
{
printf("请输入一个整数值:");
scanf("%d", &item);
ptr->data = item;
printf("输入要插入的位置:");
scanf("%d", &loc);
temp = head;
for (i = 0;i < loc;i++)
{
temp = temp->next;
if (temp == NULL)
{
printf("此处不能插入节点\n");
return;
}
}
ptr->next = temp->next;
temp->next = ptr;
printf("节点已经成功插入\n");
}
}
void begin_delete()
{
struct node *ptr;
if (head == NULL)
{
printf("链表为空,没有什么可以删除!\n");
}
else
{
ptr = head;
head = ptr->next;
free(ptr);
printf("已经删除头部节点 ...\n");
}
}
void last_delete()
{
struct node *ptr, *ptr1;
if (head == NULL)
{
printf("链表为空,没有什么可以删除!\n");
}
else if (head->next == NULL)
{
head = NULL;
free(head);
printf("唯一的节点已经被删除了...\n");
}
else
{
ptr = head;
while (ptr->next != NULL)
{
ptr1 = ptr;
ptr = ptr->next;
}
ptr1->next = NULL;
free(ptr);
printf("已删除最后一个节点...\n");
}
}
void random_delete()
{
struct node *ptr, *ptr1;
int loc, i;
printf("输入要在此节点之后执行删除的节点的位置:");
scanf("%d", &loc);
ptr = head;
for (i = 0;i < loc;i++)
{
ptr1 = ptr;
ptr = ptr->next;
if (ptr == NULL)
{
printf("不能删除\n");
return;
}
}
ptr1->next = ptr->next;
free(ptr);
printf("\n第 %d 个节点已经被删除了", loc + 1);
}
void search()
{
struct node *ptr;
int item, i = 0, flag;
ptr = head;
if (ptr == NULL)
{
printf("链表为空!\n");
}
else
{
printf("请输入要搜索的项目:");
scanf("%d", &item);
while (ptr != NULL)
{
if (ptr->data == item)
{
printf("在 %d 位置找到数据项\n", i + 1);
flag = 0;
}
else
{
flag = 1;
}
i++;
ptr = ptr->next;
}
if (flag == 1)
{
printf("数据项未找到\n");
}
}
}
/**
* 显示链表中的数据
*/
void display()
{
struct node *ptr;
ptr = head;
if (ptr == NULL)
{
printf("链表为空,没有数据可以显示。");
}
else
{
printf("链表中的数据值如下所示:\n");
printf("--------------------------------------------------\n");
while (ptr != NULL)
{
printf("\n%d", ptr->data);
ptr = ptr->next;
}
}
printf("\n\n\n");
}

View File

@@ -0,0 +1,329 @@
# 双链表
## 1 双链表的简介
### 概念
* 双向链表是一种复杂类型的链表,它的节点包含指向序列中前一个节点和下一个节点的指针。
* 在双向链表中,节点由三部分组成:节点数据,指向下一个节点的指针(next指针),指向前一个节点的指针(prev指针)。
![](2021-03-12-21-14-57.png)
![](2021-03-12-21-14-50.png)
## 2 双链表的存储和实现
### 数据存储
* 双向链表为每个节点消耗更多空间
* 可以更灵活地操作链表中的元素。
![](双链表的数据存储.png)
### 数据实现
* 使用结构体和指针实现。
```C
struct node
{
struct node *prev;
int data;
struct node *next;
}
```
* 使用STL中的list实现
```
#include<list>
list<int> li;
```
## 3 双链表的操作
### 基本操作
* 创建
* 遍历、搜索
* 插入
* 删除
### 实现
```C++
#include<stdio.h>
#include<stdlib.h>
struct node
{
struct node *prev;
struct node *next;
int data;
};
struct node *head;
void insertion_beginning();
void insertion_last();
void insertion_specified();
void deletion_beginning();
void deletion_last();
void deletion_specified();
void display();
void search();
void main()
{
int choice = 0;
while (choice != 9)
{
printf("*********Main Menu*********\n");
printf("Choose one option from the following list ...\n");
printf("===============================================\n");
printf("1.Insert in begining\n2.Insert at last\n3.Insert at any random location\n4.Delete from Beginning\n5.Delete from last\n6.Delete the node after the given data\n7.Search\n8.Show\n9.Exit\n");
printf("Enter your choice?\n");
scanf("%d", &choice);
switch (choice)
{
case 1:
insertion_beginning();
break;
case 2:
insertion_last();
break;
case 3:
insertion_specified();
break;
case 4:
deletion_beginning();
break;
case 5:
deletion_last();
break;
case 6:
deletion_specified();
break;
case 7:
search();
break;
case 8:
display();
break;
case 9:
exit(0);
break;
default:
printf("Please enter valid choice..");
}
}
}
void insertion_beginning()
{
struct node *ptr;
int item;
ptr = (struct node *)malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("OVERFLOW\n");
}
else
{
printf("Enter Item value");
scanf("%d", &item);
if (head == NULL)
{
ptr->next = NULL;
ptr->prev = NULL;
ptr->data = item;
head = ptr;
}
else
{
ptr->data = item;
ptr->prev = NULL;
ptr->next = head;
head->prev = ptr;
head = ptr;
}
printf("Node inserted\n");
}
}
void insertion_last()
{
struct node *ptr, *temp;
int item;
ptr = (struct node *) malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("OVERFLOW\n");
}
else
{
printf("Enter value");
scanf("%d", &item);
ptr->data = item;
if (head == NULL)
{
ptr->next = NULL;
ptr->prev = NULL;
head = ptr;
}
else
{
temp = head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = ptr;
ptr->prev = temp;
ptr->next = NULL;
}
}
printf("node inserted\n");
}
void insertion_specified()
{
struct node *ptr, *temp;
int item, loc, i;
ptr = (struct node *)malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("OVERFLOW\n");
}
else
{
temp = head;
printf("Enter the location ");
scanf("%d", &loc);
for (i = 0;i < loc;i++)
{
temp = temp->next;
if (temp == NULL)
{
printf("There are less than %d elements", loc);
return;
}
}
printf("Enter value");
scanf("%d", &item);
ptr->data = item;
ptr->next = temp->next;
ptr->prev = temp;
temp->next = ptr;
temp->next->prev = ptr;
printf("node inserted\n");
}
}
void deletion_beginning()
{
struct node *ptr;
if (head == NULL)
{
printf("UNDERFLOW\n");
}
else if (head->next == NULL)
{
head = NULL;
free(head);
printf("node deleted\n");
}
else
{
ptr = head;
head = head->next;
head->prev = NULL;
free(ptr);
printf("node deleted\n");
}
}
void deletion_last()
{
struct node *ptr;
if (head == NULL)
{
printf("UNDERFLOW\n");
}
else if (head->next == NULL)
{
head = NULL;
free(head);
printf("node deleted\n");
}
else
{
ptr = head;
if (ptr->next != NULL)
{
ptr = ptr->next;
}
ptr->prev->next = NULL;
free(ptr);
printf("node deleted\n");
}
}
void deletion_specified()
{
struct node *ptr, *temp;
int val;
printf("Enter the data after which the node is to be deleted : ");
scanf("%d", &val);
ptr = head;
while (ptr->data != val)
ptr = ptr->next;
if (ptr->next == NULL)
{
printf("Can't delete\n");
}
else if (ptr->next->next == NULL)
{
ptr->next = NULL;
}
else
{
temp = ptr->next;
ptr->next = temp->next;
temp->next->prev = ptr;
free(temp);
printf("node deleted\n");
}
}
void display()
{
struct node *ptr;
printf("printing values...\n");
ptr = head;
while (ptr != NULL)
{
printf("%d\n", ptr->data);
ptr = ptr->next;
}
}
void search()
{
struct node *ptr;
int item, i = 0, flag;
ptr = head;
if (ptr == NULL)
{
printf("Empty List\n");
}
else
{
printf("Enter item which you want to search?\n");
scanf("%d", &item);
while (ptr != NULL)
{
if (ptr->data == item)
{
printf("item found at location %d ", i + 1);
flag = 0;
break;
}
else
{
flag = 1;
}
i++;
ptr = ptr->next;
}
if (flag == 1)
{
printf("Item not found\n");
}
}
}
```

283
数据结构/2.2.cpp Normal file
View File

@@ -0,0 +1,283 @@
#include<stdio.h>
#include<stdlib.h>
struct node
{
struct node *prev;
struct node *next;
int data;
};
struct node *head;
void insertion_beginning();
void insertion_last();
void insertion_specified();
void deletion_beginning();
void deletion_last();
void deletion_specified();
void display();
void search();
void main()
{
int choice = 0;
while (choice != 9)
{
printf("*********Main Menu*********\n");
printf("Choose one option from the following list ...\n");
printf("===============================================\n");
printf("1.Insert in begining\n2.Insert at last\n3.Insert at any random location\n4.Delete from Beginning\n5.Delete from last\n6.Delete the node after the given data\n7.Search\n8.Show\n9.Exit\n");
printf("Enter your choice?\n");
scanf("%d", &choice);
switch (choice)
{
case 1:
insertion_beginning();
break;
case 2:
insertion_last();
break;
case 3:
insertion_specified();
break;
case 4:
deletion_beginning();
break;
case 5:
deletion_last();
break;
case 6:
deletion_specified();
break;
case 7:
search();
break;
case 8:
display();
break;
case 9:
exit(0);
break;
default:
printf("Please enter valid choice..");
}
}
}
void insertion_beginning()
{
struct node *ptr;
int item;
ptr = (struct node *)malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("OVERFLOW\n");
}
else
{
printf("Enter Item value");
scanf("%d", &item);
if (head == NULL)
{
ptr->next = NULL;
ptr->prev = NULL;
ptr->data = item;
head = ptr;
}
else
{
ptr->data = item;
ptr->prev = NULL;
ptr->next = head;
head->prev = ptr;
head = ptr;
}
printf("Node inserted\n");
}
}
void insertion_last()
{
struct node *ptr, *temp;
int item;
ptr = (struct node *) malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("OVERFLOW\n");
}
else
{
printf("Enter value");
scanf("%d", &item);
ptr->data = item;
if (head == NULL)
{
ptr->next = NULL;
ptr->prev = NULL;
head = ptr;
}
else
{
temp = head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = ptr;
ptr->prev = temp;
ptr->next = NULL;
}
}
printf("node inserted\n");
}
void insertion_specified()
{
struct node *ptr, *temp;
int item, loc, i;
ptr = (struct node *)malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("OVERFLOW\n");
}
else
{
temp = head;
printf("Enter the location ");
scanf("%d", &loc);
for (i = 0;i < loc;i++)
{
temp = temp->next;
if (temp == NULL)
{
printf("There are less than %d elements", loc);
return;
}
}
printf("Enter value");
scanf("%d", &item);
ptr->data = item;
ptr->next = temp->next;
ptr->prev = temp;
temp->next = ptr;
temp->next->prev = ptr;
printf("node inserted\n");
}
}
void deletion_beginning()
{
struct node *ptr;
if (head == NULL)
{
printf("UNDERFLOW\n");
}
else if (head->next == NULL)
{
head = NULL;
free(head);
printf("node deleted\n");
}
else
{
ptr = head;
head = head->next;
head->prev = NULL;
free(ptr);
printf("node deleted\n");
}
}
void deletion_last()
{
struct node *ptr;
if (head == NULL)
{
printf("UNDERFLOW\n");
}
else if (head->next == NULL)
{
head = NULL;
free(head);
printf("node deleted\n");
}
else
{
ptr = head;
if (ptr->next != NULL)
{
ptr = ptr->next;
}
ptr->prev->next = NULL;
free(ptr);
printf("node deleted\n");
}
}
void deletion_specified()
{
struct node *ptr, *temp;
int val;
printf("Enter the data after which the node is to be deleted : ");
scanf("%d", &val);
ptr = head;
while (ptr->data != val)
ptr = ptr->next;
if (ptr->next == NULL)
{
printf("Can't delete\n");
}
else if (ptr->next->next == NULL)
{
ptr->next = NULL;
}
else
{
temp = ptr->next;
ptr->next = temp->next;
temp->next->prev = ptr;
free(temp);
printf("node deleted\n");
}
}
void display()
{
struct node *ptr;
printf("printing values...\n");
ptr = head;
while (ptr != NULL)
{
printf("%d\n", ptr->data);
ptr = ptr->next;
}
}
void search()
{
struct node *ptr;
int item, i = 0, flag;
ptr = head;
if (ptr == NULL)
{
printf("Empty List\n");
}
else
{
printf("Enter item which you want to search?\n");
scanf("%d", &item);
while (ptr != NULL)
{
if (ptr->data == item)
{
printf("item found at location %d ", i + 1);
flag = 0;
break;
}
else
{
flag = 1;
}
i++;
ptr = ptr->next;
}
if (flag == 1)
{
printf("Item not found\n");
}
}
}

View File

@@ -0,0 +1,297 @@
# 循环单向链表
## 1 简介
### 概念
* 在循环单链表中,链表的最后一个节点包含指向链表的第一个节点的指针。可以有循环单向链表以及循环双链表。
* 遍历一个循环单链表直到到达开始的同一个节点。循环单链表类似于链表但它没有开始也没有结束。任何节点的下一部分都不存在NULL值。
![](循环单向链表.png)
## 2 数据存储和实现
### 数据存储
链表的最后一个节点包含链表的第一个节点的地址。
![](2021-03-12-21-21-07.png)
### 数据实现
* 链表通过结构体和指针实现
```C++
struct node
{
int data;
struct node *next;
};
struct node *head, *ptr;
ptr = (struct node *)malloc(sizeof(struct node *));
```
* C++ STL提供了链表的实现
```
#include<list>
list<int> li;
forward_list<int> li;
```
## 3 操作
### 基本操作
* 创建
* 遍历、搜索
* 插入
* 删除
### 实现
```C++
#include<stdio.h>
#include<stdlib.h>
struct node
{
int data;
struct node *next;
};
struct node *head;
void beginsert();
void lastinsert();
void randominsert();
void begin_delete();
void last_delete();
void random_delete();
void display();
void search();
int main()
{
int choice = 0;
while (choice != 7)
{
printf("*********Main Menu*********\n");
printf("Choose one option from the following list ...\n");
printf("===============================================\n");
printf("1.Insert in begining\n2.Insert at last\n");
printf("3.Delete from Beginning\n4.Delete from last\n");
printf("5.Search for an element\n6.Show\n7.Exit\n");
printf("Enter your choice?\n");
scanf("%d", &choice);
switch (choice)
{
case 1:
beginsert();
break;
case 2:
lastinsert();
break;
case 3:
begin_delete();
break;
case 4:
last_delete();
break;
case 5:
search();
break;
case 6:
display();
break;
case 7:
exit(0);
break;
default:
printf("Please enter valid choice..");
}
}
}
void beginsert()
{
struct node *ptr, *temp;
int item;
ptr = (struct node *)malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("OVERFLOW");
}
else
{
printf("Enter the node data?");
scanf("%d", &item);
ptr->data = item;
if (head == NULL)
{
head = ptr;
ptr->next = head;
}
else
{
temp = head;
while (temp->next != head)
temp = temp->next;
ptr->next = head;
temp->next = ptr;
head = ptr;
}
printf("node inserted\n");
}
}
void lastinsert()
{
struct node *ptr, *temp;
int item;
ptr = (struct node *)malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("OVERFLOW\n");
}
else
{
printf("Enter Data?");
scanf("%d", &item);
ptr->data = item;
if (head == NULL)
{
head = ptr;
ptr->next = head;
}
else
{
temp = head;
while (temp->next != head)
{
temp = temp->next;
}
temp->next = ptr;
ptr->next = head;
}
printf("node inserted\n");
}
}
void begin_delete()
{
struct node *ptr;
if (head == NULL)
{
printf("UNDERFLOW");
}
else if (head->next == head)
{
head = NULL;
free(head);
printf("node deleted\n");
}
else
{
ptr = head;
while (ptr->next != head)
ptr = ptr->next;
ptr->next = head->next;
free(head);
head = ptr->next;
printf("node deleted\n");
}
}
void last_delete()
{
struct node *ptr, *preptr;
if (head == NULL)
{
printf("UNDERFLOW");
}
else if (head->next == head)
{
head = NULL;
free(head);
printf("node deleted\n");
}
else
{
ptr = head;
while (ptr->next != head)
{
preptr = ptr;
ptr = ptr->next;
}
preptr->next = ptr->next;
free(ptr);
printf("node deleted\n");
}
}
void search()
{
struct node *ptr;
int item, i = 0, flag = 1;
ptr = head;
if (ptr == NULL)
{
printf("Empty List\n");
}
else
{
printf("Enter item which you want to search?\n");
scanf("%d", &item);
if (head->data == item)
{
printf("item found at location %d", i + 1);
flag = 0;
}
else
{
while (ptr->next != head)
{
if (ptr->data == item)
{
printf("item found at location %d ", i + 1);
flag = 0;
break;
}
else
{
flag = 1;
}
i++;
ptr = ptr->next;
}
}
if (flag != 0)
{
printf("Item not found\n");
}
}
}
void display()
{
struct node *ptr;
ptr = head;
if (head == NULL)
{
printf("nothing to print");
}
else
{
printf("printing values ... \n");
while (ptr->next != head)
{
printf("%d\n", ptr->data);
ptr = ptr->next;
}
printf("%d\n", ptr->data);
}
}
```

246
数据结构/2.3.cpp Normal file
View File

@@ -0,0 +1,246 @@
#include<stdio.h>
#include<stdlib.h>
struct node
{
int data;
struct node *next;
};
struct node *head;
void beginsert();
void lastinsert();
void randominsert();
void begin_delete();
void last_delete();
void random_delete();
void display();
void search();
int main()
{
int choice = 0;
while (choice != 7)
{
printf("*********Main Menu*********\n");
printf("Choose one option from the following list ...\n");
printf("===============================================\n");
printf("1.Insert in begining\n2.Insert at last\n");
printf("3.Delete from Beginning\n4.Delete from last\n");
printf("5.Search for an element\n6.Show\n7.Exit\n");
printf("Enter your choice?\n");
scanf("%d", &choice);
switch (choice)
{
case 1:
beginsert();
break;
case 2:
lastinsert();
break;
case 3:
begin_delete();
break;
case 4:
last_delete();
break;
case 5:
search();
break;
case 6:
display();
break;
case 7:
exit(0);
break;
default:
printf("Please enter valid choice..");
}
}
}
void beginsert()
{
struct node *ptr, *temp;
int item;
ptr = (struct node *)malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("OVERFLOW");
}
else
{
printf("Enter the node data?");
scanf("%d", &item);
ptr->data = item;
if (head == NULL)
{
head = ptr;
ptr->next = head;
}
else
{
temp = head;
while (temp->next != head)
temp = temp->next;
ptr->next = head;
temp->next = ptr;
head = ptr;
}
printf("node inserted\n");
}
}
void lastinsert()
{
struct node *ptr, *temp;
int item;
ptr = (struct node *)malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("OVERFLOW\n");
}
else
{
printf("Enter Data?");
scanf("%d", &item);
ptr->data = item;
if (head == NULL)
{
head = ptr;
ptr->next = head;
}
else
{
temp = head;
while (temp->next != head)
{
temp = temp->next;
}
temp->next = ptr;
ptr->next = head;
}
printf("node inserted\n");
}
}
void begin_delete()
{
struct node *ptr;
if (head == NULL)
{
printf("UNDERFLOW");
}
else if (head->next == head)
{
head = NULL;
free(head);
printf("node deleted\n");
}
else
{
ptr = head;
while (ptr->next != head)
ptr = ptr->next;
ptr->next = head->next;
free(head);
head = ptr->next;
printf("node deleted\n");
}
}
void last_delete()
{
struct node *ptr, *preptr;
if (head == NULL)
{
printf("UNDERFLOW");
}
else if (head->next == head)
{
head = NULL;
free(head);
printf("node deleted\n");
}
else
{
ptr = head;
while (ptr->next != head)
{
preptr = ptr;
ptr = ptr->next;
}
preptr->next = ptr->next;
free(ptr);
printf("node deleted\n");
}
}
void search()
{
struct node *ptr;
int item, i = 0, flag = 1;
ptr = head;
if (ptr == NULL)
{
printf("Empty List\n");
}
else
{
printf("Enter item which you want to search?\n");
scanf("%d", &item);
if (head->data == item)
{
printf("item found at location %d", i + 1);
flag = 0;
}
else
{
while (ptr->next != head)
{
if (ptr->data == item)
{
printf("item found at location %d ", i + 1);
flag = 0;
break;
}
else
{
flag = 1;
}
i++;
ptr = ptr->next;
}
}
if (flag != 0)
{
printf("Item not found\n");
}
}
}
void display()
{
struct node *ptr;
ptr = head;
if (head == NULL)
{
printf("nothing to print");
}
else
{
printf("printing values ... \n");
while (ptr->next != head)
{
printf("%d\n", ptr->data);
ptr = ptr->next;
}
printf("%d\n", ptr->data);
}
}

View File

@@ -0,0 +1,298 @@
# 循环双向链表
## 1 简介
### 概念
* 循环双向链表是一种更复杂的数据结构类型,它的节点包含指向其前一节点以及下一节点的指针。
* 循环双向链表在任何节点中都不包含NULL。
* 链表的最后一个节点包含列表的第一个节点的地址。
* 链表的第一个节点还包含的前一个指针是指向最后一个节点的地址。
![](2021-03-12-21-22-43.png)
## 2 数据存储和实现
### 数据存储
* 起始节点包含最后一个(也是前一个)节点的地址即8和下一个节点即4。链表的最后一个节点存储在地址8并包含数据6包含链表的第一个节点的地址
![](循环双向链表内存.png)
### 数据实现
* 使用结构体和指针实现。
```C
struct node
{
struct node *prev;
int data;
struct node *next;
}
```
* 使用STL中的list实现
```
#include<list>
list<int> li;
```
## 3 操作
### 基本操作
* 创建
* 遍历、搜索
* 插入
* 删除
### 实现
```
#include<stdio.h>
#include<stdlib.h>
struct node
{
struct node *prev;
struct node *next;
int data;
};
struct node *head;
void insertion_beginning();
void insertion_last();
void deletion_beginning();
void deletion_last();
void display();
void search();
int main()
{
int choice = 0;
while (choice != 9)
{
printf("*********Main Menu*********\n");
printf("Choose one option from the following list ...\n");
printf("===============================================\n");
printf("1.Insert in Beginning\n2.Insert at last\n");
printf("3.Delete from Beginning\n4.Delete from last\n");
printf("5.Search\n6.Show\n7.Exit\n");
printf("Enter your choice?\n");
scanf("\n%d", &choice);
switch (choice)
{
case 1:
insertion_beginning();
break;
case 2:
insertion_last();
break;
case 3:
deletion_beginning();
break;
case 4:
deletion_last();
break;
case 5:
search();
break;
case 6:
display();
break;
case 7:
exit(0);
break;
default:
printf("Please enter valid choice..");
}
}
}
void insertion_beginning()
{
struct node *ptr, *temp;
int item;
ptr = (struct node *)malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("OVERFLOW");
}
else
{
printf("Enter Item value");
scanf("%d", &item);
ptr->data = item;
if (head == NULL)
{
head = ptr;
ptr->next = head;
ptr->prev = head;
}
else
{
temp = head;
while (temp->next != head)
{
temp = temp->next;
}
temp->next = ptr;
ptr->prev = temp;
head->prev = ptr;
ptr->next = head;
head = ptr;
}
printf("Node inserted\n");
}
}
void insertion_last()
{
struct node *ptr, *temp;
int item;
ptr = (struct node *) malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("OVERFLOW");
}
else
{
printf("Enter value");
scanf("%d", &item);
ptr->data = item;
if (head == NULL)
{
head = ptr;
ptr->next = head;
ptr->prev = head;
}
else
{
temp = head;
while (temp->next != head)
{
temp = temp->next;
}
temp->next = ptr;
ptr->prev = temp;
head->prev = ptr;
ptr->next = head;
}
}
printf("node inserted\n");
}
void deletion_beginning()
{
struct node *temp;
if (head == NULL)
{
printf("UNDERFLOW");
}
else if (head->next == head)
{
head = NULL;
free(head);
printf("node deleted\n");
}
else
{
temp = head;
while (temp->next != head)
{
temp = temp->next;
}
temp->next = head->next;
head->next->prev = temp;
free(head);
head = temp->next;
}
}
void deletion_last()
{
struct node *ptr;
if (head == NULL)
{
printf("UNDERFLOW");
}
else if (head->next == head)
{
head = NULL;
free(head);
printf("node deleted\n");
}
else
{
ptr = head;
if (ptr->next != head)
{
ptr = ptr->next;
}
ptr->prev->next = head;
head->prev = ptr->prev;
free(ptr);
printf("node deleted\n");
}
}
void display()
{
struct node *ptr;
ptr = head;
if (head == NULL)
{
printf("nothing to print");
}
else
{
printf("printing values ... \n");
while (ptr->next != head)
{
printf("%d\n", ptr->data);
ptr = ptr->next;
}
printf("%d\n", ptr->data);
}
}
void search()
{
struct node *ptr;
int item, i = 0, flag = 1;
ptr = head;
if (ptr == NULL)
{
printf("Empty List\n");
}
else
{
printf("Enter item which you want to search?\n");
scanf("%d", &item);
if (head->data == item)
{
printf("item found at location %d", i + 1);
flag = 0;
}
else
{
while (ptr->next != head)
{
if (ptr->data == item)
{
printf("item found at location %d ", i + 1);
flag = 0;
break;
}
else
{
flag = 1;
}
i++;
ptr = ptr->next;
}
}
if (flag != 0)
{
printf("Item not found\n");
}
}
}
```

249
数据结构/2.4.cpp Normal file
View File

@@ -0,0 +1,249 @@
#include<stdio.h>
#include<stdlib.h>
struct node
{
struct node *prev;
struct node *next;
int data;
};
struct node *head;
void insertion_beginning();
void insertion_last();
void deletion_beginning();
void deletion_last();
void display();
void search();
int main()
{
int choice = 0;
while (choice != 9)
{
printf("*********Main Menu*********\n");
printf("Choose one option from the following list ...\n");
printf("===============================================\n");
printf("1.Insert in Beginning\n2.Insert at last\n");
printf("3.Delete from Beginning\n4.Delete from last\n");
printf("5.Search\n6.Show\n7.Exit\n");
printf("Enter your choice?\n");
scanf("\n%d", &choice);
switch (choice)
{
case 1:
insertion_beginning();
break;
case 2:
insertion_last();
break;
case 3:
deletion_beginning();
break;
case 4:
deletion_last();
break;
case 5:
search();
break;
case 6:
display();
break;
case 7:
exit(0);
break;
default:
printf("Please enter valid choice..");
}
}
}
void insertion_beginning()
{
struct node *ptr, *temp;
int item;
ptr = (struct node *)malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("OVERFLOW");
}
else
{
printf("Enter Item value");
scanf("%d", &item);
ptr->data = item;
if (head == NULL)
{
head = ptr;
ptr->next = head;
ptr->prev = head;
}
else
{
temp = head;
while (temp->next != head)
{
temp = temp->next;
}
temp->next = ptr;
ptr->prev = temp;
head->prev = ptr;
ptr->next = head;
head = ptr;
}
printf("Node inserted\n");
}
}
void insertion_last()
{
struct node *ptr, *temp;
int item;
ptr = (struct node *) malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("OVERFLOW");
}
else
{
printf("Enter value");
scanf("%d", &item);
ptr->data = item;
if (head == NULL)
{
head = ptr;
ptr->next = head;
ptr->prev = head;
}
else
{
temp = head;
while (temp->next != head)
{
temp = temp->next;
}
temp->next = ptr;
ptr->prev = temp;
head->prev = ptr;
ptr->next = head;
}
}
printf("node inserted\n");
}
void deletion_beginning()
{
struct node *temp;
if (head == NULL)
{
printf("UNDERFLOW");
}
else if (head->next == head)
{
head = NULL;
free(head);
printf("node deleted\n");
}
else
{
temp = head;
while (temp->next != head)
{
temp = temp->next;
}
temp->next = head->next;
head->next->prev = temp;
free(head);
head = temp->next;
}
}
void deletion_last()
{
struct node *ptr;
if (head == NULL)
{
printf("UNDERFLOW");
}
else if (head->next == head)
{
head = NULL;
free(head);
printf("node deleted\n");
}
else
{
ptr = head;
if (ptr->next != head)
{
ptr = ptr->next;
}
ptr->prev->next = head;
head->prev = ptr->prev;
free(ptr);
printf("node deleted\n");
}
}
void display()
{
struct node *ptr;
ptr = head;
if (head == NULL)
{
printf("nothing to print");
}
else
{
printf("printing values ... \n");
while (ptr->next != head)
{
printf("%d\n", ptr->data);
ptr = ptr->next;
}
printf("%d\n", ptr->data);
}
}
void search()
{
struct node *ptr;
int item, i = 0, flag = 1;
ptr = head;
if (ptr == NULL)
{
printf("Empty List\n");
}
else
{
printf("Enter item which you want to search?\n");
scanf("%d", &item);
if (head->data == item)
{
printf("item found at location %d", i + 1);
flag = 0;
}
else
{
while (ptr->next != head)
{
if (ptr->data == item)
{
printf("item found at location %d ", i + 1);
flag = 0;
break;
}
else
{
flag = 1;
}
i++;
ptr = ptr->next;
}
}
if (flag != 0)
{
printf("Item not found\n");
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -3,7 +3,7 @@
## 1 简介
### 概念
* 堆栈(英语:stack又称为栈或堆叠,是计算机科学中一种特殊的串列形式的抽象数据类型,
* 堆栈stack又称为栈是计算机科学中一种特殊的串列形式的抽象数据类型
* 堆栈(Stack)是一个有序列表,它只能在顶端执行插入和删除。
* 堆栈(Stack)是一个递归数据结构,具有指向其顶部元素的指针。
* 堆栈为后进先出(LIFO)列表,即首先插入堆栈的元素将最后从堆栈中删除.
@@ -12,7 +12,7 @@
### 应用
* 回溯
* 深度优先搜索与回溯
* 递归
* 表达式评估和转换
* 解析
@@ -26,9 +26,10 @@
1. 先入后出,后入先出。
2. 除头尾节点之外,每个元素有一个前驱,一个后继。
### 操作
堆栈数据结构使用两种基本操作推入压栈push和弹出弹栈pop
## 2 栈的操作
### 基础操作
- 推入 push
- 将数据放入堆栈的顶端(数组形式或串列形式),堆栈顶端 top 指针加一。
@@ -37,16 +38,218 @@
- 将顶端数据数据输出(回传),堆栈顶端数据减一。
![](2021-03-12-21-29-49.png)
- 查看 top
- 查看堆栈的所有元素而不删除它们。
- 查看栈顶的元素而不删除它们。
> 以后有时间来实现这些数据结构
## 3 栈的实现
### 数组实现
```C++
#include <stdio.h>
int stack[100], i, j, choice = 0, n, top = -1;
void push();
void pop();
void show();
void main()
{
printf("Enter the number of elements in the stack ");
scanf("%d", &n);
printf("*********Stack operations using array*********");
printf("----------------------------------------------\n");
while (choice != 4)
{
printf("Chose one from the below options...\n");
printf("1.Push\n2.Pop\n3.Show\n4.Exit");
printf("Enter your choice \n");
scanf("%d", &choice);
switch (choice)
{
case 1:
{
push();
break;
}
case 2:
{
pop();
break;
}
case 3:
{
show();
break;
}
case 4:
{
printf("Exiting....");
break;
}
default:
{
printf("Please Enter valid choice ");
}
};
}
}
void push()
{
int val;
if (top == n)
printf("Overflow");
else
{
printf("Enter the value?");
scanf("%d", &val);
top = top + 1;
stack[top] = val;
}
}
void pop()
{
if (top == -1)
printf("Underflow");
else
top = top - 1;
}
void show()
{
for (i = top;i >= 0;i--)
{
printf("%d\n", stack[i]);
}
if (top == -1)
{
printf("Stack is empty");
}
}
```
### 链表实现
```C++
#include <stdio.h>
#include <stdlib.h>
void push();
void pop();
void display();
struct node
{
int val;
struct node *next;
};
struct node *head;
void main()
{
int choice = 0;
printf("*********Stack operations using linked list*********\n");
printf("----------------------------------------------\n");
while (choice != 4)
{
printf("Chose one from the below options...\n");
printf("1.Push\n2.Pop\n3.Show\n4.Exit");
printf("Enter your choice \n");
scanf("%d", &choice);
switch (choice)
{
case 1:
{
push();
break;
}
case 2:
{
pop();
break;
}
case 3:
{
display();
break;
}
case 4:
{
printf("Exiting....");
break;
}
default:
{
printf("Please Enter valid choice ");
}
};
}
}
void push()
{
int val;
struct node *ptr = (struct node*)malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("not able to push the element");
}
else
{
printf("Enter the value");
scanf("%d", &val);
if (head == NULL)
{
ptr->val = val;
ptr->next = NULL;
head = ptr;
}
else
{
ptr->val = val;
ptr->next = head;
head = ptr;
}
printf("Item pushed");
}
}
void pop()
{
int item;
struct node *ptr;
if (head == NULL)
{
printf("Underflow");
}
else
{
item = head->val;
ptr = head;
head = head->next;
free(ptr);
printf("Item popped");
}
}
void display()
{
int i;
struct node *ptr;
ptr = head;
if (ptr == NULL)
{
printf("Stack is empty\n");
}
else
{
printf("Printing Stack elements \n");
while (ptr != NULL)
{
printf("%d\n", ptr->val);
ptr = ptr->next;
}
}
}
```

View File

@@ -3,17 +3,17 @@
## 1 简介
### 概念
* 队列可以定义为有序列表,它允许在一端执行插入操作称为REAR,删除操作在另一端执行,称为FRONT
* 队列可以定义为有序列表在一端执行插入操作rear,删除操作在另一端执行,称为front
* 队列被称为先进先出列表。
![](2021-03-12-21-36-39.png)
### 应用
* 队列被广泛用作单个共享资源(如打印机磁盘CPU)的等待列表。
* 队列用于异步数据传输(例如,数据不以两个进程之间的相同速率传输)。 管道文件IO套接字。
* 队列在大多数应用程序中用作缓冲区.
* 队列在操作系统中用于处理中断。
* 单个共享资源(如打印机磁盘CPU)的等待列表。
* 异步数据传输管道文件IO套接字。
* 缓冲区.
* 操作系统中处理中断。
### 时间复杂度
@@ -23,49 +23,237 @@
| 最坏情况 | θ(n) | θ(n) | θ(1) | θ(1) |
## 2 队列的操作
### 基本操作
* 创建
* 遍历、搜索、查找(显示第一个元素)
* 遍历(显示第一个元素)
* 插入
* 删除
## 3 队列的实现
### 队列的数组实现
```C++
#include<stdio.h>
#include<stdlib.h>
#define maxsize 5
void insert();
void delete();
void display();
int front = -1, rear = -1;
int queue[maxsize];
void main()
{
int choice;
while (choice != 4)
{
printf("*************************Main Menu*****************************\n");
printf("=================================================================\n");
printf("1.insert an element\n2.Delete an element\n3.Display the queue\n4.Exit\n");
printf("Enter your choice ?");
scanf("%d", &choice);
switch (choice)
{
case 1:
insert();
break;
case 2:
delete();
break;
case 3:
display();
break;
case 4:
exit(0);
break;
default:
printf("Enter valid choice??\n");
}
}
}
void insert()
{
int item;
printf("\nEnter the element\n");
scanf("\n%d", &item);
if (rear == maxsize - 1)
{
printf("OVERFLOW\n");
return;
}
if (front == -1 && rear == -1)
{
front = 0;
rear = 0;
}
else
{
rear = rear + 1;
}
queue[rear] = item;
printf("Value inserted ");
}
void delete()
{
int item;
if (front == -1 || front > rear)
{
printf("UNDERFLOW\n");
return;
}
else
{
item = queue[front];
if (front == rear)
{
front = -1;
rear = -1;
}
else
{
front = front + 1;
}
printf("value deleted ");
}
}
void display()
{
int i;
if (rear == -1)
{
printf("Empty queue\n");
}
else
{
printf("printing values .....\n");
for (i = front;i <= rear;i++)
{
printf("\n%d\n", queue[i]);
}
}
}
```
### 队列的链表实现
```C++
#include<stdio.h>
#include<stdlib.h>
struct node
{
int data;
struct node *next;
};
struct node *front;
struct node *rear;
void insert();
void delete();
void display();
void main()
{
int choice;
while (choice != 4)
{
printf("*************************Main Menu*****************************\n");
printf("=================================================================\n");
printf("1.insert an element\n2.Delete an element\n3.Display the queue\n4.Exit\n");
printf("Enter your choice ?");
scanf("%d", &choice);
switch (choice)
{
case 1:
insert();
break;
case 2:
delete();
break;
case 3:
display();
break;
case 4:
exit(0);
break;
default:
printf("Enter valid choice??\n");
}
}
}
void insert()
{
struct node *ptr;
int item;
ptr = (struct node *) malloc(sizeof(struct node));
if (ptr == NULL)
{
printf("OVERFLOW\n");
return;
}
else
{
printf("Enter value?\n");
scanf("%d", &item);
ptr->data = item;
if (front == NULL)
{
front = ptr;
rear = ptr;
front->next = NULL;
rear->next = NULL;
}
else
{
rear->next = ptr;
rear = ptr;
rear->next = NULL;
}
}
}
void delete ()
{
struct node *ptr;
if (front == NULL)
{
printf("UNDERFLOW\n");
return;
}
else
{
ptr = front;
front = front->next;
free(ptr);
}
}
void display()
{
struct node *ptr;
ptr = front;
if (front == NULL)
{
printf("Empty queue\n");
}
else
{
printf("printing values .....\n");
while (ptr != NULL)
{
printf("%d\n", ptr->data);
ptr = ptr->next;
}
}
}
```
## 2 循环队列
### C++模板
### 概念
* 在循环队列中,第一个索引紧跟在最后一个索引之后。
* 当front = -1和rear = max-1时循环队列将满。循环队列的实现类似于线性队列的实现。只有在插入和删除的情况下实现的逻辑部分与线性队列中的逻辑部分不同。
![](循环队列.png)
### 时间复杂度
| 操作 | |
|-----------|------|
| Front | O(1) |
| Rear | O(1) |
| enQueue() | O(1) |
| deQueue() | O(1) |
### 基本操作
* 创建
* 遍历、搜索、查找(显示第一个元素)
* 插入
* 删除
### 实现
```
#include<queue>
queue<int> que;
```

View File

@@ -0,0 +1,141 @@
# 循环队列
## 1 简介
### 概念
* 在循环队列中,最后一个索引紧跟在第一个索引之后。
* 当front = -1和rear = max-1时循环队列将满。循环队列的实现类似于线性队列的实现。只有在插入和删除的情况下实现的逻辑部分与线性队列中的逻辑部分不同。
![](循环队列.png)
### 时间复杂度
| 操作 | |
|-----------|------|
| Front | O(1) |
| Rear | O(1) |
| enQueue() | O(1) |
| deQueue() | O(1) |
## 2 循环队列的操作
### 基本操作
* 创建
* 遍历、搜索、查找(显示第一个元素)
* 插入
* 删除
## 3 循环队列的实现
### 数组实现
```
#include<stdio.h>
#include<stdlib.h>
#define maxsize 5
void insert();
void delete();
void display();
int front = -1, rear = -1;
int queue[maxsize];
void main()
{
int choice;
while (choice != 4)
{
printf("*************************Main Menu*****************************\n");
printf("=================================================================\n");
printf("1.insert an element\n2.Delete an element\n3.Display the queue\n4.Exit\n");
printf("Enter your choice ?");
scanf("%d", &choice);
switch (choice)
{
case 1:
insert();
break;
case 2:
delete();
break;
case 3:
display();
break;
case 4:
exit(0);
break;
default:
printf("Enter valid choice??\n");
}
}
}
void insert()
{
int item;
printf("Enter the element\n");
scanf("%d", &item);
if ((rear + 1) % maxsize == front)
{
printf("OVERFLOW");
return;
}
else if (front == -1 && rear == -1)
{
front = 0;
rear = 0;
}
else if (rear == maxsize - 1 && front != 0)
{
rear = 0;
}
else
{
rear = (rear + 1) % maxsize;
}
queue[rear] = item;
printf("Value inserted ");
}
void delete()
{
int item;
if (front == -1 & rear == -1)
{
printf("UNDERFLOW\n");
return;
}
else if (front == rear)
{
front = -1;
rear = -1;
}
else if (front == maxsize - 1)
{
front = 0;
}
else
front = front + 1;
}
void display()
{
int i;
if (front == -1)
printf("Circular Queue is Empty!!!\n");
else
{
i = front;
printf("Circular Queue Elements are : \n");
if (front <= rear) {
while (i <= rear)
printf("%d %d %d\n", queue[i++], front, rear);
}
else {
while (i <= maxsize - 1)
printf("%d %d %d\n", queue[i++], front, rear);
i = 0;
while (i <= rear)
printf("%d %d %d\n", queue[i++], front, rear);
}
}
}
```
### 循环链表实现

View File

@@ -0,0 +1,31 @@
# 优先队列
## 1 优先队列简介
## 3 优先队列的操作
### 基本操作
* 创建
* 遍历、搜索、查找(显示第一个元素)
* 插入
* 删除
## 3 优先队列的实现
### 数组实现
### 循环链表实现
### C++模板
```
#include<queue>
queue<int> que;
```

View File

@@ -0,0 +1,82 @@
# 哈希表
## 1 哈希表简介
### 哈希表的概念
* `哈希表`是一种使用`哈希函数`组织数据,以支持快速插入和搜索的数据结构。
* 通过选择合适的哈希函数,哈希表可以在插入和搜索方面实现`出色的性能`
### 哈希表的分类
* 有两种不同类型的哈希表:哈希集合和哈希映射。
- `哈希集合``集合set`数据结构的实现之一,用于存储`非重复值`
- `哈希映射``映射map` 数据结构的实现之一,用于存储`(key, value)`键值对。
## 2 哈希表的原理
### 原理说明
* 哈希表的关键思想是使用哈希函数将键映射到存储桶。更确切地说,
1. 当我们插入一个新的键时,哈希函数将决定该键应该分配到哪个桶中,并将该键存储在相应的桶中;
2. 当我们想要搜索一个键时,哈希表将使用相同的哈希函数来查找对应的桶,并只在特定的桶中进行搜索。
### 哈希函数示例
![](2021-03-13-12-51-08.png)
* 在示例中,我们使用 y = x 5 作为哈希函数。让我们使用这个例子来完成插入和搜索策略:
1. 插入:我们通过哈希函数解析键,将它们映射到相应的桶中。
- 例如1987 分配给桶 2而 24 分配给桶 4。
2. 搜索:我们通过相同的哈希函数解析键,并仅在特定存储桶中搜索。
- 如果我们搜索 1987我们将使用相同的哈希函数将 1987 映射到 2。因此我们在桶 2 中搜索,我们在那个桶中成功找到了 1987。
- 例如,如果我们搜索 23将映射 23 到 3并在桶 3 中搜索。我们发现 23 不在桶 3 中,这意味着 23 不在哈希表中。
### 哈希表的关键——哈希函数
* 哈希函数是哈希表中最重要的组件,该哈希表用于将键映射到特定的桶。在上一节的示例中,我们使用 `y = x % 5` 作为散列函数,其中 `x` 是键值,`y` 是分配的桶的索引。
* 哈希函数将取决于`键值的范围``桶的数量。`
* 哈希函数的示例:
![](2021-03-13-12-53-35.png)
* 哈希函数的设计是一个开放的问题。其思想是尽可能将键分配到桶中,理想情况下,完美的哈希函数将是键和桶之间的一对一映射。然而,在大多数情况下,哈希函数并不完美,它需要在桶的数量和桶的容量之间进行权衡。
### 哈希表的关键——冲突解决
* 理想情况下如果我们的哈希函数是完美的一对一映射我们将不需要处理冲突。不幸的是在大多数情况下冲突几乎是不可避免的。例如在我们之前的哈希函数_y = x 5_1987 和 2 都分配给了桶 2这是一个`冲突`
* 冲突解决算法应该解决以下几个问题:
1. 如何组织在同一个桶中的值?
2. 如果为同一个桶分配了太多的值,该怎么办?
3. 如何在特定的桶中搜索目标值?
* 根据我们的哈希函数,这些问题与`桶的容量`和可能映射到`同一个桶``键的数目`有关。
* 让我们假设存储最大键数的桶有 `N` 个键。如果 N是常数且很小我们可以简单地使用一个数组将键存储在同一个桶中。如果 N 是可变的或很大,我们可能需要使用`高度平衡的二叉树`来代替。
## 哈希表的实现
### 自己设计哈希函数
### C++STL中的实现
* 哈希集合
```
#include<set>
set<int> s;
multiset<int> ms;
unordered_multiset<int> ums;
```
* 哈希映射
```
#include<map>
map<map> m;
multimap<map> mm;
unordered_multimap<map> umm;
```

View File

@@ -1,35 +0,0 @@
# 二叉搜索树
> 树>二叉树>二叉搜索树
## 1 简介
### 概念
* 二叉搜索树它是二叉树的一种类别,其节点按特定顺序排列。
* 这也称为有序二叉树。在二叉搜索树中,左子树中所有节点的值小于根的值。
* 类似地,右子树中所有节点的值大于或等于根的值。
* 此规则将递归地应用于根的所有左子树和右子树
![](2021-03-12-22-46-59.png)
### 优点
搜索在二叉搜索树中变得非常有效,因为得到每个步骤的提示,哪个子树包含所需元素。与数组和链表相比,二叉搜索树被认为是有效的数据结构。 在搜索过程中,它会在每一步删除半个子树。 在二叉搜索树中搜索元素需要o(log2n)时间。 在最坏的情况下搜索元素所花费的时间是0(n)。与数组和链表中的操作相比,它还加快了插入和删除操作。
## 2 基本操作
### 基本操作
* 创建
* 遍历、搜索、查找
* 插入
* 删除
```
```
### 创建
使用下列元素创建二叉树的过程
```
43, 10, 79, 90, 12, 54, 11, 9, 50
```
![](2021-03-12-22-48-28.png)

View File

@@ -1,51 +0,0 @@
# 平衡二叉树
> 树>二叉树>二叉搜索树>二叉平衡树
## 1 简介
### 概念
* 如果每个节点的平衡因子在-1到1之间则称树是平衡的否则树将是不平衡的并且需要平衡。
```
平衡系数(k)=高度(左(k)) - 高度(右(k))
```
* 如果任何节点的平衡因子为1则意味着左子树比右子树高一级。
* 如果任何节点的平衡因子为0则意味着左子树和右子树包含相等的高度。
* 如果任何节点的平衡因子是-1则意味着左子树比右子树低一级。
![](2021-03-12-22-53-43.png)
### 复杂性
| 算法 | 平均情况 | 最坏情况 |
|----|----------|----------|
| 空间 | o(n) | o(n) |
| 搜索 | o(log n) | o(log n) |
| 插入 | o(log n) | o(log n) |
| 删除 | o(log n) | o(log n) |
### 优势
* AVL树通过不让它倾斜来控制二叉搜索树的高度。 高度为h的二叉搜索树中的所有操作所花费的时间是O(h)。 但是,如果二叉搜索树变得偏斜(即最坏的情况)它可以扩展到O(n)。
* 通过将该高度限制为log nAVL树将每个操作的上限强加为O(log n)其中n是节点的数量
## 2 操作
### 基本操作
* 创建
* 遍历、搜索、查找
* 插入
* 删除
```
```
### 遍历
* AVL树也是二叉搜索树所有操作都以与在二叉搜索树中执行的相同方式执行。 搜索和遍历不会导致AVL树违反属性
### 插入
* AVL树中的插入的执行方式与在二叉搜索树中执行的方式相同。但是它可能会导致违反AVL树属性因此树可能需要平衡。可以通过应用旋转来平衡树。
### 删除
* 删除也可以按照在二叉搜索树中执行的相同方式执行。 删除也可能会扰乱树的平衡,因此,使用各种类型的旋转来重新平衡树。

View File

@@ -1,25 +0,0 @@
# 红黑树
> 树>二叉树>二叉搜索树>平衡二叉搜索树>红黑树(自平衡二叉搜索树)
## 1 简介
### 定义
### 优势
## 2 操作
### 创建
### 遍历
### 插入
### 删除
### 实现
```
```

View File

@@ -1,56 +0,0 @@
# B+树
## 1 简介
### 概念
> 像是在建立额外的索引
* B+树是B树的扩展允许有效的插入删除和搜索操作。满足B树的所有特性
* 在B树中键和记录都可以存储在内部节点和叶子节点中。 然而在B+树中,记录(数据)只能存储在叶节点上,而内部节点只能存储键值。
* B+树的叶节点以单链表的形式链接在一起,以使搜索查询更有效。
* B+树用于存储无法存储在主存储器中的大量数据。 由于主存储器的大小总是有限的事实B+树的内部节点(访问记录的键)存储在主存储器中,而叶节点存储在辅助存储器中。
* B+树的内部节点通常称为索引节点。
### 优点
* 可以在相同数量的磁盘访问中获取记录。
* 树高度保持平衡与B树相比较少。
* 可以顺序访问存储在B+树中的数据,也可以直接访问。
* 键用于索引。更快的搜索查询,因为数据仅存储在叶节点上。
## 2 操作
### 搜索
### 插入
* 第1步 :将新节点作为叶节点插入。
* 第2步 :如果叶子没有所需空间,则拆分节点并将中间节点复制到下一个索引节点。
* 第3步 :如果索引节点没有所需空间,则拆分节点并将中间元素复制到下一个索引节点
> 实例
* 将值195插入到下图所示的5阶B+树中。
![](2021-03-12-23-58-07.png)
* 在190之后将在右子树120中插入195。将其插入所需位置。
![](2021-03-12-23-58-14.png)
* 该节点包含大于最大的元素数量即4因此将其拆分并将中间节点放置到父节点。
![](2021-03-12-23-58-20.png)
* 现在索引节点包含6个子节点和5个键违反B+树属性因此需要将其拆分如下所示。将108提为根节点。60,78左子节点120,190右子节点。
![](2021-03-12-23-58-29.png)
### 删除
* 第1步从叶子中删除键和数据。
* 第2步如果叶节点包含少于最小数量的元素则将节点与其兄弟节点合并并删除它们之间的键。
* 第3步如果索引节点包含少于最小数量的元素则将节点与兄弟节点合并并在它们之间向下移动键。
> 示例
* 从下图所示的B+树中删除键为200。
![](2021-03-13-00-03-01.png)
* 在195之后的190的右子树中存在200删除它。
![](2021-03-13-00-03-07.png)
* 使用195,190,154和129合并两个节点。
![](2021-03-13-00-03-13.png)
* 现在元素120是节点中存在的违反B+树属性的单个元素。 因此需要使用60,78,108和120来合并它。现在B+树的高度将减1。
![](2021-03-13-00-03-30.png)

View File

@@ -1,20 +0,0 @@
# 堆
## 1 简介
Heap是一个可以被看成近似完全二叉树的数组。树上的每一个结点对应数组的一个元素。除了最底层外该树是完全充满的而且是从左到右填充。
堆包括最大堆和最小堆:最大堆的每一个节点(除了根结点)的值不大于其父节点;最小堆的每一个节点(除了根结点)的值不小于其父节点。
堆常见的操作:
- HEAPIFY 建堆:把一个乱序的数组变成堆结构的数组,时间复杂度为 $$O(n)$$。
- HEAPPUSH把一个数值放进已经是堆结构的数组中并保持堆结构时间复杂度为 $$O(log N)$$
- HEAPPOP从最大堆中取出最大值或从最小堆中取出最小值并将剩余的数组保持堆结构时间复杂度为 $$O(log N)$$。
- HEAPSORT借由 HEAPFY 建堆和 HEAPPOP 堆数组进行排序,时间复杂度为$$ O(N log N)$$,空间复杂度为 $$O(1)$$。
堆结构的一个常见应用是建立优先队列Priority Queue
![img](http://dunwu.test.upcdn.net/cs/data-structure/heap/heap.png)
## 实战

View File

@@ -1,10 +1,11 @@
# 树
> 树
## 0 简介
### 概念
在计算机科学中,**树**英语tree是一种抽象数据类型ADT或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由 nn>0个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
### 定义
**树**是一种抽象数据类型或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由 nn>0个有限节点组成一个具有层次关系的集合。
* 树是一种递归数据结构,包含一个或多个数据节点的集合,其中一个节点被指定为树的根,而其余节点被称为根的子节点。
* 除根节点之外的节点被划分为非空集,其中每个节点将被称为子树。
@@ -34,11 +35,13 @@
- **节点的度**:一个节点含有的子树的个数称为该节点的度;
- **树的度**:一棵树中,最大的节点度称为树的度;
- 节点的**层次**:从根开始定义起,根为第 1 层,根的子节点为第 2 层,以此类推;
- **深度**:对于任意节点 n,n 的深度为从根到 n 的唯一路径长,的深度为 0
- **高度**:对于任意节点 n,n 的高度为从 n 到一片树叶的最长路径长,所有树叶的高度为 0
- **深度**:对于任意节点 n,n 的深度为从根到 n 的唯一路径长,**根节点的深度为0**
- **高度**:对于任意节点 n,n 的高度为从 n 到一片树叶的最长路径长,所有**叶节点的高度为0**
- **森林**:由 mm>=0棵互不相交的树的集合称为森林
- **路径** 连续边的序列称为路径。 在上图所示的树中节点E的路径为A→B→E。
- **级别编号** 为树的每个节点分配一个级别编号,使得每个节点都存在于高于其父级的一个级别。树的根节点始终是级别0
- **级别=水平** 为树的每个节点分配一个级别编号,使得每个节点都存在于高于其父级的一个级别。**根节点级别0**
- **键**:节点总存储的值。
- **索引**:该节点子节点的个数。
### 性质
@@ -49,16 +52,6 @@
- 具有 n 个节点的 m 次树的最小高度为 $\log_m{(n(m-1)+1)}$ 。
### 数据结构
```
struct treenode
{
int root;
struct treenode *father;
struct treenode *son
struct treenode *next;
}
```
## 2 树的种类
按节点有无顺序划分
@@ -86,3 +79,15 @@ struct treenode
* 竞赛树:用于记录两名比赛者之间每轮比赛的胜者。 比赛树也可以称为选择树或获胜者树。 外部节点表示正在播放匹配的比赛者,而内部节点表示所播放的匹配的胜者。 在最顶层,比赛的获胜者作为树的根节点出现。
![](2021-03-12-22-07-20.png)
## 3 树的实现
### 数据实现
* 使用结构体和指针实现树
```C++
struct treenode
{
int root;
struct treenode *father;//父节点
struct treenode *son;//子节点
struct treenode *next;//兄弟节点
}
```

View File

@@ -4,9 +4,10 @@
## 1 二叉树简介
### 概念
二叉树是一种特殊类型的通用树,它的每个节点最多可以有两个子节点。 二叉树通常被划分为三个不相交的子集。
* 节点的根左二叉树
* 二叉树
* 二叉树是一种特殊类型的通用树,它的每个节点最多可以有两个子节点。 二叉树通常被划分为三个不相交的子集。
* 节点的根
* 二叉树
* 右二叉树
![](2021-03-12-22-10-38.png)
@@ -17,7 +18,7 @@
3. 包含 n 个结点的二叉树的高度至少为 **log<sub>2</sub>(n+1)**
4. 在任意一棵二叉树中,若终端结点的个数为 n0度为 2 的结点数为 n2则 n0=n2+1。
## 2 二叉树类
## 2 二叉树
### 严格二叉树
@@ -27,26 +28,12 @@
### 完全二叉树
* 如果所有叶子都位于相同的水平d则称二元树是完全二叉树。完全二叉树是二叉树在0级和d级之间的每个级别包含正好2 ^ 1个节点。 具有深度d的完全二叉树中的节点总数是2^d+1 -1其中叶节点是2d而非叶节点是2^d-1
* 如果所有叶子都位于相同的水平d则称二元树是完全二叉树。完全二叉树是二叉树在0级和d级之间的每个级别包含正好$2 ^d$个节点。 具有深度d的完全二叉树中的节点总数是$2^{(d+1)}-1$,其中叶节点是$2^d&,非叶节点是$2^d-1$。
![](2021-03-12-22-12-52.png)
## 3 二叉树实现
### 链表实现
* 二叉树以链表的形式存储在存储器中,其节点的数量存储在非连续的存储器位置并通过像树一样继承父子关系而链接在一起。 每个节点包含三个部分:指向左节点的指针,数据元素和指向右节点的指针。 每个二叉树都有一个根指针,指向二叉树的根节点。 在空二进制树中根指针将指向null。
![](2021-03-12-22-16-50.png)
### 数组实现
* 这是存储树元素的最简单的内存分配技术,但它是一种效率低下的技术,因为它需要大量空间来存储树元素。 下图显示了二叉树及其内存分配。
![](2021-03-12-22-17-33.png)
* 在这种表示方式中,数组用于存储树元素。 数组的大小将等于树中存在的节点数。 树的根节点将出现在数组的第一个索引处。 如果节点存储在第i个索引处则其左右子节点将存储在2i和2i + 1位置。 如果数组的第一个索引即tree[1]为0则表示树为空。
## 4 二叉树遍历搜索
## 3 二叉树的操作——遍历搜索
### 遍历搜索方法
@@ -61,7 +48,7 @@
| 遍历 | 描述 |
|------|----------------------------------------|
|----------|----------------------------------------|
| 前序遍历 | 首先遍历根,然后分别遍历左子树和右子树。 该过程将递归地应用于树的每个子树。 |
| 中序遍历 | 首先遍历左子树,然后分别遍历根和右子树。 该过程将递归地应用于树的每个子树。 |
| 后序遍历 | 遍历左子树,然后分别遍历右子树和根。 该过程将递归地应用于树的每个子树。 |
@@ -80,6 +67,23 @@
### 层序遍历
```
```
## 4 二叉树实现
### 链表实现
* 二叉树以链表的形式存储在存储器中,其节点的数量存储在非连续的存储器位置并通过像树一样继承父子关系而链接在一起。 每个节点包含三个部分:指向左节点的指针,数据元素和指向右节点的指针。 每个二叉树都有一个根指针,指向二叉树的根节点。 在空二进制树中根指针将指向null。
![](2021-03-12-22-16-50.png)
```
```
### 数组实现
* 这是存储树元素的最简单的内存分配技术,但它是一种效率低下的技术,因为它需要大量空间来存储树元素。 下图显示了二叉树及其内存分配。
![](2021-03-12-22-17-33.png)
* 在这种表示方式中,数组用于存储树元素。 数组的大小将等于树中存在的节点数。 树的根节点将出现在数组的第一个索引处。 如果节点存储在第i个索引处则其左右子节点将存储在2i和2i + 1位置。 如果数组的第一个索引即tree[1]为0则表示树为空。
```
```
## 5 二叉树相关的问题

View File

@@ -0,0 +1,169 @@
# 二叉搜索树
> 树>二叉树>二叉搜索树
## 1 简介
### 概念
* 二叉搜索树是二叉树,其节点按特定顺序排列。也称为称为有序二叉树。
* 左子树中所有节点的值小于根的值。
* 右子树中所有节点的值大于或等于根的值。
* 此规则将递归地应用于根的所有左子树和右子树
![](2021-03-12-22-46-59.png)
### 优点
* 在搜索过程中,它会在每一步删除半个子树。
* 在二叉搜索树中搜索元素需要o(log2n)时间。
* 在最坏的情况下搜索元素所花费的时间是0(n)。
* 与数组和链表中的操作相比,它还加快了插入和删除操作。
## 2 基本操作
### 基本操作
* 创建
* 遍历(同上)
* 插入
* 删除
### 插入
* 使用下列元素创建二叉树的过程
```
43, 10, 79, 90, 12, 54, 11, 9, 50
```
![](2021-03-12-22-48-28.png)
### 删除
## 3 二叉搜索树实现
```C++
#include <iostream>
#include <stdlib.h>
using namespace std;
struct Node {
int data;
Node *left;
Node *right;
};
Node* create(int item)
{
Node* node = new Node;
node->data = item;
node->left = node->right = NULL;
return node;
}
void inorder(Node *root)
{
if (root == NULL)
return;
inorder(root->left);
cout<< root->data << " ";
inorder(root->right);
}
Node* findMinimum(Node* cur)
{
while(cur->left != NULL) {
cur = cur->left;
}
return cur;
}
Node* insertion(Node* root, int item)
{
if (root == NULL)
return create(item);
if (item < root->data)
root->left = insertion(root->left, item);
else
root->right = insertion(root->right, item);
return root;
}
void search(Node* &cur, int item, Node* &parent)
{
while (cur != NULL && cur->data != item)
{
parent = cur;
if (item < cur->data)
cur = cur->left;
else
cur = cur->right;
}
}
void deletion(Node*& root, int item)
{
Node* parent = NULL;
Node* cur = root;
search(cur, item, parent);
if (cur == NULL)
return;
if (cur->left == NULL && cur->right == NULL)
{
if (cur != root)
{
if (parent->left == cur)
parent->left = NULL;
else
parent->right = NULL;
}
else
root = NULL;
free(cur);
}
else if (cur->left && cur->right)
{
Node* succ = findMinimum(cur- >right);
int val = succ->data;
deletion(root, succ->data);
cur->data = val;
}
else
{
Node* child = (cur->left)? Cur- >left: cur->right;
if (cur != root)
{
if (cur == parent->left)
parent->left = child;
else
parent->right = child;
}
else
root = child;
free(cur);
}
}
int main()
{
Node* root = NULL;
int keys[8];
for(int i=0;i<8;i++)
{
cout << "Enter value to be inserted";
cin>>keys[i];
root = insertion(root, keys[i]);
}
inorder(root);
cout<<"\n";
deletion(root, 10);
inorder(root);
return 0;
}
```

View File

@@ -0,0 +1,220 @@
# 平衡二叉树
> 树>二叉树>二叉搜索树>二叉平衡树
> 参考文献
> * [AVL](https://zhuanlan.zhihu.com/p/56066942)
## 1 简介
### 概念
* 所有结点的平衡因子的绝对值都不超过1即平衡因子只能是1,0,-1三个值。
```
平衡系数=左子树的高度 - 右子树的高度
```
* 如果任何节点的平衡因子为1则意味着左子树比右子树高一级。
* 如果任何节点的平衡因子为0则意味着左子树和右子树包含相等的高度。
* 如果任何节点的平衡因子是-1则意味着左子树比右子树低一级。
![](2021-03-12-22-53-43.png)
### 复杂性
| 算法 | 平均情况 | 最坏情况 |
|----|----------|----------|
| 空间 | o(n) | o(n) |
| 搜索 | o(log n) | o(log n) |
| 插入 | o(log n) | o(log n) |
| 删除 | o(log n) | o(log n) |
### 优势
* AVL树能防止二叉树偏斜控制二叉搜索树的高度。高度为h的二叉搜索树中的所有操作所花费的时间是O(h)。
* 如果普通二叉搜索树变得偏斜(即最坏的情况)它可以扩展到O(n)。
* 通过将该高度限制为log nAVL树将每个操作的上限强加为O(log n)其中n是节点的数量
## 2 操作
### 基本操作
* 创建
* 遍历(同上)
* 插入
* 删除
### 插入
* AVL树中的插入的执行方式与在二叉搜索树中执行的方式相同。
* 它可能会导致违反AVL树属性因此树可能需要平衡。可以通过应用旋转来平衡树。
### 删除
* 删除也可以按照在二叉搜索树中执行的相同方式执行。
* 删除也可能会扰乱树的平衡,因此,使用各种类型的旋转来重新平衡树。
* 删除节点的步骤如下:
1. 以前三种情况为基础尝试删除节点,并将访问节点入栈。
2. 如果尝试删除成功,则依次检查栈顶节点的平衡状态,遇到非平衡节点,即进行旋转平衡,直到栈空。
3. 如果尝试删除失败,证明是第四种情况。这时先找到被删除节点的右子树最小节点并删除它,将访问节点继续入栈。
4. 再依次检查栈顶节点的平衡状态和修正直到栈空。
## 3 操作——平衡
### 左旋
> 为了使树平衡,使用的手段有 左旋和右旋。右旋, 即是顺时针旋转。左旋,即是逆时针旋转。
* 左旋。加入新节点 99 后,节点 66 的左子树高度为 1右子树高度为 3此时平衡因子为 -2。为保证树的平衡此时需要对节点 66 做出旋转,因为右子树高度高于左子树,对节点进行左旋操作,
1. 节点的右孩子替代此节点位置
2. 右孩子的左子树变为该节点的右子树
3. 节点本身变为右孩子的左子树
![](2021-03-13-14-06-44.png)
![](2021-03-13-14-09-24.png)
### 右旋
* 右旋。
1. 节点的左孩子代表此节点
2. 节点的左孩子的右子树变为节点的左子树
3. 将此节点作为左孩子节点的右子树。
![](2021-03-13-14-10-25.png)
![](2021-03-13-14-10-40.png)
### 插入过程的四种旋转
> 删除与插入节点的四种旋转方式类似
| 插入方式 | 描述 | 旋转方式 |
| -------- | --- | ------------ |
| LL | 在 A 的左子树的根节点的左子树上插入节点而破坏平衡 | 右旋转 |
| RR | 在 A 的右子树的根节点的右子树上插入节点而破坏平衡 | 左旋转 |
| LR | 在A的左子树的根节点的右子树上插入节点而破坏平衡 | 先左旋后右旋 |
| RL | 在 A 的右子树的根节点的左子树上插入节点而破坏平衡 | 先右旋后左旋 |
### LL单次右旋
* 由于在**A的左子树的根结点的左子树**上插入结点FLL需进行一次的向右的顺时针旋转操作
![](2021-03-13-14-25-55.png)
```C++
//LL型调整函数
//返回:新父节点
Tree LL_rotate(Tree node){
//node为离操作结点最近的失衡的结点
Tree parent=NULL,son;
//获取失衡结点的父节点
parent=node->parent;
//获取失衡结点的左孩子
son=node->lchild;
//设置son结点右孩子的父指针
if (son->rchild!=NULL) son->rchild->parent=node;
//失衡结点的左孩子变更为son的右孩子
node->lchild=son->rchild;
//更新失衡结点的高度信息
update_depth(node);
//失衡结点变成son的右孩子
son->rchild=node;
//设置son的父结点为原失衡结点的父结点
son->parent=parent;
//如果失衡结点不是根结点,则开始更新父节点
if (parent!=NULL){
//如果父节点的左孩子是失衡结点指向现在更新后的新孩子son
if (parent->lchild==node){
parent->lchild=son;
}else{
//父节点的右孩子是失衡结点
parent->rchild=son;
}
}
//设置失衡结点的父亲
node->parent=son;
//更新son结点的高度信息
update_depth(son);
return son;
}
```
### RR单次左旋
* 在 A 的右子树的根节点的右子树上插入节点而破坏平衡。需要进行一次左旋操作。
![](2021-03-13-14-28-34.png)
```C++
//RR型调整函数
//返回新父节点
Tree RR_rotate(Tree node){
//node为离操作结点最近的失衡的结点
Tree parent=NULL,son;
//获取失衡结点的父节点
parent=node->parent;
//获取失衡结点的右孩子
son=node->rchild;
//设置son结点左孩子的父指针
if (son->lchild!=NULL){
son->lchild->parent=node;
}
//失衡结点的右孩子变更为son的左孩子
node->rchild=son->lchild;
//更新失衡结点的高度信息
update_depth(node);
//失衡结点变成son的左孩子
son->lchild=node;
//设置son的父结点为原失衡结点的父结点
son->parent=parent;
//如果失衡结点不是根结点,则开始更新父节点
if (parent!=NULL){
//如果父节点的左孩子是失衡结点指向现在更新后的新孩子son
if (parent->lchild==node){
parent->lchild=son;
}else{
//父节点的右孩子是失衡结点
parent->rchild=son;
}
}
//设置失衡结点的父亲
node->parent=son;
//更新son结点的高度信息
update_depth(son);
return son;
}
```
### LR先左旋后右旋
* 在A的左子树的根节点的右子树上插入节点F而破坏平衡。需要先进行子树的左旋再进行A节点的右旋。
![](2021-03-13-14-29-50.png)
```C++
//LR型先左旋转再右旋转
//返回:新父节点
Tree LR_rotate(Tree node){
RR_rotate(node->lchild);
return LL_rotate(node);
}
```
### RL先右旋后左旋
* 在 A 的右子树的根节点的左子树上插入节点F而破坏平衡。需要先进行子树右旋然后进行A节点的左旋。
![](2021-03-13-14-30-35.png)
```C++
//RL型先右旋转再左旋转
//返回:新父节点
Tree RL_rotate(Tree node){
LL_rotate(node->rchild);
return RR_rotate(node);
}
```
## 4 实现
```C++
typedef int ElementType;
struct AVLNode{
int depth; //深度,这里计算每个结点的深度,通过深度的比较可得出是否平衡
Tree parent; //该结点的父节点
ElementType val; //结点值
Tree lchild;
Tree rchild;
AVLNode(int val=0) {
parent = NULL;
depth = 0;
lchild = rchild = NULL;
this->val=val;
}
};
```

View File

@@ -0,0 +1,140 @@
# 红黑树
> 树>二叉树>二叉搜索树>平衡二叉搜索树>红黑树(自平衡二叉搜索树)
> 参考文献
> * [红黑树](https://www.cnblogs.com/skywang12345/p/3245399.html#a1)
## 1 简介
### 定义
1. 每个节点或者是黑色,或者是红色。
2. 根节点是黑色。
3. 每个叶子节点NIL是黑色。这里叶子节点是指为空NULL的叶子节点
4. 如果一个节点是红色的,则它的子节点必须是黑色的。
5. 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
### 优势
* 主要是用它来存储有序的数据它的时间复杂度是O(lgn),效率非常之高。
### 时间复杂度
* 红黑树的时间复杂度为: O(lgn)
## 2 操作
> 太过复杂,日后处理,根据参考文献。
### 基础操作
* 创建
* 遍历(同上)
* 插入
* 删除
### 插入
* 第一步: 将红黑树当作一颗二叉查找树,将节点插入。
* 红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。此外,无论是左旋还是右旋,若旋转之前这棵树是二叉查找树,旋转之后它一定还是二叉查找树。这也就意味着,任何的旋转和重新着色操作,都不会改变它仍然是一颗二叉查找树的事实。
* 好吧?那接下来,我们就来想方设法的旋转以及重新着色,使这颗树重新成为红黑树!
* 第二步:将插入的节点着色为"红色"。
* 为什么着色成红色,而不是黑色呢?为什么呢?
* 将插入的节点着色为红色,不会违背"特性(5)"少违背一条特性就意味着我们需要处理的情况越少。接下来就要努力的让这棵树满足其它性质即可满足了的话它就又是一颗红黑树了。o(∩∩)o...哈哈
* 第三步: 通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。
* 第二步中,将插入节点着色为"红色"之后,不会违背"特性(5)"。那它到底会违背哪些特性呢?
* 对于"特性(1)",显然不会违背了。因为我们已经将它涂成红色了。
* 对于"特性(2)",显然也不会违背。在第一步中,我们是将红黑树当作二叉查找树,然后执行的插入操作。而根据二叉查找数的特点,插入操作不会改变根节点。所以,根节点仍然是黑色。
* 对于"特性(3)",显然不会违背了。这里的叶子节点是指的空叶子节点,插入非空节点并不会对它们造成影响。
* 对于"特性(4)",是有可能违背的!
* 那接下来,想办法使之"满足特性(4)",就可以将树重新构造成红黑树了。
* 使用伪代码了解流程
```
RB-INSERT(T, z)
y ← nil[T] // 新建节点“y”将y设为空节点。
x ← root[T] // 设“红黑树T”的根节点为“x”
while x ≠ nil[T] // 找出要插入的节点“z”在二叉树T中的位置“y”
do y ← x
if key[z] < key[x]
then x ← left[x]
else x ← right[x]
p[z] ← y // 设置 “z的父亲” 为 “y”
if y = nil[T]
then root[T] ← z // 情况1若y是空节点则将z设为根
else if key[z] < key[y]
then left[y] ← z // 情况2若“z所包含的值” < “y所包含的值”则将z设为“y的左孩子”
else right[y] ← z // 情况3(“z所包含的值” >= “y所包含的值”)将z设为“y的右孩子”
left[z] ← nil[T] // z的左孩子设为空
right[z] ← nil[T] // z的右孩子设为空。至此已经完成将“节点z插入到二叉树”中了。
color[z] ← RED // 将z着色为“红色”
RB-INSERT-FIXUP(T, z) // 通过RB-INSERT-FIXUP对红黑树的节点进行颜色修改以及旋转让树T仍然是一颗红黑树
```
* 使用改善伪代码
```
RB-INSERT-FIXUP(T, z)
while color[p[z]] = RED // 若“当前节点(z)的父节点是红色”,则进行以下处理。
do if p[z] = left[p[p[z]]] // 若“z的父节点”是“z的祖父节点的左孩子”则进行以下处理。
then y ← right[p[p[z]]] // 将y设置为“z的叔叔节点(z的祖父节点的右孩子)”
if color[y] = RED // Case 1条件叔叔是红色
then color[p[z]] ← BLACK ▹ Case 1 // (01) 将“父节点”设为黑色。
color[y] ← BLACK ▹ Case 1 // (02) 将“叔叔节点”设为黑色。
color[p[p[z]]] ← RED ▹ Case 1 // (03) 将“祖父节点”设为“红色”。
z ← p[p[z]] ▹ Case 1 // (04) 将“祖父节点”设为“当前节点”(红色节点)
else if z = right[p[z]] // Case 2条件叔叔是黑色且当前节点是右孩子
then z ← p[z] ▹ Case 2 // (01) 将“父节点”作为“新的当前节点”。
LEFT-ROTATE(T, z) ▹ Case 2 // (02) 以“新的当前节点”为支点进行左旋。
color[p[z]] ← BLACK ▹ Case 3 // Case 3条件叔叔是黑色且当前节点是左孩子。(01) 将“父节点”设为“黑色”。
color[p[p[z]]] ← RED ▹ Case 3 // (02) 将“祖父节点”设为“红色”。
RIGHT-ROTATE(T, p[p[z]]) ▹ Case 3 // (03) 以“祖父节点”为支点进行右旋。
else (same as then clause with "right" and "left" exchanged) // 若“z的父节点”是“z的祖父节点的右孩子”将上面的操作中“right”和“left”交换位置然后依次执行。
color[root[T]] ← BLACK
```
* 根据被插入节点的父节点的情况,可以将"当节点z被着色为红色节点并插入二叉树"划分为三种情况来处理。
* 情况说明:被插入的节点是根节点。处理方法:直接把此节点涂为黑色。
* 情况说明:被插入的节点的父节点是黑色。处理方法:什么也不需要做。节点被插入后,仍然是红黑树。
* 情况说明:被插入的节点的父节点是红色。处理方法:那么,该情况与红黑树的“特性(4)”相冲突。这种情况下,被插入节点是一定存在非空祖父节点的;进一步的讲,被插入节点也一定存在叔叔节点(即使叔叔节点为空,我们也视之为存在,空节点本身就是黑色节点)。理解这点之后,我们依据"叔叔节点的情况"将这种情况进一步划分为3种情况(Case)。
### 删除
## 3 旋转
### 左旋
* 失去平衡,逆时针旋转
![](2021-03-13-14-43-51.png)
```
LEFT-ROTATE(T, x)
y ← right[x] // 前提这里假设x的右孩子为y。下面开始正式操作
right[x] ← left[y] // 将 “y的左孩子” 设为 “x的右孩子”即 将β设为x的右孩子
p[left[y]] ← x // 将 “x” 设为 “y的左孩子的父亲”即 将β的父亲设为x
p[y] ← p[x] // 将 “x的父亲” 设为 “y的父亲”
if p[x] = nil[T]
then root[T] ← y // 情况1如果 “x的父亲” 是空节点则将y设为根节点
else if x = left[p[x]]
then left[p[x]] ← y // 情况2如果 x是它父节点的左孩子则将y设为“x的父节点的左孩子”
else right[p[x]] ← y // 情况3(x是它父节点的右孩子) 将y设为“x的父节点的右孩子”
left[y] ← x // 将 “x” 设为 “y的左孩子”
p[x] ← y // 将 “x的父节点” 设为 “y”
```
### 右旋
* 失去平衡,向右旋转
![](2021-03-13-14-44-34.png)
```
RIGHT-ROTATE(T, y)
x ← left[y] // 前提这里假设y的左孩子为x。下面开始正式操作
left[y] ← right[x] // 将 “x的右孩子” 设为 “y的左孩子”即 将β设为y的左孩子
p[right[x]] ← y // 将 “y” 设为 “x的右孩子的父亲”即 将β的父亲设为y
p[x] ← p[y] // 将 “y的父亲” 设为 “x的父亲”
if p[y] = nil[T]
then root[T] ← x // 情况1如果 “y的父亲” 是空节点则将x设为根节点
else if y = right[p[y]]
then right[p[y]] ← x // 情况2如果 y是它父节点的右孩子则将x设为“y的父节点的左孩子”
else left[p[y]] ← x // 情况3(y是它父节点的左孩子) 将x设为“y的父节点的左孩子”
right[x] ← y // 将 “y” 设为 “x的右孩子”
p[y] ← x // 将 “y的父节点” 设为 “x”
```
## 4 实现
```
```

View File

@@ -3,21 +3,25 @@
## 1 简介
### 概念
* 大规模数据存储中二叉树节点存储的元素数量是有限的如果元素数量非常多的话查找就退化成节点内部的线性查找了这样导致二叉查找树结构由于树的深度过大而造成磁盘I/O读写过于频繁进而导致查询效率低下
* 如何减少树的深度,一个基本的想法就是:采用多叉树结构(由于树节点元素数量是有限的,自然该节点的子树数量也就是有限的)。
* 多路查找树。根据平衡二叉树的启发使用平衡多路查找树结构也就是这篇文章所要阐述的第一个Btree。
* B 树又叫平衡多路查找树。一棵m阶的B 树 的特性如下:
* B树中的每个节点最多包含m个子节点。最多包含m-1个键。
* 除根节点和叶节点外B树中的每个节点至少包含[m/2]的上界个子节点。
* 根节点必须至少有2个节点。
* 所有叶节点必须处于同一层
* 每个非终端结点中包含有n个键。每个键按顺序升序排序
* B 树又叫平衡多路查找树。一棵m阶的B树定义如下:
1. B树中的每个节点最多包含m个子节点。最多包含m-1个键。
2. 除根节点和叶节点外B树中的每个节点至少包含[m/2](向上取整)个子节点。
3. 根节点要么是空、要么是独自的根、要么必须至少有2个节点。
4. 有k个子节点的节点必有k-1个键。每个键按顺序升序排序
5. 所有叶节点必须处于同一层(水平)
* 4阶B树如下
![](2021-03-12-23-35-36.png)
### 应用
* 大规模数据存储中二叉树节点存储的元素数量是有限的如果元素数量非常多的话查找就退化成节点内部的线性查找了这样导致二叉查找树结构由于树的深度过大而造成磁盘I/O读写过于频繁进而导致查询效率低下
* 如何减少树的深度,一个基本的想法就是:采用多叉树结构(由于树节点元素数量是有限的,自然该节点的子树数量也就是有限的)。
* 多路查找树。根据平衡二叉树的启发使用平衡多路查找树结构也就是这篇文章所要阐述的第一个Btree。
### 分类
* B树
@@ -26,6 +30,14 @@
## 2 操作
### 基础操作
* 创建
* 遍历和搜索
* 插入
* 删除
### 创建
@@ -56,7 +68,7 @@
![](2021-03-12-23-47-41.png)
* 8将插入5的右侧因此插入8。
![](2021-03-12-23-47-46.png)
* 该节点现在包含5个键大于(5 -1 = 4)个键。 因此将节点从中间分开即8并将其推到其父节点如下所示。
* 该节点现在包含5个键大于(5 -1 = 4)个键。 因此将节点从中间分开即8并将其推到其父节点如下所示。(图有问题)
![](2021-03-12-23-47-52.png)
### 删除

57
数据结构/6.6 B+树.md Normal file
View File

@@ -0,0 +1,57 @@
# B+树
> 参考文献
> * [B树B+树](https://www.cnblogs.com/lianzhilei/p/11250589.html)
## 1 简介
### 概念
> 像是在建立额外的索引
1. 有m个子树的中间节点包含有m个元素B树中是k-1个元素每个元素不保存数据只用来索引
2. 所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接。 (而B 树的叶子节点并没有包括全部需要查找的信息)
3. 所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (而B 树的非终节点也包含需要查找的有效信息)
### 优点
* B+树的磁盘读写代价更低
* B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了
* B+树查询效率更加稳定
* 由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当;
* B+树便于范围查询(最重要的原因,范围查找是数据库的常态)
* B树在提高了IO性能的同时并没有解决元素遍历的我效率低下的问题正是为了解决这个问题B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的而B树不支持这样的操作或者说效率太低不懂可以看看这篇解读-》范围查找
## 2 操作
### 搜索
### 插入
* 第1步 :将新节点作为叶节点插入。
* 第2步 :如果叶子没有所需空间,则拆分节点并将中间节点复制到下一个索引节点。
* 第3步 :如果索引节点没有所需空间,则拆分节点并将中间元素复制到下一个索引节点
> 实例
* 将值195插入到下图所示的5阶B+树中。
![](2021-03-12-23-58-07.png)
* 在190之后将在右子树120中插入195。将其插入所需位置。
![](2021-03-12-23-58-14.png)
* 该节点包含大于最大的元素数量即4因此将其拆分并将中间节点放置到父节点。
![](2021-03-12-23-58-20.png)
* 现在索引节点包含6个子节点和5个键违反B+树属性因此需要将其拆分如下所示。将108提为根节点。60,78左子节点120,190右子节点。
![](2021-03-12-23-58-29.png)
### 删除
* 第1步从叶子中删除键和数据。
* 第2步如果叶节点包含少于最小数量的元素则将节点与其兄弟节点合并并删除它们之间的键。
* 第3步如果索引节点包含少于最小数量的元素则将节点与兄弟节点合并并在它们之间向下移动键。
> 示例
* 从下图所示的B+树中删除键为200。
![](2021-03-13-00-03-01.png)
* 在195之后的190的右子树中存在200删除它。
![](2021-03-13-00-03-07.png)
* 使用195,190,154和129合并两个节点。
![](2021-03-13-00-03-13.png)
* 现在元素120是节点中存在的违反B+树属性的单个元素。 因此需要使用60,78,108和120来合并它。现在B+树的高度将减1。
![](2021-03-13-00-03-30.png)

113
数据结构/6.8 堆树.md Normal file
View File

@@ -0,0 +1,113 @@
# 堆
> * 二叉搜索树、多路搜索树都是左小右大。堆树是上下大小不一样。
> * 二叉搜索树是从根节点向叶子节点构造。进行插入。堆树是从叶子节点,向根节点进行构造。
> 参考文献
> * [堆,但是有很严重的错误](https://blog.csdn.net/qq_34270874/article/details/113091364)
## 1 简介
1. 堆是一颗完全二叉树;
2. 堆中的某个结点的值总是大于等于(最大堆)或小于等于(最小堆)其孩子结点的值。
3. 堆中每个结点的子树都是堆树
![](2021-03-13-15-23-14.png)
![](2021-03-13-15-23-24.png)
* 使用数组表示的堆树
![](2021-03-13-15-31-06.png)
### 应用
* 堆结构的一个常见应用是建立优先队列Priority Queue
![](2021-03-13-15-25-15.png)
* 普通树占用的内存空间比它们存储的数据要多。你必须为节点对象以及左/右子节点指针分配内存。堆仅仅使用一个数据来存储数组,且不使用指针。
## 2 操作
### 基本操作
* 上浮
* 下沉
* 取顶
* 创建:把一个乱序的数组变成堆结构的数组,时间复杂度为 $O(n)$
* 插入:把一个数值放进已经是堆结构的数组中,并保持堆结构,时间复杂度为 $O(log N)$
* 删除:从最大堆中取出最大值或从最小堆中取出最小值,并将剩余的数组保持堆结构,时间复杂度为 $O(log N)$。
* 堆排序:借由 HEAPFY 建堆和 HEAPPOP 堆数组进行排序,时间复杂度为$O(N log N)$,空间复杂度为 $O(1)$。
### 上浮-向上调整
> 最小堆为例
* 向上调整 是让调整的结点与其父亲结点进行比较。从**当前节点递归处理到根节点**。
1. 先设定倒数的第一个叶子节点为当前节点(通过下标获取标记为cur)找出他的父亲节点用parent来标记。
2. 比较parent和cur的值如果cur比parent小则不满足小堆的规则需要进行交换。
3. 如果cur比parent大满足小堆的规则不需要交换调整结束。
4. 处理完一个节点之后从当前的parent出发循环之前的过程。
* 向上调整算法 小堆
* 向上调整算法 大堆
* **上浮能够保证路径上最好的值,上浮到根节点。**
### 下沉-向下调整
> 最小堆为例
* 向下调整 是让调整的结点与其孩子节点进行比较。从**当前节点递归处理到叶节点**。
1. 先设定根节点为当前节点(通过下标获取标记为cur)比较左右子树的值找出更小的值用child来标记。
2. 比较child和cur的值如果child比cur小则不满足小堆的规则需要进行交换。
3. 如果child比cur大满足小堆的规则不需要交换调整结束。
4. 处理完一个节点之后从当前的child出发循环之前的过程
* 向下调整(小堆)示例:
![](2021-03-13-15-43-40.png)
* 向下调整(大堆)示例:
![](2021-03-13-15-43-53.png)
* **下沉不能保证路径上最坏的值下放到叶节点。**
### 创建
> 类似于冒泡的思想,但是是一中更快的冒泡。保证最后能够建立最好的堆。
* 从根节点向叶节点开始,从后往前开始。进行多次上浮操作。
* 从叶节点向根节点开始,从后往前开始。进行多次下沉操作。
### 插入(堆尾的数据)
* 将数据插入到数组最后,再进行向上调整。
![](2021-03-13-16-12-10.png)
### 删除(堆顶的数据)
* 删除堆是删除堆顶的数据。将堆顶的数据和最后一个数据交换,然后删除数组最后一个数据,再进行向下调整算法
![](2021-03-13-17-02-40.png)
### 堆排序
* 堆排序
1. 将待排序序列构造成一个大顶堆。
2. 此时,整个序列的最大值就是堆顶的根节点。
3. 将其与末尾元素进行交换,此时末尾就为最大值。
4. 然后将剩余n-1个元素重新构造成一个堆从根节点开始进行一次向下调整这样会得到n-1个元素的次小值。如此反复执行便能得到一个有序序列了。
## 3 堆的实现
```
struct MaxHeap
{
EType *heap; //存放数据的空间下标从1开始存储数据下标为0的作为工作空间存储临时数据。
int MaxSize; //MaxSize是存放数据元素空间的大小
int HeapSize; //HeapSize是数据元素的个数
};
MaxHeap H;
```

View File

@@ -1,81 +0,0 @@
# 哈希表
> 关键词: hash, 哈希表, 哈希函数
<!-- TOC depthFrom:2 depthTo:2 -->
- [简介](#简介)
- [原理](#原理)
- [更多内容](#更多内容)
<!-- /TOC -->
## 简介
---
`哈希表`是一种使用`哈希函数`组织数据,以支持快速插入和搜索的数据结构。
有两种不同类型的哈希表:哈希集合和哈希映射。
- `哈希集合``集合`数据结构的实现之一,用于存储`非重复值`
- `哈希映射``映射` 数据结构的实现之一,用于存储`(key, value)`键值对。
`标准模板库`的帮助下,哈希表是`易于使用的`。大多数常见语言(如 JavaC ++ 和 Python都支持哈希集合和哈希映射。
通过选择合适的哈希函数,哈希表可以在插入和搜索方面实现`出色的性能`
## 原理
---
哈希表的关键思想是使用哈希函数将键映射到存储桶。更确切地说,
1. 当我们插入一个新的键时,哈希函数将决定该键应该分配到哪个桶中,并将该键存储在相应的桶中;
2. 当我们想要搜索一个键时,哈希表将使用相同的哈希函数来查找对应的桶,并只在特定的桶中进行搜索。
### 哈希函数示例
![img](http://dunwu.test.upcdn.net/cs/data-structure/hash/哈希函数.png!zp)
在示例中,我们使用 y = x 5 作为哈希函数。让我们使用这个例子来完成插入和搜索策略:
1. 插入:我们通过哈希函数解析键,将它们映射到相应的桶中。
- 例如1987 分配给桶 2而 24 分配给桶 4。
2. 搜索:我们通过相同的哈希函数解析键,并仅在特定存储桶中搜索。
- 如果我们搜索 1987我们将使用相同的哈希函数将 1987 映射到 2。因此我们在桶 2 中搜索,我们在那个桶中成功找到了 1987。
- 例如,如果我们搜索 23将映射 23 到 3并在桶 3 中搜索。我们发现 23 不在桶 3 中,这意味着 23 不在哈希表中。
### 哈希表的关键
#### 1. 哈希函数
哈希函数是哈希表中最重要的组件,该哈希表用于将键映射到特定的桶。在上一节的示例中,我们使用 `y = x % 5` 作为散列函数,其中 `x` 是键值,`y` 是分配的桶的索引。
散列函数将取决于`键值的范围``桶的数量。`
下面是一些哈希函数的示例:
![img](http://dunwu.test.upcdn.net/cs/data-structure/hash/哈希函数示例.png!zp)
哈希函数的设计是一个开放的问题。其思想是尽可能将键分配到桶中,理想情况下,完美的哈希函数将是键和桶之间的一对一映射。然而,在大多数情况下,哈希函数并不完美,它需要在桶的数量和桶的容量之间进行权衡。
#### 2. 冲突解决
理想情况下如果我们的哈希函数是完美的一对一映射我们将不需要处理冲突。不幸的是在大多数情况下冲突几乎是不可避免的。例如在我们之前的哈希函数_y = x 5_1987 和 2 都分配给了桶 2这是一个`冲突`
冲突解决算法应该解决以下几个问题:
1. 如何组织在同一个桶中的值?
2. 如果为同一个桶分配了太多的值,该怎么办?
3. 如何在特定的桶中搜索目标值?
根据我们的哈希函数,这些问题与`桶的容量`和可能映射到`同一个桶``键的数目`有关。
让我们假设存储最大键数的桶有 `N` 个键。
通常,如果 _N_ 是常数且很小,我们可以简单地使用一个数组将键存储在同一个桶中。如果 _N_ 是可变的或很大,我们可能需要使用`高度平衡的二叉树`来代替。
## 更多内容
https://leetcode-cn.com/explore/learn/card/hash-table/