目录
1、算法概念相关
2、查找算法
2.1 线性查找
2.2 二分查找
3、排序算法
3.1 插入排序
3.2 快速排序
3.3 选择排序
3.4 冒泡排序
3.5 归并排序
3.6 堆排序
3.7 计数排序
3.8 希尔排序
3.9 拓扑排序
正文
程序 = 算法 + 数据结构
程序:算法用某种程序设计语言的具体实现,程序可以不满足有穷性。
算法:指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。
算法只不过是流程或菜谱的时髦说法,详尽地描述了如何完成某项任务。 请看下面的菜谱:西红柿炒鸡蛋
1)先取一些西红柿、鸡蛋;
2)切好西红柿、打好鸡蛋;
3)打开电磁炉,先将鸡蛋煎好;
4)再加入西红柿;
5)如果喜欢吃甜的,加些糖;
6)煮熟为止;
7)记得每隔3分钟检查一次。
这个菜谱并不神奇,但其结构很有启发性。它由一系列必须按顺序执行的操作说明组成,其中有些可直接完成(取些西红柿和鸡蛋),有些需要特别注意(如果喜欢吃甜的),还有一些需要重复多次(每隔3分钟检查一次)。
菜谱和算法都由原料(对象)和操作说明(语句)组成。在这个示例中,西红柿和鸡蛋是原料,而操作说明包括切西红柿、打鸡蛋、翻炒、烹饪指定的时间等。
这就是计算机的简单算法逻辑。
算法的描述形式 有:
算法的 5个特征:
判定算法优劣 的5个标准:一个算法的评价主要从时间复杂度 和空间复杂度来考虑。
T(n)=Ο(f(n))
f(n)
的增长率正相关,称作渐进时间复杂度(Asymptotic Time Complexity)。其中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间,时间复杂度 常用O
表述,使用这种方式时,时间复杂度可被称为是渐近的,它考察当输入值大小趋近无穷时的情况。
时间复杂度是用来估计算法运行时间的一个式子(单位),一般来说,时间复杂度高的算法比复杂度低的算法慢。
print('Hello world') # O(1) “循环” 1次
# O(1) 执行1次
print('Hello World')
print('Hello Python')
print('Hello Algorithm')
for i in range(n): # O(n) “循环” n次
print('Hello world')
for i in range(n): # O(n^2) “循环” n^2次
for j in range(n):
print('Hello world')
for i in range(n): # O(n^2) “循环” n^2次
print('Hello World')
for j in range(n):
print('Hello World')
for i in range(n): # O(n^2) “循环” n^2次
for j in range(i):
print('Hello World')
for i in range(n):# O(n^3) “循环” n^3次
for j in range(n):
for k in range(n):
print('Hello World')
几次循环
就是n
的几次方
的时间复杂度。
n = 64
while n > 1:#循环 6次 O(6)
print(n)
n = n // 2
其中,2^6 = 64
,O(f(n)) = log2(64) = 6
。所以循环减半的时间复杂度为O(log2(n))
,即O(logn)
。
常见的时间复杂度高低排序:O(1)
O(1):常数型
O(log2 n):对数型
O(n):线性型
O(nlog2n):二维型
O(n^2):平方型
O(n^3):立方型
O(2^n):指数型
算法的空间复杂度 用来评估算法内存占用大小的一个式子。
定义一个或多个变量,空间复杂度都是为1,列表的空间复杂度为列表的长度。
# 空间复杂度为1
a = 'Python'
num1 = [1, 2, 3, 4, 5] # 空间复杂度为5
num2 = [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]] # 空间复杂度为5*4
num = [[[1, 2], [1, 2]], [[1, 2], [1, 2]] , [[1, 2], [1, 2]]] # 空间复杂度为3*2*2
算法设计的一般过程:
也称 线性搜索(或 顺序查找)。从第一个元素m
开始逐个与需要查找的元素x
进行比较,当比较到元素值相同(即m=x
)时返回元素m的下标
,如果比较到最后都没有找到,则返回-1。速度最慢,但是适用性最广。
适合于存储结构为 顺序存储 或链式存储的线性表。
缺点:是当n 很大时,平均查找长度较大,效率低;
优点:是对表中数据元素的存储没有要求。另外,对于线性链表,只能进行顺序查找。
[root@master linearsearch]# cat ls.py
#coding=utf-8
def linear_Search(arr, n, x):
for i in range(0, n):
if arr[i] == x:
return i
return -1;
arr = ['A', 'B', 'C', 'D', 'E']
x = 'D'
n = len(arr)
result = linear_Search(arr, n, x)
if result == -1:
print('未找到!')
else:
print('找到!%s在数组中的索引为%s' %(x, result))
[root@master linearsearch]# python3 ls.py
找到!D在数组中的索引为3
上述这个算法的 时间复杂度分析:效率
查找成功时的平均查找长度为:(假设每个数据元素的概率相等)ASL = 1/n(1+2+3+…+n) = (n+1)/2
;
当查找不成功时,需要n+1
次比较,时间复杂度为O(n)
;
所以,顺序查找的时间复杂度为O(n)
。
二分查找是一种在有序数组(也可以是有序的列表 或其他数据类型)中查找某一特定元素的查找算法。
输入 要查找的目标,比如 1
最后得到:1 在什么位置(得到一个索引)
有时候,在实际生产中可能并不需要知道它在哪个位置,只是确认它在里面。
【递归版】
[root@master algorithm]# pwd
/usr/local/src/badou/code/algorithm
[root@master algorithm]# cat binarySearch.py
#coding=utf-8
import sys
def binary_search(list_args, left, right, target, count):
if left > right:#递归结束条件
return [-1, count]
count += 1#进行二分的次数
mid = (left + right) // 2 #地板除法:整数除法,返回不大于结果的最大整数
if target < list_args[mid]:#目标小于中间位置元素,只需再比较左边的元素
right = mid - 1
elif target > list_args[mid]:#目标大于中间位置元素,只需再比较右边的元素
left = mid + 1
else:#目标正好等于中间位置元素,函数结束,返回值
return [mid, count]
#print('left:%d, right:%d' %(left, right))
return binary_search(list_args, left, right, target, count)
list_args = [1, 3, 4, 6, 7, 8, 10, 13, 14]
#若list_args非顺序,则做一个处理
#list_args = list_args.sort()
target = int(sys.argv[1])
res = binary_search(list_args, 0, len(list_args)-1, target, 0)
if res[0] == -1:
print('未找到!需查找的目标是%s,共进行了%d次二分.' % (target,res[1]))
else:
print('找到!需要查找的目标%s在索引位置%d,共进行了%d次二分.' %(target,res[0],res[1]))
执行:
[root@master algorithm]# python3 binarySearch.py 10
找到!需要查找的目标10在索引位置6,共进行了2次二分.
[root@master algorithm]# python3 binarySearch.py 7
找到!需要查找的目标7在索引位置4,共进行了1次二分.
[root@master algorithm]# python3 binarySearch.py 66
未找到!需查找的目标是66,共进行了4次二分.
二分查找算法的时间复杂度:
假设共有n
个元素,每次查找区间大小依次是:
n
n/2
n/4
...
n/(2^k)
其中,k为循环次数
因为n/(2^k)
向上取整 ≥1
,
>>> import math
>>> math.ceil(4/5)
1
计算时间复杂度是按照最坏的情况进行计算,即 在排除到只剩下最后一个值之后得到结果:
所以,令n/(2^k) = 1
(最差的情况,即区间大小为1),那么k = log2(n)
(以2
为底,n
的对数)。
因此:时间复杂度可表示为 O(f(n)) = O(log2(n))
,在数据结构这样写 O(logn)
。
二分查找算法(递归版)的空间复杂度:每次递归所开空间 * 深度
= O(log2(n))
。
参考:
分析时间复杂度&空间复杂度,以二分查找和斐波那契数的递归和非递归算法为例
BFPRT算法,也称 中位数的中位数算法。
排序算法的思想就是将一堆无序的数据采用某一种方法让它变得有序,而通常由于数据的组合形式不同,采用不同的方法有助于提升排序效率,这就是为什么会有这么多的排序算法的原因。
排序算法(sort algorithm)有:插入排序、快速排序、选择排序、冒泡排序、归并排序、堆排序、计数排序、希尔排序、拓扑排序。
上述动图:
插入排序(Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
输入:是一个无序的数据
输出:是一个有序的数据
[root@master algorithm]# cat insertsort.py
#coding=utf-8
def insertSort(data):
for i in range(1, len(data)):#从索引1开始,因为索引0前面没有元素,不用比较。
key = data[i] #获取目标
j = i - 1#获取目标前一个的索引 以便获取它的值
while j >= 0 and key < data[j]:#满足两个条件:1 目标前一个的索引得大于等于0,否则无意义;2 目标 小于前一个值。若不满足这两个条件,则结束while循环,目标待在原位置。
data[j+1] = data[j]#换位置:将目标换到 前一个位置上
j -= 1#获取目标换位置后的前一个位置的索引,以便下一轮循环跟前一个比较大小。接着检查while循环条件,若为True,即目标继续比前一个小,则继续换位置,直到前面都更小(即 while循环为False结束循环,进入下一行执行语句)。
data[j+1] = key#将目标放在 当前位置,进入下一轮for循环,遍历下一个元素
return data#返回经过上述循环处理后的结果
data = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
sort_data = insertSort(data)
print(sort_data)
[root@master algorithm]# python3 insertsort.py
[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
快速排序使用分治法(Divide and conquer)策略。由图灵奖得主C. A. R. Hoare(托尼·霍尔)在1962年提出。
基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都 比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
步骤:【原理】
递归到最底部的判断条件是数列的大小是零或一,此时该数列显然已经有序。
选取基准值有多种具体方法,此选取方法对排序的时间性能有决定性影响。
简例:[10, 7, 8, 9, 1, 5]
使用quick sort的详细步骤:
8
作为基准值10
开始和基准值8
进行比较,大于基准值,将其放入右边的分区中;第2个元素7
比基准值8
小,放入左边分区中;直到最后一个元素代码:去中间位置的元素作为基准值
[root@master algorithm]# cat quick_sort_demo.py
#-*-coding:utf-8-*-
def quick_sort(lst):
"""快速排序"""
#若列表只有1个元素,直接返回它自己。这也是递归结束条件
if len(lst) < 2:
return lst
#step1:选取基准值。在此选取中间位置的元素,便于理解
pivot = lst[len(lst) //2]
#定义基准值左、右两个列表
left,right = [],[]
#从原始列表中移除基准值
lst.remove(pivot)
#step2:分割
for item in lst:
#大于基准值的元素放在右边(默认与基准值相等的元素放在右边)
if item >= pivot:
right.append(item)
else:
#小于基准值的元素放在左边
left.append(item)
#step3:递归排序子列表
return quick_sort(left) + [pivot] +quick_sort(right)
data = [10, 7, 8, 9, 1, 5]
print(quick_sort(data))
[root@master algorithm]# python3 quick_sort_demo.py
[1, 5, 7, 8, 9, 10]
一行代码:
>>> quick_sort = lambda lst:lst if len(lst) < 2 else quick_sort([item for item in lst[1:] if item <= lst[0]]) +[lst[0]] +quick_sort([item for item in lst[1:] if item > lst[0]])
>>> data = [1, 5, 7, 8, 9, 10]
>>> quick_sort(data)
[1, 5, 7, 8, 9, 10]
快速排序的
O(nlogn)
O(nlogn)
下方展示 以最后一个元素作为基准值的代码示例:不过不好理解,可读性没那么好
[root@master algorithm]# cat quicksort.py
#-*-coding=utf-8-*-
#low --> 起始索引
#high--> 结束索引
#分区函数,分区的过程中始终对小于等于基准值的元素依次按照遍历顺序放置,得到分区后的新data
def partition(data, low, high):
i = low -1#得到索引值low前面一个元素的索引值
pivot = data[high]#将最后一个元素作为基准值
for j in range(low, high):#不会遍历到最后一个元素(避开它,因为它是基准值)
if data[j] <= pivot:#所遍历元素小于或等于【基准值】
i = i +1#表示小于等于基准值的元素个数
data[i],data[j] = data[j],data[i]#交换两个位置的值,即根据小于等于基准值的元素个数依次排好序。比如当前遍历的元素j是小于等于基准值的第二个,就将其排在i这个位置
data[i+1],data[high] = data[high],data[i+1]#得到分区后的列表。即基准值放在用于分区的位置
return i+1#得到用于划分分区的索引值
#快速排序函数
def quick_sort(data, low, high):
if low < high:#递归结束条件是low>=high
pi = partition(data, low, high)#划分分区,并得到分区后的data
#分别对分区后的data 左右两部分进行再次分区
quick_sort(data, low, pi-1)#对左边部分进行再分区
quick_sort(data, pi+1, high)#对右边部分进行再分区
data = [10, 7, 8, 9, 1, 5]
n = len(data)
quick_sort(data, 0, n-1)
print(data)
[root@master algorithm]# python3 quicksort.py
[1, 5, 7, 8, 9, 10]
原理或简单处理流程:
每一次排序中,都将当前第 i 小的元素放在位置 i 上。
[root@master algorithm]# cat select_sort.py
#-*-coding=utf-8-*-
def select_sort(data):
n = len(data)
for i in range(n):#默认从0开始遍历
min_idx = i#将i赋值给最小值索引
for j in range(i+1, n):#从i+1开始遍历,比较大小
if data[j] < data[min_idx]:#若小于min_idx,将索引j赋值给min_idx,直至遍历到最后一个元素,找到最小值
min_idx = j
data[i],data[min_idx] = data[min_idx],data[i]#将最小元素 跟索引i元素互换
return data#返回排好序的data
data = [9,1,2,5,7,4,8,6,3,5]
print(select_sort(data))
[root@master algorithm]# python3 select_sort.py
[1, 2, 3, 4, 5, 5, 6, 7, 8, 9]
选择排序的:
n
个元素,则比较次数总是n (n - 1) / 2
。0
。3n (b - 1) / 2
。O(n^2)
。O(1)
。通过比较两个相邻元素的大小实现排序,如果前一个元素大于后一个元素,就交换这两个元素。这样就会让每一趟冒泡都能找到最大一个元素并放到最后。
这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
[root@master algorithm]# cat bubble_sort.py
#-*-coding=utf-8-*-
def bubble_sort(data):
n = len(data)
for i in range(n):#每次找出最大的元素放到最后
for j in range(0, n-i-1):#只需比较剩余的(即前面的n-i个元素,最后一次比较是:第n-i-1个元素 与第n-i个元素比较)
if data[j] > data[j+1]:#前者大于后者,则互换位置。将大于变成小于将变成冒泡排序(降序排列)
data[j],data[j+1] = data[j+1],data[j]
return data
data = [8, 1, 4, 6, 2, 3, 5, 7]
print(bubble_sort(data))
[root@master algorithm]# python3 bubble_sort.py
[1, 2, 3, 4, 5, 6, 7, 8]
冒泡排序的
O(n^2
);O(1)
;1945年由约翰·冯·诺伊曼
首次提出,创建在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。
归并排序的核心思想是利用递归与分治的技术将数据序列划分为越来越小的半子表,再对半子表排序,最后再用递归方法将排好序的半子表合并成越来越大的有序序列。通过递归,层层合并,即为归并。
算法导论中分冶策略定义:将原问题划分为n个规模较小,与原问题结构相似的子问题;递归解决这些子问题,然后再合并其结果,就得到原问题的解。
分冶模式三大步骤:
1,分解:将原问题分解为规模较小的n个子问题;
2,解决:递归得解决个子问题,若子问题足够小,则直接求解;
3,合并:将子问题合并为原问题的解;
[root@master algorithm]# cat merge_sort.py
[root@master algorithm]# cat merge_sort.py
#-*-coding=utf-8-*-
def merge(left, right, data):#在此的left、right都已经是有序的
"""合并:在合并的过程中完成了排序"""
i = j = 0#i 左边的索引值;j 右边的索引值。初始都为 0
while i+j < len(data):
#j==len(right)时,表示右边元素遍历完了;或 左边没有遍历完,同时左边小于右边
if j == len(right) or (i < len(left) and left[i] < right[j]):
data[i+j] = left[i]#将left[i]赋值给data[i+j]
i += 1#将索引值加1,即接下来是遍历左边下一个元素了
else:#右边元素小于左边
data[i+j] = right[j]
j += 1#将索引值加1,即接下来是遍历右边下一个元素了
def merge_sort(data):
"""归并排序"""
n = len(data)
#当data元素个数为1或0时,返回其本身即可。也是递归结束的条件
if n < 2:
return data
"""step1 分:平均切分为左右两个部分"""
mid = n // 2
left = data[0:mid]#左闭右开
right = data[mid:n]
#递归调用【merge_sort归并排序函数】:即 分别对左、右两部分 继续切分
merge_sort(left)
merge_sort(right)
"""step2 合(治 or 并):将左边两部分合并起来,合的过程进行排序"""
merge(left, right, data)
data = [5, 4, 7, 9, 3, 8, 2, 1]
#data = [14, 2, 34, 43, 21, 19]
#data = [8, 4, 5, 7, 1, 3, 6, 2]
merge_sort(data)
print(data)
[root@master algorithm]# python3 merge_sort.py
[1, 2, 3, 4, 5, 7, 8, 9]
归并排序的:
O(n log n)
O(n)
,归并排序需要一个与原数组相同长度的数组做辅助来排序参考:流程画的特别好
Python实现合并排序(归并排序)(一文看懂)
[图解] 归并排序
堆是一种完全二叉树,且堆有两种类型:大根堆、小根堆。
堆排序是利用 堆进行排序的。
堆排序的思想(以大根堆为例):
[root@master algorithm]# cat heap_sort.py
#-*-coding=utf-8-*-
def swap(data, root, last):#root,last代表索引值
"""交换:堆顶元素与最后元素"""
data[root],data[last] = data[last],data[root]
def heap_adjust(data, par_node, high):
"""堆调整函数:调整父节点与子节点,生成大根堆"""
new_par_node = par_node
j = 2 * par_node +1#取得根节点的左子节点:若只有一个节点,则high为左子节点;若有两个子节点,则high为右子节点
while j <= high:#若j、high相等,则表示无右子节点,即high为左子节点
if j < high and data[j]<data[j+1]:#在此若不进行判断,则j<high可能超出索引
#一个根节点下若有两个子节点,将j指向值大的节点
j += 1
if data[j] > data[new_par_node]:#若子节点值大于父节点,则互换
data[new_par_node],data[j] = data[j],data[new_par_node]
new_par_node = j#将当前节点作为父节点,查找它的子树
j = j*2 +1
else:
#因为调整是从上到下,所以下面的所有子树肯定是排好序的
#如果调整的父节点依然 比下面最大的子节点大,就直接打断循环,堆已调整好了
break
def heap_sort(data):
"""
堆排序函数
索引计算:0 -->1 --> ...
父节点 i
左子节点 偶数 2i+1
右子节点 奇数 2i+2
注意:当用长度表示最后一个叶子节点时,得-1
从第1个 非叶子节点(即 最后一个父节点)开始,即
开始循环到 root索引为0的第1个根节点,将所有的【根-叶子】调整好,成为一个大根堆
根据data长度,找到最后一个非叶子节点,开始循环到root根节点,制作大根堆
"""
length = len(data)
last = length - 1#最后一个元素的索引
last_par_node = (length // 2) -1
while last_par_node >= 0:
heap_adjust(data, last_par_node, length-1)
last_par_node -= 1#每调整好一个节点,从后往前移动一个节点
while last > 0:
#swap(data, 0, last)
data[0],data[last] = data[last],data[0]
#调整堆 好让adjust处理最后已经排好序的数,就不处理了
heap_adjust(data, 0, last-1)
last -= 1
return data
data = [4, 7, 0, 9, 1, 5, 3, 3, 2, 6]
heap_sort(data)
print(data)
[root@master algorithm]# python3 heap_sort.py
[0, 1, 2, 3, 3, 4, 5, 6, 7, 9]
参考:
堆排序的Python实现(附详细过程图和讲解)
堆排、python实现堆排
PYTHON实现堆排序:里面 有一个 打印堆排序使用
的函数非常6
堆排序的:
O(n log2 n)
O(1)
是一个非基于比较的排序算法,优势在于在对一定范围内的整数排序时,快于基于比较的排序算法。
算法思想
在于给定的输入序列中的每一个元素x
,确定该序列中值小于等于x
元素的个数,然后将x
直接存放到最终的排序序列的正确位置上。
找出待排序的数组中最大和最小的元素;
统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
[root@master algorithm]# cat count_sort.py
#-*- coding: utf-8 -*-
def count_sort(data):
n = len(data)
res = [None] *n
#第1次遍历,每个元素的次数都统计
for i in range(n):
p = 0 #p表示a[i]大于列表其他元素的 次数
q = 0 #q表示等于a[i]的次数
for j in range(n):#第2次循环,列表中的每个元素 都和第1次循环的元素比较
if data[i] > data[j]:
p += 1
elif data[i] == data[j]:
q += 1
for k in range(p, p+q):#q表示相等的次数,即从p开始索引后,连续p次,都是相同的数
res[k] = data[i]
return res
data = [4, 7, 0, 9, 1, 5, 3, 3, 2, 6]
print(count_sort(data))
[root@master algorithm]# python3 count_sort.py
[0, 1, 2, 3, 3, 4, 5, 6, 7, 9]
空间复杂度和时间复杂度
O(N+M)
O(M)
。局限性
希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。
该方法因DL.Shell
于1959年提出而得名。 希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的元素越来越多,当增量减至1时,整个数据恰被分成一组,算法便终止。
[root@master algorithm]# cat shell_sort.py
#-*-coding:utf-8-*-
def shell_sort(data):
"""希尔排序"""
n = len(data)
gap = n // 2
while gap >= 1:
for j in range(gap, n):
i = j
while i-gap >= 0:
if data[i] < data[i-gap]:
data[i],data[i-gap] = data[i-gap],data[i]
i -= gap
else:
break
gap //= 2
data = [4, 7, 0, 9, 1, 5, 3, 3, 2, 6]
shell_sort(data)
print(data)
[root@master algorithm]# python3 shell_sort.py
[0, 1, 2, 3, 3, 4, 5, 6, 7, 9]
时间复杂度:O(n^2)
空间复杂度:O(1)
过程简述:
[root@master algorithm]# cat topo_sort.py
#-*-coding:utf-8-*-
def topo_sort(graph):#如果没有现成的Graph,就要自己想办法造一个出来
#创建入度字典
in_degree = dict((u,0) for u in graph)#初始化所有顶点入度为0
vertex_num = len(in_degree)
#获取每个节点的入度
for u in graph:
for v in graph[u]:
in_degree[v] += 1#计算每个顶点的入度,对应的key每在graph[u]里被提到一次,入度就+1
#使用列表作为队列并将入度为0的添加到队列中
q = [u for u in in_degree if in_degree[u] == 0]#筛选入度为0的顶点
result = []
#当队列中有元素时执行
while q:
u = q.pop()#默认从最后一个删除
result.append(u)#将取出的元素存入结果中
for v in graph[u]:
in_degree[v] -= 1#类似之前,对应的key每在graph[u]里被提到一次,入度就-1
if in_degree[v] == 0:
q.append(v)#再次筛选入度为0的顶点
if len(result) == vertex_num:#如果循环结束后存在非0入度的顶点说明图中有环,不存在拓扑排序
return result
else:
print('this is a circle')
return result
graph = {
"A": ["B","C"],
"B": ["D","E"],
"C": ["D","E"],
"D": ["F"],
"E": ["F"],
"F": [],
}
print(topo_sort(graph))
[root@master algorithm]# python3 topo_sort.py
['A', 'C', 'B', 'E', 'D', 'F']
参考:
python拓扑排序
Python图的拓扑排序
算法的稳定性定义:对于待排序列中相同项的原来次序不能被算法改变则称该算法稳定。
菜鸟Python3 实例
Python算法基础:非常好,含时间、空间复杂度
Python之路:常用算法与设计模式:含时间复杂度
七大查找算法(Python)
十大编程算法助程序员走上高手之路:动态图做的非常好