Python实现排序算法
一:排序算法
归并排序
归并排序的基本思想:
\qquad
归并排序可以看做一个分而治之的过程,先将待排序列等分为两半,再对每一半继续归并排序(递归操作,将序列等分直到每个序列只有一个元素时结束递归),最后将得到的每两个有序序列归并成一个即可
归并排序的性能分析:
时间复杂度:
- 排序趟次为logn,每趟需做n次基本操作(每个序列中每个元素都是操作目标),则总操作次数为n*logn,时间复杂度则为O(nlogn)
空间复杂度:
- 用到了辅助数组,长度与待排序列长度一致,故空间复杂度为O(n)
def mergeSort(arr, low, high):
if low < high:
mid = (low + high) // 2
mergeSort(arr, low, mid)
mergeSort(arr, mid+1, high)
merge(arr, low, mid, high)
helper = [None] * len(arr) # 与整个待排序列等长的辅助数组helper
def merge(arr, low, mid, high):
n = low; i = low; j = mid+1
for k in range(low,high+1):
helper[k] = arr[k]
while i < mid+1 and j < high+1:
if(helper[i] < helper[j]):
arr[n] = helper[i]
i += 1
else:
arr[n] = helper[j]
j += 1
n += 1
# 将剩下的未归并的序列元素依次放入辅助数组中(两个while循环只会执行其中一个)
while i < mid+1:
arr[n] = helper[i]
i += 1; n += 1
while j < high+1:
arr[n] = helper[j]
j += 1; n += 1
快速排序
快速排序的基本思想:
\qquad
通过一趟排序将要排序的数据根据枢轴值分割成独立的两部分,一部分都比枢轴值小,另外一部分都比枢轴值大,然后再按此方法对这两部分数据再分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
- 初始序列越接近有序,快速排序算法的效率就越低;
- 当根据枢轴值区域被划分为0个元素和n-1个元素时,效率最低;
时间复杂度:
- 最好情况:O(logn)
- 最坏情况:O(n^2) (待排序列已经有序)
空间复杂度:
- 递归用到了栈,空间复杂度为O(logn)
from random import randint
def quickSort(arr, low, high):
'''
这里取arr[low]到arr[high]的任意一个值作为枢轴值pivot作为对快速排序的改进
(减少区域被划分为0个元素和n-1个元素的几率)
'''
i = low; j = high
if low < high:
pivotPos = randint(low, high)
pivot = arr[pivotPos]
arr[low], arr[pivotPos] = arr[pivotPos], arr[low] # 将枢轴值放到下标low处
while i < j:
while i < j and arr[j] > pivot:
j -= 1
if i < j: # 每次交换前,判断该轮是否其实已经结束
arr[i] = arr[j]
i += 1
while i < j and arr[i] < pivot:
i += 1
if i < j:
arr[j] = arr[i]
j -= 1
arr[i] = pivot
quickSort(arr, low, i-1)
quickSort(arr, i+1, high)
插入排序
直接插入排序
直接插入排序的基本思想:
\qquad
每趟将一个待排序列的关键字按照其值的大小插入到前面已经排好的有序序列中,直到所有待排序列的关键字都被插入到有序序列
时间复杂度:
- 最坏情况:整个序列逆序,则每次都要进行插入,总执行次数为1+2+…+n-1=(n-1)*n/2,时间复杂度为O(n^2)
- 最好情况:整个序列有序,则每次都不需要插入,时间复杂度为O(n)
空间复杂度:O(1)
def DinsertSort(arr):
# 直接插入排序算法
length = len(arr)
for i in range(1, length):
j = i - 1
temp = arr[i]
while j>=0 and arr[j]>temp:
arr[j+1] = arr[j]
j -= 1
arr[j+1] = temp
折半插入排序
折半插入排序的基本思想:
\qquad
其实是直接插入排序的改进,只是将查找有序序列的插入位置的方法改为二分查找法
时间复杂度:
- 最坏情况:整个序列逆序,则每次都要进行插入,总执行次数为1+2+…+n-1=(n-1)*n/2,时间复杂度为O(n^2)
- 最好情况:整个序列有序,则每次都不需要插入(但每次都查找了logn次),时间复杂度为O(nlogn)
空间复杂度:O(1)
def Bsearch(arr, low, high, k):
# 二分查找法
while low <= high:
mid = (low + high) // 2
if arr[mid] == k:
return mid
elif arr[mid] < k:
low = mid + 1
else:
high = mid - 1
return high + 1
def BinsertSort(arr):
length = len(arr)
for i in range(1,length):
temp = arr[i]
insertPos = Bsearch(arr, 0, i-1, temp)
for j in range(i-1, insertPos-1, -1):
arr[j+1] = arr[j]
arr[insertPos] = temp
冒泡排序
冒泡排序基本思想:
\qquad
第一趟从头开始依次将连在一起的两个数两两比较,较大的数换到后一位,第一趟结束时最大值换到了序列最后;同理对序列进行第二趟排序,结束位置此时为倒数第二个;当没有发生交换时排序结束
时间复杂度:
- 最坏情况:待排序列逆序,时间复杂度为O(n^2)
- 最好情况:待排序列有序,时间复杂度为0(n)
空间复杂度:O(1)
def bubbleSort(arr):
# 这里用lastIndex记录每一趟最后发生交换的位置,依次作为对普通冒泡排序的改进
length = len(arr)
change = length - 1
while change:
lastIndex = 0 # lastIndex记录该趟最后移动元素的位置
for i in range(change):
if a[i] > a[i+1]:
a[i+1], a[i] = a[i], a[i+1]
lastIndex = i + 1
change = count # 当该趟没有移动元素,lastIndex的值为0,结束排序
选择排序
简单选择排序基本思想:
\qquad
从头到尾扫描整个序列,找出最小的关键字和第一个关键字交换,接着从剩余的关键字中继续这种选择和交换,最终使序列有序
- 时间复杂度:O( n 2 n^{2} n2)
- 空间复杂度:O(1)
def selectSort(arr):
length = len(arr)
for i in range(length):
minnumIndex = i
for j in range(i+1, length):
if arr[j] < arr[minnumIndex]:
minnumIndex = j
arr[i], arr[minnumIndex] = arr[minnumIndex], arr[i]
堆排序
堆可以看做是一棵完全二叉树,这里使用大顶堆,则该完全二叉树的根结点即为最大值。
堆排序的基本思想:
\qquad
将初始序列按照顺序写出其完全二叉树形式,对所有的非叶子节点从下往上,从右往左依次做调整(要求以其为根结点的二叉树也是大顶堆),建堆完毕;此时根结点为最大值,将其与最后一个结点交换,则最大值到达其最终位置,之后继续对二叉树剩下的结点做同样的调整(此时只有刚换上去的根结点需要调整)
def adjust(arr, low, high):
# 向下调整k位置上的结点
i = low; j = 2*i;
temp = arr[low]
while i <= j and j < high:
if arr[j] < arr[j+1]: # 将左右结点中最大的结点当做arr[j]
j += 1
if arr[j] > temp:
arr[i] = arr[j]
i = j; j = 2*i # 循环做对下一个根结点的调整
else:
break
arr[i] = temp # 存于最终位置
def heapSort(arr):
length = len(arr)
for i in range(length//2,-1,-1): # 建堆
adjust(arr, i ,length-1)
# 将根结点与每一趟的最后一个结点交换,再调整
for index in range(length-1, -1, -1):
arr[index], arr[0] = arr[0], arr[index] # 该趟最大值已到最终位置
adjust(arr, 0, index-1) # 新一轮的调整
二:二分查找
实现一个有序数组的二分查找算法
def Bsearch(arr, low, high, k):
# 二分查找法
while low <= high:
mid = (low + high) // 2
if arr[mid] == k:
return mid
elif arr[mid] < k:
low = mid + 1
else:
high = mid - 1
return high + 1
实现模糊二分查找算法(比如大于等于给定值的第一个元素)
def Bsearch(arr, low, high, k):
# 模糊二分查找法查找大于等于给定值的第一个元素和其下标
highTemp = high
while low <= high:
mid = (low + high) // 2
if arr[mid] == k:
while arr[mid-1] == k:
mid -= 1
return arr[mid],mid
elif arr[mid] < k:
low = mid + 1
else:
high = mid - 1
if high+1 < highTemp:
return arr[high+1],high+1
else:
return
用python编写程序实现Sqrt(x) (x 的平方根)
def mySqrt(x):
if x == 1:
return 1
left = 0
right = x
while left+1 < right:
m = (right + left) / 2
if m ** 2 < x:
left = m
elif m ** 2 == x:
return int(m)
else:
right = m
if int(right) ** 2 <= x:
return int(right)
else:
return int(left)