数据结构与算法基础

一. 数组
1. 数组结构的特点

(1) 数组元素数目不变,一旦定义了一个数组结构,数组元素个数就不会发生改变
(2) 数组元素具有相同的数据类型
(3) 数组元素的下标关系具有上下界的约束且下标有序

2. 数组的两个基本运算

(1) 给定一组下标,存取相应的数据元素
(2) 给定一组下标,修改相应的数组元素中某个数据项的值

3. 数组的存储

根据数组结构的特点知道数组一般不做插入和删除操作,数组的存储适合采用顺序存储结构,二维数组存储结构可分为按行为主序和按列为主序两种存储方法。

4. 数组存储地址的计算
数组类型 存储地址的计算(a是数组首地址,len是每个数组元素所占长度)
一维数组 a[i]的存储地址:a+i*len
二维数组:a[m][n] 按行存储:a+(i*n+j) *len;按列存储:a+(j *m+i) *len

数组存储地址的计算示例:

1)已知一维数组a中每个元素占用2个字节,求a[10]的存储地址?
:a[10]的存储地址为:a+10*2=a+20
2)已知4行5列的二维数组a中的每个元素占用2个字节,求元素a[3][2]按行为主序存储的存储地址和按列为主序存储的存储地址?


按行存储:a+(3*5+2)2 = a+34
按列存储:a+(2
4+3)*2 = a+22

二. 矩阵
1. 稀疏矩阵的定义

在一个矩阵中,非零的元素的个数远远多于零元素的个数,并且非零元素的分布没有规律,称为稀疏矩阵。

2. 稀疏矩阵的存储

存储稀疏矩阵中非零元素时,必须存储其位置。三元组(i,j,aij)可唯一确定矩阵中的一个元素。稀疏矩阵的三元组表的顺序存储称为三元组顺序表,常用的三元组表的链式存储结构是十字链表。

3.上三角矩阵与下三角矩阵

主对角线以下都是零的方阵称为上三角矩阵,主对角线以上都是零的方阵称为下三角矩阵。稀疏矩阵大量元素是零,存储一部分内容就已经把有效数据存储了,这可以节省很多空间。存储稀疏矩阵一般存储上三角矩阵或下三角矩阵,如在上三角矩阵或者下三角矩阵中,遇到上半部分和下半部分数据可能重复的情况下,只需要存储上半部分的数据或者下半部分的数据即可。

4. 求三角矩阵中某一个元素对应一维数组的下标
稀疏矩阵 计算方法
上三角矩阵 下标分别为i和j的元素对应的一维数组的下标计算公式是:(2 n - i + 1) * i / 2 + j
下三角矩阵 下标分别为i和j的元素对应的一维数组的下标计算公式是:(i + 1) * i / 2 + j

示例:
设有如下所示的下三角矩阵 A [ 0…8 ,0…8 ],将该三角矩阵的非0元素(即:行下标不小于列下标的所有元素)以行优先压缩存储到 M [ 1…m ]中,则元素 A [ i , j ] (0<=i<8,j<=i)存储在数组M中的( )中?

A(0,0)
A(1,0) A(1,1)
A(2,0) A(2,1) A(2,2)
A(2,0) A(2,1) A(2,2) A(2,3)
. . .
. . .
A(8,0) A(8,1) A(8,2) A(8,3) A(8,4) . . . . . A(8,8)

答案: M [ (i + 1) * i / 2 + j+1 ]

分析:
将A(0,0)即:i=0,j=0代入 (i + 1) * i / 2 + j 中,查看结果是不是M [ 1 ]中的1。很明显,计算结果是0。这里将0加1后的结果才是1,所以我们考虑M中的结果是 (i + 1) * i / 2 + j+1。将A [2,1]中i=2,j=1代入推算的公式: (i + 1) * i / 2 + j+1,发现A[1,2]对应M [5 ],满足条件。通过代入矩阵中的其它元素后,发现我们推导的公式: (i + 1) * i / 2 + j+1是满足题意的。所以,答案是:M [ (i + 1) * i / 2 + j+1 ]

