原理
一般的递归排序每次递归是按照当前列表长度的中间值来把当前列表分成两个列表,直到当前列表的长度为1时结束递归。现在我们将采用一种不同的分割方式来归并排序,假设需要排序列表的长度为x。第一次排序把列表分成个列表,第一个和第二个列表比较元素大小,第三个和第四个列表比较元素大小,第五个和第六个列表比较元素大小,以此类推。比较完成后,按照比较的大小顺序,小的元素排前面,大的元素排后面,排成一个新的列表。第二次把经过部分排序的新列表分成个列表,重复前面的比较排序过程又得到一个新的列表。第三次把经过部分排序的新列表分成个列表,再重复前面的比较排序过程又得到一个新的列表。直到第n次,2的n-1次方大于等于x时结束。这时我们就会得到一个从小到大排序的列表。
完整代码
通过上面的原理我们可以设计出如下代码:
def merge_sort_2n(list_: list):
"""
2的n次方归并排序
:param list_: 需要排序的列表
:return: 排序后的列表
"""
n = 0 # 设置n的初始值为0
max_index = int(len(list_) / 2) if len(list_) % 2 == 0 else len(list_) // 2 + 1
while len(list_) > 2 ** n: # 当2的n次方大于等于需要排序列表的长度时,结束循环
step = 2 ** n # 创建一个步长值
result = [] # 创建一个中间列表来存储每次比较排序后的元素
for i in range(0, max_index, step): # 根据步长值来控制每次参与比较的元素个数
list_a = list_[2 * i:2 * i + step] # 通过切片把要参与比较的元素取出来变成一个新列表
list_b = list_[2 * i + step:2 * (i + step)] # 通过切片把要参与比较的元素取出来变成一个新列表
pointer_a, pointer_b = 0, 0 # 设置两个索引下标
while pointer_a < len(list_a) and pointer_b < len(list_b): # 当有一个索引下标超出索引范围时,结束循环
if list_a[pointer_a] < list_b[pointer_b]: # 判断两个列表中相同索引位置的元素大小
result.append(list_a[pointer_a]) # 当列表list_a的元素比较小时,把list_a的当前索引元素放到result列表中
pointer_a += 1 # list_a的索引下标加一
else:
result.append(list_b[pointer_b]) # 当列表list_b的元素比较小时,把list_b的当前索引元素放到result列表中
pointer_b += 1 # list_b的索引下标加一
result += list_a[pointer_a:] # 把list_a中剩余的元素并入result后面,可能是空列表
result += list_b[pointer_b:] # 把list_b中剩余的元素并入result后面,可能是空列表
print(result) # 打印出每次排序后的列表
list_ = result # 把排序后的列表重新赋值给list_
n += 1 # 循环一次后n的值加一
return list_ # 返回排序后的列表
动态展示
我们可以使用tkinter图形界面来动态展示2的n次方归并排序过程,代码如下:
import threading
import time
import tkinter as tk
index = []
class MergeSort2n:
"""
2的n次方归并排序动态展示\n
使用tkinter中的方法创建UI界面
"""
def __init__(self, list_: list):
"""
UI界面窗口初始化\n
:return:
"""
self.list = list_
root = tk.Tk()
root.title('2的n次方归并排序')
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_2n(self, list_: list):
"""
2的n次方归并排序\n
:param list_: 需要排序的列表
:return:
"""
n = 0
max_index = int(len(list_) / 2) if len(list_) % 2 == 0 else len(list_) // 2 + 1
while len(list_) > 2 ** n:
step = 2 ** n
result = []
for i in range(0, max_index, step):
list_a = list_[2 * i:2 * i + step]
list_b = list_[2 * i + step:2 * (i + step)]
min_ = 2 * i
max_ = 2 * (i + step) if 2 * (i + step) <= len(list_) else len(list_)
pointer_a, pointer_b = 0, 0
while pointer_a < len(list_a) and pointer_b < len(list_b):
if list_a[pointer_a] < list_b[pointer_b]:
result.append(list_a[pointer_a])
pointer_a += 1
else:
result.append(list_b[pointer_b])
pointer_b += 1
result += list_a[pointer_a:]
result += list_b[pointer_b:]
self.update_display(list(range(min_, max_)), result[min_:max_])
list_ = result
n += 1
def thread_ui(self):
"""
多线程执行任务\n
:return:
"""
thread = threading.Thread(target=self.merge_sort_2n, args=(self.list, ))
thread.setDaemon(True)
thread.start()
if __name__ == '__main__':
list1 = [6, 3, 9, 1, 4, 7, 2, 8, 5]
MergeSort2n(list1)
执行结果如下: