1.数据:
数据是信息的载体,是描述客观事物属性的数,字符及所有能输入到计算机中被计算机程序识别和处理的符号的集合.
e.g:某些表的合集
1.人员表
姓名 | 年龄 | 性别 | 课程编码 | 身高 |
---|---|---|---|---|
小田 | 20 | 男 | A | 180 |
小李 | 20 | 女 | B | 165 |
小王 | 20 | 男 | C | 185 |
2.课程表
课程编码 | 课程名称 |
---|---|
A | 语文 |
B | 数学 |
C | 英语 |
这两张表的集合就可以称为一项数据
2.数据元素:
数据的基本单位,通常作为一个整体进行考虑和处理.也被称为 记录
e.g:人员表中小田这一行被整体称为是一个数据元素,也可以称作是一条人员记录
3.数据项:
数据元素的基本组成单位,数据项是构成数据元素的不可分割的最小单位.
e.g:人员表中某一列就是组成某一条人员记录的某一个数据项,一个记录由多个数据项组成
4.数据对象:
具有相同性质的数据元素的集合,是数据的一个子集.
相同性质:这个集合里面的数据元素都具有相同个数和相同类型的数据项
e.g:人员表就是一个数据对象
5.数据结构:
相互之间存在一种或多种特定关系的数据元素的集合
e.g:人员表中小王和课程表中课程编码为C的课程,两者存在一定的关系,所以这两个数据元素就可以组成一个数据结构
数据结构的三大要素: 1.逻辑结构 2.存储结构 3.数据运算
解释: 必须存在逻辑结构,存储结构以及数据运算的数据元素集合才能被称为数据结构
数据的逻辑结构分为:1.线性结构 2.非线性结构
线性结构: 数据元素之间就是一个连接一个这种结构 e.g: 线性表
非线性结构:数据元素之间不是一个连接一个这种结构 e.g:树,图
数据的存储结构分为: 1.顺序存储 2.链式存储 3.索引存储 4.散列存储
1.顺序存储: 把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中
优点: 1.可以实现随机存取(查询数据的时间复杂度是O(1))<随机存取:通过下标可以直接找到对应的数据> 2.存储密度大(存储单元全部是数据元素,所以存储密度大)
缺点: 1.增删某一数据元素操作麻烦 2.可能会产生较多的外部碎片(开的是固定的空间,用的可能少)
外部碎片的理解:
2.链式存储:逻辑上相邻的元素不要求物理结构上也相邻,借助指针来表示逻辑关系
优点:1.不会出现碎片现象(用多少开多少空间) 2.增删某一项元素操作简单
缺点:1.存储密度小(存储单元中不全是数据元素,还有指针所需的内存) 2.顺序存取(查询数据的时间复杂度O(n))<顺序存取:读取第n元素的时候要将前n-1个元素全部遍历一遍>
3.索引存储:在存储数据元素信息的同时,还建立附加的索引表,索引表**<一个数据对象>**中的每项称为索引项<数据元素>,索引项的一般形式是关键字与地址<数据项>
优点: 随机存取(查询数据的时间复杂度O(1))
缺点: 1.引入了索引表占用了空间 2.增删数据速度慢<要更改索引表,但索引表很难更改>
形象解释: 所有的数据元素好比书的每一页,现在添加了索引表就相当于给书上增加了一页目录,而且目录是按照顺序来排的
优点: 可以快速的找到每一页的内容
缺点:1.增加了书的厚度 2.增删书的每一页的时候都要将完全改变,这样才能保证增删完的数据,还能拿顺序目录来查找
4.散列存储:在存储数据元素信息的同时,还建立附加的散列表,散列表<一个数据对象>中的每项称为散列项<数据元素>,散列项的一般形式是关键字与hash值<数据项>
优点: 1.随机存取(查询数据的时间复杂度O(1)) 2.增删速度快<要更改散列表,但散列表很好更改>
缺点: 1.占用了空间 2.散列函数不好,可能出现元素存储单元冲突,而解决元素存储单元冲突就得耗时间
形象解释: 所有的数据元素好比书的每一页,现在添加了散列表就相当于给书上增加了一页,但是目录不是按照页面顺序排的,而是每一页经过hash运算得到对应的页数
优点:1.可以快速找到每一页 2.增删书的每一页不太会影响目录,增删完的数据还是可以用随机目录来查找
缺点:1.增加了书的厚度 2.由于用书的每一页来计算对应页数所在目录位置因此可能出现几张页面计算出的页数所在位置是一样的
数据运算分为: 1.运算的定义 2.运算的实现
1.运算的定义: 指出运算功能针对于逻辑结构 ----> 比如push做法的含义就是理论上出栈(逻辑结构)的功能
2.运算的实现: 实现运算功能针对于存储结构 ----> 比如push()函数的含义实现了将顺序表中一个数据元素弹出来的功能
1.相同点: 它们都是存取方式而不是存储方式
2.区别:
3.图形解释:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8EMUnayn-1681012736697)(…/数据结构图片/第一章/随机存取和顺序存取的区别.png)]
输入1A 2D 3B的时候两个存取方式进行的操作
随机存取: 直接读取 1A(你) 然后 2D(吗) 其次 3B(好)
顺序存取:
读取1A: 从1A开始 是 所以读取1A(你)
读取2D: 从1A开始 不是
然后1B 不是
其次1C 不是
…
到了2D 是 所以读取2D(吗)
读取3B: 从1A开始
从1A开始 不是
然后1B 不是
其次1C 不是
…
到了2D 不是
然后3A 不是
最后3B 是 所以读取3B(好)
4.没有索引存取和散列存取这两种存取方式
6.数据类型:
数据类型是一个值的集合和定义在此集合上的一组操作总称
值的集合:数据结构或者数,字符串等集合 —>注: 数据结构就是数据元素<值>的集合
集合上的操作: 增删改查等
7.抽象数据类型:
不用区分该值是不是可以分割,但只能对该值进行逻辑操作 ----> 值之间的关系太过于复杂,物理上实现不了只能逻辑上实现
图文解释:各个数据类型
注意事项:
逻辑操作: 脑子想把这些值以线性的关系存入电脑
物理操作: 用电脑实际将这些值以线性的关系存入电脑
只能逻辑操作不能物理操作: 脑子想将这些值以非线性的关系存入,但用电脑无法将其非线性的存入电脑
1.算法的基本概念
算法是对特定问题求解步骤的一种描述,它是指令的有限序列,其中的每条指令表示一个或多个操作.
e.g:冒泡排序算法: 描述数从小到大这个特定问题求解的步骤,每一句代码都是一个指令,一句代码让计算机进行多个操作<比如:int i = 1;先开辟空间,然后将1存入到空间中,一句代码对应两个操作>
2.算法的几个重要特性
注:有穷性: 算法步骤的有穷和算法时间的有穷
注:确定性:每一条代码确定的含义,算法结果的确定
注:可行性:代码的可运行
注:输入可以是0个,如果输入非0则说明输入是某个特定对象集合里面的某几个对象
输入0个: 算法里面已经将初始条件模拟好了,人工不需要在设初始条件了
输入非0个: 冒牌排序需要输入多个实数,这时候某几个实数就是特定的实数对象集合里的某几个实数,而不是字符串对象集合里面的
注:必须有1个输出,若有输入且这个输出必定和输入有关系
有着特定的关系: 冒牌排序就是不同顺序的输入一串数据到从小到大顺序(特定关系) 输出一串值不变的相同数据
3.好算法的特性
效率:算法执行所需要的时间
存储量:在算法执行的过程中所需要的存储空间
4.算法效率的度量
概念:算法效率的度量:①时间复杂度②空间复杂度 统称算法效率的度量
频度:一个语句在算法中被重复执行的次数
算法问题规模:算法求解问题的**输入量(键盘上输入的东西)**称为算法问题的规模
//比如数组an{1,4,5,6,9}要进行排序 即算法问题规模就是 n = an 但是在这个算法里可将问题规模看作an的长度,因为算法的时间复杂度只和数组长度有关
public int[] bubbleSort(int[] arr){
int temp; //用来中间交换的存储
for (int i = 1; i < arr.length "= n = 5(问题规模)" ; i++) {
boolean flag = true;
for (int j = 0; j < arr.length - i; j++) {
if (arr[j] > arr[j+1]) {
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
flag = false;
}
if(flag){
break;
}
}
return arr;
}
T(n):算法中所有语句的频度之和,是算法问题规模n的函数,时间复杂度主要分析T(n)的数量级
T(n)数量级: 影响T(n)数值大小比较大的值
e.g: T(n) = 3n + 2 当n趋于无穷大的时候T(n)的数量级就是 n
算法中基本运算: 最深层循环内的语句.
注:算法中基本运算的频度(算法最深循环内所有语句之和)的数量级 = T(n)的数量级
//比如数组an{1,4,5,6,9}要进行排序 即算法问题规模就是 n = arr; arr里面的长度是这个题的问题规模
public int[] bubbleSort(int[] arr){
int temp; //用来中间交换的存储
for (int i = 1; i < arr.length "= n = 5(问题规模)" ; i++) {
boolean flag = true;
for (int j = 0; j < arr.length - i; j++) {
if (arr[j] > arr[j+1]) {
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
//循环内最深层的三个语句 其中一个执行的频度: 4+3+2+1 = (n-1)(1+4)/2 = 5(n-1)/2
f(n) = 3 × [5(n-1)/2];
/*
T(n):
1:boolean flag = true (n-1)次;
2:最内层三个语句: = 3 × [5(n-1)/2]次;
3:flag = false; = (n-1)次;
4.if = (n-1)次
*/
T(n) = 3 × [5(n-1)/2 + (n-1)] = 3 × [7(n-1)/2];
/*
f(n) = 3 × [5(n-1)/2]
T(n) = 3 × [7(n-1)/2]
当 n 趋于 ∞ 的时候 f(n)的数量级记作 O(f(n)) = O(n); T(n)的数量级记作 T(n) = O(n)
所以 T(n) = O(f(n));
*/
}
flag = false;
}
if(flag){
break;
}
}
return arr;
}
因此时间复杂度 = T(n)的数量级 = O(f(n))或 T(n) = O(f(n)) 这时候T(n) = T(n)的数量级
注:
①:公式里 T(n) 是 T(n)的数量级;O(f(n)) 是 f(n)的数量级 —> O(f(n))是时间复杂度但是O(n^k)
O(f(n)) = C1 × n^k ≈ n^k
O(n^k) = n^k
所以 O(n^k) ≈ O(f(n)) —> 但是在表示的时候 O(n^k) = O(f(n))
②:算法的时间复杂度不仅仅依赖于问题规模<输入量>,而且还依赖于输入数据的性质
an{
1,2,3,4,5};
for(int i = 0; i < an.length {
时间复杂度不仅依赖n}; i++){
boolean flag = false;
while(a[i] == k(时间复杂度还以来输入数据的性质是不是有和k相等的数据)){
flag = true;
}
if(flag){
break;
}
}
/*
1.如果an中没有k则时间复杂度是T(n) = O(n);
2.如果an第一个数据就是k则时间复杂度T(n) = 0;
/*
如上面的代码所示:
最坏时间复杂度: 寻找n=5次才匹配上或者寻找n=5次都么匹配上 所以最坏时间复杂度是O(n)
最好时间复杂度: 寻找1次