Python 编程导论 Chapter 10 —— 一些简单算法和数据结构

本文是《Python编程导论》第十章,主要介绍搜索算法和排序算法。内容涵盖线性搜索、二分查找、归并排序及其效率分析,强调了排序在提升搜索效率中的作用。同时,讲解了Python中的排序函数以及如何使用函数作为参数来定制排序方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


typora-copy-images-to: Python 编程导论

Python 编程导论 Chapter 10 —— 一些简单算法和数据结构


  • 本章目的:

    • 建立某种通用直觉,以解决算法效率方面的问题
  • 程序效率的关键是好的算法,我们要做的是,学会在面对问题时将复杂性减到最低,并将他们转化为已经解决的问题

10.1 搜索算法

  • 搜索算法就是在一个项目集合中找出一个或一组具有某种特点的项目
# 本节研究两种搜索列表的方法
def search(L, e):
	"""假设L是列表
	如果e是L中的元素,则返回True,否则返回False"""
    
语义上和python表达式  e in L 完全相同
10.1.1 线性搜索与间接引用元素
for i in range(len(L)):
	if L[i] == e:
		return True
return False

最差结果,算法执行O(len(L))次测试
  • 计算机的间接引用:要访问目标元素时,先访问另一个元素,再通过包含在这个元素中的引用来访问目标元素
10.1.2 二分查找和利用假设
  • 假设我们对元素的存储顺序有所了解?

例如,假设我们知道一个整数列表是按照升序存储元素的,那么可以修改函数实现,搜索到一个大于目标整数的数值时,就停止搜索

def search(L, e):
	"""假设L是列表,其中元素按升序排列。
		ascending order.
		如果e是L中的元素,则返回True,否则返回False"""
	for i in range(len(L)):
		if L[i] == e:
			return True
		if L[i] > e:          比如L = [1, 2, 4, 5] e = 3 的时候
			return False
	return False

# 以上算法可以缩短平均运行时间,但是不改变复杂度
  • 使用二分查找的算法可以有效改善最差情形下的复杂度

  • 二分查找的思路:

    • 选择一个可以将列表L大致一分为二的索引
    • 是否有L[i] == e
    • 如果不是,检查L[i]大于还是小于e
    • 根据上一步结果,确定在L 的左半部还是右半部分搜索e
  • 给定算法结构之后,实现二分查找的最简单直接的方式就是使用递归

# 递归二分查找


def search(L, e):
	"""假设L是列表,其中元素按升序排列。
	ascending order.
	如果e是L中的元素,则返回True,否则返回False"""
	def bSearch(L, e, low, high):
	#Decrements high – low
		if high == low:
			return L[low] == e
		mid = (low + high)//2
		if L[mid] == e:
			return True
		elif L[mid] > e:
			if low == mid: #nothing left to search
				return False
			else:
				return bSearch(L, e, low, mid - 1)
		else:
			return bSearch(L, e, mid + 1, high)
	if len(L) == 0:
		return False
	else:
		return bSearch(L, e, 0, len(L) - 1)
  • 类似与search这样的函数经常被称作为包装器函数,为什么会有辅助函数searchb的产生,因为参数high、low与搜索这一抽象任务无关,因此需要隐藏
  • searchb的复杂度仅仅依赖于递归调用的次数
  • search函数的复杂度为O(log(len(L)))

10.2 排序算法

  • 是否意味着在有列表搜索需求时,应该先排序再执行搜索?
    • 不可以,因为排序首先就是需要对列表的每个元素检查一次
    • 但是当我们要对目标列表多次搜索时,性价比就会提高
    • 使用python的内置函数sorted,生成一个排序后的列表,不对L修改
# 选择排序

def selSort(L):
	"""假设L是列表,其中的元素可以用>进行比较。
		compared using >.
		对L进行升序排列"""
	suffixStart = 0
	while suffixStart != len(L):
	#检查后缀集合中的每个元素
		for i in range(suffixStart, len(L)):
			if L[i] < L[suffixStart]:
			#交换元素位置
				L[suffixStart], L[i] = L[i], L[suffixStart]
		suffixStart += 1

在这里插入图片描述

  • 该函数的复杂度为L长度的平方,非常低效
10.02.1 归并排序
  • 分治算法的基本思想是,先找出出事问题的一些简单实例的解,再组合起来,作为整体解分治算法的特征:

    • 一个输入规模的阈值,低于这个阈值的问题不会进行分解
    • 一个实例分解成子实例的规模和数量
    • 合并子解的算法
  • 阈值有时候成为递归基

  • 归并排序是一种典型的分治算法

    • 如果列表的长度是0或者1,那么它已经排好序了
    • 如果包含多于1个元素,分成两个列表,并分别使用归并排序法进行排序
    • 合并结果

假设要合并列表[1, 5, 12, 18, 19, 20] 和 [2, 3, 4, 17]:

