python算法和数据结构刷题[4]:查找算法和排序算法

devtools/2025/2/6 14:58:59/

分治算法

排序

冒泡排序

不断交换相邻元素的位置来将元素按照从小到大(或从大到小)的顺序排列。

import random# 生成随机数列表
num_list = [random.randint(1, 100) for _ in range(10)]print("原始列表:", num_list)# 冒泡排序
def bubble_sort(arr):n = len(arr)for i in range(n):for j in range(0, n-i-1):if arr[j] > arr[j+1]:arr[j], arr[j+1] = arr[j+1], arr[j]bubble_sort(num_list)print("排序后:", num_list)

时间复杂度:O(n方),空间复杂度O(1) 

 选择排序

不断选择未排序序列中最小(或最大)的元素来将元素按照从小到大(或从大到小)的顺序排列。

import random# 生成随机数列表
num_list = [random.randint(1, 100) for _ in range(10)]print("原始列表:", num_list)# 选择排序
def selection_sort(arr):n = len(arr)for i in range(n):min_index = ifor j in range(i+1, n):if arr[j] < arr[min_index]:min_index = jarr[i], arr[min_index] = arr[min_index], arr[i]selection_sort(num_list)

 时间复杂度为O(n^2),空间复杂度为O(1)

插入排序

它通过不断将未排序元素插入已排序序列中来将元素按照从小到大(或从大到小)的顺序排列。
插入排序的主要思想是将数组分为已排序和未排序两部分。初始时,将第一个元素视为已排序,然后从未排序部分逐个选择元素,插入到已排序部分的适当位置,以保持已排序部分始终有序。

时间复杂度为O(n^2),空间复杂度为O(1)

import random# 生成随机数列表
num_list = [random.randint(1, 100) for _ in range(10)]print("原始列表:", num_list)# 插入排序
def insertion_sort(arr):n = len(arr)for i in range(1, n):key = arr[i]j = i - 1while j >= 0 and key < arr[j]:arr[j + 1] = arr[j]j -= 1arr[j + 1] = keyinsertion_sort(num_list)print("排序后:", num_list)

快速排序

通过选取一个基准元素将数组分为两个子数组,然后递归对两个子数组进行排序来将元素按照从小到大(或从大到小)的顺序排列。
快速排序是一种高效的分治法排序算法,其主要思想是选择一个基准元素,将数组分成小于基准的左子数组和大于基准的右子数组,然后递归地对左右子数组进行排序,最终将它们合并起来。

