我的第一本算法书之第一章数据结构

1-1 什么是数据结构

  • 决定了数据的顺序和位置关系
  • 数据存储于内存时,决定了数据顺序和位置关系的便是“数据结构”。

电话簿的数据结构
例① 从上往下顺序添加
例② 按姓名的拼音顺序排列

  • 两种方法的优缺点
    • 总的来说,数据按获取顺序排列的话,虽然添加数据非常简单,只需要把数据加在最后就可以了,但是在查询时较为麻烦;以拼音顺序来排列的话,虽然在查询上较为简单,但是添加数据时又会比较麻烦。
    • 虽说这两种方法各有各的优缺点,但具体选择哪种还是要取决于这个电话簿的用法。如果电话簿做好之后就不再添加新号码,那么选择后者更为合适;如果需要经常添加新号码,但不怎么需要再查询,就应该选择前者。
  • 将获取顺序与拼音顺序结合起来怎么样
    • 分别使用不同的表存储不同的拼音首字母,比如表L、表M、表N等,然后将同一张表中的数据按获取顺序进行排列。
  • 选择合适的数据结构以提高内存的利用率
    • 数据结构方面的思路也和制作电话簿时的一样。将数据存储于内存时,根据使用目的选择合适的数据结构,可以提高内存的利用率。

