参考: 十大经典排序算法动图图解
动画+原理+代码,解读十大经典排序算法
堆排序的Python实现(附详细过程图和讲解)
算法基础
基础概念特征
- 定义
算法(Algorithm)是一个有穷规则(或语句、指令)的有序集合。它确定了解决某一问题的一个运算序列。对于问题的初始输入,通过算法有限步的运行,产生一个或多个输出。
数据的逻辑结构与存储结构密切相关:
- 算法设计: 取决于选定的逻辑结构
- 算法实现: 依赖于采用的存储结构
- 算法的特性
- 有穷性 —— 算法执行的步骤(或规则)是有限的;
- 确定性 —— 每个计算步骤无二义性;
- 可行性 —— 每个计算步骤能够在有限的时间内完成;
- 输入 ,输出 —— 存在数据的输入和出输出
- 评价算法好坏的方法
- 正确性:运行正确是一个算法的前提。
- 可读性:容易理解、容易编程和调试、容易维护。
- 健壮性:考虑情况全面,不容以出现运行错误。
- 时间效率高:算法消耗的时间少。
- 储存量低:占用较少的存储空间。
时间复杂度计算
算法效率——用依据该算法编制的程序在计算机上执行所消耗的时间来度量。“O”表示一个数量级的概念。根据算法中语句执行的最大次数(频度)来 估算一个算法执行时间的数量级。
计算方法:
写出程序中所有运算语句执行的次数,进行加和
如果得到的结果是常量则时间复杂度为1
如果得到的结果中存在变量n则取n的最高次幂作为时间复杂度
下图表示随问题规模n的增大,算法执行时间的增长率。
排序和查找
排序
排序(Sort)是将无序的记录序列(或称文件)调整成有序的序列。
常见排序方法:
- 冒泡排序
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
- 选择排序
工作原理为,首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。
- 插入排序
对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
- 快速排序
步骤:
从数列中挑出一个元素,称为 “基准”(pivot),
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
常见排序代码实现: day3/sort.py
查找
查找(或检索)是在给定信息集上寻找特定信息元素的过程。
二分法查找
当数据量很大适宜采用该方法。采用二分法查找时,数据需是排好序的。
二分查找代码实现: day3/search.py
辅助记忆特点:
冒泡–相临元素边比较边交换
选择–每次都查找无序区的所有元素,找到最(小)值然后放在有序区的(结尾)。
插入–每次都查找有序区的元素,以决定从无序区取出的元素插入有序区的位置。
希尔–增量分组排序(分组大小逐渐增大),各分组内部采用直接插入排序。
归并–采用分治法划分若干子序列,然后使每个子序列有序,再使子序列段间有序(若将两个有序表合并成一个有序表,称为2-路归并,这就是归并名称的由来)
快速–比较基准值,大小各分一边得到两个分区,然后在各分区分别进行同样的操作
1,冒泡排序
1)基础版
import copy
def BubbleSort(iterable):
"""
冒泡排序
params:
iterable: 列表
return:返回一个新的升序列表
"""
if "__iter__" not in dir(iterable): # 简单检查是否为可迭代对象
raise TypeError
iterable = copy.deepcopy(iterable) # 列表深拷贝,不影响原列表
iterable_len = len(iterable)
for i in range(iterable_len ): # 核心冒泡算法(升序)
for j in range(iterable_len - 1 - i):
if iterable[j] > iterable[j + 1]:
iterable[j], iterable[j + 1] = iterable[j + 1], iterable[j]
return iterable
if __name__ == "__main__":
list_a = [8, 0, 3, 2, 6, 7, 9, 1, 4, 5]
print(BubbleSort(list_a))
2)进阶版–传参决定升序还是降序
import copy
def BubbleSort(iterable, reverse=False):
"""
冒泡排序
params:
iterable: 列表
reverser: False代表升序,True代表降序
return:返回一个新的有序列表
"""
if "__iter__" not in dir(iterable): # 简单检查是否为可迭代对象
raise TypeError
iterable = copy.deepcopy(iterable) # 列表深拷贝,不影响原列表
for i in range(len(iterable)): # 核心冒泡算法
for j in range(iterable.__len__() - 1 - i):
if reverse == True:
if iterable[j] < iterable[j + 1]:
iterable[j], iterable[j + 1] = iterable[j + 1], iterable[j]
else:
if iterable[j] > iterable[j + 1]:
iterable[j], iterable[j + 1] = iterable[j + 1], iterable[j]
return iterable
if __name__ == "__main__":
list_a = [8, 0, 3, 2, 6, 7, 9, 1, 4, 5]
print(BubbleSort(list_a, True))
- 进阶版–传参提供自定义key函数来决定排序规则(这对于复杂类型排序有用),其实走到这一步,已经和内置函数sorted()几乎一样了。请参考python字典的排序
import copy
def BubbleSort(iterable, key=None, reverse=False):
"""
冒泡排序
params:
iterable: 可迭代对象
key:提供自定义key函数来决定排序规则(这对于复杂类型排序有用)
reverser: False代表升序,True代表降序
return:返回一个新的有序列表
"""
if "__iter__" not in dir(iterable): # 简单检查是否为可迭代对象
raise TypeError
try:
iterable = copy.deepcopy(iterable) # 列表深拷贝,不影响原列表
except TypeError: # 字典类型
iterable = [(k, v) for k, v in iterable]
if key == None: # 没有指定key函数的情况
# key = lambda x:x
def key(x): return x
iterable_len = len(iterable)
for i in range(iterable_len): # 核心冒泡算法
for j in range(iterable_len - 1 - i):
if reverse == True:
if key(iterable[j]) < key(iterable[j + 1]):
iterable[j], iterable[j + 1] = iterable[j + 1], iterable[j]
else:
if key(iterable[j]) > key(iterable[j + 1]):
iterable[j], iterable[j + 1] = iterable[j + 1], iterable[j]
return iterable
if __name__ == "__main__":
list_a = [8, 0, 3, 2, 6, 7, 9, 1, 4, 5]
print(BubbleSort(list_a, reverse=True))
dict_b = {"a": 1, "c": 3, "b": 2, "d": 4, "g": 7}
print(BubbleSort(dict_b.items(), key=lambda x: x[0], reverse=False))
额,下面的演示我们就只关注核心算法了!而不去搞那些花样了!
2,选择排序
import copy
def SelectionSort(iterable):
"""
选择排序
params:
iterable: 列表
return:返回一个新的升序列表
"""
if "__iter__" not in dir(iterable): # 简单检查是否为可迭代对象
raise TypeError
iterable = copy.deepcopy(iterable) # 列表深拷贝,不影响原列表
iterable_len = len(iterable)
for i in range(iterable_len): # 核心选择排序算法(升序)
temp_min_index = i # 记录最小值的索引
for j in range(i + 1, iterable_len):
if iterable[j] < iterable[temp_min_index]:
temp_min_index = j
# 将最小值和无序区的开头交换位置后就变成了有序区的末尾元素
iterable[temp_min_index], iterable[i] = iterable[i], iterable[temp_min_index]
return iterable
if __name__ == "__main__":
list_a = [8, 0, 3, 2, 6, 7, 9, 1, 4, 5]
print(SelectionSort(list_a))
3,插入排序
import copy
def InsertionSort(iterable):
"""
插入排序
params:
iterable: 列表
return:返回一个新的升序列表
"""
if "__iter__" not in dir(iterable): # 简单检查是否为可迭代对象
raise TypeError
iterable = copy.deepcopy(iterable) # 列表深拷贝,不影响原列表
for i in range(1, len(iterable)): # 核心插入排序算法(升序),外层控制无序分区依次每次取出一个元素X
temp_index = i
for j in range(i - 1, -1, -1): # 内层将X和有序分区倒序一一比较
if iterable[j] > iterable[temp_index]: #遇到比X还大的,交换位置继续找
iterable[j], iterable[temp_index] = iterable[temp_index], iterable[j]
temp_index -= 1 # 交换完将temp_index索引减1,使得temp_index始终索引X
else: #遇到比X还小的或者相等的,就说明X因该排在它后面(把等号归入此是为了达到稳定排序的效果)
break
return iterable
if __name__ == "__main__":
list_a = [8, 0, 3, 2, 6, 6, 7, 9, 6, 4, 5]
print(InsertionSort(list_a))
4,希尔排序
import copy
def ShellSort(iterable):
"""
希尔排序
params:
iterable: 列表
return:返回一个新的升序列表
"""
if "__iter__" not in dir(iterable): # 简单检查是否为可迭代对象
raise TypeError
iterable = copy.deepcopy(iterable) # 列表深拷贝,不影响原列表
iterable_len = len(iterable)
gap = iterable_len // 2 # 起始分组增量
while(gap > 0): # 控制分组增量,若iterable_len为10,则gap依次为5,2,1,即分为5组,2组,1组
for i in range(gap): # 控制分组0~(gap-1)组,其中分组的第一个位置索引等于分组号
# 每一分组都采用插入排序算法
for j in range(i + gap, iterable_len, gap): # 控制分组的未排序成员依次取出X,X下标为j
temp_index = j # 因不能为j赋值,所以创建一个临时变量记录X的位置
for k in range(j - gap, i - gap, -gap): # 将X和有序分区倒序一一比较
if iterable[k] > iterable[temp_index]: # 遇到比X还大的,交换位置继续找
iterable[k], iterable[temp_index] = iterable[temp_index], iterable[k]
temp_index -= gap # 交换完将temp_index索引减gap,使得temp_index始终索引X
else: # 遇到比X还小的或者相等的,就说明X应排在它后面(把等号归入此可稳定排序)
break
gap //= 2
return iterable
if __name__ == "__main__":
list_a = [8, 9, 1, 7, 2, 3, 5, 4, 6, 0]
print(ShellSort(list_a))
5,快速排序
def quick_sort(iteratable, start, end):
if start >= end: # 递归退出条件
return
# 定义两个游标,分别指向0和末尾位置
left = start
right = end
# 把0位置的数据,认为是中间值
mid = iteratable[left]
while left < right:
# 让右边游标往左移动,目的是找到小于mid的值,放到left游标位置
while left < right and iteratable[right] >= mid:
right -= 1
iteratable[left] = iteratable[right]
# 让左边游标往右移动,目的是找到大于mid的值,放到right游标位置
while left < right and iteratable[left] < mid:
left += 1
iteratable[right] = iteratable[left]
# while结束后,把mid放到中间位置,left=right
iteratable[left] = mid
# 递归处理左边的数据
quick_sort(iteratable, start, left - 1)
# 递归处理右边的数据
quick_sort(iteratable, left + 1, end)
if __name__ == '__main__':
l = [6, 5, 4, 3, 4, 4, 4, 2, 1]
quick_sort(l, 0, len(l) - 1)
print(l)
6,归并排序
def mergeSort(arr):
import math
if(len(arr) < 2):
return arr
middle = math.floor(len(arr) / 2)
left, right = arr[0:middle], arr[middle:]
return merge(mergeSort(left), mergeSort(right))
def merge(left, right):
result = []
while left and right:
if left[0] <= right[0]:
result.append(left.pop(0))
else:
result.append(right.pop(0))
while left:
result.append(left.pop(0))
while right:
result.append(right.pop(0))
return result
if __name__ == "__main__":
list_a = [8, 9, 1, 7, 2, 3, 5, 4, 6, 0]
print(mergeSort(list_a))
别人写好的排序算法类
"""
列表排序:冒泡、选择、插入、快速与堆排序
"""
class ListSort(object):
"""
排序类
"""
def __init__(self, listing):
self.___listing = listing
def bubbling_sort(self):
"""
冒泡排序
:return:
"""
for i in range(len(self.___listing)):
for j in range(len(self.___listing) - 1):
if self.___listing[j] > self.___listing[j + 1]:
self.___listing[j], self.___listing[j + 1] = \
self.___listing[j + 1], self.___listing[j]
return self.___listing
def select_sort(self):
"""
选择排序
:return:
"""
for i in range(len(self.___listing) - 1):
min_index = i
for j in range(i + 1, len(self.___listing)):
if self.___listing[min_index] > self.___listing[j]:
min_index = j
self.___listing[i], self.___listing[min_index] = \
self.___listing[min_index], self.___listing[i]
return self.___listing
def insert_sort(self):
"""
插入排序
:return:
"""
for i in range(1, len(self.___listing)):
key = self.___listing[i]
j = i
while j > 0 and self.___listing[j - 1] > key:
self.___listing[j] = self.___listing[j - 1]
j -= 1
self.___listing[j] = key
return self.___listing
@staticmethod
def quick_sort(listing,low,high):
"""
快速排序
:param listing:
:param low:
:param high:
:return:
"""
if low >= high:
return listing
else:
left = low
right = high
privot = listing[low]
while left < right:
while left < right and listing[right] >= privot:
right -= 1
listing[left] = listing[right]
while left < right and listing[left] <= privot:
left += 1
listing[right] = listing[left]
listing[left] = privot
ListSort.quick_sort(listing,low,left-1)
ListSort.quick_sort(listing,left+1,high)
return listing
@staticmethod
def quick_sort_pythonic(listing):
"""
快速排序
:param listing: 待排序列表
:return:
"""
length = len(listing)
if length <= 1:
return listing
else:
pivot = listing[0]
left = [element for element in listing[1:] if element <= pivot]
right = [element for element in listing[1:] if element > pivot]
return ListSort.quick_sort_pythonic(left) + [pivot] \
+ ListSort.quick_sort_pythonic(right)
if __name__ == "__main__":
import random
import time
lis = list(range(1000001))
listing = [1,4,5,45,56,54,231,1,4,5,2,1,0,1,2,5,4,5,6,21,20,0,0,0,54,54,0,0,0,265455,4]
random.shuffle(lis)
print(lis)
start = time.time()
# ls = ListSort(lis)
print(ListSort.quick_sort(lis,0,len(lis)-1))
stop = time.time()
print('执行时间%.10f' % (stop - start))
# print(ListSort.quick_sort_pythonic(lis))
# stop = time.time()
# print('执行时间%.10f' % (stop - start))
整理后的:
"""
列表排序:冒泡、选择、插入、快速与堆排序
"""
class ListSort_helper:
"""
排序类
"""
@staticmethod
def bubbling_sort(list):
"""
冒泡排序
:return:
"""
for i in range(len(list)):
for j in range(len(list) - 1 - i):
if list[j] > list[j + 1]:
list[j], list[j + 1] = list[j + 1], list[j]
return list
@staticmethod
def select_sort(list):
"""
选择排序
:return:
"""
for i in range(len(list)):
min_index = i
for j in range(i + 1, len(list)):
if list[j] < list[min_index]:
min_index = j
list[min_index], list[i] = list[i], list[min_index]
return list
@staticmethod
def insert_sort(list):
"""
插入排序
:return:
"""
for i in range(1, len(list)): # 核心插入排序算法(升序),外层控制无序分区依次每次取出一个元素X
temp_index = i
for j in range(i - 1, -1, -1): # 内层将X和有序分区倒序一一比较
if list[j] > list[temp_index]: # 遇到比X还大的,交换位置继续找
list[j], list[temp_index] = list[temp_index], list[j]
temp_index -= 1 # 交换完将temp_index索引减1,使得temp_index始终索引X
else: # 遇到比X还小的或者相等的,就说明X因该排在它后面(把等号归入此是为了达到稳定排序的效果)
break
return list
@staticmethod
def quick_sort_pythonic(list):
"""
快速排序
:param listing: 待排序列表
:return:
"""
if len(list) <= 1:
return list
else:
pivot = list[0]
left = [item for item in list[1:] if item <= pivot]
right = [item for item in list[1:] if item > pivot]
return ListSort_helper.quick_sort_pythonic(left) + [pivot] \
+ ListSort_helper.quick_sort_pythonic(right)
if __name__ == "__main__":
listing = [1, 5, 3, 4, 6, 9, 7, 8, 0, 2]
print(ListSort_helper.quick_sort_pythonic(listing))
"""
二叉树的构建与遍历(先序/中序/后序遍历)
重点代码
"""
# 二叉树的节点类
class TreeNode:
def __init__(self, data=None, left=None, right=None):
self.data = data
self.left = left
self.right = right
# 二叉树类,进行遍历操作
class Bitree:
def __init__(self, root=None):
self.root = root
def is_empty(self):
if self.root is None:
return True
else:
return False
# 先序遍历
def preOrder(self,node):
if node is None:
return
print(node.data,end=' ')
self.preOrder(node.left)
self.preOrder(node.right)
# 中序遍历
def inOrder(self,node):
if node is None:
return
self.inOrder(node.left)
print(node.data, end=' ')
self.inOrder(node.right)
# 后序遍历
def postOrder(self, node):
if node is None:
return
self.postOrder(node.left)
self.postOrder(node.right)
print(node.data, end=' ')
if __name__ == "__main__":
# 按照后序遍历增加结点
b = TreeNode('B')
f = TreeNode('F')
g = TreeNode('G')
d = TreeNode('D', f, g)
i = TreeNode('I')
h = TreeNode('H')
e = TreeNode('E', i, h)
c = TreeNode('C', d, e)
a = TreeNode('A', b, c) # 根节点
bt = Bitree(a) # 初始化树对象,传入根结点
print("pre order .....")
bt.preOrder(bt.root)
print()
print("in order .....")
bt.inOrder(bt.root)
print()
print("post order .....")
bt.postOrder(bt.root)
print()