《数据结构与算法图解》 学习笔记

这两天学习了下数据结构与算法,因为本科是通信工程专业,只学过一点点数据结构与算法,差不多也还给老师了,所以选了《数据结构与算法图解》这本入门级教材学了下。这本书讲的确实很基础,我花了两天时间学完了,这里简单总结一下。
在这里插入图片描述
数据结构指的是“一组数据的存储结构”,算法指的是“操作数据的一组方法”。
数据结构是为算法服务的,算法是要作用再特定的数据结构上的。

常用的数据结构与算法:

  • 数据结构:数组、散列表、栈、队列、链表、二叉树、图等
  • 算法:排序、递归等

大O记法

用大O记法可以表示算法复杂度,包括时间复杂度和空间复杂度。

  1. 在时间复杂度里,大O解答的问题:当数据增长时,步数如何变化?
  2. 在无特殊说明时,大O记法一般都指最坏情况。
  3. 大O记法忽略常数。
  4. 大O只保留最高阶。
  5. 在空间复杂度里,大O用来描述算法额外消耗的内存空间。

数组

数据结构一般有4种操作:读取、查找、插入、删除。数组一般分为无序数组和有序数组。

  1. 因为数组是连续存放,所以在读取操作中只需一步就能索引到指定位置,其复杂度为O(1);
  2. 常规数组(无序数组)的查找方法一般是线性查找,计算复杂度为O(N),有序数组除线性查找外,可用二分查找,可使复杂度降为O(logN);
  3. 常规数组的插入最坏情况为在数组开头插入,需N+1步(N表示数组元素的个数),时间复杂度为O(N);有序数组同常规数组相比,需要多做一次查找以确定插入的位置,但由于大O忽略常数,其时间复杂度仍为O(N);
  4. 删除操作最坏情况是删除第一个元素,需N步,其时间复杂度为O(N);
  5. 集合:是一种基于数组但不允许元素重复的数据结构。它在插入前要做查找来确定插入的值在不在其中,最坏情况需2N+1步。

排序算法

很多时候,虽然大O表示一样,但,

  • 大O忽略常数(因为大O关心的是长期增长率),实际步数会不同;
  • 大O考虑的是最坏情况,但最坏情况毕竟是少数,各种算法或操作间平均情况会不同;

所以,大O记法合适于不同大O分类下(如O(logN)和O(N))的算法的对比,对于大O同类(即表示一样)的算法,需进一步分析才能什么情形下用什么算法。

一、冒泡排序

  1. 每轮过后,未排序中最大的值冒在正确位置上。
  2. 其执行步骤包括比较和交换两种,比较(N-1)+(N-2)+ … + 1次,在最坏情况下,每次比较过后进行一次交换,大约需要N2步,所以时间复杂度为O(N2)。
  3. 像冒泡排序这种嵌套循环算法的效率(时间复杂度)就是O(N2)

二、选择排序

  1. 从索引0开始为第一轮,将其值记为最小值,再与后面值比较,将最小值交换到索引0位置。
  2. 其执行步骤包括比较和交换两种,比较(N-1)+(N-2)+ … + 1次,但每轮最多交换一次,所以步数大概只有N2/2,但由于大O忽略常数,所以时间复杂度仍为O(N2)

三、插入排序

  1. 每轮都是将其值(索引从1开始)取出作为临时变量temp_value,与前面值挨个比较,每比较一次,若值比temp_value大,先将其右移,再与前一个比较。
  2. 其执行步骤包括移除、比较、平移和插入。最坏情况下(即数组完全逆序时),有多少次比较就要多少次平移,需N2/2次比较和N2/2次平移,另外,移出和插入在每一轮都会各发生一次,所以所需步数为N2+2N-2次,但由于大O只保留最高阶,所以时间复杂度为O(N2)

通过对比这三种排序算法,你会发现选择排序所需步数最少,但不能说它就是三者中最优的,例如插入排序在最坏、平均、最好情况时分别需要N2、N2/2、N步,而选择排序最坏、平均、最好时都要N2/2步。
所以,对于哪种算法更好?答案是看情况。如果你确信数组大致有序,那么插入排序比较好;如果是大致逆序,则选择排序更快;如果你无法确定,那就算是平均情况,两种都可以。

散列表

不同的语言中有不同叫法,如Python中叫字典。它是由键值对组成。

  1. 无序数组查找需要O(N),有序数组需要O(logN),而散列表利用键查找只需O(1)。
  2. 不同的值可能会对应同一个键,会造成冲突,解决方法是散列表的格子含有数组(即将不同的值放在一个格子的数组中)。
  3. 散列表的效率取决于要存多少数据、有多少可用的格子和用什么样的散列函数。权衡原则为:既要避免冲突,又要节约空间。一般每增加7个元素,就增加10个格子(即负载因子为0.7)。

栈和队列