import random# 生成随机数列表
num_list = [random.randint(1, 100) for _ in range(10)]print("原始列表:", num_list)# 快速排序
def quick_sort(arr):if len(arr) <= 1:return arrpivot = arr[len(arr) // 2]  # 选择中间元素作为基准值left = [x for x in arr if x < pivot]middle = [x for x in arr if x == pivot]right = [x for x in arr if x > pivot]return quick_sort(left) + middle + quick_sort(right)sorted_list = quick_sort(num_list)print("排序后:", sorted_list)

 时间复杂度为O(n log n)(平均情况,在最坏情况下可能达到O(n^2)),空间复杂度为O(log n)

归并排序

归并排序是一种稳定的分治法排序算法,它通过将数组分为两个子数组,递归对两个子数组进行排序,然后将两个有序子数组归并为一个有序数组来将元素按照从小到大(或从大到小)的顺序排列。

时间复杂度为O(n log n),空间复杂度为O(n)。

归并排序的优点之一是它不受输入数据分布的影响,始终保持O(n log n)的时间复杂度,但其空间复杂度较高,需要额外的存储空间来保存临时数组。在处理大型数据集或要求稳定排序的情况下,归并排序是一个很好的选择。

import random# 生成随机数列表
num_list = [random.randint(1, 100) for _ in range(10)]print("原始列表:", num_list)# 归并排序
def merge_sort(arr):if len(arr) <= 1:return arrmid = len(arr) // 2left_half = arr[:mid]right_half = arr[mid:]left_half = merge_sort(left_half)right_half = merge_sort(right_half)return merge(left_half, right_half)def merge(left, right):result = []left_index, right_index = 0, 0while left_index < len(left) and right_index < len(right):if left[left_index] < right[right_index]:result.append(left[left_index])left_index += 1else:result.append(right[right_index])right_index += 1result.extend(left[left_index:])result.extend(right[right_index:])return resultsorted_list = merge_sort(num_list)print("排序后:", sorted_list)

查找

线性查找

 若数据是无序的, 则只能采取顺序查找。

二分查找

元素必须是有序的,如果是无序的则要先进行排序操作。

使用两次二分查找,也就是 O(logn) 的时间复杂度

基本思想:也称为是折半查找,属于有序查找算法。用给定值k先与中间结点的关键字比较,中间结点把线形表分成两个子表,若相等则查找成功;若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表,这样递归进行,直到查找到或查找结束发现表中没有这样的结点。

35. 搜索插入位置 - 力扣(LeetCode)

一种是在[0, n - 1]这个左闭右闭区间内找,一种是在[0, n)这个左闭右开区间内找

class Solution:def searchInsert(self, nums: List[int], target: int) -> int:left = 0right = len(nums) - 1while left <= right:mid = (left + right) // 2if nums[mid] == target:return midelif nums[mid] > target:right = mid - 1else:left = mid + 1return left
class Solution:def searchInsert(self, nums: List[int], target: int) -> int:left = 0right = len(nums)while left < right:mid = left + (right - left) // 2if nums[mid] == target:return midelif nums[mid] > target:right = midelse:left = mid + 1return left

74. 搜索二维矩阵 - 力扣(LeetCode) 

第一步,我们先使用一次二分查找来找到对应的 target 值所在的一维数组里面,一旦锁定一维数组,就可以使用我们平时最熟悉的一维数组的二分查找了。但是需要注意的是,在第一次二分的过程中,需要注意边界的处理。

class Solution:def searchMatrix(self, matrix, target):# 初始化行搜索的左右边界top = 0bottom = len(matrix) - 1# 在行中进行二分查找,寻找目标值所在的行while top < bottom:mid_row = (top + bottom) // 2# 如果中间行的第一个元素小于目标值,且下一行的第一个元素大于等于目标值if matrix[mid_row][0] < target and matrix[mid_row + 1][0] <= target:top = mid_row + 1else:bottom = mid_row# 初始化列搜索的左右边界left = 0right = len(matrix[top]) - 1# 在确定的行中进行二分查找,寻找目标值while left < right:mid_col = (left + right) // 2# 如果中间列的元素大于等于目标值,则缩小搜索范围到左侧if matrix[top][mid_col] >= target:right = mid_colelse:# 否则缩小搜索范围到右侧left = mid_col + 1# 检查最终确定的元素是否是目标值return matrix[top][left] == target

 34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

 

class Solution:def searchRange(self, nums: List[int], target: int) -> List[int]:# 取起始下标l, r = 0, len(nums) - 1while l < r:mid = (l + r) // 2if nums[mid] >= target:r = midelse:l = mid + 1# 没找到if not nums or nums[l] != target:return [-1,-1]# 取结束下标a, b = l, len(nums) - 1while a < b:mid = (a + b + 1) // 2if nums[mid] <= target:a = midelse:b = mid - 1return [l,a]

 33. 搜索旋转排序数组 - 力扣(LeetCode)

在旋转点左侧的元素是升序的,在旋转点右侧的元素也是升序的。我们可以利用这一性质来修改二分查找算法,使其适用于旋转数组。

def search(nums, target):left, right = 0, len(nums) - 1while left <= right:mid = (left + right) // 2if nums[mid] == target:return mid# 判断哪部分是有序的if nums[mid] >= nums[left]:  # 左侧有序# 判断目标值是否在左侧有序部分if nums[left] <= target < nums[mid]:right = mid - 1else:left = mid + 1else:  # 右侧有序# 判断目标值是否在右侧有序部分if nums[mid] < target <= nums[right]:left = mid + 1else:right = mid - 1return -1  # 如果找不到目标值,返回 -1# 示例
nums = [4, 5, 6, 7, 0, 1, 2]
target = 0
print(search(nums, target))  # 输出应为 4

 153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)

最小元素是唯一一个比其右侧元素小的元素.

def find_min(nums):left, right = 0, len(nums) - 1while left < right:mid = (left + right) // 2if nums[mid] > nums[right]:left = mid + 1else:right = midreturn nums[left]# 示例
nums = [4, 5, 6, 7, 0, 1, 2]
print(find_min(nums))  # 输出应为 0

4. 寻找两个正序数组的中位数 - 力扣(LeetCode) 

 

