排序算法总结以及python代码实现

最近在找工作面试的时候总是会被问到排序算法的种种,现在对排序算法进行一个系统的总结。也算是再复习一遍的,有好多自己也忘得差不多了。


排序有内部排序和外部排序两大类。内部排序值得是所有的排序是在内存中进行;外部排序则是因为数据量太大,一次不能将全部数据放在内存中,在排序过程中,需要访问外存。

spacer.gifwKioL1YbcrmA3MCeAAHx5kBeWIE174.jpg

关于时间复杂度和空间复杂度

    由于时间关系,我没有自己画,以下的表格是在别处转的,详见常用排序算法总结

                        wKiom1Ybc7WyN_ktAAOUTdyWHXg570.jpg  

wKioL1Ybc9LzvHuHAAIqJuh3C8w016.jpg

1.插入排序--直接插入排序(Straight Insertion Sort)

基本思想:

将一个记录插入到已经有序的表中,从而得到一个新的,记录数+1的有序表。即:先将序列的第一个记录看成是一个有序的子序列,然后从第二个记录逐个进行插入,直至整个序列有序为止。

如果遇到元素相等时,那么将插入的元素插入到与其相等的元素之后。相等元素的前后顺序并没有改变,所以插入排序是稳定的。

算法的实现:

def insertion_sort(arr):
        arrlen = len(arr)
        for i in range(1, arrlen):
            insert(arr, i)
    
def insert(arr, i):
        tmp = arr[i]
        j = i
        # 查找第i的元素应该的位置, 并且
        # 顺便把比它大的元素往后挪 
        # 其实是用了心思在里面的
        while j > 0 and tmp < arr[j - 1]:
            arr[j] = arr[j - 1]
            j -= 1
            arr[j] = tmp
a = [65,6,3,5,54,65]
insertion_sort(a)
print a

 2.希尔排序

基本思想:

希尔排序又叫做缩小增量排序。先将整个序列分割成若干个子序列,并对每个子序列进行直接插入排序,再对全部序列进行直接插入排序。

                        wKiom1YbiaHi_7B_AAEWU2ohFCA072.jpg


算法实现:

def shell_sort(seq):
    incr = len(seq)/2
    while(incr>=1):
        for i in range(incr,len(seq)):
            tmp=seq[i]
            pos=i;
            for j in range(i-incr,-1,-incr):
                if seq[j]>tmp:
                    seq[j+incr]=seq[j]
                    pos=j
            seq[pos]=tmp
        incr = incr/2  
    return seq
if __name__ == '__main__':  
    A = [10, -3, 5, 7, 1, 3, 7]    
    print 'Before sort:',A    
    shell_sort(A)    
    print 'After sort:',A

3.选择排序--简单选择排序

基本思想:

在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。

算法实现:

def selectionSort(seq):
    length=len(seq)
    for i in range(length):
        mini=min(seq[i:])
        if seq[i]>mini:
            j=seq.index(mini,i)
            seq[i],seq[j]=seq[j],seq[i]


if __name__=='__main__':
    seq=[3,4,5,9,3,1,5,7,90,-2,]
    selectionSort(seq)
    print(seq)


效率:

最坏情况下,即待排序记录初始状态是按第一条记录最大,之后的记录从小到大顺序排列,则需要移动记录的次数最多为3(n-1)。简单选择排序过程中需要进行的比较次数与初始状态下待排序的记录序列的排列情况无关。当i=1时,需进行n-1次比较;当i=2时,需进行n-2次比较;依次类推,共需要进行的比较次数是(n-1)+(n-2)+…+2+1=n(n-1)/2,即进行比较操作的时间复杂度为O(n^2),进行移动操作的时间复杂度为O(n)。(百度百科

简单选择排序是不稳定排序。



简单选择排序的改进——二元选择排序

简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可。


4.选择排序--堆排序

基本思想:

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。

堆排序的过程相对来说比较复杂,现只给出算法实现,详细过程请参照另外一篇博文:堆排序学习笔记

算法实现:

def fixDown(a,k,n): #自顶向下堆化,从k开始堆化
	N=n-1
	while 2*k<=N:
		j=2*k
		if j<N and a[j]<a[j+1]: #选出左右孩子节点中更大的那个
			j+=1
		if a[k]<a[j]:
			a[k],a[j]=a[j],a[k]
			k=j
		else:
			break

def heapSort(l):
	n=len(l)-1
	for i in range(n//2,0,-1):
		fixDown(l,i,len(l))
	while n>1:
		l[1],l[n]=l[n],l[1]
		fixDown(l,1,n)
		n-=1
	return l[1:]

l=[-1,26,5,77,1,61,11,59,15,48,19] #第一个元素不用,占位
res=heapSort(l)
print(res)

wKiom1YbntywVGmvAAD91O9CnxA624.jpg

5.交换排序--冒泡排序

基本思想:

在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的数往上冒。即:每当相邻的数比较后,发现排序不符合要求时,将他们互换。

示例:

wKiom1Ybwe_yBhTDAAHwxegrlhM430.jpgpython代码实现:

#!/usr/bin/env python
#coding:utf-8
def bubbleSort(seq):
    length=len(seq)
    for i in range(length):
        for j in range(length-1,i,-1):
            if seq[j-1]>seq[j]:
                seq[j-1],seq[j]=seq[j],seq[j-1]


if __name__=='__main__':
    seq=[2,9,7,7,4,3,2,-4,54,-7,0]
    bubbleSort(seq)
    print(seq)

冒泡排序算法的改进(两种)


    1. 设置一标志性变量pos,用于记录每趟排序中最后一次进行交换的位置。由于pos位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到pos位置即可。

    2. 传统冒泡排序中每一趟排序操作只能找到一个最大值或最小值,我们考虑利用在每趟排序中进行正向和反向两遍冒泡的方法一次可以得到两个最终值(最大者和最小者) , 从而使排序趟数几乎减少了一半。

6. 交换排序—快速排序(Quick Sort)

本思想:

1)选择一个基准元素,通常选择第一个元素或者最后一个元素,

2)通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。

3)此时基准元素在其排好序后的正确位置

4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。

示例:

                                      wKioL1YbxtCCsAz8AADuk4NPmU4294.jpg

python代码实现:

#!/usr/bin/env python
#coding:utf-8
def qsort(seq):
    if seq==[]:
        return []
    else:
        pivot=seq[0]
        lesser=qsort([x for x in seq[1:] if x<pivot])
        greater=qsort([x for x in seq[1:] if x>=pivot])
        return lesser+[pivot]+greater

if __name__=='__main__':
    seq=[5,6,78,9,0,-1,2,3,-65,12]
    print(qsort(seq))

7. 归并排序(Merge Sort)

基本思想:

归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

wKiom1Yb0_GR1OnFAAJj-LTFvpw029.jpg代码实现:

def mergesort(seq):
	if len(seq)<=1:
		return seq
	mid=int(len(seq)/2)
	left=mergesort(seq[:mid])
	right=mergesort(seq[mid:])
	return merge(left,right)

def merge(left,right):
	result=[]
	i,j=0,0
	while i<len(left) and j<len(right):
		if left[i]<=right[j]:
			result.append(left[i])
			i+=1
		else:
			result.append(right[j])
			j+=1
	result+=left[i:]
	result+=right[j:]
	return result

if __name__=='__main__':
	seq=[4,5,7,9,7,5,1,0,7,-2,3,-99,6]
	print(mergesort(seq))	

效率:

时间复杂度不管在什么情况下都为n(nlogn)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章