当前位置: 首页 > news >正文

数据结构之排序算法

一.冒泡排序

对于一个数组,从前往后依次比较相邻的两个元素,将较大的元素放在较小的元素的后面。

假设数组的长度为n,第一次比较需要比较n-1次,第二次在剩下的n-1个元素里面找最大值,需要比较n-2次......直到比较到还剩下一个元素,这时剩下的这个元素就一定是最小的,从小到大的排序任务就完成了。这个过程中一共要遍历数组n-1次(只剩下一个元素时这种情况不用比较,也就不用遍历,所以遍历数组的次数是n-1),而且每次遍历数组时要遍历的长度都在变小,第一次需要全部遍历,第一次遍历完后已经找到了n个元素中的最大值并把它放到了数组的末尾,第二次就不需要比较这个最大的元素了,只需要在剩下的n-1个元素里找次大的,所以第二次遍历数组时不用全部都遍历,只需要遍历n-1个长度,即从第1个元素遍历到倒数第二个元素(第n-1个);然后依次类推

def bubble_sort(arr):n = len(arr)for i in range(n-1):for j in range(n-i-1):if arr[j] > arr[j+1]:arr[j], arr[j+1] = arr[j+1], arr[j]return arrdef bubble_sort_short(arr):"""短冒泡排序"""n = len(arr)for i in range(n-1):swapped = False  # 优化:提前终止标志 值为False说明该轮比较中没有元素交换for j in range(1, n-i):if arr[j] < arr[j-1]:arr[j], arr[j-1] = arr[j-1], arr[j] swapped = Trueif not swapped: # 如果本轮比较没有交换,则说明数组已经有序,直接退出循环breakif __name__ == "__main__":result = bubble_sort([2,3,1,0,6,-4,9,-6])print(result)a_list = [4,2,-1,9,45,76,-99,3]bubble_sort_short(a_list) # 进行原地排序print(a_list)

输出:

[-6, -4, 0, 1, 2, 3, 6, 9]
[-99, -1, 2, 3, 4, 9, 45, 76]

时间复杂度分析:

最好情况下只需比较1次,时间复杂度为o(1)

最坏情况下要遍历n-1次,比较次数依次为:n-1, n-2, n-3, ... , 3, 2, 1 。共要比较\frac{n(n-1)}{2}次,所以时间复杂度为o(n^2)

平均情况下要比较\frac{n(n-1)}{2} 除以 (n-1)也就是\frac{n}{2}次,所以时间复杂度为o(n)

二.选择排序

选择排序是在冒泡排序的基础上进行了一些改进。改进的地方在于每次遍历数组时只交换一次

要实现这一点,就要在每次遍历数组时找出数组的最大值,然后在该次遍历完之后把这个最大值放在合适的位置上

假设数组元素个数为n,只需遍历n-1次,找出n-1个较大值,剩下的那个元素自然就有序了

def selected_sort_min(arr):"""找最小值,把最小值放到数组的第一个位置"""n = len(arr)for i in range(n-1):min_idx = ifor j in range(i+1,n):if arr[j] < arr[min_idx]:min_idx = jarr[i], arr[min_idx] = arr[min_idx], arr[i]return arrdef selected_sort_max(arr):"""找最大值,把最大值放到数组的末尾"""n = len(arr)for i in range(n-1,0,-1): # 序列为(n-1, n-2, n-3, ..., 2, 1)max_idx = ifor j in range(i):if arr[j] > arr[max_idx]:max_idx = jif max_idx != i: # 只有位置不同时才交换,位置相同说明该次遍历中没有元素比该元素大arr[i], arr[max_idx] = arr[max_idx], arr[i]return arrif __name__ == "__main__":result = selected_sort_min([1,5,6,2,-6,7,-9,4])print(result)result = selected_sort_max([3,1,9,0,4,-5,5])print(result)

输出:

[-9, -6, 1, 2, 4, 5, 6, 7]
[-5, 0, 1, 3, 4, 5, 9]

时间复杂度分析:

最坏情况下:同冒泡排序一样,为o(n^2)

三.插入排序

基本思想:

  • 将数组分为“已排序”和“未排序”两部分
  • 每次从数组中的“未排序”部分取出一个元素
  • 将取出的这个元素插入到“已排序部分”的正确位置
  • 重复这个过程直到“未排序”部分的所有元素都被插入到“已排序”部分中
def insertion_sort(arr):n = len(arr)for i in range(1,n):key = arr[i]j = i - 1while j >= 0 and arr[j] > key:arr[j+1] = arr[j] j -= 1arr[j+1] = keyreturn arrdef insertion_sort_swap(arr):n = len(arr)for i in range(1,n):j = i while j > 0 and arr[j] < arr[j-1]:arr[j], arr[j-1] = arr[j-1], arr[j]j -= 1return arrdef binary_insertion_sort(arr):"""二分插入排序 - 减少比较次数"""n = len(arr)for i in range(1, n):key = arr[i]# 使用二分查找找到插入位置left, right = 0, i - 1while left <= right:mid =(left + right) // 2if arr[mid] > key:right = mid - 1else:left = mid + 1# 将元素向右移动,为插入腾出空间for j in range(i - 1, left - 1, -1):arr[j + 1] = arr[j]# 插入元素arr[left] = keyreturn arrif __name__ == "__main__":result = insertion_sort([2,4,1,5,-9,8,3])result_1 = insertion_sort_swap([1,4,98,-6,0,3,-9])result_2 = binary_insertion_sort([3,5,9,0,7,-5,2,6])print(result)print(result_1)print(result_2)

