近来,笔试面试总是遇到各类的排序问题,作为一个纯统计出身的直女,对数据结构那是一窍不通,然后就被花式虐了,昨天被问到归并排序,明明之前有好好看过,然而被问起来,大脑仍旧是一片空白,为了鞭策自己,准备潜心归纳一波,归纳不好的,欢迎指正啦!另外,笔者十分辣鸡,不懂c语言,以后可能会懂吧,所以附上的代码都会是python的。
冒泡排序
这个想必大家都有所耳闻吧,实在是太普遍了,冒泡,顾名思义一下,泡泡都是从水的底端往上冒,这个泡泡在这个方法中喻指值比较小的数值,因为他比他临近的数字小,所以就一直往上冒,直到到达他的位置,好,还是认真的解释一下吧,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录位置。举个栗子
有一个数列[9,1,5,8,3,7,4,6]有8个数,那我们循环7次,依次冒出最小的,第二小,第三小。。的数值,第一次循环,也就是i=1时,从下标为9的数字开始,依次比较,数值小的往上冒,这样通过第一次循环,数值最小的1就被找到了,并且顺利归位到了他所属的下标1
来自《大话数据结构》
第二次,也就是当i=2时,顺利找到数值第二小的2,将之排到下标为2的位置,然后依次循环。。。
时间复杂度:算算,假设有n个树,第一次循环需要比较(n-1)次,第二次需要比较(n-2)次,第n-1次需要比较1次,因此总共比较了n(n-1)/2次,所以总的时间复杂度就是。
上代码,自行体会~~
#冒泡排序(简单版)
def BubbleSort_1(arr):
count = len(arr)
for i in range(0,count-1):
for j in range(0,count-1-i):
if arr[j] > arr[j+1]:
arr[j],arr[j+1] = arr[j+1],arr[j]
return arr
那么问题来了,如果这个数列本身就是有序的,或者只需要简单交换一个数字就能达到有序了,那有必要还每次都两两比较吗? 答:没必要。再举个栗子{2,1,3,4,5,6,7,8}这个数列,再经过第一次冒泡后,就达到有序了,在第二轮循环中,若没有发现任何数据交换,就说明此序列已经有序了,就没有必要进行后续操作了,省时省力,岂不快活。代码只需要加一个判断是否存在数据d交换的语句便可,空说无凭,继续上代码
#冒泡排序(优化)
def BubbleSort_2(arr):
count = len(arr)
for i in range(0, count):
swapFlag = False #先假设未做交换操作
for j in range(i + 1, count):
if arr[i] > arr[j]:
arr[i],arr[j] = arr[j],arr[i]
swapFlag = True #设置交互操作标志
if not swapFlag:
break #无交换操作,表示已完成排序,退出循环
return arr
再次分析一下复杂度 ,在最好的情况下,也就是排序的表本身就是有序的,那么比较次数是n-1次,此时时间复杂度为0(n)。
当最坏的情况,即待排序表是逆序的,复杂度仍为
简单选择排序
冒泡排序的思想就是通过不断交换完成最终的排序,那是否可以在排序时找到合适的关键字再做交换,并且只移动一次就完成相应关键字的排序工作呢,这里引入选择排序
基本思想:通过n-1次的关键字间的比较,从n-i+1中选出关键字最小的记录,并和第i个记录交换之
代码如下:
def Select_sort(arr):
count = len(arr)
for i in range(0, count):
min = i
for j in range(i + 1, count):
if arr[min] > arr[j]:
min = j
arr[min], arr[i] = arr[i], arr[min]
return arr
以序列{9,1,5,8,3,7,4,6,2}为例子,当i=0时,min开始是0,然后将arr[0]与arr[j=1]到j=8进行比较,找到最小的值1,此时对应j=1,与arr[0]交换一次,也就是说在这一轮循环中,虽比较了8次大小,却只交换了一次。如图
接下来,依次类推,i=2时,交换9和2的位置
直到序列有序,最多经过8次交换。
复杂度分析
从简单选择排序的过程来看,它的最大特点即交换数据次数想当少,节约了时间。同时,无论最好最差情况,比较次数均为
对于交换次数,最好的情况当序列升序时,交换0次,当初始序列降序时,情况最差,交换次数是n-1次。基于最终排序时间是比较与交换次数总和,因此时间复杂度依然是O(n^2)
尽管与冒泡法排序复杂度相同,但简单选择方法在性能上的排序还是要略优于冒泡排序。
直接插入排序
基本思想:将一个记录插入到已经排好序的有序表中,从而得到一个新的,记录数增1的有序表
解释的稍微通俗点就是,你拿到一个序列{9,1,5,8,3,7,4,6,2},从第二个数字开始与前面比较,1比9大,所以1,9交换位置,那个1,9算是个有序数列列了,现在看数字5,大小介于1和9之间,于是将之插入到1与9之间,如此循环,便能得到有序数列,代码如下
def insert_sort(arr):
count = len(arr)
for i in range(1,count):
key = arr[i]
j = i - 1
while j >= 0:
if arr[j] > key:
arr[j+1] = arr[j]
arr[j] = key
j -= 1
return arr
时间复杂度
当最好的情况,也就是排序表本身就是有序的,那么时间复杂度为O(n)
当最坏的情况,即时间表是逆序的,需要比较(n+2)(n-1)/2次,记录移动次数达到最大值(n+4)(n-1)/2次
如果记录是随机的,依据概率相同的原则,平均比较和移动次数均为(n^2)/4,因此直接插入排序法的时间复杂度是O(n^2)
基于同样的时间复杂度,直接插入排序比冒泡法和简单选择排序的性能要好一些