三. 数据结构的定义
1. 数据结构的概念

计算机存储以及组织数据的方式。

2. 为什么去研究数据结构

选择不同的数据结构,可能带来的运行效率差异比较大。

3. 数据逻辑结构
  • 线性结构
    线性结构类似一个个元素连接成一条线
  • 非线性结构
    非线性结构可以分为:树形结构和图。树形结构和图的区别是:树形结构里面没有环路,而图中有环路。广义的图既包括树,也包括线性结构,而广义的树还包括线性结构。
四. 顺序表与链表
1. 顺序表概述

顺序表开辟了连续的空间,顺次将数据存储。

2. 链表概述
  • 单链表
    单链表中指针是单向的,头节点指向第一个元素,第一个元素指向第二个元素,第二个元素指向第三个元素……以此类推。
  • 循环链表
    把尾元素的指针指向了头节点,指向头节点的好处是:如果当前指针是在尾元素,要定位在其它任何一个元素的时候,可以next,定位到它。而不用重新定位到头节点才开始。
  • 双向链表
    单链表只能指向一个方向,而双链表两个方向都可以指向。
3. 链表的操作
  • 单链表删除节点
    在这里插入图片描述
q->nest = p->next
  • 单链表插入节点
    在这里插入图片描述
s->next = p->nest
p->next = s
  • 双链表删除节点
    在这里插入图片描述
  • 双链表插入节点
    在这里插入图片描述

单链表是分有头节点和没有头节点的,头节点不存任何信息, 头节点下一个元素才存储信息。有头节点的好处是:引入的头节点可以让所有节点的操作方式变成一致的。如果头节点存有别的元素即存储具体内容,往往需要采取不同的处理方式。

五. 算法的基本特性
  • 有穷性
    执行有穷步之后结束。
  • 确定性
    算法中每一条指令都必须有确切的含义,不能含糊不清。
  • 输入
    0个或0个以上的输入。
  • 输出
    至少有一个输出。
  • 有效性
    算法中的每一个步骤都能得到有效地执行并且能得到确定的结果。如:a = 0,c = b/9这一步就是无效的
六. 查找
1. 顺序查找
  • 顺序查找的思想
    将待查找的关键字为key的元素从头到尾与表中元素进行比较,如果中间存在关键字为key的元素,则返回查找成功;否则返回查找失败。
    | 1 |2 |3| 4 |5 |6| 7 |8 |
    |–|--|–|--|–|--|–|--|
  • 顺序查找的平均查找长度
    ASL = (n+1)/ 2
    时间复杂度:O(n)
2. 折半查找(二分查找)
  • 折半查找的思想
    不是所有的序列都可以用折半查找法,折半查找的前提是:查找的序列中的数是有序(从大到小或者从小到大)排列的。
  • 查找过程
    【例题】请给出在含有12个元素的有序表{1,4,10,16,17,18,23,29,33,40,50,51}中查找关键字17的过程:

将12个元素按照顺序排序(正序和逆序都可以):

1 4 10 16 17 18 23 29 33 40 50 51

第一轮查找:将第一个元素的位置下标与最后一个元素的位置下标相加,然后除以2向下取整,得到17第一次要比较的数值。(1+12)/2 =6.5,向下取整就是6。所以,17要与下标为6的数值做比较,下标6对应的数值是18。因为17<18,所以17必然在下标为6的左边部分,把查找的范围缩小一半。

第二轮查找:(1+5)/2 = 3,所以17要与3对应的数值进行比较。因为17>10,所以17应该在4的左边部分。

第三轮查找:范围缩小到4~5之间,(4+5)/2 = 4.5,则17要与下标4所在位置的值做比较。因为17>16,所以17在座标为4的右边部分

