【Python 算法零基础 4.排序 ④ 计数排序】

目录

一、引言

二、算法思想

三、算法分析

1.时间复杂度

2.空间复杂度

3.算法的优缺点

Ⅰ、算法的优点

Ⅱ、算法的缺点

四、实战练习

75. 颜色分类

算法与思路

① 初始化计数数组

② 统计元素频率

③ 重构有序数组

1046. 最后一块石头的重量

算法与思路

① 计数排序

② 石头碰撞模拟

1984. 学生分数的最小差值

算法与思路

① 计数排序

② 最小差值


风永远吹向不缺风的山谷,祝你也是,缤纷争渡

                                                                      —— 25.5.22

选择排序回顾

① 遍历数组从索引 0 到 n-1n 为数组长度)。

② 每轮确定最小值假设当前索引 i 为最小值索引 min_index。从 i+1 到 n-1 遍历,若找到更小元素,则更新 min_index

③ 交换元素若 min_index ≠ i,则交换 arr[i] 与 arr[min_index]

def selectionSort(arr):n = len(arr)for i in range(n):min_index = i# 找到最小值索引for j in range(i+1, n):if arr[j] < arr[min_index]:min_index = j# 仅交换一次if min_index != i:arr[i], arr[min_index] = arr[min_index], arr[i]return arr

冒泡排序回顾

① 初始化设数组长度为 n

② 外层循环遍历 i 从 0 到 n-1(共 n 轮)。

③ 内层循环对于每轮 i,遍历 j 从 0 到 n-i-1

④ 比较与交换若 arr[j] > arr[j+1],则交换两者。

⑤ 结束条件重复步骤 2-4,直到所有轮次完成。

def bubbleSort(arr: List[int]):n = len(arr)for i in range(n):for j in range(n-i-1):if arr[j] > arr[j+1]:arr[j], arr[j+1] = arr[j+1], arr[j]return arr

插入排序回顾

① 遍历未排序元素从索引 1 到 n-1

② 保存当前元素将 arr[i] 存入 current

③ 元素后移从已排序部分的末尾(索引 j = i-1)向前扫描,将比 current 大的元素后移。直到找到第一个不大于 current 的位置或扫描完所有元素。

④ 插入元素将 current 放入 j+1 位置。

def insertSort(arr: List[int]):n = len(arr)for i in range(1, n):current = arr[i]j = i - 1while j >= 0 and arr[j] > current:# 将元素后移arr[j+1] = arr[j]j -= 1# 放到第一个不大于要插入元素的元素后面arr[j+1] = currentreturn arr

一、引言

        计数排序(Counting Sort)是一种基于哈希的排序算法。它的基本思想是通过统计每个元素的出现次数,然后根据统计结果将元素依次放入排序后的序列中。这种排序算法适用于元素范围较小的情况,例如整数范围在 0到 k之间。


二、算法思想

        计数排序的核心是创建一个计数数组,用于记录每个元素出现的次数。然后,根据计数数组对原始数组进行排序。

        具体步骤如下:

                ① 初始化一个长度为最大元素值加1的计数数组,所有元素初始化为 0。

                ② 遍历原始数组,将每个元素的值作为索引,在计数数组中对应位置加 1。

                ③ 将原数组清空。

                ④ 遍历计数器数组,按照数组中元素个数放回到原数组中。


三、算法分析

1.时间复杂度

        Ⅰ、我们假设一次「 哈希」和「计数 」的时间复杂度均为O(1)。并且总共n个数,数字范围为(1,K)

        Ⅱ、除了输入输出以外,「计数排序 」中总共有四个循环

                ① 第一个循环,用于初始化「计数器数组」,时间复杂度O(k);

                ② 第二个循环,枚举所有数字,执行「哈希」和「计数」操作,时间复杂度O(n);

                ③ 第三个循环,枚举所有范围内的数字,时间复杂度O(k);

                ④ 第四个循环,是嵌套在第三个循环内的,最多走O(n),虽然是嵌套,但是它和第三个循环是相加的关系,而并非相乘的关系。所以,总的时间复杂度为:O(n+k)


