《算法图解》——第二章 选择排序
第二章 选择排序
2 链表
数组中添加新元素很麻烦,预留只是权变措施,有两处缺点:
①额外请求的位置可能根本用不上,这将浪费内存。你没有使用,别人也用不了。
②超过内存之后,还是要转移。
链表中的元素可存储在内存的任何地方。
链表的每个元素都存储了下一个元素的地址,从而使一系列随机的内存地址串在一起。
犹如寻宝游戏一样,在链表中添加元素很容易:只需将其放入内存,并将其地址存储到前一个元素中。
因此,只要有足够的内存空间,就能为链表分配内存。链表的优势在插入元素方面。
3 数组
需要同时读取所有元素时,链表的效率很高。但如果你需要跳跃,链表的效率真的很低。
数组则不同:你知道其中每个元素的地址。需要随机地读取元素时,数组的效率很高,因为可迅速找到数组的任何元素。在链表中,元素并非靠在一起的,你无法迅速计算出第五个元素的内存地址,而必须先访问第一个元素以获取第二个元素的地址,再访问第二个元素以获取第三个元素的地址,以此类推,直到访问第五个元素。
练习
2.1假设你要编写一个记账的应用程序。
你每天都将所有的支出记录下来,并在月底统计支出,算算当月花了多少钱。因此,你执行的插入操作很多,但读取操作很少。该使用数组还是链表呢?
链表;聊表读取速度慢,插入速度快。而且只有要随机访问元素时,链表的读取速度慢。但是要读取所有元素,这种情况下,聊表的读取速度并不慢。
4 在中间插入
使用链表插入元素很简单,只需要修改它前面的那个元素指向的地址。而使用数组时,则必须将后面的元素都向后移,数组同时还要考虑空间的问题。
5 删除
同样,链表更好,因为只要修改前一个元素指向的地址即可。使用数组时,删除元素后,必须将后面的元素都向前移,不过不同于插入,不用考虑空间的问题。
下面是常见数组和链表操作的运行时间
有两种访问方式:随机访问和顺序访问。顺序访问意味着从第一个元素开始逐个地读取元素。链表只能顺序访问:要读取链表的第十个元素,得先读取前九个元素,并沿链接找到第十个元素。随机访问意味着可直接跳到第十个元素。
练习
2.2 假设你要为饭店创建一个接受顾客点菜单的应用程序。这个应用程序存储一系列点菜单。服务员添加点菜单,而厨师取出点菜单并制作菜肴。这是一个点菜单队列:服务员在队尾添加点菜单,厨师取出队列开头的点菜单并制作菜肴。你使用数组还是链表来实现这个队列呢?
链表;因为要执行插入操作,不需要执行查找,厨师总是从队列中取出第一个。
2.3 我们来做一个思考实验。假设Facebook记录一系列用户名,每当有用户试图登录Facebook时,都查找其用户名,如果找到就允许用户登录。由于经常有用户登录Facebook,因此需要执行大量的用户名查找操作。假设Facebook使用二分查找算法,而这种算法要求能够随机访问——立即获取中间的用户名。考虑到这一点,应使用数组还是链表来存储用户名呢?
有序数组;数组支持随机访问,二分查找算法的话还要是有序的。
2.4 经常有用户在Facebook注册。假设你已决定使用数组来存储用户名,在插入方面数组有何缺点呢?具体地说,在数组中添加新用户将出现什么情况?
数组插入的速度很慢。而且是二分查找,数组必须是有序的,那么添加一个新用户,都必须都数组进行排序。
2.5 实际上,Facebook存储用户信息时使用的既不是数组也不是链表。假设Facebook使用的是一种混合数据:链表数组。这个数组包含26个元素,每个元素都指向一个链表。例如,该数组的第一个元素指向的链表包含所有以A打头的用户名,第二个元素指向的链表包含所有以B打头的用户名,以此类推。假设Adit B在Facebook注册,而你需要将其加入前述数据结构中。因此,你访问数组的第一个元素,再访问该元素指向的链表,并将Adit B添加到这个链表末尾。现在假设你要查找Zakhir H。因此你访问第26个元素,再在它指向的链表(该链表包含所有以z打头的用户名)中查找Zakhir H。请问,相比于数组和链表,这种混合数据结构的查找和插入速度更慢还是更快?你不必给出大O运行时间,只需指出这种新数据结构的查找和插入速度更快还是更慢。
查找时,比链表快,数组慢;插入时,比数组快,但与链表相当。
6 选择排序
选择排序是一种灵巧的算法,但其速度不是很快,其运算时间为O(n*n)。
代码(将数组元素按从小到大的顺序排列):
①先编写一个用于找到数组中最小元素的函数
②再进行排序
def findSmallest(arr): #函数是找到数组中最小的元素 smallest = arr[0] #用于存储最小的值,同时假定第一个元素是最小的 smallest_index = 0 #最小的值的索引,同时也假定最小值的索引是0 for i in range(1, len(arr)): #遍历数组中每一个元素,从第二个开始 if arr[i] < smallest: #如果第二个元素小于第一个元素 smallest = arr[i] #那么赋值给smallest smallest_index = i #索引同样操作 return smallest_index #返回数组中最小值的索引 def selectionSort(arr): #选择排序函数 newArr = [] #一个列表用于保存 for i in range(len(arr)): #遍历每一个元素 smallest = findSmallest(arr) #用上面的函数找到最小值
return newArrprint(selectionSort([5, 3, 6, 2, 10]))
7 小结
计算机内存犹如一大堆抽屉。
需要存储多个元素时,可使用数组或链表。
数组的元素都在一起。
链表的元素是分开的,其中每个元素都存储了下一个元素的地址。
数组的读取速度很快。
链表的插入和删除速度很快。
在同一个数组中,所有元素的类型都必须相同(都为int、double等)。