数据结构---第一章

第一章:绪论

1.数据结构的基本概念

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. 存储结构

数据结构的三大要素: 1.逻辑结构 2.存储结构 3.数据运算

解释: 必须存在逻辑结构,存储结构以及数据运算的数据元素集合才能被称为数据结构

  • 逻辑结构: 指数据元素之间的逻辑关系,从逻辑关系上描述数据.与数据的存储无关,独立于计算机之外的。

数据的逻辑结构分为:1.线性结构 2.非线性结构

线性结构: 数据元素之间就是一个连接一个这种结构 e.g: 线性表

非线性结构:数据元素之间不是一个连接一个这种结构 e.g:树,图

  • 存储结构: 数据元素之间关系在计算机中的表示(也称映射或物理结构)

数据的存储结构分为: 1.顺序存储 2.链式存储 3.索引存储 4.散列存储

1.顺序存储: 把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中

​ 优点: 1.可以实现随机存取(查询数据的时间复杂度是O(1))<随机存取:通过下标可以直接找到对应的数据> 2.存储密度大(存储单元全部是数据元素,所以存储密度大)

​ 缺点: 1.增删某一数据元素操作麻烦 2.可能会产生较多的外部碎片(开的是固定的空间,用的可能少)

外部碎片的理解:

数据结构---第一章_第1张图片

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.数据类型:

数据类型是一个值的集合定义在此集合上的一组操作总称

值的集合:数据结构或者数,字符串等集合 —>注: 数据结构就是数据元素<值>的集合

集合上的操作: 增删改查等

  • 原子类型: 其值不能在分割的数据类型并且对该值进行逻辑操作和物理操作 ----> int整型集合,里面的值全是整数无法继续分割
  • 结构类型: 其值能在分割的数据类型并且可以对该值进行逻辑操作和物理操作 ----> 数据结构,里面的值<数据元素>可以在分割称数据项

7.抽象数据类型:

不用区分该值是不是可以分割,但只能对该值进行逻辑操作 ----> 值之间的关系太过于复杂,物理上实现不了只能逻辑上实现

图文解释:各个数据类型

数据结构---第一章_第2张图片

注意事项:

逻辑操作: 脑子想把这些值以线性的关系存入电脑

物理操作: 用电脑实际将这些值以线性的关系存入电脑

只能逻辑操作不能物理操作: 脑子想将这些值以非线性的关系存入,但用电脑无法将其非线性的存入电脑

2.算法和算法评价

1.算法的基本概念

算法是对特定问题求解步骤的一种描述,它是指令的有限序列,其中的每条指令表示一个或多个操作.

e.g:冒泡排序算法: 描述数从小到大这个特定问题求解的步骤,每一句代码都是一个指令,一句代码让计算机进行多个操作<比如:int i = 1;先开辟空间,然后将1存入到空间中,一句代码对应两个操作>

2.算法的几个重要特性

  • 有穷性: 一个算法必须总在执行有穷步骤之后结束,且每一步都可在有穷时间内完成

注:有穷性: 算法步骤的有穷和算法时间的有穷

  • 确定性: 算法中每条指令必须有确切的含义,对于相同的输入只能得出相同的输出

注:确定性:每一条代码确定的含义,算法结果的确定

  • 可行性: 算法中描述的操作都可以通过已经实现的基本运算执行有限次来实现

注:可行性:代码的可运行

  • 输入: 一个算法有0个输入或者多个输入,这些输入取自于某个特定的对象的集合

注:输入可以是0个,如果输入非0则说明输入是某个特定对象集合里面的某几个对象

输入0个: 算法里面已经将初始条件模拟好了,人工不需要在设初始条件了

输入非0个: 冒牌排序需要输入多个实数,这时候某几个实数就是特定的实数对象集合里的某几个实数,而不是字符串对象集合里面的

  • 输出:一个算法有1个或多个输出,若有输入则这些输出是与输入有着某种特定关系的量

注:必须有1个输出,若有输入且这个输出必定和输入有关系

有着特定的关系: 冒牌排序就是不同顺序的输入一串数据从小到大顺序(特定关系) 输出一串值不变的相同数据

3.好算法的特性

  • 正确性: 算法应能够正确地解决求解问题
  • 可读性: 良好的可读性,帮助人们理解
  • 健壮性: 输入非法数据时,算法能适当地做出反应或进行处理,而不是莫名的输出结果
  • 高效率与低存储量需求:时间短,运行时消耗存储空间小

效率:算法执行所需要的时间

存储量:在算法执行的过程中所需要的存储空间

4.算法效率的度量

概念:算法效率的度量:①时间复杂度②空间复杂度 统称算法效率的度量

  • 时间复杂度: T(n)的数量级

频度:一个语句在算法中被重复执行的次数

算法问题规模:算法求解问题的**输入量(键盘上输入的东西)**称为算法问题的规模

//比如数组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;
/*
  • 最坏时间复杂度: 在最坏情况下,算法的时间复杂度 —>如上述代码中1
  • 最好时间复杂度: 在最好情况下,算法的时间复杂度 ---->如上述代码中2
  • 平均时间复杂度: 所有可能输入实例在等概率出现的情况下,算法的期望运行时间

如上面的代码所示:

最坏时间复杂度: 寻找n=5次才匹配上或者寻找n=5次都么匹配上 所以最坏时间复杂度是O(n)

最好时间复杂度: 寻找1次

你可能感兴趣的:(数据结构,算法,java)