列表1中剩余元素 | 列表2中剩余元素 | 目标列表 |
[1,5,12,18,19,20] [2,3,4,17] []
[5,12,18,19,20] [2,3,4,17] [1]
[5,12,18,19,20] [3,4,17] [1,2]

合并两个有序列表的复杂度与列表的长度成线性关系

def merge(left, right, compare):
    """假设left和right是两个有序列表,compare定义了一种元素排序规则。
    返回一个新的有序列表(按照compare定义的顺序),其中包含与
    (left+right)相同的元素。"""
    result = []
    i,j = 0, 0
    while i < len(left) and j < len(right):
        if compare(left[i], right[j]):
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    while (i < len(left)):
        result.append(left[i])
        i += 1
    while (j < len(right)):
        result.append(right[j])
        j += 1
    return result

def mergeSort(L, compare = lambda x, y: x < y):
    """假设L是列表,compare定义了L中元素的排序规则
        on elements of L
        返回一个新的具有L中相同元素的有序列表。"""
    if len(L) < 2:
        return L[:]
    else:
        middle = len(L)//2
        left = mergeSort(L[:middle], compare)
        right = mergeSort(L[middle:], compare)
        return merge(left, right, compare)
    
L = [2, 1, 4, 5, 3]
print(mergeSort(L),mergeSort(L, lambda x, y: x > y))
10.2.2 将函数用作参数
def lastNameFirstName(name1, name2):
	arg1 = name1.split(' ')
	arg2 = name2.split(' ')
	if arg1[1] != arg2[1]:
		return arg1[1] < arg2[1]
	else: #姓相同,则按照名排序
		return arg1[0] < arg2[0]
def firstNameLastName(name1, name2):
	arg1 = name1.split(' ')
	arg2 = name2.split(' ')
	if arg1[0] != arg2[0]:
		return arg1[0] < arg2[0]
	else: #名相同,则按照姓排序
		return arg1[1] < arg2[1]

    
L = ['Tom Brady', 'Eric Grimson', 'Gisele Bundchen']
newL = mergeSort(L, lastNameFirstName)
print('Sorted by last name =', newL)
newL = mergeSort(L, firstNameLastName)
print('Sorted by first name =', newL)

# 将函数用作了参数

10.2.3 Python 中的排序

L = [3,5,2]
D = {'a':12, 'c':5, 'b':'dog'}
print(sorted(L))
print(L)
L.sort()
print(L)
print(sorted(D))
D.sort()


# 输出
[2, 3, 5]
[3, 5, 2]
[2, 3, 5]
['a', 'b', 'c']
AttributeError: 'dict' object has no attribute 'sort'
  • sorted 应用于字典时,会返回排好序的字典键的列表

  • list.sort 方法和 sorted 函数都可以有两个附加参数。参数 key 的作用和我们实现归并排序
    时的 compare 一样:提供用于排序的比较函数,参数 reverse 指定对列表进行升序还是降序排序

10.3 散列表(略)

变分模态分解(Variational Mode Decomposition, VMD)是一种强大的非线性、无参数信号处理技术,专门用于复杂非平稳信号的分析与分解。它由Eckart DietzHerbert Krim于2011年提出,主要针对传统傅立叶变换在处理非平稳信号时的不足。VMD的核心思想是将复杂信号分解为一系列模态函数(即固有模态函数,IMFs),每个IMF具有独特的频率成分局部特性。这一过程与小波分析或经验模态分解(EMD)类似,但VMD通过变分优化框架显著提升了分解的稳定性准确性。 在MATLAB环境中实现VMD,可以帮助我们更好地理解应用这一技术。其核心算法主要包括以下步骤:首先进行初始化,设定模态数并为每个模态分配初始频率估计;接着采用交替最小二乘法,通过交替最小化残差平方以及模态频率的离散时间傅立叶变换(DTFT)约束,更新每个模态函数中心频率;最后通过迭代优化,在每次迭代中优化所有IMF的幅度相位,直至满足停止条件(如达到预设迭代次数或残差平方小于阈值)。 MATLAB中的VMD实现通常包括以下部分:数据预处理,如对原始信号进行归一化或去除直流偏置,以简化后续处理;定义VMD结构,设置模态数、迭代次数约束参数等;VMD算法主体,包含初始化、交替最小二乘法迭代优化过程;以及后处理,对分解结果进行评估可视化,例如计算每个模态的频谱特性,绘制IMF的时频分布图。如果提供了一个包含VMD算法的压缩包文件,其中的“VMD”可能是MATLAB代码文件或完整的项目文件夹,可能包含主程序、函数库、示例数据结果可视化脚本。通过运行这些代码,可以直观地看到VMD如何将复杂信号分解为独立模态,并理解每个模态的物理意义。 VMD在多个领域具有广泛的应用,包括信号处理(如声学、振动、生物医学信号分析)、图像处理(如图像去噪、特征提取)、金融时间序列分析(识
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值