def findMedianSortedArrays(nums1, nums2):# 确保 nums1 是较短的数组if len(nums1) > len(nums2):nums1, nums2 = nums2, nums1m, n = len(nums1), len(nums2)imin, imax, half_len = 0, m, (m + n + 1) // 2while imin <= imax:i = (imin + imax) // 2j = half_len - iif i < m and nums1[i] < nums2[j - 1]:# i 太小,必须增加imin = i + 1elif i > 0 and nums1[i - 1] > nums2[j]:# i 太大,必须减小imax = i - 1else:# i 正确if i == 0: max_of_left = nums2[j - 1]elif j == 0: max_of_left = nums1[i - 1]else: max_of_left = max(nums1[i - 1], nums2[j - 1])if (m + n) % 2 == 1:return max_of_leftif i == m: min_of_right = nums2[j]elif j == n: min_of_right = nums1[i]else: min_of_right = min(nums1[i], nums2[j])return (max_of_left + min_of_right) / 2.0raise ValueError("Input arrays are not sorted or of zero length.")


http://www.ppmy.cn/devtools/156555.html

相关文章

FPGA 时钟拓扑结构建议

时钟拓扑结构建议 赛灵思建议使用简单的时钟树拓扑结构&#xff0c;因其设计所需的时钟缓存数量最少。使用额外的时钟缓存需要更多的布线轨 道&#xff0c;这可能导致在时钟布线要求高并且接近最大容量的时钟区域中的布局错误或布线冲突。 以下是针对 BUFGCE/BUFGCTRL/BUFGC…

git reset 命令

git reset 的作用 git reset 是一个非常强大的命令&#xff0c;用于将当前分支的 HEAD&#xff08;即当前指向的提交&#xff09;重置到指定的提交。它还可以根据参数的不同&#xff0c;对工作区&#xff08;Working Directory&#xff09;和暂存区&#xff08;Staging Area&a…

C++并发:设计无锁数据结构

只要摆脱锁&#xff0c;实现支持安全并发访问的数据结构&#xff0c;就有可能解决大粒度锁影响并发程度以及错误的加锁方式导致死锁的问题。这种数据结构称为无锁数据结构。 在了解本文时&#xff0c;务必读懂内存次序章节。 在设计无锁数据结构时&#xff0c;需要极为小心谨…

Effective Objective-C 2.0 读书笔记—— 消息转发

Effective Objective-C 2.0 读书笔记—— 消息转发 文章目录 Effective Objective-C 2.0 读书笔记—— 消息转发前言消息转发机制概述动态方法解析处理dynamic的属性用于懒加载 消息转发快速消息转发完整消息转发 总结 前言 在前面我学习了关联对象和objc_msgSend的相关内容&a…

【实战篇章】深入探讨:服务器如何响应前端请求及后端如何查看前端提交的数据

文章目录 深入探讨&#xff1a;服务器如何响应前端请求及后端如何查看前端提交的数据一、服务器如何响应前端请求HTTP 请求生命周期全解析1.前端发起 HTTP 请求&#xff08;关键细节强化版&#xff09;2. 服务器接收请求&#xff08;深度优化版&#xff09; 二、后端如何查看前…

4.回归与聚类算法 4.1线性回归

4.1.1 线性回归的原理 1 线性回归应用场景&#xff1a; 房价预测 销售额度预测 金融&#xff1a;贷款额度预测&#xff0c;利用线性回归以及系数分析因子 2 什么是线性回归 1&#xff09; 定义&#xff1a;利用回归方程&#xff08;函数&#xff09;对一个或者多个自变量…

大话特征工程:3.特征扩展

公元 2147 年&#xff0c;人类文明站在科技的巅峰&#xff0c;所有决策、发展甚至感知都被“全维计算网络”所掌控。这套系统以高维空间中的数据为基础&#xff0c;试图预测并塑造未来。然而&#xff0c;这场辉煌的技术革命却在悄无声息之间酿成了人类最大的危机——维数灾难。…

Unity游戏(Assault空对地打击)开发(7) 飞机坠毁后的操作

前言 本文之后基本操作不再演示。 详细操作 导入Free Fire VFX插件&#xff0c;生成火的效果。 在该文件夹下挑一个你喜欢的火&#xff0c;拖至Camera下&#xff0c;重命名为Fire。 调整一下火的位置&#xff0c;让摄像机清晰看到火&#xff0c;如下图&#xff0c;火在摄像机的…