Python数据结构与算法 算法基础6

归并排序:

        首先介绍并实现归并算法:

                假设列表以某个下标为界,两侧分别有序,则可以使用两个指针,分别比较两侧最小的数,若左侧最小的数小于右侧,则将左侧该数取出,指针向后滑动一个位置,继续比较,直到有一侧没有数,将另一侧剩余的数写入即可,因为两侧均有序。

def merge(li, low, mid, high):
    """

    :param li: 传入列表
    :param low: 前半段开始位置
    :param mid: 前半段结束位置
    :param high: 后半段结束位置
    :return:
    """
    i = low
    j = mid + 1
    tli = []
    while i <= mid and j <= high : # 两侧都有数
        if li[i] > li[j] :
            tli.append(li[j])
            j += 1
            print(f"right,j = {j}, tli = {tli}")
        else :
            tli.append(li[i])
            i += 1
            print(f"left,i = {i}, tli = {tli}")
    # 此时至少有一侧是没有数的
    while i <= mid :
        tli.append(li[i])
        i += 1
        print(f"left,i = {i}, tli = {tli}")
    while j <= high :
        tli.append(li[j])
        j += 1
        print(f"right,j = {j}, tli = {tli}")
    li[low: high + 1] = tli


s = [1, 6, 7, 2, 3, 5, 8, 9]
merge(s, 0, 2, 7)
print(s)
left,i = 1, tli = [1]
right,j = 4, tli = [1, 2]
right,j = 5, tli = [1, 2, 3]
right,j = 6, tli = [1, 2, 3, 5]
left,i = 2, tli = [1, 2, 3, 5, 6]
left,i = 3, tli = [1, 2, 3, 5, 6, 7]
right,j = 7, tli = [1, 2, 3, 5, 6, 7, 8]
right,j = 8, tli = [1, 2, 3, 5, 6, 7, 8, 9]
[1, 2, 3, 5, 6, 7, 8, 9]

归并排序思路:

        

        一个数或者零个数一定是有序的,所以先将列表不断二分,直到一个数或零个数,这样每个子列表是有序的,将两个子列表合并后,达到了归并的条件,即左右两侧都是有序的,继续合并直到整个列表有序。

        则可以将列表看作三部分

                有序的列表左侧;

                有序的列表右侧;

                对列表归并;

        使用递归:

m = 0
def merge(li, low, mid, high):
    """

    :param li: 传入列表
    :param low: 前半段开始位置
    :param mid: 前半段结束位置
    :param high: 后半段结束位置
    :return:
    """
    global m
    m += 1
    i = low
    j = mid + 1
    tli = []
    print(f"第{m}次调用merge函数:")
    print(f"low = {low}, mid = {mid}, high = {high}")
    print("all : ")
    print(li)
    print("将要归并的范围:")
    print(li[low: high + 1])
    while i <= mid and j <= high : # 两侧都有数
        if li[i] > li[j] :
            tli.append(li[j])
            j += 1
        else :
            tli.append(li[i])
            i += 1
    # 此时至少有一侧是没有数的
    while i <= mid :
        tli.append(li[i])
        i += 1
    while j <= high :
        tli.append(li[j])
        j += 1
    print("范围归并后:")
    print(tli)
    li[low: high + 1] = tli
    print("归并后的总列表")
    print(li)
    print("******************************************************************")
def merge_sort(li, low, high):
    if low < high : # 即列表中至少还有两个元素
        mid = (low + high) // 2
        merge_sort(li, low, mid)
        merge_sort(li, mid + 1, high)
        merge(li, low, mid, high)


s = list(range(17))
import random
random.shuffle(s)
print(s)
merge_sort(s, 0, len(s) - 1)
print(s)

 

