python列表递归排序

递归排序

        递归排序就是通过不断的从中间位置分割列表,递归一次就分割一次,一直分到分不动位置(只有一个元素或没有元素)。然后比较左右两个元素的大小,按大小顺序重新放到一个列表中;再比较两个有序列表中每一个元素的大小,按照大小顺序再放入一个新的列表中;一直比较有序列表直到第一个递归函数出栈,即可得到一个有序列表。

        递归排序的本质就是利用函数递归的方式,不断的对一个列表进行分割。每一层递归函数都对传入的列表从中间分割,分成两个列表,再把分割后的两个列表分别传入下一层递归函数中,继续对它们进行分割,直到第n层递归函数接受到的列表中只有一个元素或没有元素时停止递归。停止递归后,第n层递归函数把接受到的列表原原本本的返回给第n-1层递归函数,第n层递归函数完成使命光荣出栈。第n-1层递归函数把第n层返回的两个列表进行归并为一个列表,再把归并后的列表返回给n-2层递归函数,第n-1层递归函数完成使命光荣出栈。第n-2层递归函数又开始归并,直到第一层递归函数把归并后的列表返回后,我们就能得到一个有序的列表了。如下图所示:

分割列表 

        通过上面的原理我们可以设计出一个用于不断分割列表的递归函数,每一次分割列表都通过列表的中间位置,把列表分成左右两部分。然后通过函数递归继续对左右两部分列表进行分割,直到列表中只剩下一个元素时停止递归。(蓝色分割路径)

def merge_sort(list_: list):
    if len(list_) <= 1:  # 判断传入的列表长度是否小于等于1
        return list_  # 如果小于等于1则返回列表本身
    middle = len(list_) // 2  # 算出列表长度的中间值
    l_list = merge_sort(list_[:middle])  # 利用函数递归,把使用中间值来切片后得到的列表递归排序,把递归排序后的列表赋值给l_list
    r_list = merge_sort(list_[middle:])  # 利用函数递归,把使用中间值来切片后得到的列表递归排序,把递归排序后的列表赋值给r_list

归并排序

        现在我们完成了对列表的分割,接下来就需要对列表进行归并了。需要把每个递归函数得到的左右两部分列表进行归并排序,归并后得到一个有序列表后再返回给上一层递归函数。(红色返回路径)

    result = []  # 创建一个列表来存储每次比较排序后的元素
    l_pointer, r_pointer = 0, 0  # 设置两个索引下标
    while l_pointer < len(l_list) and r_pointer < len(r_list):  # 当有一个索引下标超出索引范围时,结束循环
        if l_list[l_pointer] < r_list[r_pointer]:  # 判断两个列表中相同索引位置的元素大小
            result.append(l_list[l_pointer])  # 当列表l_list的元素比较小时,把l_list的当前索引元素放到result列表中
            l_pointer += 1  # l_list的索引下标加一
        else:
            result.append(r_list[r_pointer])  # 当列表r_list的元素比较小时,把r_list的当前索引元素放到result列表中
            r_pointer += 1  # r_list的索引下标加一
    result += l_list[l_pointer:]  # 把l_list中剩余的元素并入result后面,可能是空列表
    result += r_list[r_pointer:]  # 把r_list中剩余的元素并入result后面,可能是空列表
    return result  # 返回排序后的列表

完整代码

        结合分割列表和归并排序后即可得到一个完整的递归排序函数,代码如下:

