再帰的ソート
再帰的ソートとは、リストを中央の位置から一度に 1 回ずつ、その位置が分割できなくなる (要素が 1 つだけ、または要素がない) まで連続的に分割することです。次に、左右の 2 つの要素のサイズを比較し、サイズ順にリストに戻します。次に、順序付けされた 2 つのリストの各要素のサイズを比較し、サイズ順に新しいリストに戻します。 ; 順序付きリストを比較し続けます。最初の再帰関数がスタックからポップされるまで、順序付きリストを取得できます。
再帰的ソートの本質は、関数再帰を使用してリストを連続的に分割することです。再帰関数の各レベルは、入力されたリストを中央から 2 つのリストに分割し、次に分割された 2 つのリストを再帰関数の次のレベルに渡し、再帰関数の n 番目のレベルまで分割を続けます。 受け取ったリストに次の内容が含まれる場合、再帰を停止します。要素が 1 つだけか、要素がありません。再帰を停止した後、第 n レベルの再帰関数は受け取ったリストをそのまま第 n-1 レベルの再帰関数に返し、第 n レベルの再帰関数はその使命を完了してスタックから飛び出します。n-1 レベルの再帰関数は、n レベルによって返された 2 つのリストを 1 つのリストにマージし、そのマージされたリストを n-2 レベルの再帰関数に返します。n-1 レベルの再帰関数は、その使命を完了し、華麗に積み上げます。n-2 番目のレベルの再帰関数は再びマージを開始し、最初のレベルの再帰関数がマージされたリストを返すまで、順序付きリストを取得できます。以下に示すように:
分割リスト
上記の原理により、リストを分割するたびに、リストの中央を基準として左と右に分割する再帰関数を設計することができます。次に、リストに要素が 1 つだけ残ったときに再帰が停止するまで、関数の再帰によってリストの左側と右側の部分を分割し続けます。(青い分岐パス)
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)
実行結果は以下の通りです。
このようにして、再帰的ソートの操作プロセスを視覚化できます。