说明:
-
冒泡和直接选择区别:
冒泡排序是通过比较相邻两个位置的元素,前面的值大了把两个位置的元素交换,把最大的值放到后面序号上;
直接选择排序是把最大值与各元素依次比较,把最大值存储起来,最终放到最后的位置上,它没有交换元素。 -
nums属于列表,是可更改元素
输入元素nums属于列表,是可更改参数,当作为函数调用的时候,fun(nums),函数内部对nums的更改在函数外部同样会生效 ;若在调用的时候写成(nums[:]),这时候传递的不是nums,所以这样是不会改变nums的值。 -
每种排序方法的时间、空间复杂度以及稳定性
时间复杂度:对数据总的操作次数。反映当n变化时,操作次数呈现什么规律。
空间复杂度:算法在计算机内执行时所需存储空间的度量。
稳定性:两个相当元素,在排序之后,元素索引的位置不变。稳定是指:一切尽在掌控中,元素的位置在控制中;不稳定相等的元素,在排序之后,元素位置可能变化,也可能不变,是随机的。
稳定性的用处:如果排序元素只是单单的一维元素,那么排序之后考虑稳定性没有意义;如果排序元素是高维或者字典,每个元素都关联了其它信息,相等的两个元素在排序之后希望不要更换,这时候对于不稳定的排序方法就是不可行了。(比如数据库中一个表的主键排序,主键是有关联到其他信息.另外比如对英语字母排序,英语字母的数值关联到了字母这个有意义的信息)
排序方法 | 时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 稳定 |
短冒泡排序 | O(n^2) | O(n^2) | O(n) | O(1) | 稳定 |
快速排序 | O(nlog2^n) | O(n^2) | O(nlog2^n) | O(nlog2^n) | 不稳定(不稳定发生在中枢元素和i交换的时刻) |
直接选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定(不稳定发生在每次最大值和应该放在位置交换的时刻) |
大根堆排序 | O(nlog2^n) | O(nlog2^n) | O(nlog2^n ) | O(1) | 不稳定(在一个树上的不会破坏稳定性,但不在一个树上时候,前一个树交换了元素,就会破坏树的稳定性) |
直接插入排序 | O(n^2) | O(n^2) | O(n) | O(1) | 稳定 |
希尔排序 | O(n^2) | O(n^2) | O(n) | O(1) | 不稳定(因为是跨元素比较,所以会破坏稳定性) |
归并排序 | O(nlog2^n) | O(nlog2^n) | O(nlog2^n) | O(n) | 稳定 |
稳定性参考:https://www.cnblogs.com/lxy-xf/p/11321536.html
代码分享如下:
'''
参数说明:
nums:列表形式的数据;
unsort:5000以内随机打乱的数据
'''
import time
#冒泡排序
'''遍历剩下的元素,把最大值放最后'''
def bubble_sort(nums):
n = len(nums)
if n<=1:
return nums
for i in range(n-1):
for j in range(n-i-1):#这里处理的是剩下的n-i个数据,后面已经排好的i个数据已经不需要遍历了。
if nums[j+1] < nums[j]:
nums[j+1], nums[j] = nums[j], nums[j+1]
return nums
nums = [1,4,2,7,4]
bubble_sort(nums)
#短冒泡排序
def shortbubble_sort(nums):
n = len(nums)
exchange = True
i = 0
while i<n and exchange:#如果第一遍排序之后,列表没有变化即灭有交换过一次,则说明列表本来就是有序的
exchange = False
for j in range(n-i-1):
if nums[j+1] < nums[j]:
nums[j], nums[j+1] = nums[j+1], nums[j]
exchange = True
i += 1
return nums
l = [1,2,3,4,5,6,7,8,9]
print(shortbubble_sort(l))
#快速排序
'''从后开始判断,把小于第一个元素的值放前面,然后从前开始判断,把大于第一个元素的值放后面,循环
pivot:每个序列的第一个元素
'''
def quick_sort(nums,start_index,end_index):
if start_index >= end_index:
return
pivot = nums[start_index]
i,j = start_index, end_index
while i<j:
while i<j and nums[j] >= pivot:
j -= 1
nums[i], nums[j] = nums[j], nums[i]
while i<j and nums[i] <= pivot:
i += 1
nums[i], nums[j] = nums[j], nums[i]
quick_sort(nums,0,i)
quick_sort(nums,i+1,end_index)
return nums
nums = [1,2,5,7,9,2,6,1]
quick_sort(nums,0,len(nums)-1)
#直接选择排序
'''选择出最小的值,放在前面'''
def straight_select_sort(nums):
n = len(nums)
if n <= 1:
return nums
for i in range(n):
max_index = 0
for j in range(n - i):
'''这一块交换太多,应该只交换索引,提高效率.即找出最大值的索引,然后把最大值的索引和要放的位置交换'''
if nums[j] > nums[max_index]:#这一块区分冒泡排序:冒泡是相邻比较,直接选择是把j之前的最大值与j比较
max_index = j
nums[-i - 1], nums[max_index] = nums[max_index], nums[-i - 1]
return nums
nums = [1,4,2,7,4]
straight_select_sort(nums)
#堆排序
'''
相当于二叉树的思想,先建立大根堆,然后把堆顶元素放在最后,重新建立大根堆,再把对顶元素放在最后面
巧妙的地方是:1、根节点与子节点的关系,例如,根节点是i=0,则他的两个子节点是2*i+1,2*i+2;2、待排序数据的最后一个根节点是n/2向下取整
速度快的原因是,比较的次数减小了,只把父节点和自己的子节点进行比较
'''
#调整某个根节点及其子节点都满足大根堆
def heap_adjust(nums, root_index, end):
n = len(nums[:end])
while 2 * root_index + 1 < n:#如果是根
if 2 * root_index + 1 < n:#左子树存在
if nums[2 * root_index + 1] > nums[root_index]:#左子树大于根
nums[2 * root_index + 1], nums[root_index] = nums[root_index], nums[2 * root_index + 1]
if 2 * root_index + 2 < n:#右子树存在
if nums[2 * root_index + 2] > nums[root_index]:#右子树大于根
nums[2 * root_index + 2], nums[root_index] = nums[root_index], nums[2 * root_index + 2]
root_index += 1#向下遍历根
def heap_sort(nums):
n = len(nums)
root_index = int((n - 1) // 2)#第一个根节点
while root_index > -1:
heap_adjust(nums, root_index, n)
root_index -= 1#向上遍历根
end = n - 1
while end > -1:
nums[0], nums[end] = nums[end], nums[0]#把顶根(最大值)放到最后一个最后
heap_adjust(nums, 0, end)#重新调整顶根
end -= 1#下一次放的位置
return nums
nums = [2,1,5,8,3,1]
heap_sort(nums)
#直接插入排序
'''
直接插入排序:从1开始,每次与前一个元素n-1比较,直到小于前一个元素就停止比较,
然后继续访问后一个元素i+1
'''
def straight_insertion_sort(nums):
n = len(nums)
if n <= 1:
return nums
i = 1 #从第2个元素开始
while i < n:
j = i
while j>0:
if nums[j-1]>nums[j]:
nums[j],nums[j-1] = nums[j-1], nums[j]
j -= 1
else:
break
i += 1
return nums
nums = [1,5,3,8,2,7]
straight_insertion_sort(nums)
#希尔排序
'''希尔排序是在直接插入排序的基础上进行的,它是以gap大小的间隔进行直接插入排序,
所以只用把直接插入排序中的1换成gap就可以,然后gap减半直到1
技巧:这里可以学习一下以间隔gap大小如何取值
'''
def shell_sort(nums):
n = len(nums)
gap = int(n/2)
while gap > 0:#直到间隔为1
for i in range(gap, n):#从第gap个元素开始
#ni = i
while i>0:
if nums[i-gap]>nums[i]:
nums[i], nums[i-gap] = nums[i-gap], nums[i]
i -= gap
else:
break
gap = int(gap/2)#间隔减半
return nums
nums = [1,4,2,7,4]
shell_sort(nums)
#归并排序, ========= 分而治之 ==========
'''
分成左右两部分,然后分别排序,然后合并。
1、比较 a[i] 和 b[j] 的大小,若 a[i]≤b[j],则将第一个有序表中的元素a[i]复制到 r[k] 中,
并令i 和 k 分别加上1;否则将第二个有序表中的元素b[j]复制到r[k] 中,并令 j 和 k 分别加上1;
2、如此循环下去,直到其中一个有序表取完;
3、然后再将另一个有序表中剩余的元素复制到 r 中从下标 k 到下标t的单元。
'''
def merge(left_nums,right_nums):
n1 = len(left_nums)
n2 = len(right_nums)
i = 0
j = 0
merge_result = []
while i<n1 and j<n2:
if left_nums[i] <= right_nums[j]:#如果左边小,把左边先放入
merge_result.append(left_nums[i])
i += 1
else:
merge_result.append(right_nums[j])#右边小,把右边先放入
j += 1
merge_result.extend(left_nums[i:] or right_nums[j:])#如果左半部分取完了,把右半部分全部加入,否则右半部分加入
return merge_result
def merge_sort(nums):
n = len(nums)
if n<=1:#当只剩下一个元素时候,就返回该元素
return nums
middle = int(n//2)
left = merge_sort(nums[:middle])
right = merge_sort(nums[middle:])
return merge(left, right)#把最后的两个元素比较,合并之后,返回给这两个元素的上一层的结果。
nums = [1,5,6,9,3,2,5,8,1,5,2,8,3,9,0]
print(merge_sort(nums))
#生成数据
unsort = [1,5,6,9,3,2,5,8,1,5,2,8,3,9]
#测试
n = len(unsort)-1
start = time.clock()
bubble_resurt= bubble_sort(unsort)
end = time.clock()
time_bubble = end-start
start = time.clock()
quick_resurt = quick_sort(unsort, 0, n)
end = time.clock()
time_quick = end-start
start = time.clock()
straight_selsct_result = straight_select_sort(unsort)
end = time.clock()
time_straight_select = end-start
start = time.clock()
heap_result = heap_sort(unsort)
end = time.clock()
time_heap = end-start
start = time.clock()
straight_insertion_result = straight_insertion_sort(unsort)
end = time.clock()
time_straight_insert = end-start
start = time.clock()
shell_result = shell_sort(unsort)
end = time.clock()
time_shell = end-start
start = time.clock()
merge_result = merge_sort(unsort)
end = time.clock()
time_merge = end-start2