排序算法的下界和如何超越下界——python实现Thomas H.Cormen算法基础中的算法

2023-10-11 21:10

本文主要是介绍排序算法的下界和如何超越下界——python实现Thomas H.Cormen算法基础中的算法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 一、排序算法分类
    • 二、算法复杂度
    • 三、时间复杂度下界
    • 四、超越下界
      • 1.范例1——严格的约束(排序仅有两个值)
      • 2.范例2——扩展1至每个元素可以取m个连续整数中的一个

一、排序算法分类

  • 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。
  • 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。

排序算法分类

二、算法复杂度

算法复杂度
相关概念:

  • 稳定:如果a原本在b的前面,而a=b,排序之后a仍然在b的前面
  • 不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面
  • 时间复杂度:对排序数据的总的操作次数。反映当n发生变化的时候,操作次数呈现什么规律
  • 空间复杂度:算法在计算机中执行时所需要存储空间的度量,是数据规模n的函数

三、时间复杂度下界

观察插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序的时间复杂度,在没有特殊规则的时候时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)就是最优的排序算法了

也就是说通用排序算法的时间复杂度下界就是 O ( n l o g n ) O(nlogn) O(nlogn)

如果限定一些规则,是可以打破这个下界的。

下面说一下尽在O(n)时间内就能实现对数组排序的算法、

四、超越下界

问题:基于什么样的规则才能突破排序的下界呢?

基础思想:我们需要分析一下排序消耗的时间。排序需要遍历,比较,交换。能否省略其中的一些步骤呢?这就是要定义的规则,通过规则减少排序步骤。

1.范例1——严格的约束(排序仅有两个值)

一组待排序的元素仅有1和2,没有其它值,对这组数进行排序。

输入 A 0 , A 1 , A 2 , A 3 . . . . . . A n − 1 A_0,A_1,A_2,A_3......A_{n-1} A0A1,A2,A3......An1,Ai为1或者2

排序步骤

  1. 令k=0
  2. 令i从0到n-1依次取值,如果A[i]=1,k自增1
  3. 令i从0到k-1依次取值,将A[i]赋值为1
  4. 令i从k到n-1依次取值,将A[i]赋值为2

这样我们完成了排序,花费的时间为O(n)
之前我们所说的算法都是通过比较元素对来确定顺序,那种排序叫做比较排序。凡是比较排序,通用下界为O(nlogn)

# 非常简单的实现,每一步都对应于上述四个步骤
A = [1,2,1,1,1,2,1,2,1,2,1]
print('排序前:',A)
n = len(A)
k = 0
for i in range(0,n):if A[i]==1:k+=1for i in range(0,k):A[i] = 1for i in range(k,n):A[i] = 2print('排序后:',A)

结果:

排序前: [1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1]
排序后: [1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2]

2.范例2——扩展1至每个元素可以取m个连续整数中的一个

只要每个元素在m个连续整数值中取值,算法都是通用的

该算法的实现需要三个基本方法:

COUNT-KEY-EQUAL(A,n,m)

输入:A 一个数组,n 数组A中的元素个数m数组A中元素的取值范围
输出:一个数组 e q u a l [ 0...... m ] equal[0......m] equal[0......m],是 e q u a l [ j ] equal[j] equal[j]等于数组A中元素值为j的元素个数

  1. 创建一个新数组 e q u a l [ 0...... m ] equal[0......m] equal[0......m]
  2. e q u a l equal equal数组每个元素都为0
  3. i从0到n-1依次取值,每次将 e q u a l [ A [ i ] ] equal[A[i]] equal[A[i]]的值自增1
  4. 返回 e q u a l equal equal

COUNT-KEY-LESS(equal,m)

输入值COUNT-KEY-EQUAL方法对应的值 e q u a l , m equal,m equalm
输出一个数组 l e s s [ 0...... m ] , l e s s [ j ] = e q u a l [ 0 ] + e q u a l [ 1 ] + . . . . . . + e q u a l [ j − 1 ] less[0......m],less[j]=equal[0]+equal[1]+......+equal[j-1] less[0......m],less[j]=equal[0]+equal[1]+......+equal[j1]

  1. 创建一个新数组 l e s s [ 0... m ] less[0...m] less[0...m]
  2. l e s s [ 0 ] = 0 less[0]=0 less[0]=0
  3. j从1取到m, l e s s [ j ] = l e s s [ j − 1 ] + e q u a l [ j − 1 ] less[j]=less[j-1]+equal[j-1] less[j]=less[j1]+equal[j1](这是普通的迭代算法)
  4. 返回 l e s s less less

REARRANGE(A,less,n,m)

输入COUNT-KEY-EQUAL COUNT-KEY-LESS方法对应的 A , l e s s , n , m A,less,n,m Aless,n,m
输出数组B,B中包含A中所有元素,并且已经排好序

  1. 创建新数组 B [ 0... n − 1 ] , n e x t [ 0..... m ] B[0...n-1],next[0.....m] B[0...n1],next[0.....m]
  2. j从0到m依次取值
    n e x t [ j ] = l e s s [ j ] + 1 next[j]=less[j]+1 next[j]=less[j]+1
  3. 令i从0到n-1依次取值
    k e y = A [ i ] ; i n d e x = n e x t [ k e y ] , B [ i n d e x ] = A [ i ] , n e x t [ k e y ] + + key=A[i];index=next[key],B[index]=A[i],next[key]++ key=A[i];index=next[key],B[index]=A[i],next[key]++
  4. 返回数组B
## 代码实现:
import numpy as np
def CountKeyEqual(A,n,m):equal = np.zeros(m+1)for i in range(0,n):equal[A[i]]+=1return equal
def CountKeyLess(equal,m):less = np.ones(m+1)less[0] = 0 for j in range(1,m+1):less[j] = less[j-1]+equal[j-1]return less
def Rearrange(A,less,n,m):B = np.ones(n)next_ = np.ones(m+1)for j in range(m+1):next_[j] = less[j]+1for i in range(n):key = A[i]index = next_[key]B[int(index)-1] = A[i] # 注意这里与文本序数有些把不同在于B的索引大小应该与A的一致next_[key]+=1return B
if __name__=='__main__':A = [4,1,5,0,1,6,5,1,5,3]n = len(A)m = 6print('排序前:',A)equal = CountKeyEqual(A,n,m)less = CountKeyLess(equal,m)B = Rearrange(A,less,n,m)B = B.astype(np.int).tolist()print('排序后:',B)

结果:

排序前: [4, 1, 5, 0, 1, 6, 5, 1, 5, 3]
排序后: [0, 1, 1, 1, 3, 4, 5, 5, 5, 6]

这篇关于排序算法的下界和如何超越下界——python实现Thomas H.Cormen算法基础中的算法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/190839

相关文章

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

Python版本信息获取方法详解与实战

《Python版本信息获取方法详解与实战》在Python开发中,获取Python版本号是调试、兼容性检查和版本控制的重要基础操作,本文详细介绍了如何使用sys和platform模块获取Python的主... 目录1. python版本号获取基础2. 使用sys模块获取版本信息2.1 sys模块概述2.1.1

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4

Python实现字典转字符串的五种方法

《Python实现字典转字符串的五种方法》本文介绍了在Python中如何将字典数据结构转换为字符串格式的多种方法,首先可以通过内置的str()函数进行简单转换;其次利用ison.dumps()函数能够... 目录1、使用json模块的dumps方法:2、使用str方法:3、使用循环和字符串拼接:4、使用字符