1-2 链表

  • 链表是数据结构之一,其中的数据呈线性排列。在链表中,数据的添加和删除都较为方便,就是访问比较耗费时间。
  • 在链表中,数据一般都是分散存储于内存中的,无须存储在连续空间内。
  • 因为数据都是分散存储的,所以如果想要访问数据,只能从第1个数据开始,顺着指针的指向一一往下访问(这便是顺序访问
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

对链表的操作所需的运行时间到底是多少呢?在这里,我们把链表中的数据量记成n。访问数据时,我们需要从链表头部开始查找(线性查找),如果目标数据在链表最后的话,需要的时间就是O(n)。另外,添加数据只需要更改两个指针的指向,所以耗费的时间与n无关。如果已经到达了添加数据的位置,那么添加操作只需花费O(1)的时间。删除数据同样也只需O(1)的时间。

  • 循环链表

    • 虽然上文中提到的链表在尾部没有指针,但我们也可以在链表尾部使用指针,并且让它指向链表头部的数据,将链表变成环形。这便是“循环链表”,也叫“环形链表”。循环链表没有头和尾的概念。想要保存数量固定的最新数据时通常会使用这种链表。
      在这里插入图片描述
  • 双向链表

    • 上文链表里的每个数据都只有一个指针,但我们可以把指针设定为两个,并且让它们分别指向前后数据,这就是“双向链表”。使用这种链表,不仅可以从前往后,还可以从后往前遍历数据,十分方便。
    • 但是,双向链表存在两个缺点:一是指针数的增加会导致存储空间需求增加;二是添加和删除数据时需要改变更多指针的指向。
      在这里插入图片描述

1-3 数组

  • 数组也是数据呈线性排列的一种数据结构。与前一节中的链表不同,在数组中,访问数据十分简单,而添加和删除数据比较耗工夫

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 由于数据是存储在连续空间内的,所以每个数据的内存地址(在内存上的位置)都可以通过数组下标算出,我们也就可以借此直接访问目标数据(这叫作“随机访问”)。

这里讲解一下对数组操作所花费的运行时间。假设数组中有n个数据,由于访问数据时使用的是随机访问(通过下标可计算出内存地址),所以需要的运行时间仅为恒定的O(1)。但另一方面,想要向数组中添加新数据时,必须把目标位置后面的数据一个个移开。所以,如果在数组头部添加数据,就需要O(n)的时间。删除操作同理。

  • 在链表和数组中,数据都是线性地排成一列。在链表中访问数据较为复杂,添加和删除数据较为简单;而在数组中访问数据比较简单,添加和删除数据却比较复杂。
    在这里插入图片描述

1-4 栈

  • 栈也是一种数据呈线性排列的数据结构,不过在这种结构中,我们只能访问最新添加的数据。
    在这里插入图片描述
    在这里插入图片描述

像栈这种最后添加的数据最先被取出,即“后进先出”的结构,我们称为Last InFirst Out,简称LIFO。
与链表和数组一样,栈的数据也是线性排列,但在栈中,添加和删除数据的操作只能在一端进行,访问数据也只能访问到顶端的数据。想要访问中间的数据时,就必须通过出栈操作将目标数据移到栈顶才行。

1-5 队列

  • 与前面提到的数据结构相同,队列中的数据也呈线性排列。虽然与栈有些相似,但队列中添加和删除数据的操作分别是在两端进行的。就和“队列”这个名字一样,把它想象成排成一队的人更容易理解。在队列中,处理总是从第一名开始往后进行,而新来的人只能排在队尾。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

像队列这种最先进去的数据最先被取来,即“先进先出”的结构,我们称为FirstIn First Out,简称FIFO。与栈类似,队列中可以操作数据的位置也有一定的限制。
在栈中,数据的添加和删除都在同一端进行,而在队列中则分别是在两端进行的。队列也不能直接访问位于中间的数据,必须通过出队操作将目标数据变成首位后才能访问。

1-6 哈希表

  • 在哈希表这种数据结构中,使用将在5-3节讲解的“哈希函数”,可以使数据的查询效率得到显著提升。
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在哈希表中,我们可以利用哈希函数快速访问到数组中的目标数据。如果发生哈希冲突,就使用链表进行存储。这样一来,不管数据量为多少,我们都能够灵活应对。
如果数组的空间太小,使用哈希表的时候就容易发生冲突,线性查找的使用频率也会更高;反过来,如果数组的空间太大,就会出现很多空箱子,造成内存的浪费。因此,给数组设定合适的空间非常重要。

  • 解决冲突方法
    • 链地址法:在存储数据的过程中,如果发生冲突,可以利用链表在已有数据的后面插入新数据来解决冲突。
    • 开放地址法:当冲突发生时,立刻计算出一个候补地址(数组上的位置)并将数据存进去。如果仍然有冲突,便继续计算下一个候补地址,直到有空地址为止。可以通过多次使用哈希函数或“线性探测法”等方法计算候补地址

1-7 堆

  • 堆是一种图的树形结构,被用于实现“优先队列”(priority queues)
  • 在堆中存储数据时必须遵守这样一条规则:子结点必定大于父结点
  • 此处所指的堆是特指的小顶堆,顾名思义就是最小的数字是在最上面堆着摆放,而大顶堆则是最大的数字在最上面摆放。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

堆中最顶端的数据始终最小,所以无论数据量有多少,取出最小值的时间复杂度都为O(1)。
因为取出数据后需要将最后的数据移到最顶端,然后一边比较它与子结点数据的大小,一边往下移动,所以取出数据需要的运行时间和树的高度成正比。假设数据量为n,根据堆的形状特点可知树的高度为log2n,那么重构树的时间复杂度便为O(logn)。
添加数据也一样。在堆的最后添加数据后,数据会一边比较它与父结点数据的大小,一边往上移动,直到满足堆的条件为止,所以添加数据需要的运行时间与树的高度成正比,也是O(logn)。

  • 如果需要频繁地从管理的数据中取出最小值,那么使用堆来操作会非常方便。比如4-5节中提到的狄克斯特拉算法,每一步都需要从候补顶点中选择距离起点最近的那个顶点。此时,在顶点的选择上就可以用到堆。

1-8 二叉查找树

  • 二叉查找树(又叫作二叉搜索树或二叉排序树)是一种数据结构,采用了图的树形结构(关于树形结构的详细说明请参考4-2节)。数据存储于二叉查找树的各个结点中。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

我们可以把二叉查找树当作是二分查找算法思想的树形结构体现(二分查找的详细说明在3-2节)。因为它具有前面提到的那两个性质,所以在查找数据或寻找适合添加数据的位置时,只要将其和现有的数据比较大小,就可以根据比较结果得知该往哪边移动了。
比较的次数取决于树的高度。所以如果结点数为n,而且树的形状又较为均衡的话,比较大小和移动的次数最多就是log2n。因此,时间复杂度为O(logn)。
但是,如果树的形状朝单侧纵向延伸,树就会变得很高,此时时间复杂度也就变成了O(n)。

  • 有很多以二叉查找树为基础扩展的数据结构,比如“平衡二叉查找树”。这种数据结构可以修正形状不均衡的树,让其始终保持均衡形态,以提高查找效率。
  • 另外,虽然文中介绍的二叉查找树中一个结点最多有两个子结点,但我们可以把子结点数扩展为m(m为预先设定好的常数)。像这种子结点数可以自由设定,并且形状均衡的树便是B树。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值