冒泡排序
Python实现代码:
如果数组本身就是升序的,则时间复杂度为O(n),如果数组本身是降序的则时间复杂度为O(n^2).
def bubble_sort(self,nums):
#每一轮排序都把大的元素往数组的后部移动
#每一轮排序完成后,数组的最后一个元素必定是这一轮找到的最大的元素,这个元素的位置就可以定死了
#定义一个标志位flag,如果一轮排序中全程没有元素发生交换,则flag=0,表示此时数组已经是有序的了,于是可以直接结束排序,否则本轮排序结束后还要进行下一轮排序
for end in range(len(nums)-1,-1,-1):#end就是每一轮冒泡遍历的最后一个元素位置,end不断的向前移动
flag = 0 #用于标志本轮遍历过程中是否存在元素交换
for i in range(end):#本轮冒泡遍历开始
if nums[i]>nums[i+1]:
nums[i],nums[i+1] = nums[i+1],nums[i]
flag = 1 #标志
if flag == 0:#如果全程无交换,则表示nums已经有序了,结束
break
return
插入排序
如果数组本身就是升序的,则时间复杂度为O(n),如果数组本身是降序的则时间复杂度为O(n^2).
def insertion_sort(self,nums):
#从第二个元素开始插入
for i in range(1,len(nums)):
tmp = nums[i] #本次循环插入的元素
j = i #j指向本次循环插入元素的位置
while(j-1>=0 and nums[j-1]>tmp): #如果前一个元素比本次插入的元素大,则不断向后移动前一个元素
nums[j] = nums[j-1]
j-=1
nums[j] = tmp
return
快速排序
参考:https://www.bilibili.com/video/BV1at411T75o?share_source=copy_web
#步骤:
#选取一个元素作为pivot中心轴(可以直接选择数组最左边的那一个)
#将数组中所有小于pivot中心轴的元素移动到pivot中心轴的左边
#将数组中所有大于pivot中心轴的元素移动到pivot中心轴的右边
#递归的处理pivot中心轴左边的数组和右边的数组
如果数组本身就是有序的,又指定选择数组最左边的元素作为中心轴的话,那么时间复杂度就是O(n^2),因为递归对pivot左边和右边的数组进行排序,都会发现左边的数组为空,右边的数组为除去pivot元素以外剩余的元素。
def fast_sort(self,nums,L,R):
#递归终止条件
if L>=R:
return
left = L
right = R
#随机选择一个pivot中心轴,然后把它放到最左边去,作为pivot
n = random.randint(L,R)
nums[left],nums[n] = nums[n],nums[left]
pivot = nums[left]
while(left<right):
#先移动右边right
while(left<right and nums[right]>=pivot):
right-=1
if left<right:#说明当前pivot中心轴的值大于nums[right],于是要把nums[right]移动到nums[left]处
nums[left] = nums[right]
#移动完right之后,再移动left
while(left<right and nums[left]<=pivot):
left+=1
if left<right:
nums[right] = nums[left]
nums[left] = pivot
self.fast_sort(nums,L,left-1)
self.fast_sort(nums,left+1,R)
希尔排序
#希尔排序:增量从大到小的插入排序
def shell_sort(self,nums):
D = len(nums)//2 #初始增量
while(D>0):
print(D)
#进行增量为D的希尔排序
for i in range(D,len(nums)):#从第二个元素开始插入
tmp = nums[i] #本次循环插入的元素
j = i
while(j-D>=0 and nums[j-D]>tmp):#如果前一个元素比本次插入的元素大,则不断向后移动前一个元素
nums[j] = nums[j-D]
j-=D #移动指针
nums[j] = tmp
#一次间隔为D的希尔排序已完成,更新间隔
D = D//2
return
选择排序
#选择排序,第一次遍历数组所有元素,选择最小的元素放到数组第一个位置,第二次遍历数组剩余的元素,选择最小的元素放到数组的第二个位置....
时间复杂度为O(n^2)
def selector_sort(self,nums):
for i in range(len(nums)):
min_Index = i #记录最小元素的索引
for j in range(i+1,len(nums)):
if nums[j]<nums[min_Index]:
min_Index = j
nums[min_Index],nums[i] = nums[i],nums[min_Index]
return
归并排序
参考:https://www.bilibili.com/video/BV1et411N7Ac?share_source=copy_web
#步骤:
#归并排序,将待排序数组分为若干组,每个数字单独作为一组。
#将若干组进行两两合并,保证合并后的序列是有序的
#重复第二步操作,直到只剩下一个序列为止。
设数组长度为N,则将数组不断二分直到每个元素作为一组需要logN步,每一步都需要合并有序数列的过程,其时间复杂度为O(N),所以归并排序算法的时间复杂度是NlogN。归并排序是稳定的排序算法
def mergesort(self,nums,left,right): #对数组nums中下标从left到right的元素进行排序,调用此函数的方法为self.mergesort(nums,0,len(nums)-1)
if left>=right: #默认空数组和单个字符的数组是有序的,直接返回
return
#二分数组,递归到数组中每个数组单独作为一组为止
mid = (left+right)//2
self.mergesort(nums,left,mid)
self.mergesort(nums,mid+1,right)
#有序合并数组
self.merge_array(nums,left,mid,right)
def merge_array(self,nums,left,mid,right):#有序合并nums数组中下标从left到mid和从mid+1到right的两部分(都是闭区间),易知合并的时间复杂度是O(N),其中N为合并后数组的长度
temp = [] #暂存有序合并后的元素
cur1 = left #双指针法有序合并两个数组
cur2 = mid+1
while(cur1<=mid and cur2<=right):
if nums[cur1]<nums[cur2]:
temp.append(nums[cur1])
cur1+=1
elif nums[cur1]>nums[cur2]:
temp.append(nums[cur2])
cur2+=1
else:
temp.append(nums[cur1])
cur1+=1
temp.append(nums[cur2])
cur2+=1
while cur1<=mid:
temp.append(nums[cur1])
cur1+=1
while cur2<=right:
temp.append(nums[cur2])
cur2+=1
nums[left:right+1] = temp #导回有序合并后的元素
堆排序
大顶堆的定义:
- 是一个完全二叉树
- 根节点大于左子节点和右子节点
- 左子树和右子树也是一个大顶堆
#步骤:
堆排序(Heap Sort)是利用堆进行排序的方法。其基本思想为:将待排序列构造成一个大顶堆(或小顶堆),整个序列的最大值(或最小值)就是堆顶的根结点,将根节点的值和堆数组的末尾元素交换,此时末尾元素就是最大值(或最小值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次大值(或次小值),如此反复执行,最终得到一个有序序列。
如何将二叉树调整为堆:
从二叉树的最后一个非叶子节点开始(如果二叉树用数组存储且下标从0开始,则最后一个非叶子节点的下标为len(nums)/2-1)从后向前,将非叶子节点的值和其左右子节点的值进行比较,如果子节点大则将其值与当前节点值进行交换,从后向前对数组中的所有非叶子节点进行此操作即可将树调整为一个大顶堆,如下图所示:
删除大顶堆的最大元素并重新调整大顶堆的方法:
取出nums中的第一个元素nums[0]即是最大元素,将nums的最后一个元素nums[-1]代替nums[0],此时左子树和右子树都是最大堆,但是树的根节点小于左子树根节点和右子树根节点。比较根节点和左右子节点,选出其中大的与根节点交换,如果是左子节点和根节点交换,则递归的对左子树进行同样的调整操作,如果是右子节点和根节点交换,则递归的对右子树进行同样的调整操作。
def HeapAdjust(self,nums,i,length):#调整索引为i的节点为根节点的子树,使其成为大顶堆,这个length是用来标志待排序的nums的长度的而不是len(nums),所以这个参数必须保留
max_index = i #最大元素的索引
lchild = 2*i+1 #左孩子节点的索引
rchild = 2*i+2 #右孩子节点的索引
#如果左子节点和右子节点的值大于当前根节点的值,则找出其中较大的那个跟根节点进行交换,lchild<length判断这个子节点是否存在
if lchild<length and nums[lchild]>nums[max_index]:
max_index = lchild
if rchild<length and nums[rchild]>nums[max_index]:
max_index = rchild
if max_index!=i:
nums[i],nums[max_index] = nums[max_index],nums[i]
#此外,交换之后,还要递归的去调整子树
self.HeapAdjust(nums,max_index,length)
def HeapSort(self,nums):
#初始化调整出一个最大堆,len(nums)//2-1就是最后一个非叶子节点的索引,从最后一个非叶子节点开始从后向前调整这棵树为一个最大堆
cur = len(nums)//2-1
while(cur>=0):
self.HeapAdjust(nums,cur,len(nums))
cur-=1
end = len(nums)-1
while(end>=0):
#交换堆顶元素和最后一个元素
nums[end],nums[0] = nums[0],nums[end]
self.HeapAdjust(nums,0,end)
end-=1