排序
1、冒泡排序
基本思想:比较相邻的元素大小,将小的前移,大的后移,就像水中的气泡一样,最小的元素经过几次移动,会最终浮到水面上。原地排序,不需要返回值。
import random
import time
def bubbleSort(list1):
for i in range(len(list1)-1):
for j in range(i+1,len(list1)):
if list1[i] > list1[j]:
list1[i],list1[j] = list1[j],list1[i]
list1 = [random.randint(1,999) for i in range(10000)]
list11 = list1.copy()
start1 = time.clock()
bubbleSort(list1)
end1 = time.clock()
print("冒泡排序 ",end1 - start1)
start2 = time.clock()
list4 = sorted(list11)
end2 = time.clock()
print("函数sorted排序",end2-start2)
print("排序结果",list1==list4)
输出:
冒泡排序 9.2384
函数sorted排序 0.0041
排序结果 True
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
2、选择排序
基本思想:第1趟,在待排序记录r1 ~ r[n]中选出最小的记录,
将它与r1交换;第2趟,在待排序记录r2 ~ r[n]中选出最小的记录,
将它与r2交换;以此类推,第i趟在待排序记录r[i] ~ r[n]中选出最小的记录,
将它与r[i]交换,使有序序列不断增长直到全部排序完毕。
原地排序,不需要返回值。
import random
import time
def selectSort(list1):
for i in range(len(list1)-1):
min = i #已排序好的序列的最后一个元素的下标i
for j in range(i+1,len(list1)):
if list1[min] > list1[j]: #找出未排序的序列中最小的元素的下标min
min = j
if min != i: # 对应的元素值交换
list1[i], list1[min] = list1[min], list1[i]
list1 = [random.randint(1,999) for i in range(10000)]
list11 = list1.copy()
start1 = time.clock()
selectSort(list1)
end1 = time.clock()
print("选择排序 ",end1 - start1)
start2 = time.clock()
list4 = sorted(list11)
end2 = time.clock()
print("函数sorted排序",end2-start2)
print("排序结果",list1==list4)
输出:
选择排序 7.4946
函数sorted排序 0.0040
排序结果 True
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
3、插入排序
插入排序总结:
当前需要排序的元素(array[i]),跟已经排序好的最后一个元素比较(array[i-1]),
如果满足条件继续执行后面的程序,否则循环到下一个要排序的元素。
缓存当前要排序的元素的值,以便找到正确的位置进行插入。
排序的元素跟已经排序好的元素比较,比它大的向后移动(升序)。
要排序的元素,插入到正确的位置。
import random
import time
def insertSort(list1):
for i in range(1,len(list1)):
if list1[i] < list1[i-1]: #当前要排序的元素比前面的小
index = i #保存当前元素的下标,这个变量是用来记录排序元素需要插入的位置
temp = list1[i] #保存当前元素的值
while index > 0 and list1[index-1] > temp: #找到正确的排序位置
list1[index] = list1[index-1] #如果前面的比当前元素值大,就往后移动一位
index -= 1
if index != i:
list1[index] = temp
list1 = [random.randint(1,999) for i in range(10000)]
list11 = list1.copy()
start1 = time.clock()
insertSort(list1)
end1 = time.clock()
print("插入排序 ",end1 - start1)
start2 = time.clock()
list4 = sorted(list11)
end2 = time.clock()
print("函数sorted排序",end2-start2)
print("排序结果",list1==list4)
输出:
插入排序 9.1573
函数sorted排序 0.0042
排序结果 True
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
5、希尔排序
希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,
是直接插入排序算法的一种更高效的改进版本。
希尔排序是非稳定排序算法。该方法因DL.Shell于1959年提出而得名。
希尔排序是记录按下标的一定增量分组,对每组使用直接插入排序算法排序;
随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,
整个文件恰被分成一组,算法便终止。
import random
import time
def shellSort(list1):
# 设定步长
step = len(list1)//2
while step > 0:
for i in range(step, len(list1)):
# 类似插入排序, 当前值与指定步长之前的值比较, 符合条件则交换位置
while i >= step and list1[i-step] > list1[i]:
list1[i], list1[i-step] = list1[i-step], list1[i]
i -= step
step = step//2 #步长值改为之前的二分之一
list1 = [random.randint(1,999) for i in range(10000)]
list11 = list1.copy()
start1 = time.clock()
shellSort(list1)
end1 = time.clock()
print("希尔排序 ",end1 - start1)
start2 = time.clock()
list4 = sorted(list11)
end2 = time.clock()
print("函数sorted排序",end2-start2)
print("排序结果",list1==list4)
输出:
希尔排序 0.0957
函数sorted排序 0.0041
排序结果 True
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
6、基数排序
基数排序(radix sort)属于“分配式排序”(distribution sort),桶式排序
又称“桶子法”(bucket sort)或bin sort,顾名思义,
它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,
藉以达到排序的作用,基数排序法是属于稳定性的排序,
其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,
在某些时候,基数排序法的效率高于其它的稳定性排序法。
例如:对数组中的元素按照从低位到高位排序,对于[192,221,12,23]
第一轮按照个位数字相同的放在一组,是s[1] =[221],s[2]=[192,12],s[3]=23,
第二轮按照十位数字进行排序,s[1]=[12],s[2]=[221,23],s[9]=[192],
第三轮按照百位数字进行排序,s[0]=[12,23],s[1]=[192],s[2]=[221]
import random
import time
def radixSort(list1,d):
for i in range(d):#多少位数就进行几轮排序
s = [[] for x in range(10)] # 因为每一位数字都是0~9,故建立10个桶
for j in list1:
s[j//(10**i)%10].append(j) #第一次取个位数,第二次取十位数,第三次取百位数
list1 = [k for t in s for k in t] #遍历二维列表s中的元素,赋值给list1
return list1
list1 = [random.randint(1,999) for i in range(10000)]
list11 = list1.copy()
start1 = time.clock()
list2 = radixSort(list1,len(str(max(list1))))
end1 = time.clock()
print("基数排序 ",end1 - start1)
start2 = time.clock()
list4 = sorted(list11)
end2 = time.clock()
print("函数sorted排序",end2-start2)
print("排序结果",list2==list4)
输出:
基数排序 0.0208
函数sorted排序 0.0047
排序结果 True
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
7、桶排序
桶排序 (Bucket sort)或所谓的箱排序,顾名思义就是运用桶的思想来将数据放到相应的桶内,再将每一个桶内的数据进行排序,最后把所有桶内数据按照顺序取出来,得到的就是我们需要的有序数据
import random
import time
def bucketSort(list1):
maxNum = max(list1)
minNum = min(list1)
bucket = [0] *(maxNum-minNum+1) #创建一个桶列表,长度为要排序的列表中最大的数与最小的数的差加1
for i in list1:
bucket[i-minNum] += 1 #把要排序的列表中的元素放到相应下标的桶中
list2 = []
for i in range(len(bucket)): #遍历桶列表,输出排序好的新列表
if bucket[i] != 0:
list2 += [i + minNum] * bucket[i] #对桶列表中的元素进行计数,有几个就加几个相同的元素
return list2
list1 = [random.randint(1,999) for i in range(10000)]
list11 = list1.copy()
start1 = time.clock()
list2 = bucketSort(list1)
end1 = time.clock()
print("桶排序 ",end1 - start1)
start2 = time.clock()
list4 = sorted(list11)
end2 = time.clock()
print("函数sorted排序",end2-start2)
print("排序结果",list2==list4)
输出:
桶排序 0.0027
函数sorted排序 0.0046
排序结果 True
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
8、快速排序
快速排序(quickSort)
快速排序是对冒泡排序的改进
快排的思想:首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,
然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,
这个过程称为一趟快速排序。
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j–),找到第一个小于key的值A[j],
将A[j]和A[i]互换;
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],
将A[i]和A[j]互换;
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,
即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,
直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。
另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。
时间复杂度:O(nlgn)
快速排序是原地排序,不需要返回值
8.1 采用递归
import random
import time
def parttion(list1,left,right): # 找到中间那个数
key = list1[left] #以第一个数组元素作为关键数据
while left < right:
# 由后开始向前搜索(right–),找到第一个小于key的值A[right],将A[left]和A[right]互换
while left < right and list1[right] >= key:
right -= 1
list1[left] = list1[right]
# 由前开始向后搜索(left++),找到第一个大于key的A[left],将A[right]和A[left]互换;
while left < right and list1[left] <= key:
left += 1
list1[right] = list1[left]
list1[left] = key
return left
def quickSort(list1,left,right):
if left < right:
# 先对序列排序,并找到一个元素A,这个元素的特点是:左边的所有元素<=A,右边的所有元素>=A
p = parttion(list1,left,right)
quickSort(list1,left,p) #对A的左边进行递归,重复过程parttion()
quickSort(list1,p+1,right) #对A的右边进行递归,重复过程parttion()
list1 = [random.randint(1,999) for i in range(10000)]
list11 = list1.copy()
start1 = time.clock()
quickSort(list1,0,len(list1)-1)
end1 = time.clock()
print(“快速排序(递归)”,end1 - start1)
start2 = time.clock()
list4 = sorted(list11)
end2 = time.clock()
print(“函数sorted排序”,end2-start2)
print(“排序结果”,list1==list4)
输出:
快速排序(递归) 0.0593
函数sorted排序 0.0042
排序结果 True
采用递归弊端:当数据量大的时候,在python中采用递归方式时有可能会造成内存溢出,并且排序时间较长,不建议使用。
8.2 采用栈
import random
import time
def quickSort(list1):
stack = []
stack.append(0)
stack.append(len(list1)-1)
while stack:
high = right = stack.pop()
low = left = stack.pop()
if low == high:
continue
key = list1[left]
while left < right:
while left < right and list1[right] >= key:
right -= 1
list1[left] = list1[right]
while left < right and list1[left] <= key:
left += 1
list1[right] = list1[left]
list1[left] = key
<span class="hljs-keyword">if</span> left - <span class="hljs-number">1</span> > low:
stack.append(low)
stack.append(left-<span class="hljs-number">1</span>)
<span class="hljs-keyword">if</span> left +<span class="hljs-number">1</span> < high:
stack.append(left+<span class="hljs-number">1</span>)
stack.append(high)
list1 = [random.randint(1,999) for i in range(10000)]
list11 = list1.copy()
start1 = time.clock()
quickSort(list1)
end1 = time.clock()
print("快速排序(栈) ",end1 - start1)
start2 = time.clock()
list4 = sorted(list11)
end2 = time.clock()
print(“函数sorted排序”,end2-start2)
print(“排序结果”,list1==list4)
输出:
快速排序(栈) 0.0570
函数sorted排序 0.0058
排序结果 True
9、归并排序
归并排序:先分开再合并,分开成单个元素,合并的时候按照正确顺序合并。
分:
假如我们有一个n个数的数列,下标从0到n-1
首先是分开的过程
1 我们按照 n//2 把这个数列分成两个小的数列
2 把两个小数列 再按照新长度的一半 把每个小数列都分成两个更小的
。。。一直这样重复,一直到每一个数分开了
并:
两个有序数组排序的方法则非常简单,同时对两个数组的第一个位置进行比大小,将小的放入一个空数组,然后被放入空数组的那个位置的指针往后 移一个,然后继续和另外一个数组的上一个位置进行比较,以此类推。到最后任何一个数组先出栈完,就将另外一个数组里的所有元素追加到新数组后面。
由于递归拆分的时间复杂度是logN 然而,进行两个有序数组排序的方法复杂度是N,该算法的时间复杂度是N*logN 所以是NlogN。
9.1 采用递归
import random import time
def merge(left,right ):
# 从两个有顺序的列表里边依次取数据比较后放入result
# 每次我们分别拿出两个列表中最小的数比较,把较小的放入result
result = []
while len(left)>0 and len(right)>0 :
#为了保持稳定性,当遇到相等的时候优先把左侧的数放进结果列表,因为left本来也是大数列中比较靠左的
if left[0] <= right[0]:
result.append( left.pop(0) )
else:
result.append( right.pop(0) )
#while循环出来之后 说明其中一个数组没有数据了,我们把另一个数组添加到结果数组后面
result += left
result += right
return result
def mergeSort( li ):
#不断递归调用自己一直到拆分成成单个元素的时候就返回这个元素,不再拆分了
if len(li) == 1:
return li
<span class="hljs-comment">#取拆分的中间位置</span>
mid = len(li) // <span class="hljs-number">2</span>
<span class="hljs-comment">#拆分过后左右两侧子串</span>
left = li[:mid]
right = li[mid:]
<span class="hljs-comment">#对拆分过后的左右再拆分 一直到只有一个元素为止</span>
<span class="hljs-comment">#最后一次递归时候ll和lr都会接到一个元素的列表</span>
<span class="hljs-comment"># 最后一次递归之前的ll和rl会接收到排好序的子序列</span>
ll = mergeSort( left )
rl = mergeSort( right )
<span class="hljs-comment"># 我们对返回的两个拆分结果进行排序后合并再返回正确顺序的子列表</span>
<span class="hljs-comment"># 这里我们调用拎一个函数帮助我们按顺序合并ll和lr</span>
<span class="hljs-keyword">return</span> merge(ll , rl)
list1 = [random.randint(1,999) for i in range(10000)]
list11 = list1.copy()
start1 = time.clock()
list2 = mergeSort(list1)
end1 = time.clock()
print("归并排序 ",end1 - start1)
start2 = time.clock()
list4 = sorted(list11)
end2 = time.clock()
print(“函数sorted排序”,end2-start2)
print(“排序结果”,list2==list4)
输出:
归并排序 0.1365
函数sorted排序 0.0043
排序结果 True
10、堆排序
堆排序是采用二叉堆的数据结构来实现的。
二叉堆具有以下性质:
- 父节点的键值总是大于或等于(小于或等于)任何一个子节点的键值。
- 每个节点的左右子树都是一个二叉堆(都是最大堆或最小堆)。
步骤:
构造最大堆(Build_Max_Heap):若数组下标范围为0~n,考虑到单独一个元素是大根堆,则从下标n/2开始的元素为大根堆。于是只要从n/2-1开始,向前依次构造大根堆,这样就能保证,构造到某个节点时,它的左右子树都已经是大根堆。
堆排序(HeapSort):由于堆是用数组模拟的。得到一个大根堆后,数组内部并不是有序的。因此需要将堆化数组有序化。思想是移除根节点,并做最大堆调整的递归运算。第一次将heap[0]与heap[n-1]交换,再对heap[0…n-2]做最大堆调整。第二次将heap[0]与heap[n-2]交换,再对heap[0…n-3]做最大堆调整。重复该操作直至heap[0]和heap[1]交换。由于每次都是将最大的数并入到后面的有序区间,故操作完后整个数组就是有序的了。
最大堆调整(Max_Heapify):该方法是提供给上述两个过程调用的。目的是将堆的末端子节点作调整,使得子节点永远小于父节点 。
import random
import time
def heapSort(list1) :
n = len(list1)
first = n//2-1
for start in range(first,-1,-1) : #构造大根堆
maxHeapify(list1,start,n-1)
for end in range(n-1,0,-1): #堆排,将大根堆转换成有序数组
list1[end],list1[0] = list1[0],list1[end]
maxHeapify(list1,0,end-1)
#最大堆调整:将堆的末端子节点作调整,使得子节点永远小于父节点
#start为当前需要调整最大堆的位置,end为调整边界
def maxHeapify(list1,start,end):
root = start
while True :
child = root*2 +1 #调整节点的子节点
if child > end : break
if child+1 <= end and list1[child] < list1[child+1] :
child = child+1 #取较大的子节点
if list1[root] < list1[child] : #较大的子节点成为父节点
list1[root],list1[child] = list1[child],list1[root] #交换
root = child
else :
break
list1 = [random.randint(1,999) for i in range(10000)]
list11 = list1.copy()
start1 = time.clock()
heapSort(list1)
end1 = time.clock()
print("推排序 ",end1 - start1)
start2 = time.clock()
list4 = sorted(list11)
end2 = time.clock()
print(“函数sorted排序”,end2-start2)
print(“排序结果”,list1==list4)
输出:
推排序 0.0940
函数sorted排序 0.0041
排序结果 True