def merge_sort(list_: list):
    """
    利用函数递归,递归排序

    :param list_: 需要排序的列表
    :return: 排序后的列表
    """
    if len(list_) <= 1:  # 判断传入的列表长度是否小于等于1
        return list_  # 如果小于等于1则返回列表本身
    middle = len(list_) // 2  # 算出列表长度的中间值
    l_list = merge_sort(list_[:middle])  # 利用函数递归,把使用中间值来切片后得到的列表递归排序,把递归排序后的列表赋值给l_list
    r_list = merge_sort(list_[middle:])  # 利用函数递归,把使用中间值来切片后得到的列表递归排序,把递归排序后的列表赋值给r_list
    result = []  # 创建一个列表来存储每次比较排序后的元素
    l_pointer, r_pointer = 0, 0  # 设置两个索引下标
    while l_pointer < len(l_list) and r_pointer < len(r_list):  # 当有一个索引下标超出索引范围时,结束循环
        if l_list[l_pointer] < r_list[r_pointer]:  # 判断两个列表中相同索引位置的元素大小
            result.append(l_list[l_pointer])  # 当列表l_list的元素比较小时,把l_list的当前索引元素放到result列表中
            l_pointer += 1  # l_list的索引下标加一
        else:
            result.append(r_list[r_pointer])  # 当列表r_list的元素比较小时,把r_list的当前索引元素放到result列表中
            r_pointer += 1  # r_list的索引下标加一
    result += l_list[l_pointer:]  # 把l_list中剩余的元素并入result后面,可能是空列表
    result += r_list[r_pointer:]  # 把r_list中剩余的元素并入result后面,可能是空列表
    return result  # 返回排序后的列表

动态展示

        我们可以使用tkinter图像化动态展示递归排序的过程,代码如下:

import threading
import time
import tkinter as tk

index = []


class MergeSort:
    """
    递归排序动态展示\n
    使用tkinter中的方法创建UI界面
    """

    def __init__(self, list_: list):
        """
        UI界面窗口初始化\n
        :return:
        """
        self.list = list_
        root = tk.Tk()
        root.title('递归排序')
        width = 800
        height = 500
        win_width = root.winfo_screenwidth()
        win_height = root.winfo_screenheight()
        x = win_width / 2 - width / 2
        y = win_height / 2 - height / 2
        root.geometry('%dx%d+%d+%d' % (width, height, x, y))
        self.canvas = tk.Canvas(root, bg="#FFFFFF", width=800, height=500)
        self.canvas.grid(row=1, column=0, rowspan=10, columnspan=10)
        self.display_list()
        self.thread_ui()
        root.mainloop()

    def display_list(self):
        """
        画出需要排序的列表,并把组件id放入index列表中\n
        :return:
        """
        n = 0
        for i in self.list:
            id_ = self.canvas.create_rectangle(60 * n + 140, 400, 60 * n + 170, 400 - 30 * i, fill='#5B9BD6')
            index.append(id_)
            n += 1

    def update_display(self, origin: list, now: list):
        """
        更新界面显示\n
        :param origin: 需要更新的位置索引
        :param now: 索引对应部分的新值
        :return:
        """
        time.sleep(1)
        for i in origin:
            self.canvas.itemconfigure(index[i], fill='#00B005')
        time.sleep(1)
        for i in range(len(origin)):
            self.canvas.coords(index[origin[i]], 60 * origin[i] + 140, 400, 60 * origin[i] + 170, 400 - 30 * now[i])
        for i in origin:
            self.canvas.itemconfigure(index[i], fill='#5B9BD6')

    def merge_sort(self, list_: list):
        """
        利用函数递归,递归排序\n
        :param list_: 需要排序的列表索引
        :return: 已排序的列表索引
        """
        if len(list_) <= 1:
            return list_
        middle = len(list_) // 2
        l_list = self.merge_sort(list_[:middle])
        r_list = self.merge_sort(list_[middle:])
        l_pointer, r_pointer = 0, 0
        original = l_list + r_list
        result = []
        while l_pointer < len(l_list) and r_pointer < len(r_list):
            if self.list[l_list[l_pointer]] < self.list[r_list[r_pointer]]:
                result.append(self.list[l_list[l_pointer]])
                l_pointer += 1
            else:
                result.append(self.list[r_list[r_pointer]])
                r_pointer += 1
        result += [self.list[i] for i in l_list[l_pointer:]]
        result += [self.list[i] for i in r_list[r_pointer:]]
        for i in range(len(original)):
            self.list[original[i]] = result[i]
        self.update_display(original, result)
        return original

    def thread_ui(self):
        """
        多线程执行任务\n
        :return:
        """
        list_ = list(range(len(self.list)))
        thread = threading.Thread(target=self.merge_sort, args=(list_, ))
        thread.setDaemon(True)
        thread.start()


if __name__ == '__main__':
    list1 = [6, 3, 9, 1, 4, 7, 2, 8, 5]
    MergeSort(list1)

执行结果如下:

通过这种方式,我们就可以可视化递归排序的操作过程了。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值