四.希尔排序

插入排序的改进版本,也称为“缩小增量排序”

核心思想:通过将原始列表分割为多个子序列,分别对这些子序列进行插入排序,从而使得整个列表逐渐趋于“基本有序”,最后再对整个列表进行一次插入排序。

这样做的原理在于插入排序在对“基本有序”的列表进行排序时,效率非常高,接近o(n)

工作原理:

  • 选择增量序列:首先确定一个增量序列。增量是一个间隔,用于将列表划分为子序列。最常用且最简单的增量是初始增量=列表长度/2,然后每次再减半,直到增量为1
  • 按增量分组并排序:根据当前增量,将列表分割成多个子序列。每个子序列由相隔“增量”位置的元素组成,然后对每个子序列分别进行插入排序
  • 缩小增量:减小增量的值(如减半),重复步骤二
  • 最终排序:当增量减小到1时,整个列表被视为一个子序列,进行最后一次插入排序。此时,由于列表已经基本有序,这次插入排序会非常快

举例说明:

对数组[8,3,5,1,4,7,2,6]进行希尔排序

初始数组为[8,3,5,1,4,7,2,6]

长度n=8,选择增量序列gap=n/2, n/4, n/8,....  即4,2,1

第一轮循环:

增量为4

子序列1:[8,4]      --->     [4,8]

子序列2:[3,7]    --->      [3,7]

子序列3:[5,2]    --->      [2,5]

子序列4:[1,6]    --- >      [1,6]

分别对这四个子序列进行插入排序,得到右边的排好序的子序列

在将这些排好序的子序列按照原来的位置放回原始数组

得到:

[4,3,2,1,8,7,5,6]

第二轮循环:

增量为2

子序列分别为[4,2,8,5]  [3,1,7,6]

分别对这两个子序列进行插入排序得到排好序的子序列:[2,4,5,8]  [1,3,6,7]

第二轮循环得到的数组:[2,1,4,3,5,6,8,7]

第三轮循环:

增量为1,此时整个数组就是一个子序列

再对其进行插入排序,得到最终的排好序的数组:[1,2,3,4,5,6,7,8]

def shell_sort(arr):n = len(arr)# 初始增量gap = n // 2# 循环直到gap缩小到0while gap > 0:# 从gap开始,对每个子序列进行插入排序for i in range(gap,n):temp = arr[i] # 当前要插入的元素j = i# 对子序列进行插入排序# j >= gap 确保不越界# arr[j - gap] > temp 是插入排序的比较条件while j >= gap and arr[j - gap] > temp:arr[j] = arr[j - gap] # 向后移动元素j -= gaparr[j] = temp # 插入到正确位置# 缩小增量gap //= 2return arrif __name__ == "__main__":print(shell_sort([2,7,4,-7,9,6,-9,5]))

稳定性:希尔排序不稳定,在排序过程中,相等的原始可能会因为分属不同的子序列而改变相对次序

最坏情况下的时间复杂度:o(n^2)

最好情况下的时间复杂度: o(nlogn)

http://www.dtcms.com/a/441794.html

相关文章:

  • 绍兴网站建设优化手机企业网站建设开发
  • 偏导数解释
  • Linux内核与设备管理:USB存储驱动usb_storage/uas的安全卸载与复原
  • fallocate: fallocate failed: Text file busy
  • visio实现扇形图绘制的方式方法-以三等分扇形为例
  • 以太坊私有链搭建与智能合约部署指南
  • 网站开发 教学大纲网页设计图片与图片的位置
  • python+flask_socketio+pyautogui实现简易远程桌面功能
  • flask_socketio+pyautogui实现的具有加密传输功能的极简远程桌面
  • 深入了解linux网络—— TCP网络通信(上)
  • Android Jetpack 核心组件实战:ViewModel + LiveData + DataBinding 详解
  • 商务厅网站建设意见怎么做网站注册推广
  • Fragment 崩溃恢复后出现重叠问题的复现方式
  • 设计模式(C++)详解——策略模式(2)
  • 使客户能够大规模交付生产就绪的人工智能代理
  • Layui 前端和 PHP 后端的大视频分片上传方案
  • 无状态HTTP的“记忆”方案:Spring Boot中CookieSession全栈实战
  • Java 内存模型(JMM)面试清单(含超通俗生活案例与深度理解)
  • 2015网站建设专业建网站设计公司
  • vue+springboot项目部署到服务器
  • QT肝8天17--优化用户管理
  • QT肝8天19--Windows程序部署
  • 【开题答辩过程】以《基于 Spring Boot 的宠物应急救援系统设计与实现》为例,不会开题答辩的可以进来看看
  • 成都seo网站建设沈阳网站建设推广服务
  • 网站栏目名短链接在线生成官网免费
  • Task Schemas: 基于前沿认知的复杂推理任务架构
  • 第三十七章 ESP32S3 SPI_SDCARD 实验
  • 企业营销型网站特点企业信息查询系统官网山东省
  • docker-compose 安装MySQL8.0.39
  • Go语言入门(18)-指针(上)