## 1.1 什么是数据结构
* 基本上每一个新的名词出现,都要了解一下它,我的理解就是数据结构是由硬件和软件及数学进行交互产生的,对待实际的问题,都有不同的结构。很抽象,每个人有每个人的理解,大致相近就行了。百度上的答案是:==数据结构是一种具有一定逻辑关系,在计算机中应用某种存储结构,并且封装了相应操作的数据元素的集合。它包含三方面的内容,逻辑关系、存储关系以及操作。==
==暂时没有合适的图,后面补==
## 1.2数据结构的基本概念和术语
* 1、数据:
对客观事物的符号表示。简单理解就是所有能输入到计算机中并被计算机程序处理的符号的总称。(举个例子嘛输入的数字,字符,等等。==值得强调的是数据的定义很广泛,图像、声音等也算数据。==)
* 2、数据元素:
==数据元素是数据的基本单位==,在计算机程序中通常作为一个整体进行考虑和处理。但数据元素还不是最小的单位。数据元素有时是由多个数据项组成的,而==数据项是数据的最小单位==。为了更好的理解,我举两个例子。(1)人口普查时,一个家庭代表一个数据元素,每个人代表一个数据项,一家有三口人,那么这个数据元素(一个家庭)就有三个数据项(三口人)。(2)你要给10个数根据大小来进行排序,那么每一个数都是你的数据元素。
* 3、数据对象:
数据对象是==性质相同==的数据元素的集合,是数据的一个子集。
* 4、数据结构:
* 数据结构分为对操作对象的数学描述即逻辑结构,以及在计算机中的表示(又称影响)即物理结构(又称储存结构)。
**==逻辑结构==**
==书上的正规简单解释==是数据结构是相互之间存在一种或多种特定关系的数据元素的集合。数据结构有4种基本结构,分别是(1)集合(2)线性结构(3)树形结构(4)图状结构或网状结构。
(1)集合:结构中的数据元素==除了”同属于一个集合“的关系外,别无其他的关系。==
==暂时没有合适的图,后期补==
(2)线性结构:结构中的数据元素之间存在==一个元素对一个元素==的关系。
==暂时没有合适的图,后面补==
(3)树型结构:结构中的数据元素之间存在==一个元素对多个元素==的关系。
==暂时没有合适的图,后面补==
(4)图状结构或网状结构:结构中的数据元素之间存在==多个元素对多个元素==的关系。
==暂时没有合适的图,后面补==
以上我们是数据结构中逻辑结构。
**==物理结构(存储结构)==**
在计算机中表示信息的最小单位是二进制数的一位,叫做位(bit),我们可以用若干个位组合成一个位串表示一个数据元素,通常称这个位串为元素或节点,当数据元素由多个数据项组成的时侯,每个数据项对应的子位串被成为数据域。
数据元素之间的关系在计算机中有两种不同的表示方法:顺序映像和非顺序映像,并由此得到两种不同的储存结构,顺序储存结构和链式储存结构。
简单的理解一下,顺序储存结构就是在计算机的储存空间中,我们是按照顺序进行储存,而链式储存结构则不然,==链式储存结构是利用指针进行逻辑上的相连,在储存位置上并不要求连续。==
* 5、数据类型:
数据类型是一个值的集合和定义在这个值集上的一组操作的总称。
按照值的不同可以分为两类==(1)原子类型(2)结构类型==
(1)原子类型:原子类型的==值不可分解==,如基本类型(整形,实型,字符型...)。
(2)结构类型:由==多种类型按照一定结构组成,可分解==,成分可以是非结构的也可以是结构的。(例如c语言中的结构体)。
* 6、抽象数据类型(简称ADT):
抽象数据类型是指一个数学模型以及定义在该模型上的一组操作,且不论其内部结构如何变化,只要他的数学特性不变,都不影响其外部使用。(我的理解就是抽象数据类型就类似于c语言的函数,函数内部你怎么写都可以,只要输出的结果一致就行。)
抽象数据类型可以分为3种类型==(1)原子类型(2)固定聚合类型(3)可变聚合类型==
(1)原子类型:属原子类型的变量的值是不可分解的。
(2)固定聚合类型:属该类型的变量,其值由确定数目的成分按某种结构组成
(3)可变聚合类型:和固定聚合类型相比较,构成可变聚合类型”值“的成分的数目不确定
多型数据类型:指其值的成分不确定的数据类型。
## 1.3抽象数据类型的表示与实现
这里的知识基本都是实际操作,不作示例,将在跟进的c++学习笔记种使用。
## 1.4算法和算法分析
* 算法是对特定问题求解步骤的一种描述,它是指令的有限序列,其中每一条指令表示一个或多个操作,此外算法有五个重要特征:(1)有穷性(2)确定性(3)可行性(4)输入(5)输出
(1)有穷性:一个算法必须是(对任何而合法的输入值)在执行的有穷部之后结束,且每一步都可在有穷时间内完成。
(2)确定性:算法中的每一条指令必须有确切的含义,读者理解是不会产生二义性。并且算犯法只有唯一的执行路径,对于相同的输入只能得出相同的输出。
(3)可行性:一个算法是能行 ,即算法描述的操作都是可以通过以及实现的基本运算执行有限次来实现。
(4)输入:一个算法有零个或多个的输入,这些输入取自于某个特定的对象的集合。
(5)输出:一个算法有一个或多个的输出,这些输出是同输入有着某些特定关系的量。
一个算法要是”好“算法必须考虑以下4点:(1)正确性(2)可读性(3)健壮性(4)效率与低储存需求
(1)正确性:算法应当满足具体问题的需求
(2)可读性:算法是为了人的阅读与交流,其次才是机器执行。
(3)健壮性:简单来说就是当你的输入并未按照你的需求时,你的代码能做出反应,告诉输入的人,他并没有按照正确的输入格式,而不是胡乱的输出莫名其妙的值。
(4)效率与低储存需求:通俗的说效率就是指算法执行的时间。
*算法分析:
着重讲时间复杂度:表示随问题规模n的增大,算法执行时间的增长率和重复执行次数增长率相同。
* ==具体计算机运行的时间复杂度日后在此处弥补==
# 数据结构(c语言版) 第二章 线性表
## 2.1线性表的类型定义
* 线性表:
线性表是最常用且最简单的一种数据结构。
在稍复杂的线性表中,我们常把数据元素称为记录,含有大量记录的线性表又称为文件。
线性表中的数据元素可以是各种各样的,但==同一线性表中的元素必定具有相同特性。 ==
## 2.2线性表的顺序表示和实现
* 线性表的顺序表示指的是用一组==地址连续==的存储单元==依次==存储线性表的数据元素。
直接上代码:
### 首先是用结构体构造你需要的节点
```cpp
struct node{ //构造线性表的顺序结构的节点(数据元素)
int *elem; //注意你要存储什么类型的数据就定义怎样的指针
int len; //表中实际使用的长度
int listlen; //表被分配的长度
};
```
### 然后是创建顺序结构的线性表
```cpp
void creatlist(node *list,int llen){ //创建顺序结构的线性表,向函数中传输两个参数,
list->elem = (int *)malloc(llen*sizeof(int)); //一个是即将创建的线性表 ,一个是表的长度。
if(!list->elem){
printf("空间分配失败!");
return;
}else{
list->len = 0;
list->listlen = llen;
}
}
```
### 创建链表的顺序结构后坑定要向链表中添加元素,所以需要插入元素的代码
```cpp
void insertnode(node list,int insertplace,int element){ //这里是顺序线性表的插入insertplace是插入的位置,element是要插入的数据,首先要检测
if(list.len+1>list.listlen){ //首先要检测线性表的长度是不是能够满足插入后所需要的长度
int *newbase = (int*)realloc(list.elem,list.listlen+1*sizeof(int)); //不满足时要增加空间,用realloc函数增加
if(newbase) list.elem = newbase;
}
for(int i = list.len+1;i > insertplace;i --){ //将从插入位置开始的数据依次向后移动一位
*(list.elem+i) = *(list.elem+i-1);
}
*(list.elem+insertplace) = element; //在需要的位置插入数据
list.len += 1; //别忘记插入成功后把线性表实际使用的长度+1
}
```
==这段代码就是向你想要添加元素的位置进行元素的添加==
### 顺序结构的特点——地址连续
```cpp
node testlist; //定义一个测试线性表
creatlist(&testlist,100); //开始创建个长度为100的线性表
for(int i = 0;i < 10;i ++){ //简单的输入十个数,因为是学习,就不放太多
scanf("%d",testlist.elem+i);//值得注意的是,与书上的格式不同,这里是elem+i
testlist.len ++;
} //要学会指针啊!!
for(int i = 0;i < 10;i ++){
printf("%p\n",testlist.elem+i);//这里是输出分配的地址
}
```
我们向其中输入十个数并得到这十个数的地址
![这里可以看到,每一个地址都是连续的,他们之间只差了一个int的大小](https://img-blog.csdnimg.cn/2e86be9392c3444193389c49604ffec0.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzUyNDE4Mjg5,size_16,color_FFFFFF,t_70#pic_center)
* 这里可以看到,每一个地址都是连续的,他们之间只差了一个int(==你的表中数据元素的格式是什么,这里就是什么==)的大小,这对应了上面对线性表顺序结构的介绍——==地址连续.==
### 能输入必然能输出
* 我们输入了十个数,就可以输出这十个数
```cpp
node testlist; //定义一个测试线性表
creatlist(&testlist,100); //开始创建个长度为100的线性表
for(int i = 0;i < 10;i ++){ //简单的输入十个数,因为是学习,就不放太多
scanf("%d",testlist.elem+i);//值得注意的是,与书上的格式不同,这里是elem+i
testlist.len ++;
} //要学会指针啊!!
for(int i = 0;i < 10;i ++){
printf("%d ",*(testlist.elem+i));//这里是输出我们刚刚输入的十个数
}
```
看一下输出的结果是不是相同
![在这里插入图片描述](https://img-blog.csdnimg.cn/e89111907b74426f9160233b0cbb8ada.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzUyNDE4Mjg5,size_16,color_FFFFFF,t_70#pic_center)
* 显然这里的输入输出是相同的,证明我们的线性表的顺序结构代码是对的。
### 前面还有一个插入模块
```cpp
node testlist; //定义一个测试线性表
creatlist(&testlist,100); //开始创建个长度为100的线性表
for(int i = 0;i < 10;i ++){ //简单的输入十个数,因为是学习,就不放太多
scanf("%d",testlist.elem+i);//值得注意的是,与书上的格式不同,这里是elem+i
testlist.len ++;
} //要学会指针啊!!
insertnode(testlist,5,100); //接着上面的测试,我们将测试线性表的第6个位置插入100,值得提醒的是,我们是从0开始储存,第六个位置的下表就是5
for(int i = 0;i < testlist.len;i ++){
printf("%d ",*(testlist.elem+i)); //输出插入后的线性表
}
```
* 看一下插入结果
![在这里插入图片描述](https://img-blog.csdnimg.cn/fc91728a58844669b92d04c50cc388c4.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzUyNDE4Mjg5,size_16,color_FFFFFF,t_70#pic_center)
* 数据元素成功添加进入线性表,证明插入模块没有问题。
* 最后将完整代码写入
```cpp
#include <stdio.h>
#include <stdlib.h>
#include <bits/stdc++.h>
using namespace std;
struct node{ //构造线性表的顺序结构的节点(数据元素)
int *elem; //注意你要存储什么类型的数据就定义怎样的指针
int len; //表中实际使用的长度
int listlen; //表被分配的长度
};
void creatlist(node *list,int llen){ //创建顺序结构的线性表,向函数中传输两个参数,
list->elem = (int *)malloc(llen*sizeof(int)); //一个是即将创建的线性表 ,一个是表的长度。
if(!list->elem){
printf("空间分配失败!");
return;
}else{
list->len = 0;
list->listlen = llen;
}
}
void insertnode(node list,int insertplace,int element){ //这里是顺序线性表的插入insertplace是插入的位置,element是要插入的数据,首先要检测
if(list.len+1>list.listlen){ //首先要检测线性表的长度是不是能够满足插入后所需要的长度
int *newbase = (int*)realloc(list.elem,list.listlen+1*sizeof(int)); //不满足时要增加空间,用realloc函数增加
if(newbase) list.elem = newbase;
}
for(int i = list.len+1;i > insertplace;i --){ //将从插入位置开始的数据依次向后移动一位
*(list.elem+i) = *(list.elem+i-1);
}
*(list.elem+insertplace) = element; //在需要的位置插入数据
list.len += 1; //别忘记插入成功后把线性表实际使用的长度+1
}
int main(){
node testlist; //定义一个测试线性表
creatlist(&testlist,100); //开始创建个长度为100的线性表
for(int i = 0;i < 10;i ++){ //简单的输入十个数,因为是学习,就不放太多
scanf("%d",testlist.elem+i);//值得注意的是,与书上的格式不同,这里是elem+i
testlist.len ++;
} //要学会指针啊!!
for(int i = 0;i < 10;i ++){
printf("%p\n",testlist.elem+i);//这里是输出分配的地址
}
for(int i = 0;i < 10;i ++){
printf("%d ",*(testlist.elem+i));//这里是输出我们刚刚输入的十个数
}
insertnode(testlist,5,100); //接着上面的测试,我们将测试线性表的第6个位置插入100,值得提醒的是,我们是从0开始储存,第六个位置的下表就是5
for(int i = 0;i < testlist.len;i ++){
printf("%d ",*(testlist.elem+i)); //输出插入后的线性表
}
}
```
## 2.3线性表的链式表示和实现
* 链式结构的节点特色:
链表的每个节点包括两个域:(1)数据域(2)指针域
(1)数据域:是存储数据元素信息的域。
(2)指针域:直接储存后继节点位置的域。
* 利用结构体创建链式结构的节点
```cpp
struct node{ //定义线性表的链式存储结构的节点,其中包括一个数据域date和一个指针域next.
int date;
node *next;
};
```
* 链表的创建,从表头开始
```cpp
struct node* creatlist(){ //创建一个链表的表头,表头的数据域一般我们不使用
struct node *nodehead = (struct node*)malloc(sizeof(struct node)); //给表头分配空间,虽然数据域不使用,但还是要分配他的空间
nodehead->next = NULL; //将表头的指针域初始化
return nodehead;
}
```
* 创建节点并且向节点中传输数据
```cpp
struct node* creatnode(int dates){
struct node *nodes = (struct node*)malloc(sizeof(struct node));//给每一个节点分配空间
nodes->date = dates;
nodes->next = NULL;
return nodes;
}
```
* 头插法
```cpp
struct node* insertnodehead(int dates,node* nodehead){ //这是头插法
struct node *noded = creatnode(dates);
noded->next = nodehead->next; //给将插入的节点的指针域指向原本头指针指向的后继节点
nodehead->next = noded; //将头节点的指针域指向插入的节点
return noded;
}
```
* 尾插法
```cpp
struct node* insertnodelast(int dates,node* nodehead){ //这是尾插法
struct node *noded = creatnode(dates);
struct node *nodes = nodehead; //先令一个标记指针指向头节点的位置
while(nodes->next){ //再寻找链表的尾部
nodes = nodes->next;
}
nodes->next = noded; //将链表尾部的next指针指向要插入的节点
return noded;
}
```
* 删除链表中你要删除的节点
```cpp
void deletnode(struct node*nodehead,int i){ //删除其中一个节点
node *nodes = nodehead;
for(int j = 1;j < i;j ++){ //找到要删除的位置
nodes = nodes->next;
}
node*nodeds = nodes->next;
nodes->next = nodeds->next;
free(nodeds); //释放删除节点的空间fb
}
```
* 打印链表中你储存的数据
```cpp
void printnode(struct node*nodehead){ //输入链表中的数
struct node*pmove = nodehead->next;
while(pmove){ //令标记指针移动一个一个的向后输出链表的数据域,直到尾节点。
printf("%d ",pmove->date);
pmove = pmove->next;
}
}
```
* 这是完整的线性表的链式表示于实现
```cpp
#include <bits/stdc++.h>
using namespace std;
struct node{ //定义线性表的链式存储结构的节点,其中包括一个数据域date和一个指针域next.
int date;
node *next;
};
struct node* creatlist(){ //创建一个链表的表头,表头的数据域一般我们不使用
struct node *nodehead = (struct node*)malloc(sizeof(struct node)); //给表头分配空间,虽然数据域不使用,但还是要分配他的空间
nodehead->next = NULL; //将表头的指针域初始化
return nodehead;
}
struct node* creatnode(int dates){
struct node *nodes = (struct node*)malloc(sizeof(struct node));//给每一个节点分配空间
nodes->date = dates;
nodes->next = NULL;
return nodes;
}
struct node* insertnodehead(int dates,node* nodehead){ //这是头插法
struct node *noded = creatnode(dates);
noded->next = nodehead->next; //给将插入的节点的指针域指向原本头指针指向的后继节点
nodehead->next = noded; //将头节点的指针域指向插入的节点
return noded;
}
struct node* insertnodelast(int dates,node* nodehead){ //这是尾插法
struct node *noded = creatnode(dates);
struct node *nodes = nodehead; //先令一个标记指针指向头节点的位置
while(nodes->next){ //再寻找链表的尾部
nodes = nodes->next;
}
nodes->next = noded; //将链表尾部的next指针指向要插入的节点
return noded;
}
void deletnode(struct node*nodehead,int i){ //删除其中一个节点
node *nodes = nodehead;
for(int j = 1;j < i;j ++){ //找到要删除的位置
nodes = nodes->next;
}
node*nodeds = nodes->next;
nodes->next = nodeds->next;
free(nodeds); //释放删除节点的空间fb
}
void printnode(struct node*nodehead){ //输入链表中的数
struct node*pmove = nodehead->next;
while(pmove){ //令标记指针移动一个一个的向后输出链表的数据域,直到尾节点。
printf("%d ",pmove->date);
pmove = pmove->next;
}
}
int main(){
node *list =creatlist();
for(int i = 1;i <= 4;i ++){
int x;
scanf("%d",&x);
//insertnodehead(x,list);
insertnodelast(x,list);
}
deletnode(list,2); //删除了第二个节点的数据。
printnode(list);
}
```
* 让我们输入四个数看一下打印结果
![在这里插入图片描述](https://img-blog.csdnimg.cn/1d2130a1c1fd4bffa995e675120ffa21.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzUyNDE4Mjg5,size_16,color_FFFFFF,t_70#pic_center)
显然我们输入1~4能够正常输出,线性表的链式结构成功实现
* 接下来我们试一下删除节点的功能,输入四个数,并且删除第二个节点
![在这里插入图片描述](https://img-blog.csdnimg.cn/89056839439f4b20a93aa6941cd9aade.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzUyNDE4Mjg5,size_16,color_FFFFFF,t_70#pic_center)
显然也达到了我们的预期效果,那么删除功能也成功实现
* 顺序结构与链式结构的线性表的区别:
链式线性表相比于顺序线性表的特殊点:
线性表的链式存储结构的特点是==用一组任意的存储单元存储线性表的数据元素==(这组存储单元可以是连续的也可以是不连续的)。
## 2.4静态链表的表示和实现
* 静态链表:
和链表相比,其实就是用数组来表示,其结构体是由一个整型和任意一个存储类型构成,其中任意一个存储类型用于存储你所需要存储的数据,而另一个整型用于模拟链表的指针域,所以叫做静态链表。
* 定义静态链表的节点的结构体
```cpp
struct unwork{ //静态链表与链表类似,分为数据域date,还有一个类似于一维数组下标的cur,此时的cur扮演了
char date[20]; //指针域next的角色
int cur;
};
```
* 向静态链表中加入数据
```cpp
void insertxnode(unwork *list,int place,char x[]){ //向静态链表中插入一个数据
int len = sizeof(linklist);
for(int i = 1;i < len;i ++){ //需要找到能够插入的地方
if(!list[i].cur){
strcpy(list[i+1].date,x);
list[i+1].cur = list[place].cur; //注意插入时,插入的新数据与原来的数据一定要有一个cur为0的点,不然将死循环。
list[place].cur = i+1;
break;
}
}
}
```
* 加入完整的代码
```cpp
#include <bits/stdc++.h>
using namespace std;
struct unwork{ //静态链表与链表类似,分为数据域date,还有一个类似于一维数组下标的cur,此时的cur扮演了
char date[20]; //指针域next的角色
int cur;
};
struct unwork linklist[10];
void insertxnode(unwork *list,int place,char x[]){ //向静态链表中插入一个数据
int len = sizeof(linklist);
for(int i = 1;i < len;i ++){ //需要找到能够插入的地方
if(!list[i].cur){
strcpy(list[i+1].date,x);
list[i+1].cur = list[place].cur; //注意插入时,插入的新数据与原来的数据一定要有一个cur为0的点,不然将死循环。
list[place].cur = i+1;
break;
}
}
}
int main(){
linklist[0].cur = 1;
for(int i = 1;i <= 5;i ++){
linklist[i].cur = i+1;
scanf("%s",&linklist[i].date);
}
int t = 1;
while(linklist[t].cur){
printf("%s ",linklist[t].date);
t = linklist[t].cur;
}
char h[20];
scanf("%s",h);
insertxnode(linklist,2,h);
t = 0;
while(linklist[t].cur){
printf("%s ",linklist[t].date);
t = linklist[t].cur;
}
}
```
静态链表最主要的是理解思想,所以其实实现静态链表的代码并没有很多,后面还有==双向链表,循环链表==,其实都只是在上述三种类型进行一些改动,简单理解后就看可以改动出来。一元多项式就更不需要说了,链表中加几个结构单元就可以了