递归排序
递归排序就是通过不断的从中间位置分割列表,递归一次就分割一次,一直分到分不动位置(只有一个元素或没有元素)。然后比较左右两个元素的大小,按大小顺序重新放到一个列表中;再比较两个有序列表中每一个元素的大小,按照大小顺序再放入一个新的列表中;一直比较有序列表直到第一个递归函数出栈,即可得到一个有序列表。
递归排序的本质就是利用函数递归的方式,不断的对一个列表进行分割。每一层递归函数都对传入的列表从中间分割,分成两个列表,再把分割后的两个列表分别传入下一层递归函数中,继续对它们进行分割,直到第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)
执行结果如下:
通过这种方式,我们就可以可视化递归排序的操作过程了。