[16, 4, 7, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
第1次调用merge函数:
low = 0, mid = 0, high = 1
all : 
[16, 4, 7, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[16, 4]
范围归并后:
[4, 16]
归并后的总列表
[4, 16, 7, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第2次调用merge函数:
low = 0, mid = 1, high = 2
all : 
[4, 16, 7, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[4, 16, 7]
范围归并后:
[4, 7, 16]
归并后的总列表
[4, 7, 16, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第3次调用merge函数:
low = 3, mid = 3, high = 4
all : 
[4, 7, 16, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[0, 2]
范围归并后:
[0, 2]
归并后的总列表
[4, 7, 16, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第4次调用merge函数:
low = 0, mid = 2, high = 4
all : 
[4, 7, 16, 0, 2, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[4, 7, 16, 0, 2]
范围归并后:
[0, 2, 4, 7, 16]
归并后的总列表
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第5次调用merge函数:
low = 5, mid = 5, high = 6
all : 
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[3, 6]
范围归并后:
[3, 6]
归并后的总列表
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第6次调用merge函数:
low = 7, mid = 7, high = 8
all : 
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[12, 15]
范围归并后:
[12, 15]
归并后的总列表
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第7次调用merge函数:
low = 5, mid = 6, high = 8
all : 
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[3, 6, 12, 15]
范围归并后:
[3, 6, 12, 15]
归并后的总列表
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第8次调用merge函数:
low = 0, mid = 4, high = 8
all : 
[0, 2, 4, 7, 16, 3, 6, 12, 15, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[0, 2, 4, 7, 16, 3, 6, 12, 15]
范围归并后:
[0, 2, 3, 4, 6, 7, 12, 15, 16]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第9次调用merge函数:
low = 9, mid = 9, high = 10
all : 
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[1, 14]
范围归并后:
[1, 14]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第10次调用merge函数:
low = 11, mid = 11, high = 12
all : 
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[10, 11]
范围归并后:
[10, 11]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 14, 10, 11, 13, 9, 5, 8]
******************************************************************
第11次调用merge函数:
low = 9, mid = 10, high = 12
all : 
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 14, 10, 11, 13, 9, 5, 8]
将要归并的范围:
[1, 14, 10, 11]
范围归并后:
[1, 10, 11, 14]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 13, 9, 5, 8]
******************************************************************
第12次调用merge函数:
low = 13, mid = 13, high = 14
all : 
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 13, 9, 5, 8]
将要归并的范围:
[13, 9]
范围归并后:
[9, 13]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 9, 13, 5, 8]
******************************************************************
第13次调用merge函数:
low = 15, mid = 15, high = 16
all : 
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 9, 13, 5, 8]
将要归并的范围:
[5, 8]
范围归并后:
[5, 8]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 9, 13, 5, 8]
******************************************************************
第14次调用merge函数:
low = 13, mid = 14, high = 16
all : 
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 9, 13, 5, 8]
将要归并的范围:
[9, 13, 5, 8]
范围归并后:
[5, 8, 9, 13]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 5, 8, 9, 13]
******************************************************************
第15次调用merge函数:
low = 9, mid = 12, high = 16
all : 
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 10, 11, 14, 5, 8, 9, 13]
将要归并的范围:
[1, 10, 11, 14, 5, 8, 9, 13]
范围归并后:
[1, 5, 8, 9, 10, 11, 13, 14]
归并后的总列表
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 5, 8, 9, 10, 11, 13, 14]
******************************************************************
第16次调用merge函数:
low = 0, mid = 8, high = 16
all : 
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 5, 8, 9, 10, 11, 13, 14]
将要归并的范围:
[0, 2, 3, 4, 6, 7, 12, 15, 16, 1, 5, 8, 9, 10, 11, 13, 14]
范围归并后:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
归并后的总列表
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
******************************************************************
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]

进程已结束,退出代码0

        在上述代码中,我加入了多项打印函数帮助理解递归的调用过程,通过观察可以发现,此时我要排序的列表长度为17,最大下标为16,则第一次的mid为8,在第8次调用merge时,完成了对列表前半部分(左侧0~8)共9位数字的排序,之后low从9开始,对右侧的数字排序。

        在第16次调用merge时,已经与文章开头的归并排序算法一致。

时间复杂度:O(nlogn) 

        将列表二分logn

        二分之后遍历列表n

空间复杂度:O(n)

        在merge中开辟了新的列表用于储存数据

以下对学习过的排序方法进行了比较:

        空间复杂度算入了递归占用的系统空间

        稳定性是指在两个数据一样时,是否进行交换

                挨个比较大小并交换的是稳定的

                遍历后进行位置交换的是不稳定的

以上,完成了常见的排序算法的学习笔记。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值