2.空间复杂度

        假设最大的数字为k,则空间复杂度为:O(k)


3.算法的优缺点

Ⅰ、算法的优点

① 简单易懂:计数排序的原理非常简单,容易理解和实现。

② 时间复杂度低:对于范围较小的元素,计数排序的时间复杂度非常优秀

③ 适用于特定场景:当元素的范围已知且较小时,计数排序可以高效地完成排序。

Ⅱ、算法的缺点

① 空间开销:计数排序需要额外的空间来存储计数数组,当元素范围较大时,可能会消耗较多的内存。
② 局限性:计数排序只适用于元素范围较小的情况,对于大规模数据或元素范围不确定的情况并不适用。


四、实战练习

75. 颜色分类

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

    必须在不使用库内置的 sort 函数的情况下解决这个问题。

    示例 1:

    输入:nums = [2,0,2,1,1,0]
    输出:[0,0,1,1,2,2]
    

    示例 2:

    输入:nums = [2,0,1]
    输出:[0,1,2]
    

    提示:

    • n == nums.length
    • 1 <= n <= 300
    • nums[i] 为 01 或 2

    进阶:

    • 你能想出一个仅使用常数空间的一趟扫描算法吗?

    算法与思路

    ① 初始化计数数组

    创建一个长度为 r+1 的数组 count,初始值全为 0。count 数组的索引范围为 0 到 r,用于统计每个元素的出现次数。

    ② 统计元素频率

    遍历输入数组 arr,对于每个元素 x,将 count[x] 的值加 1。例如:若 arr = [2, 1, 3, 2],则 count 数组最终为 [0, 1, 2, 1](表示 0 出现 0 次,1 出现 1 次,2 出现 2 次,3 出现 1 次)。

    ③ 重构有序数组

    初始化索引 index = 0,用于将元素放回原数组 arr

    遍历 count 数组,对于每个索引 v(从 0 到 r):当 count[v] > 0 时,将 v 写入 arr[index],并将 index 加 1。同时将 count[v] 减 1,直到 count[v] 变为 0。

    class Solution:def countingSort(self, arr, r):# 定义一个计数列表,长度为 0 - rcount = [0 for i in range(r + 1)]# 遍历传入列表中的每一个元素,在计数列表中进行计数for x in arr:count[x] += 1index = 0for v in range(r + 1):while count[v] > 0:arr[index] = vindex += 1count[v] -= 1def sortColors(self, nums: List[int]) -> None:"""Do not return anything, modify nums in-place instead."""self.countingSort(nums, 2)


    1046. 最后一块石头的重量

    有一堆石头,每块石头的重量都是正整数。

    每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

    • 如果 x == y,那么两块石头都会被完全粉碎;
    • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

    最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0

    示例:

    输入:[2,7,4,1,8,1]
    输出:1
    解释:
    先选出 7 和 8,得到 1,所以数组转换为 [2,4,1,1,1],
    再选出 2 和 4,得到 2,所以数组转换为 [2,1,1,1],
    接着是 2 和 1,得到 1,所以数组转换为 [1,1,1],
    最后选出 1 和 1,得到 0,最终数组转换为 [1],这就是最后剩下那块石头的重量。
    

    提示:

    • 1 <= stones.length <= 30
    • 1 <= stones[i] <= 1000

    算法与思路

    ① 计数排序

    初始化计数数组:创建长度为 r+1 的数组 count,初始值全为 0。

    统计元素频率:遍历 arr,统计每个元素出现的次数,存入 count 数组。

    重构有序数组:按索引从小到大遍历 count,将元素按频次放回 arr

    ② 石头碰撞模拟

    初始化:获取石头数组长度 n,作为循环终止条件。

    循环条件:当 n > 1 时,持续处理(确保至少有两块石头可碰撞)。

    排序:每次循环调用 countingSort 对数组升序排序,使最重的石头位于末尾

    碰撞操作:取出最重的两块石头 stones[-1] 和 stones[-2],计算差值 v

    更新数组

            移除碰撞的石头:通过两次 pop() 移除末尾两个元素,n 减 2。

            添加新石头:若 v != 0(两块石头重量不同),将 v 加入数组,n 加 1。若 v == 0(两块石头重量相同)且 n > 0,不添加新石头。

    返回结果:循环结束后,当 n == 1,返回剩余石头 stones[0]

    class Solution:def countingSort(self, arr, r):count = [0 for i in range(r + 1)]for x in arr:count[x] += 1index = 0for v in range(r + 1):while count[v] > 0:arr[index] = vindex += 1count[v] -= 1def lastStoneWeight(self, stones: List[int]) -> int:n = len(stones)while n > 1:self.countingSort(stones, 1000)v = stones[-1] - stones[-2]n -= 2stones.pop()stones.pop()if v != 0 or n == 0:stones.append(v)n += 1return stones[0]


    1984. 学生分数的最小差值

    给你一个 下标从 0 开始 的整数数组 nums ,其中 nums[i] 表示第 i 名学生的分数。另给你一个整数 k 。

    从数组中选出任意 k 名学生的分数,使这 k 个分数间 最高分 和 最低分 的 差值 达到 最小化 。

    返回可能的 最小差值 。

    示例 1:

    输入:nums = [90], k = 1
    输出:0
    解释:选出 1 名学生的分数,仅有 1 种方法:
    - [90] 最高分和最低分之间的差值是 90 - 90 = 0
    可能的最小差值是 0
    

    示例 2:

    输入:nums = [9,4,1,7], k = 2
    输出:2
    解释:选出 2 名学生的分数,有 6 种方法:
    - [9,4,1,7] 最高分和最低分之间的差值是 9 - 4 = 5
    - [9,4,1,7] 最高分和最低分之间的差值是 9 - 1 = 8
    - [9,4,1,7] 最高分和最低分之间的差值是 9 - 7 = 2
    - [9,4,1,7] 最高分和最低分之间的差值是 4 - 1 = 3
    - [9,4,1,7] 最高分和最低分之间的差值是 7 - 4 = 3
    - [9,4,1,7] 最高分和最低分之间的差值是 7 - 1 = 6
    可能的最小差值是 2
    

    提示:

    • 1 <= k <= nums.length <= 1000
    • 0 <= nums[i] <= 10^5

    算法与思路

    ① 计数排序

    初始化计数数组:创建长度为 r+1 的数组 count,初始值全为 0。

    统计元素频率:遍历 arr,统计每个元素出现的次数,存入 count 数组。

    重构有序数组:按索引从小到大遍历 count,将元素按频次放回 arr

    ② 最小差值

    排序数组:调用 countingSort 对数组 nums 排序(假设元素范围为 0 到 100000)。

    初始化结果:设置初始结果 res 为 100000(题目中元素的最大可能值)。

    滑动窗口遍历:遍历所有可能的长度为 k 的子数组。子数组的起始索引 i 范围为 0 到 n - kn 为数组长度)。对于每个子数组,计算其最大值(nums[i + k - 1])和最小值(nums[i])的差值。

    更新最小差值:在所有差值中取最小值,更新 res

    返回结果:最终 res 即为所求的最小差值。

    class Solution:def countingSort(self, arr, r):count = [0 for i in range(r + 1)]for x in arr:count[x] += 1index = 0for v in range(r + 1):while count[v] > 0:arr[index] = vindex += 1count[v] -= 1def minimumDifference(self, nums: List[int], k: int) -> int:self.countingSort(nums, 100000)# 题目中给的最大值是10 ^ 5n = len(nums)res = 100000for i in range(n + 1 - k):l = ir = i + k -1res = min(res, nums[r] - nums[l])return res 

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/82130.shtml

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    PPP 流程已经走到启动阶段并且成功进入了 “STAGE_START_PPP

    从您最新的日志来看&#xff0c;PPP 流程已经走到启动阶段并且成功进入了 “STAGE_START_PPP”&#xff0c;但在 “STAGE_WAIT_IP” 阶段没有拿到 IP&#xff0c;约 60 s 后就报了 “Connection lost”&#xff1a; I (11161) modem_board: Modem state STAGE_START_PPP, Succ…

    siparmyknife:SIP协议渗透测试的瑞士军刀!全参数详细教程!Kali Linux教程!

    简介 SIP Army Knife 是一个模糊测试器&#xff0c;用于搜索跨站点脚本、SQL 注入、日志注入、格式字符串、缓冲区溢出等。 安装 源码安装 通过以下命令来进行克隆项目源码&#xff0c;建议请先提前挂好代理进行克隆。 git clone https://github.com/foreni-packages/sipa…

    Phantom 根据图片和文字描述,自动生成一段视频,并且动作、场景等内容会按照文字描述来呈现

    Phantom 根据图片和文字描述&#xff0c;自动生成一段视频&#xff0c;并且动作、场景等内容会按照文字描述来呈现 flyfish 视频生成的实践效果展示 Phantom 视频生成的实践 Phantom 视频生成的流程 Phantom 视频生成的命令 Wan2.1 图生视频 支持批量生成 Wan2.1 文生视频 …

    OceanBase 系统表查询与元数据查询完全指南

    文章目录 一、OceanBase 元数据基础概念1.1 元数据的定义与重要性1.2 OceanBase 元数据分类体系二、系统表查询核心技术2.1 核心系统表详解2.1.1 集群管理表2.1.2 租户资源表2.2 高级查询技巧2.2.1 跨系统表关联查询2.2.2 历史元数据查询三、元数据查询实战应用3.1 日常运维场景…

    计算机发展史

    计算机发展史 计算的需求在⼈类的历史中是⼴泛存在的&#xff0c;发展⼤体经历了从⼀般计算⼯具到机械计算机到⽬前的电⼦计算机的发展历程。 ⼈类对计算的需求&#xff0c;驱动我们不断的发明、改善计算机。⽬前这个时代是“电⼦计算机”的时代&#xff0c;发展的潮流是&…

    GD32 IIC(I2C)通信(使用示例为SD2068)

    一、前言 最近需要用到GD32的I2C通信&#xff0c;虽然是第一次做I2C通信&#xff0c;但是GD32完整的标准库有现存的I2C通信示例&#xff0c;虽然示例是EEPROM的通信&#xff0c;但是调用的函数应该是大差不差&#xff0c;所以上手比较简单&#xff0c;这里简单记录一下笔记&…

    React从基础入门到高级实战:React 基础入门 - 列表渲染与条件渲染

    列表渲染与条件渲染 在 React 开发中&#xff0c;列表渲染 和 条件渲染 是处理动态数据和用户交互的基础技术。通过列表渲染&#xff0c;你可以根据数据动态生成 UI 元素&#xff1b;而条件渲染则让你根据特定条件展示不同的内容。这两个技能在实际项目中非常常见&#xff0c;…

    在Java的list.forEach(即 Stream API 的 forEach 方法)中,无法直接使用 continue 或 break 语句的解决办法

    说明 在 Java 的 list.forEach&#xff08;即 Stream API 的 forEach 方法&#xff09;中&#xff0c;无法直接使用 continue 或 break 语句&#xff0c;因为它是一个终结操作&#xff08;Terminal Operation&#xff09;&#xff0c;依赖于 Lambda 表达式或方法引用。 有些时…

    (7)Spring 6.x 响应式编程模型

    Spring 6.x 响应式编程模型 👉 点击展开题目 Spring 6.x中的响应式编程模型与传统的Servlet模型相比有哪些优势?如何实现两者的无缝迁移? 📌 Spring 6.x 响应式编程模型概述 Spring 6.x 中的响应式编程模型基于 Project Reactor 构建,采用非阻塞、事件驱动的架构,通过…

    排序和排列——蓝桥杯备考

    1.十大排序算法 本次用下面的例题详解这十种排序算法 题目描述 将读入的 N 个数从小到大排序后输出。 输入格式 第一行为一个正整数 N。 第二行包含 N 个空格隔开的正整数 ai​&#xff0c;为你需要进行排序的数。 输出格式 将给定的 N 个数从小到大输出&#xff0c;数之间空格…

    C# 高效读取大文件

    在 C# 中高效读取大文件时&#xff0c;需根据文件类型和场景选择不同的技术方案&#xff0c;以下为综合实践方法及注意事项&#xff1a; 一、文本文件读取方案 逐行读取 StreamReader.ReadLine‌&#xff1a;通过流式处理逐行加载文本&#xff0c;避免一次性加载整个文件到内…

    深度学习模型可视化:Netron的安装和使用

    文章目录 Netron简介Netron加载模型类型Netron使用方式Netron功能介绍完整案例总结 Netron简介 Netron是一个支持PyTorch的可视化工具&#xff0c;它的开发者是微软的Lutz Roeder&#xff0c;操作简单快捷&#xff0c;就像保存文件、打开文件一样&#xff0c;简单高效。Netron…

    pytorch LSTM 结构详解

    最近项目用到了LSTM &#xff0c;但是对LSTM 的输入输出不是很理解&#xff0c;对此&#xff0c;我详细查找了lstm 的资料 import torch.nn as nnclass LSTMModel(nn.Module):def __init__(self, input_size1, hidden_size50, num_layers2):super(LSTMModel, self).__init__()…

    AUTOSAR AP 入门0:AUTOSAR_EXP_PlatformDesign.pdf

    AUTOSAR AP官网&#xff1a;AUTOSAR Adaptive Platform设计AUTOSAR AP的目的&#xff0c;翻译版官方文档 AUTOSAR_EXP_PlatformDesign.pdf &#xff1a; https://mp.weixin.qq.com/s?__bizMzg2MzAyMDIzMQ&mid2247553050&idx2&sn786c3a1f153acf99b723bf4c9832acaf …

    零碳办会新范式!第十届国际贸易发展论坛——生物能源和可持续发展专场,在京举办

    2025年5月16日&#xff0c;第十届国际贸易发展论坛在北京国际饭店盛大启幕。本届论坛由北京绿林认证有限公司主办。作为汇聚行业智慧、引领发展方向的盛会&#xff0c;国际贸易发展论坛每两年一届&#xff0c;本次会议是第十届&#xff0c;至今已走过近20年光辉历程。多年来&am…

    ECharts图表工厂,完整代码+思路逻辑

    Echart工厂支持柱状图&#xff08;bar&#xff09;折线图&#xff08;line&#xff09;散点图&#xff08;scatter&#xff09;饼图&#xff08;pie&#xff09;雷达图&#xff08;radar&#xff09;极坐标柱状图&#xff08;polarBar&#xff09;和极坐标折线图&#xff08;po…

    如何制作令人印象深刻的UI设计?

    1. 规划用户旅程 规划用户旅程是创建高效且吸引人的UI设计的第一步。设计师需要深入了解目标用户群体的需求和行为模式&#xff0c;这通常涉及用户调研、创建用户角色&#xff08;Personas&#xff09;和绘制用户旅程图&#xff08;User Journey Maps&#xff09;。通过这种方…

    k8s 离线安装 kube-prometheus-stack

    配置共享存储 Prometheus 需要配置持久化存储&#xff0c;防止数据丢失 服务端 服务端安装 NFS 服务 sudo apt install nfs-kernel-server 创建共享目录&#xff0c;在服务器端创建 /nfs 目录。 mkdir /nfs chmod -R 777 /nfs # 设置文件权限 nfs目录下只给了默认权限&#xff…

    ceph osd 磁盘分区对齐

    分区对齐可以提高读写速度的原理是什么 分区对齐可以提高磁盘读写速度的原理主要在于 磁盘的物理扇区大小与操作系统发起的读写请求之间是否对齐。如果不对齐,每次读写操作可能会跨越多个物理扇区,造成额外的 I/O 操作,从而降低性能。 🔧 原理详解 1. 物理扇区(Physica…

    Simon J.D. Prince《Understanding Deep Learning》

    学习神经网络和深度学习推荐这本书&#xff0c;这本书站位非常高&#xff0c;且很多问题都深入剖析了&#xff0c;甩其他同类书籍几条街。 多数书&#xff0c;不深度分析、没有知识体系&#xff0c;知识点零散、章节之间孤立。还有一些人Tian所谓的权威&#xff0c;醒醒吧。 …