栈和队列只不过是加了一些约束条件的数组,它们是临时处理数据的灵活工具。

  1. 栈只能在末尾插入数据,且只能读取和移除末尾的数据

  2. 栈的末尾称为栈顶,栈的开头称为栈底(可以把栈想象成一叠碟子,只能看到最顶端的那只碟子的碟面,并且只能从栈顶开始拿)。

  3. 遵循“后进后出”原则。

  4. 队列只能在末尾插入数据(跟栈一样),且只能读取和移除开头的数据

  5. 队列遵循 “先进先出”的原则(可以想象成电影院排队)。

递归算法

函数调用自身称为递归,不再递归的情形称为“基准情形”。
递归作为一种思想,简化了代码,且能递归的不只有算法,还有数据结构(如下一章的链表、二叉树、图)。

  1. 分区:从数组中随机选取一个值(一般是最右端),以其为轴,将比它小的值放到它左边,比它大的值放到它右边(具体步骤可参考教材或查阅资料)。
  2. 分区包含比较和交换两个步骤。一次分区至少有N次比较,即数组中每个值都要与轴作比较。因为每次分区时,左右指针都会从两端开始靠近,直至相遇。交换在最坏情况下是左半部分与右半部分进行交换,即N/2次,一般平均情况算一半,N/4,所以每次分区大约花1.25N步。因为是要确定N个轴,本来应该是N次分区,但是有的分区只有一个元素(即达到基准情形)。据统计,N个元素使用快速排序在平均情况和最好情况下是O(N logN),虽然在最坏情况下为O(N2),但这比之前的排序算法是有很大改善的。
  3. 快速选择的优势在于不需要把整个数组都排序就可以找到正确位置的值。它可以看成是快速排序和二分查找的结合。

链表

结点:与数组不同,这种非连续存放的格子,称为结点

  • 链表的关键:每个结点除了保存数据,还保存着链表里下一结点的内存地址,这可以将数据分散到内存各处,无须事先寻找连续的空格子。

  • 数组和链表相关操作的计算复杂度如下表:在这里插入图片描述
    可以看出,尽管两者的查找、插入、删除效率差不多,但在读取方面,数组比链表要快。既然如此,为什么还要用链表呢?
    因为队列只能在末尾插入元素,在开头删除元素,所以以链表作为队列的底层数据结构,删除的时间复杂度为O(1),而数组为O(N);虽然插入操作数组更快,但双向链表能使队列的插入和删除都为O(1)。

  • 双向链表:每个结点含有两个链:一个指向下一结点,一个指向前一结点。

二叉树

二叉树遵守的规则:

  • 每个结点的子结点数量为0、1、2。
  • 如果有两个子结点,则其中一个子结点的值必须小于父节点,另一个子结点的值必须大于父结点。

树形的数据结构除了二叉树外还有很多种,包括B数红黑树2-3-4树等。

  1. 二叉树的查找跟有序数组的二分查找有同样的效率,都是O(logN);
  2. 有序数组的插入需要O(N),而二叉树的插入只要O(logN);
  3. 有序数组的删除需要O(N),而二叉树的删除包括一次查找,以及少量额外的步骤去处理悬空的子结点,所以平均情况下二叉树的删除效率也是O(logN);

二叉树的删除算法的所有规则:

  • 如果有要删除的结点没有子结点,那直接删除它就好。
  • 如果要删除的结点有一个子结点,那删掉它之后,还要将子结点填到被删除结点的位置上。
  • 如果要删除的结点有两个子结点,那删掉它之后,将其后继结点填充到被删除结点的位置上。一个结点的后继结点,就是所有比删除结点的子结点中,最小的那个。
  • 如果后继结点带有右子结点,则在后继结点填补被删除结点后,用此右子结点代替后继结点。

图是一种善于处理关系型数据的数据结构。

广度优先搜索用队列来记录后续要处理哪些顶点,其步骤如下:

  1. 找出当前顶点的所有邻接点。如果有哪个是没访问过的,就把它标为“已访问”,并且将它入队。
  2. 如果当前顶点没有为访问的邻接点,且队列不为空,那就再从队列中移出一个顶点作为当前顶点。
  3. 如果当前顶点没有未访问的邻接点,且队列里也没有其他顶点,那么算法完成。

对于V个顶点,E条边的二叉树,因为每个顶点都会有一次出队的经历,所以有V次出队,因为每对邻接点都会被访问两次,所以有2E步访问邻接点。故广度优先搜索的效率为O(V+E)(大O忽略常数)。

Dijkstra算法:借助加权图(边上带有信息的图)来解决最短路径问题。其步骤如下:

  1. 以起步的顶点为当前顶点。
  2. 检查当前顶点的所有邻接点,计算起点到所有已知顶点的权重,并记录下来。
  3. 从未访问过(未曾作为当前顶点)的邻接点中,选取一个起点能达到的总权重最小的顶点,作为下一个当前顶点。
  4. 重复上一步,直至图中所有顶点都被访问过。

小结

数据结构与算法图解这本书讲的很简单,但数据结构与算法并不简单,我也只学会了一些基本概念,甚至很多概念没学(像堆、B树、红黑树等)。接下来通过LeetCode刷题进行巩固,建议大家也一定要多刷题巩固。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值