排序算法学习,python实现

时间:2023-12-11 17:50:08

原创博文,转载请注明出处

利用周六周末的时间把几种基本的排序方法用python实现了一下,废话少说,直接上代码。

本文不注重基础知识的讲解,只做大致的描述,大家如果不清楚概念,自行查找资料。

直接插入排序:

  每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。

 def insert(arr):
l = len(arr)
for i in range(1,l):
if arr[i]<arr[i-1]:
temp =arr[i]
j=i-1
arr[j+1]=arr[j]
j=j-1
while j>=0 and arr[j]>temp:
arr[j+1]=arr[j] #元素后移
j=j-1
arr[j+1]=temp
return arr
arra=[98,36,-9,0,47,23,1,8,10,7]
print insert(arra)

折半插入排序:

  折半插入排序就是就是将上面直接插入排序中查找有序子表的工作使用折半查找来实现,在确定出待插入位置后,就可以统一的向后移动元素。

 # -*- coding: cp936 -*-
def find_ff(arr,x):#折半查找
size =len(arr)
low = 0
high = size-1
while low<=high:
mid =(low+high)/2
if arr[low]==x:
return low
elif arr[high]==x:
return high
elif arr[mid]==x:
return mid
elif arr[mid] > x:
high = mid-1
else:
low = mid+1 def insert_ff(arr):#升序
l = len(arr)
for i in range(1,l):
if arr[i]<arr[i-1]:
temp =arr[i]
j=i-1
low = 0
high = j
while low<=high:
mid = (low+high)/2
print 'mid',mid
if arr[mid] > temp:
high = mid-1
else:
low = mid+1
print 'high+1',high+1
print 'j',j
if j==0:
arr[j+1],arr[j]=arr[j],arr[j+1]
else:
print 'action 2 '
for x in range(j,high,-1):#移动元素
print 'x',x
arr[x+1]=arr[x]
arr[high+1]=temp
print arr
return arr arra=[98,36,-9,0,47,23,1,8,10,7]
print insert_ff(arra)

在分析折半插入排序时 一定要搞清楚元素的索引值。所以我在里面加入了print语句是为了测试而加入的 ,可以忽略。

希尔排序

 def shell(arr):
l = len(arr)
h = l/2
while h>=1:
for i in range(l):
j = i
while j>=h and arr[j]<arr[j-h]:
arr[j],arr[j-h]=arr[j-h],arr[j]
h=h/2
return arr
arra=[98,36,-9,0,47,23,1,8,10,7]
print shell(arra)

交换排序 : 冒泡排序和快速排序

   冒泡排序比较简单

# -*- coding: cp936 -*-
def bubble(arr):#降序
size = len(arr)
for i in range(size-1):
for j in range(i,size-1):
if arr[j+1]>arr[j]:
arr[j+1],arr[j]=arr[j],arr[j+1] return arr arra=[98,36,-9,0,47,23,1,8,10,7]
print bubble(arra)

快速排序 :对冒泡排序的一种改进,基本思想是基于分治的

 # -*- coding: cp936 -*-
def partition(data,low,high):
if low<high:
x = data[low]#以第一个元素作为枢轴值
while low<high: #循环跳出条件
while low<high and data[high]>=x:
high -= 1
data[low]=data[high]#将小于枢轴值得元素移到左端
while low<high and data[low]<=x:
low += 1
print low
data[high]=data[low]#将大于枢轴值的元素移到右端
data[low]=x
return low
def quick(arr,low,high):
if low < high:
pivotop = partition(arr,low,high)
quick(arr,low,pivotop-1)
quick(arr,pivotop+1,high)
return arr arra=[98,36,-9,0,47,23,1,8,10,7]
print quick(arra,0,len(arra)-1)

在这儿我们举了一个特例,就是第一个元素大于后面的所有元素,在测试过程中发现了好多问题,比如第六行和第九行再次加入了low<high的判断条件是为了避免边界条件问题,边界条件作为测试过程的重要环节,由于逻辑性较强,所以比较困难调试。

选择排序 :简单排序和堆排序

简单排序比较简单实现如下

def simple(arr):
size = len(arr)
for i in range(size):
mini = arr[i]
for j in range(i,size):
if arr[j] < mini:
mini = arr[j]
arr[i],arr[j]=arr[j],arr[i]
return arr
arra=[98,36,-9,0,47,23,1,8,10,7]
print simple(arra)

堆排序:与简单排序不同,在排序过程中,将L[1...n]看成是一棵完全二叉树的顺序存储结构,利用二叉树中双亲节点和孩子结点的内在关系(搞清楚各种二叉树的区别),在当前无序区选择关键字最大或最小的元素。

堆的定义:n个关键字序列L[1...n]成为堆,当且仅当满足下列条件之一:

1. L(i)<=L(2i),且L(i)<=L(2i+1) 称为小根堆,也就是满足双亲节点小于孩子节点

2. L(i)>=L(2i),且L(i)>=L(2i+1) 称为大根堆,也就是满足双亲节点大于孩子节点