第四轮查找:(5+5)/2 = 5,17与下标为5的值做比较,17=17,所以只有下标为5符合题意。

  • 折半查找的时间复杂度
    折半查找在查找成功时,关键字的比较次数最多为:log2n +1次
    折半查找的时间复杂度:O(log2n)
七. 散列表
1. 散列表查找的基本思想

已知关键字集合U,最大关键字为m。设计一个hash函数,它一关键字为变量,关键字的存储地址为因变量,将关键字映射到一个有限的、地址连续的区间中。这个区间就是散列表,散列查找中使用的转换函数称为散列函数。

2. 散列表查找实例

【例】记录关键码为(3,8,12,17,9),取m=10(存储空间为10),p = 5,散列函数 h = key%p。

  • 3 % 5 = 3,将关键码3放置到位置为3的存储空间。
    在这里插入图片描述
  • 8 % 5 = 3,再放在存储空间3的位置就会发生冲突,使用开放定址法的思想,可将关键字8存放在存储空间3的下一个位置存储空间4。
    在这里插入图片描述

开放定址法:当构造散列表发生冲突时,使用某种探测手段,产生一个探测的散列地址序列,并且逐个查找此地址中是否存储数据元素。如果没有存储,则称该散列地址开放,并将关键字存入,否则继续查找下一个地址。只要散列表足够大,总能找到空的散列地址将数据元素存入。

  • 12 % 5 = 2,将关键码12放置在存储空间2的位置中。
    在这里插入图片描述

  • 17 % 5 = 2,存储空间2的位置已经存有元素,根据开放定址法的思想,需要向下查找,需要找到空的散列地址将元素存入。查找到存储空间5的位置是空的,所以将17存入存储空间5的位置。

  • 9 % 5 = 4,存储空间4已经存在元素,向下查找,存储空间5也存在元素。继续向下查找,存储空间6是空的,所以将关键码9存入存储空间6的位置上。

八. 排序
1. 排序的概念
  • 稳定的排序:原来的两数中第一个数大于第二个数,排序之后第一个数还是大于第二个数。两数的顺序不会因为排序之后发生顺序的改变。
  • 不稳定的排序:两数的顺序会因为排序之后发生顺序的改变。
2. 内排序与外排序

内排序:在内存里面进行排序
外排序:涉及外部存储空间

3. 排序方法的分类
  • 插入类排序
    插入排序:直接插入排序、希尔排序
    直接插入排序思路简单、操作容易,但是效率没有希尔排序高。
  • 交换类排序
    交换类排序:冒泡排序、快速排序
    快速排序效率比较高,过程复杂一些。
  • 选择类排序
    选择类排序:简单选择排序、堆排序
    堆排序效率非常高,但处理过程复杂。
  • 归并排序
  • 基数排序
4. 直接插入排序
  • 直接插入排序的思路
    当插入第i个记录时,R1,R2,……,Ri-1均已经排好序。因此,将第i个记录Ri依次与Ri-1,……,R2,R1进行比较,找到合适的位置插入。

【例】使用插入排序对57 68 59 52进行排序

(1)第一步:比较57、68两数,因为57<68,所以位置不发生改变
在这里插入图片描述
(2)第二步:将59与68做比较,59<69,所以59应该在68的前面(左边),但不确定将59插入在57的前边,还是插入到57的后面。所以,59需要与57再做比较。
在这里插入图片描述
(3)第三步:因为59>57,所以59在57的后面(右边),将59插入在57的后面的位置(也即68的前的位置)上。
在这里插入图片描述
在这里插入图片描述
(4)第四步:将52与68做比较,52<68,所以52必在68的前面。
在这里插入图片描述
(5)第五步:68的前面位置有59和57,所以需要将52与59比较。因为52<59,所以52在59的前面位置。
在这里插入图片描述
(6)第六步:因为59的前面位置还有57,所以需要将52与57比较。因为52<57,所以52要插入到57的前面位置。至此,完成排序。
在这里插入图片描述
在这里插入图片描述
直接插入排序的优点:简单明了
直接插入排序的缺点:速度很慢

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章