面试官:写一个冒泡排序和快速排序吧

Posted 爬虫工程师之家

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试官:写一个冒泡排序和快速排序吧相关的知识,希望对你有一定的参考价值。

排序算法是面试必考题,本文聊一聊通过交换顺序从而得到有序数列的冒泡排序和快速排序。




首先来看冒泡排序

冒泡排序是一种简单的排序算法,我们最终要一个由小到大或由大到小的排序数列,每次比较两个相邻的元素,如果他们的顺序错误就把他们交换过来,算法名称的由来也是因为越小或越大的元素会经由交换,慢慢的浮到数列的顶端。


算法描述:

1、比较相邻的元素,如果第一个比第二个大(或者第一个比第二个小),就交换他们两个;

2、对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大或最小的数;

3、重复以上步骤,直到排序完成。


执行一次的过程可以用动图展示出来:

该图片选自《算法从入门到“放弃”(6)- 冒泡排序》https://zhuanlan.zhihu.com/p/40859355


代码实现:

def bubbling_sort(alist): ''' 冒泡排序 :param alist: 无序数列 :return: 有序数列 ''' n = len(alist) # 共执行循环 for i in range(0,n): # 执行一次循环 for j in range(0,n-1-i):            #判断当前值和下一个值的大小 if alist[j] > alist[j+1]:                #如果当前值大于下一个值,则交换 alist[j],alist[j+1] = alist[j+1],alist[j]

if __name__ == '__main__': alist = [66,1,3,-9,20,7,100,0] bubbling_sort(alist)    print("最终结果为:",alist)    #[-9, 0, 1, 3, 7, 20, 66, 100]




接下来,我们来看快速排序


快速排序使用分拆排序方法,将一组数列分为两个子数列。


1、从数列中挑出一个元素,称为"基准",一般我们取数列中索引为0的数值

2、对数列进行拆分和排序,比基准元素小的元素放在基准元素的左侧,比基准元素大的元素放在基准元素的右侧。经过排序后,该基准就找到了自己应该在的位置,此时最原始的数列被分为两个子数列;


假设我们现在对“6 1 2 7 9 3 4 5 10 8”这个 10 个数进行排序。首先在这个序列中随便找一个数作为基准数(不要被这个名词吓到了,就是一个用来参照的数,待会你就知道它用来做啥的了)。为了方便,就让第一个数 6 作为基准数吧。接下来,需要将这个序列中所有比基准数大的数放在 6 的右边,比基准数小的数放在 6 的左边,类似下面这种排列。

3 1 2 5 4 6 9 7 10 8

该段选自《算法 3:最常用的排序——快速排序》http://wiki.jikexueyuan.com/project/easy-learn-algorithm/fast-sort.html


此时生成两个子数列,分别为3 1 2 5 4和9 7 10 8

3、通过递归,把子数列按照相同的方法进行拆分和排序。


实现方法:

要实现快速排序,就是要找到无序数列中每个元素应该放置的位置。


以下段落该段选自《算法 3:最常用的排序——快速排序》http://wiki.jikexueyuan.com/project/easy-learn-algorithm/fast-sort.html


方法其实很简单:分别从初始序列“6 1 2 7 9 3 4 5 10 8”两端开始“探测”。先从找一个小于 6 的数,再从找一个大于 6 的数,然后交换他们。这里可以用两个变量 i 和 j,分别指向序列最左边和最右边。我们为这两个变量起个好听的名字“哨兵 i”和“哨兵 j”。刚开始的时候让哨兵 i 指向序列的最左边(即 i=1),指向数字 6。让哨兵 j 指向序列的最右边(即 j=10),指向数字 8

面试官:写一个冒泡排序和快速排序吧