堆排序的关键是构建初始栈,n个结点的完全二叉树,最后一个结点是第n/2个结点的孩子。对第n/2个结点为根的子树筛选,使该子树成为堆。之后向前依次对各结点为根的子树进行筛选(对于大根堆:若根结点的关键值小于孩子结点中关键值较大者,则交换)。

 堆排序这儿还是遇到了不少麻烦,一定要理清脉络。有一点需要注意,一定要考虑到只有左孩子没有右孩子的情况,网上好多版本都没有考虑到这一特殊情况,因为加入没有右孩子结点你再载入值得话就会触发异常IndexError: list index out of range。

 # -*- coding: cp936 -*-
def buildHeap(arr):
size = len(arr)
for i in range(getParent(size-1),-1,-1):
adjustDown(arr,size-1,i) #注意 size-1不是size def getParent(k):
if k%2==1:
return int(k/2)
else:
return k/2-1 def adjustDown(arr,size,k):#一定要注意size的值
print 'k',k
left = 2*k+1
right = left+1
maxp = k
if left<size and arr[k]<arr[left]:#先判断左孩子
maxp = left
if left<size and right<size:#假如有右孩子,取两个孩子结点的最大值
maxlr = left if arr[left]>arr[right] else right
if left<size and right<size and arr[k]<arr[maxlr]:
maxp = maxlr
if maxp!=k:
arr[maxp],arr[k]=arr[k],arr[maxp]
print arr
adjustDown(arr,size,maxp) def heap(arr):
size = len(arr)
buildHeap(arr)
print 'build',arr
for i in range(size-1,-1,-1):#size-1
arr[i],arr[0]=arr[0],arr[i]
print arr[i],arr[0]
adjustDown(arr,i,0)
return arr arra=[98,36,-9,47,23,1,8]
print heap(arra)

归并排序:

  归并的含义是将两个或两个以上的有序表组合成一个新的有序表。最常见的是2-路归并排序。在程序设计中merge()是把两个有序表合并的操作。递归形式的2-路归并排序算法是基于分治的,其过程如下:

分解:将含有n个元素的待排序表分成各含n/2个元素的子表,采用 2-路归并排序算法对两个子表递归的进行排序

合并:合并两个已排序的子表得到排序结果。

性能分析: 空间复杂度为 O(n),每一趟归并的时间的复杂度为O(n),共需要进行log2n趟归并,所以算法的时间复杂度为 O(nlog2n)。 merge()操作并不会改变相同关键字记录的相对次序,故算法稳定。

 def mergeSort(arr):
if len(arr)<=1:
return arr
mid = len(arr)/2
left=mergeSort(arr[:mid])
right=mergeSort(arr[mid:])
return merge(left,right) def merge(left,right):
result=[]
i,j=0,0
while len(left)>i and len(right)>j:
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 arra=[98,36,-9,0,47,23,1,8,10,7]
print mergeSort(arra)

基数排序:

园内 有一篇讲解基数排序的,http://www.cnblogs.com/Braveliu/archive/2013/01/21/2870201.html,图文并茂,很不错,具体概念就不讲解了。这儿我就用python实现一下吧。

 # -*- coding: cp936 -*-
def radixSort_LSD(arr,d):
for i in xrange(d):
S = [[] for j in xrange(10)]
for x in arr:
S[x/(10**i)%10].append(x)
arr = [x for y in S for x in y]
return arr def radixSort_MSD(arr,d):
r = int(d-1)
if r>=0:
S = [[] for j in xrange(10)]
L =[]
for x in arr:
S[x/(10**r)%10].append(x)
for y in S:
M=radixSort_MSD(y,r)
L.append(M)
arr = [x for y in L for x in y]
return arr arra=[98,36,9,0,47,23,1,8,10,7]
print radixSort_MSD(arra,2)
print radixSort_LSD(arra,2)

注意MSD和LSD的区别,

LSD的排序方式由数值的最右边(低位)开始,而MSD则相反,由数值的最左边(高位)开始。

注意一点:LSD的基数排序适用于位数少的数列,如果位数多的话,使用MSD的效率会比较好。

MSD的方式与LSD相反,是由高位数为基底开始进行分配,但在分配之后并不马上合并回一个数组中,而是在每个“桶子”中建立“子桶”,将每个桶子中的数值按照下一数位的值分配到“子桶”中。所以在MSD中我构建了一个L作为子桶。 有没有感觉python非常优雅,简简单单的几句代码就解决了问题。

计数排序: 

  计数排序一般应用于序列元素是正整数的时候,计数排序不是比较排序,排序的速度快于任何比较排序算法。在学习计数排序的时候还是费了很大功夫,一直搞不懂其中的道理。

 # -*- coding: cp936 -*-
def countSort(arr):
size = len(arr)
mini = maxp = arr[0]
for i in arr:#找出最大最小值
if i<mini:
mini = i
if i>maxp:
maxp = i
bound = maxp - mini +1
count = [0 for i in range(bound)]
sorted_arr = [0 for i in range(size)]
for i in range(size):
count[arr[i]-mini]+=1#arr的元素值个数对应count的元素值,
x=0
for i in range(mini,maxp+1):
for j in range(0,count[i-mini]):
sorted_arr[x]=i
x+=1 return sorted_arr
arra=[98,36,5,47,23,1,8]
print countSort(arra)

计数排序的时间复杂度为O(n),这是其他排序方法难以达到的(其他算法的时间复杂度要么为O(n2),要么为O(nlogn))。