常见排序总结

说明:

  1. 冒泡和直接选择区别:
    冒泡排序是通过比较相邻两个位置的元素,前面的值大了把两个位置的元素交换,把最大的值放到后面序号上;
    直接选择排序是把最大值与各元素依次比较,把最大值存储起来,最终放到最后的位置上,它没有交换元素。

  2. nums属于列表,是可更改元素
    输入元素nums属于列表,是可更改参数,当作为函数调用的时候,fun(nums),函数内部对nums的更改在函数外部同样会生效 ;若在调用的时候写成(nums[:]),这时候传递的不是nums,所以这样是不会改变nums的值。

  3. 每种排序方法的时间、空间复杂度以及稳定性
    时间复杂度:对数据总的操作次数。反映当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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值