首先哨兵 j 开始出动。因为此处设置的基准数是最左边的数,所以需要让哨兵 j 先出动,这一点非常重要(请自己想一想为什么)。哨兵 j 一步一步地向左挪动(即 j--),直到找到一个小于 6 的数停下来。接下来哨兵 i 再一步一步向右挪动(即 i++),直到找到一个数大于 6 的数停下来。最后哨兵 j 停在了数字 5 面前,哨兵 i 停在了数字 7 面前。

面试官:写一个冒泡排序和快速排序吧

面试官:写一个冒泡排序和快速排序吧

现在交换哨兵 i 和哨兵 j 所指向的元素的值。交换之后的序列如下。

6 1 2 5 9 3 4 7 10 8

面试官:写一个冒泡排序和快速排序吧

面试官:写一个冒泡排序和快速排序吧

到此,第一次交换结束。接下来开始哨兵 j 继续向左挪动(再友情提醒,每次必须是哨兵 j 先出发)。他发现了 4(比基准数 6 要小,满足要求)之后停了下来。哨兵 i 也继续向右挪动的,他发现了 9(比基准数 6 要大,满足要求)之后停了下来。此时再次进行交换,交换之后的序列如下。

6 1 2 5 4 3 9 7 10 8

第二次交换结束,“探测”继续。哨兵 j 继续向左挪动,他发现了 3(比基准数 6 要小,满足要求)之后又停了下来。哨兵 i 继续向右移动,糟啦!此时哨兵 i 和哨兵 j 相遇了,哨兵 i 和哨兵 j 都走到 3 面前。说明此时“探测”结束。我们将基准数 6 和 3 进行交换。交换之后的序列如下。

3 1 2 5 4 6 9 7 10 8

面试官:写一个冒泡排序和快速排序吧

到此第一轮“探测”真正结束。此时以基准数 6 为分界点,6 左边的数都小于等于 66 右边的数都大于等于 6。回顾一下刚才的过程,其实哨兵 j 的使命就是要找小于基准数的数,而哨兵 i 的使命就是要找大于基准数的数,直到 i 和 j 碰头为止。

OK,解释完毕。现在基准数 6 已经归位,它正好处在序列的第 6 位。此时我们已经将原来的序列,以 6 为分界点拆分成了两个序列,左边的序列是“3 1 2 5 4”,右边的序列是“ 9 7 10 8 ”。

如上过程,我们可以通过代码来实现它

def quick_sort(alist,first,last): ''' 快速排序 :param alist: 无序序列 :param first: 游标起始位置 :param last: 游标终止位置 :return: 有序序列 ''' # 当游标重合即只有一个元素时为有序状态 if first >= last: return # 选取一个基准值 mid_value = alist[first] # 游标起始位置 low = first # 游标终止位置 high = last while low < high: # 当游标终止位置值大于基准值并且两个游标没有相遇时 while low < high and alist[high] >= mid_value: high = high - 1 # 否则退出循环进行值交换 alist[low] = alist[high]
# 当游标起始位置值小于基准值并且两个游标没有相遇时 while low < high and alist[low] < mid_value: low = low + 1 # 否则退出循环进行值交换 alist[high] = alist[low] # 找到基准值位置 alist[low] = mid_value print(alist) # 对分割的两个部分进行递归操作 quick_sort(alist,first,low-1) quick_sort(alist,low+1,last)
if __name__ == '__main__': alist = [66,1,3,-9,20,7,100] n = len(alist) quick_sort(alist,0,n-1) print("最终结果为:",alist) #[-9, 1, 3, 7, 20, 66, 100]


以上即为冒泡排序和快速排序的粗浅解释及看法,欢迎大家一起交流。

以上是关于面试官:写一个冒泡排序和快速排序吧的主要内容,如果未能解决你的问题,请参考以下文章

动画:面试官问我插入排序和冒泡排序哪个更牛逼?

动画:如何给面试官写一个满意的冒泡排序

如何给面试官写一个满意的冒泡排序

八十一最快最优的快速排序和优化

美团面试,我竟然输给了冒泡排序……

冒泡排序 面试必备