我的第一本算法书+算法图解 学习笔记

我的第一本算法书

第一章 数据结构

1-1 链表

数据呈线性排列。在链表中,数据的添加和删除都较为方便,就是访问比较耗费时间。
数据分散存储在内存中,无须存储在连续空间内。
数据的访问只能顺序访问。
可以在链表尾部使用指针,并且让它指向链表头部的数据,将链表变成环形。这便是“循环链表”,也叫“环形链表”。
可以把指针设定为两个,并且让它们分别指向前后数据,这就是“双向链表”。
双向链表存在两个缺点:一是指针数的增加会导致存储空间需求增加;二是添加和删除数据时需要改变更多指针的指向。

1-2 数组

数据按顺序存储在内存的连续空间内。
数据是存储在连续空间内的,每个数据的内存地址(在内存上的位置)都可以通过数组下标算出。
数据的访问为随机访问。
在链表中访问数据较为复杂,添加和删除数据较为简单;而在数组中访问数据比较简单,添加和删除数据却比较复杂。

1-3 栈

一种数据呈线性排列的数据结构。
只能访问最新添加的数据。
往栈中添加数据的操作叫作“入栈”,从栈中取出数据的操作叫作“出栈”。
往栈中添加数据的时候,新数据被放在最上面。从栈中取出数据时,是从最上面,也就是最新的数据开始取出的。
像栈这种最后添加的数据最先被取出,即“后进先出”的结构,我们称为 L a s t I n F i r s t O u t Last In First Out LastInFirstOut,简称 L I F O LIFO LIFO
添加和删除数据的操作只能在一端进行,访问数据也只能访问到顶端的数据。想要访问中间的数据时,就必须通过出栈操作将目标数据移到栈顶才行。

1-4 队列

往队列中添加数据的操作叫作“入队”。从队列中删除数据的操作叫作“出队”。
往队列中添加数据时,数据被加在最上面。从队列中取出(删除)数据时,是从最下面,也就是最早入队的数据开始的。
像队列这种最先进去的数据最先被取来,即“先进先出”的结构,我们称为 F i r s t I n F i r s t O u t First In First Out FirstInFirstOut,简称 F I F O FIFO FIFO
在栈中,数据的添加和删除都在同一端进行,而在队列中则分别是在两端进行的。队列也不能直接访问位于中间的数据,必须通过出队操作将目标数据变成首位后才能访问。

1-5 哈希表(散列表)

哈希表存储的是由键(key)和值(value)组成的数据。一般来说,我们可以把键当成数据的标识符,把值当成数据的内容。
使用哈希函数计算字符串的哈希值,对数组的长度求 m o d mod mod,存入数组中,存储位置冲突的话用链表在已有数据的后面继续存储新的数据。

  • 填装因子
    需要计算数组中被占用的位置数
    填装因子度量的是散列表中有多少位置是空的
    一旦填装因子开始增大(大于0.7),你就需要在散列表中添加位置,这被称为调整长度
附:哈希函数

把给定的数据转换成固定长度的无规律数值
哈希函数的特征

  1. 输出的哈希值数据长度不变。无论输入的是多大的数据,输出的哈希值的长度也保持不变。

  2. 如果输入的数据相同,那么输出的哈希值也必定相同。

  3. 即使输入的数据相似,输出的哈希值也会有很大的差异。输入相似的数据并不会导致输出的哈希值也相似。

  4. 即使输入的两个数据完全不同,输出的哈希值也有可能是相同的,虽然出现这种情况的概率比较低。这种情况叫作“哈希冲突”。
    如果两个键映射到了同一个位置,就在这个位置存储一个链表。

  5. 不可能从哈希值反向推算出原本的数据。输入和输出不可逆这一点和加密有很大不同。

  6. 求哈希值的计算相对容易。

散列函数知道数组有多大,只返回有效的索引。《算法图解》P62
Python提供的散列表实现为字典,你可使用函数dict来创建散列表。

>>> book = dict()//创建一个空的散列表
//也可以用>>> book = {
   }表示

>>> book["apple"] = 0.67
>>> book["milk"] = 1.49
>>> book["avocado"] = 1.49

>>> print book["avocado"]
1.49
  • 将散列表用作缓存
    缓存的数据存储在散列表中

1-6 堆

一种图的树形结构,被用于实现“优先队列”,优先队列是一种数据结构,可以自由添加数据,但取出数据时要从最小值开始按顺序取出。在堆的树形结构中,各个顶点被称为“结点”(node),数据就存储在这些结点中。
堆中的每个结点最多有两个子结点。结点的排列顺序为从上到下,同一行里则为从左到右。
子结点必定大于父结点。因此最小值被存储在顶端的根结点中。
数据插入:将数据插在末尾(先后顺序按排列顺序),若父结点大于子结点,则交换父子结点的位置。重复这样的操作直到数据都符合规则,不再需要交换为止。
数据取出:取出的是最上面的数据,将排列顺序位于末尾的移动到最顶端。如果子结点的数字小于父结点的,就将父结点与其左右两个子结点中较小的一个进行交换。重复这个操作直到数据都符合规则,不再需要交换为止。
堆中最顶端的数据始终最小,所以无论数据量有多少,取出最小值的时间复杂度都为 O ( 1 ) O(1) O(1)。另外,因为取出数据后需要将最后的数据移到最顶端,然后一边比较它与子结点数据的大小,一边往下移动,所以取出数据需要的运行时间和树的高度成正比。假设数据量为n,根据堆的形状特点可知树的高度为 l o g ( 2 n ) log(2n) log(2n) ,那么重构树的时间复杂度便为 O ( l o g ( n ) ) O(log(n)) O(log(n))。添加数据也一样。在堆的最后添加数据后,数据会一边比较它与父结点数据的大小,一边往上移动,直到满足堆的条件为止,所以添加数据需要的运行时间与树的高度成正比,也是 O ( l o g ( n ) ) O(log(n)) O(log(n))

1-7 二叉查找树

每个结点最多有两个子结点。每个结点的值均大于其左子树上任意一个结点的值,均小于其右子树上任意一个结点的值。
最小值的查找:从顶端开始,往其左下的末端寻找。
最大值的查找:从顶端开始,往其右下的末端寻找。
二叉查找树是二分查找算法思想的树形结构体现,如果结点数为 n n n,而且树的形状又较为均衡的话,比较大小和移动的次数最多就是 l o g ( 2 n ) log(2n) log(2n)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值