前言
学无止境,笔勤不辍。最近笔者重温了几种排序算法,之后会一一详解,今天重点来讲述一下快速排序(非常好的排序利器),本文将会用三种不同的实现方法来详解它,希望給大家一些启示。
一、快速排序的原理
快速排序算法其实也带有一部分分治法的思想在中间,它的主要步骤(以升序为例):一次快速排序,确定一个哨兵(标志元素)使得它之前的所有元素都小于它,它之后的元素都大于它,然后使用递归调用,让所有元素都排好序,快速排序最优的时间复杂度是O(n*lgn)
二、实现方式
1.方法一新建一个数组,然后递归调用
代码如下(示例):
以下用python代码为例
def quicksort(A,start,end):
if end - start==0:# 递归结束标志,说明分配下来的数组没有值,说明此部分已经整理完成
return A
else:
A1 = []#存储比标兵小的元素
A2 = []#存储比标兵大的元素
A3 = []#存储等于标兵的元素
for i in A:
if i< A[end-1]:
A1.append(i)
elif i>A[end-1]:
A2.append(i)
elif i == A[end-1]:
A3.append(i)
return quicksort(A1,start,len(A1))+A3+quicksort(A2,0,len(A2))
if __name__=='__main__':
A = [1,2,3,2,5,4,10,2,5,3,8,7,1]
print(quicksort(A,0,len(A)))
结果如下:
当然,这中解决方法,需要创建新的数组它所需要的空间复杂度很大大致是O(N^2) 时间复杂度大约是O(N*lgN)
2.方法二双指针,然后递归调用
代码如下(示例):
def quicksort(A,start,end):
i = start
j = end-1
print(i)
print(end)
if end-start==1 or end-start==0:#由于某些情况下
#end和start可能是相同的 所以用or去判断
return A
key =A[j]
while i<j:
if A[i]<key:#若i所指元素小于标兵则i++,判断下一个
i+=1
continue
else:#否则与j所指元素交换,由于此时i所指元素一定大于等于标兵
#因此此时j所指元素一定可以放在标兵值的后面所以 j--
k = A[j]
A[j] = A[i]
A[i] = k
j-=1
if A[j]>key:
j-=1
continue
else:
k =A[j]
A[j]=A[i]
A[i]=k
i+=1
return quicksort(A[start:i],start,i)+[A[i]]+quicksort(A[i+1:end],0,len(A[i+1:end]))
if __name__=='__main__':
A = [1,2,3,5,2,4]
print(quicksort(A,0,len(A)))
结果如图:
PYTHON的传参有一点点恶心。然后这个折磨了笔者好久,希望能大佬给点建议!然后此时空间复杂度是O(n) 如果原本数组空间不计,那就是O(1)了
(在解释F3时,我好像明白了这个的局限性。。到时候修改!)
3.利用原数组,创造一个新的空间
def quicksort(A, start, end):
i = -1#指向最后一个小于等于标兵的位置下标(0-0、i)是小于等于标兵的部分
j = 0#指向最新一个大于标兵的位置(i+1,j)是大于标兵的部分
#等于标兵的部分其实放哪都可以
key = A[end ]
while j <= end - 1:
if A[j] > key:#原地开辟一个大于标兵值的一个空间
j += 1
else:#如果j指向的值小于等于标兵,则可以交换i+1和j指向的元素
#因为,i指向的是最后一个小于等于标兵的值,i+1指向的是大于标兵的值
#由此交换i+1和j不会该变快排的两个部分。
k = A[j]
i += 1
A[j] = A[i]
A[i] = k
j += 1
A.insert(i+1 , key)#在相应的位置插入标兵值
A.pop(end+1)#因为list长度+1,要把末尾重复的标兵值删除
return i + 1
if __name__ == '__main__':
A = [1, 2, 3, 5, 2, 4, 5, 324, 123, 1,3]
def find(A, start, end):
if start < end:
num = quicksort(A, start, end)
find(A, start, num - 1)
find(A, num + 1, end)
find(A, 0, len(A)-1)
print(A)
结果如图:
其实和方法2差不多,大家可以比较一下,选择适合自己的双指针方式
空间复杂度是O(1) 时间复杂度最优O(n*lgn)
下标和个数不是一一对应而是数组元素个数=下标+1这一点很重要!
流程图后续也会放上!
总结
以上就是今天要讲的内容,其实研究算法是一件有趣又痛苦的事情,希望大家能通过笔者的分享收获一些有用的东西,也可以订阅一下专栏!哈哈,期待下一次的更新…