《算法图解》——第二章 选择排序

        第二章    选择排序

1    内存的工作原理
计算机就像是很多抽屉的集合体,每个抽屉都有地址。
需要将数据存储到内存时,你请求计算机提供存储空间,计算机给你一个存储地址。需要存储多项数据时,有两种基本方式——数组和链表。
 
 

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)      #用上面的函数找到最小值
newArr.append(arr.pop(smallest))  #⭐⭐pop()函数用于移除列表中的一个元素(默认最后一个元素,这里是smallest),并且返回该元素的值。

return newArrprint(selectionSort([5, 3, 6, 2, 10]))


 

 

7    小结

计算机内存犹如一大堆抽屉。
需要存储多个元素时,可使用数组或链表。
数组的元素都在一起。
链表的元素是分开的,其中每个元素都存储了下一个元素的地址。
数组的读取速度很快。
链表的插入和删除速度很快。
在同一个数组中,所有元素的类型都必须相同(都为int、double等)。

posted @ 2018-04-23 13:10  方玲是个小可爱  阅读(1043)  评论(0编辑  收藏  举报