TypeScript 算法手册 【计数排序】

news2024/12/27 16:01:50

文章目录

    • 1. 计数排序简介
      • 1.1 计数排序定义
      • 1.2 计数排序特点
    • 2. 计数排序步骤过程拆解
      • 2.1 找出数组中的最大值
      • 2.2 创建计数数组
      • 2.3 统计每个数字出现的次数
      • 2.4 重建排序后的数组
    • 3. 计数排序的优化
      • 3.1 处理负数
      • 3.2 对象数组排序
      • 案例代码和动态图
    • 4. 计数排序的优点
    • 5. 计数排序的缺点
    • 总结

在这里插入图片描述

【 已更新完 TypeScript 设计模式 专栏,感兴趣可以关注一下,一起学习交流🔥🔥🔥 】

1. 计数排序简介

1.1 计数排序定义

计数排序是一种非比较性的整数排序算法。它的核心思想是"统计数字出现次数,然后按顺序重建数组"。假如你是一位邮局工作人员,需要整理一大堆混乱的邮件,你采用这样的策略:首先统计每个邮政编码的邮件有多少封,按照邮政编码顺序,将邮件重新排列在分拣架上,这就是计数排序的基本思想。

用TypeScript代码表示一个简单的计数排序:

function countingSort(arr: number[]): number[] {
  if (arr.length <= 1) return arr;

  const max = Math.max(...arr);
  const counts = new Array(max + 1).fill(0);

  for (const num of arr) {
    counts[num]++;
  }

  const sortedArray: number[] = [];
  for (let i = 0; i <= max; i++) {
    while (counts[i] > 0) {
      sortedArray.push(i);
      counts[i]--;
    }
  }

  return sortedArray;
}

1.2 计数排序特点

  1. 线性时间复杂度:计数排序的时间复杂度为O(n+k),其中n是数组长度,k是数组中的最大值
  2. 非比较排序:计数排序不通过比较元素来排序,通过统计元素出现的次数
  3. 稳定性:计数排序是稳定的排序算法
  4. 适用范围:适用于已知范围的整数排序,特别是当范围不是很大的时候

2. 计数排序步骤过程拆解

2.1 找出数组中的最大值

const max = Math.max(...arr);

像邮局工作人员找出所有邮件中邮政编码最大的那一个,这个最大邮政编码将决定我们需要准备多少个"计数格子"。

2.2 创建计数数组

const counts = new Array(max + 1).fill(0);

这个步骤就像邮局工作人员准备了一排从0到最大邮政编码的小格子,每个格子里放一个计数器,初始值都是0。

2.3 统计每个数字出现的次数

for (const num of arr) {
  counts[num]++;
}

这个过程就像邮局工作人员收到一封邮件,看一眼邮政编码,然后在对应的格子里的计数器加1。

2.4 重建排序后的数组

const sortedArray: number[] = [];
for (let i = 0; i <= max; i++) {
  while (counts[i] > 0) {
    sortedArray.push(i);
    counts[i]--;
  }
}

这个步骤就像邮局工作人员从邮政编码0开始,查看每个格子里的计数器,如果不为0,就放入相应数量的该邮政编码的邮件,接着移动到下一个格子,直到所有的邮件都分类完毕。

3. 计数排序的优化

3.1 处理负数

function countingSortWithNegatives(arr: number[]): number[] {
  if (arr.length <= 1) return arr;

  const min = Math.min(...arr);
  const max = Math.max(...arr);
  const range = max - min + 1;
  const counts = new Array(range).fill(0);

  for (const num of arr) {
    counts[num - min]++;
  }

  const sortedArray: number[] = [];
  for (let i = 0; i < range; i++) {
    while (counts[i] > 0) {
      sortedArray.push(i + min);
      counts[i]--;
    }
  }

  return sortedArray;
}

这个优化版本就像邮局工作人员遇到了一些特殊的负数编号的邮件,他不再从0开始计数,而是从最小的编号开始,这样就可以处理所有的邮件了,无论编号是正数还是负数。

3.2 对象数组排序

interface Book {
  id: number;
  title: string;
}

function countingSortBooks(books: Book[]): Book[] {
  if (books.length <= 1) return books;

  const max = Math.max(...books.map(book => book.id));
  const counts: Book[][] = new Array(max + 1).fill(null).map(() => []);

  for (const book of books) {
    counts[book.id].push(book);
  }

  const sortedBooks: Book[] = [];
  for (const bookList of counts) {
    sortedBooks.push(...bookList);
  }

  return sortedBooks;
}

这个优化版本就像邮局工作人员不仅要整理邮件的邮政编码,还要保持每封邮件的其他信息(如邮件内容)。他在每个计数格子里不再放简单的数字,而是放一个可以容纳多封邮件信息的"小篮子"。

案例代码和动态图

const numbers = [4, 2, 2, 8, 3, 3, 1];
const sortedNumbers = countingSort(numbers);
console.log(sortedNumbers); // [1, 2, 2, 3, 3, 4, 8]

const books: Book[] = [
  { id: 3, title: "TypeScript基础" },
  { id: 1, title: "JavaScript高级程序设计" },
  { id: 3, title: "深入理解TypeScript" },
  { id: 2, title: "你不知道的JavaScript" }
];
const sortedBooks = countingSortBooks(books);
console.log(sortedBooks);
// [
//   { id: 1, title: "JavaScript高级程序设计" },
//   { id: 2, title: "你不知道的JavaScript" },
//   { id: 3, title: "TypeScript基础" },
//   { id: 3, title: "深入理解TypeScript" }
// ]

在这里插入图片描述

4. 计数排序的优点

  1. 时间复杂度低:在特定情况下,计数排序的时间复杂度可以达到O(n),这比基于比较的排序算法更快
  2. 稳定性:计数排序是稳定的排序算法,这在某些应用场景中非常重要
  3. 适合大数据量、取值范围集中的数据排序:当数据量很大但取值范围相对集中时,计数排序的优势尤为明显

5. 计数排序的缺点

  1. 空间复杂度高:计数排序需要额外的存储空间来记录每个元素的出现次数
  2. 适用范围有限:计数排序只适用于整数或可以转化为整数的数据
  3. 当数据范围很大时效率降低:如果数据范围远大于数据量,计数排序的空间复杂度和时间复杂度都会急剧增加

总结

计数排序告诉我们,面对一堆看似杂乱无章的数据,有时候直接比较和交换并不是最有效的方法。通过统计每个数字出现的次数,我们可以直接还原出有序的序列。这种"以静制动"的思想不仅在排序中有用,在我们日常解决问题时也常常能派上用场。

喜欢的话就点个赞 ❤️,关注一下吧,有问题也欢迎讨论指教。感谢大家!!!

下期预告: TypeScript 算法手册 - 基数排序

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

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

相关文章

[VULFOCUS刷题]tomcat-pass-getshell 弱口令

tomcat-pass-getshell 弱口令 启动容器&#xff0c;打开网站 点开manageapp&#xff0c;输入弱口令 tomcat/tomcat 之后在下面上传jsp大马&#xff0c;首先生成一个jsp马 这里我直接使用github别人生成好的 tennc/webshell: This is a webshell open source project (github.…

Image-Text Co-Decomposition for Text-Supervised Semantic Segmentation

highlighted region-word pair contrastive loss (L h c l _{hcl} hcl​) 辅助信息 mmcv环境不好满足&#xff0c;不建议复现

理解无监督学习、无监督图像分割

系列文章目录 文章目录 系列文章目录一、无监督学习如何学习 能不能举一个非常具体的例子&#xff0c;带着运算过程的例子总结 二、在图像分割中呢&#xff0c;具体怎样实现无监督示例&#xff1a;使用自编码器和k-means进行无监督图像分割1. **数据准备**2. **构建自编码器**3…

小练--盲打学成在线

免责声明&#xff1a;本文仅做分享&#xff01; 学成在线 (olin-yi.github.io) 目录 目录构造 基础公共样式 base.css index.html 版心居中 网页制作思路 CSS 实现思路 头部整体布局 logo 导航制作技巧&#xff08;nav&#xff09; 搜索区域&#xff08;search&am…

Leetcode 2300. 咒语和药水的成功对数

1.题目基本信息 1.1.题目描述 给你两个正整数数组 spells 和 potions &#xff0c;长度分别为 n 和 m &#xff0c;其中 spells[i] 表示第 i 个咒语的能量强度&#xff0c;potions[j] 表示第 j 瓶药水的能量强度。 同时给你一个整数 success 。一个咒语和药水的能量强度 相乘…

进程的环境

进程环境 main 函数 当内核执行 C 程序时&#xff0c;在调用 main 函数之前先调用一个特殊的启动例程。可执行文件会将此启动例程指定为程序的起始地址 —— 这是由连接编辑器设置&#xff0c;而连接编辑器是由 C 编译器调用。启动例程从内核取得命令行参数和环境变量值&…

Nodejs多版本切换工具NVM

1 nvm介绍 NVM&#xff08;Node Version Manager&#xff09;是一个用于管理多个Node.js版本的工具&#xff0c;它允许用户在同一台计算机上安装和切换不同版本的Node.js。这对于开发者来说非常有用&#xff0c;因为不同的项目可能需要不同版本的Node.js环境。 NVM功能特性&a…

【Spring】Spring Boot项目创建和目录介绍

1 Spring Boot 介绍 Spring 让 Java 程序更加快速、简单和安全&#xff0c;Spring 对于速度、简单性和生产力的关注使其成为世界上最流行的 Java 框架 Spring 官方提供了很多开源的项目&#xff0c;覆盖范围从 Web 开发到大数据&#xff0c;Spring 发展到了今天&#xff0c;已…

使用Mac高频重复输入时别再傻傻的复制粘贴了,让快捷短语解放你的双手

你在使用Mac的时候&#xff0c;常用的句子、词语、代码都还在自己手动输入吗&#xff0c;当你需要高频的输入同样的内容&#xff0c;手动输入效率太低了&#xff0c;我发现一款实用的工具帮你解决这个问题&#xff0c;快捷短语-高频输入神器&#xff0c;更支持iCloud云备份。 快…

记录一个chatgpt接口站的明文密码泄露漏洞

前言 前段时间看到的一个chatgpt接口网站&#xff0c;注册过后&#xff0c;习惯性的F12看下请求包和响应包&#xff0c;于是就有了这篇文章。 漏洞 查看登录的一系列流量&#xff0c;除了前端界面相关的&#xff0c;一共有三个请求&#xff0c;分别是login&#xff0c;getle…

msvcr110.dll丢失怎么弄,详细介绍4种可靠解决方法

1. msvcr110.dll 简介 1.1 定义及作用 msvcr110.dll 是 Microsoft Visual C 2012 Redistributable Package 的一部分&#xff0c;它是一个动态链接库&#xff08;Dynamic Link Library&#xff09;文件&#xff0c;对于运行使用 Visual C 2012 编译的应用程序至关重要。这个库…

如何在 DAX 中计算多个周期的移动平均线

在 DAX 中计算移动聚合很容易。但是&#xff0c;计算一段时间内的移动平均值时会有一些陷阱。由于其中一些陷阱是定义问题&#xff0c;因此我们必须小心&#xff0c;不要选择错误的方法。让我们看看细节。欢迎来到雲闪世界。 添加图片注释&#xff0c;不超过 140 字&#xff08…

算法笔记(五)——分治

文章目录 算法笔记&#xff08;五&#xff09;——分治快排颜色分类排序数组数组中的第K个最大元素库存管理 III 归并排序数组交易逆序对的总数计算右侧小于当前元素的个数翻转对 算法笔记&#xff08;五&#xff09;——分治 分治算法字面上的解释是“分而治之”&#xff0c;就…

mindsearch run 本地服务

bing_browser.py ~/.conda/envs/mindsearch/lib/python3.10/site-packages/lagent/actions# vim bing_browser.py 修改提示词文件 MindSearch/mindsearch/agent/mindsearch_prompt.py # flake8: noqasearcher_system_prompt_cn """## 人物简介 你是一个可以…

易贝恩副总经理朱洪泽受邀为第四届中国项目经理大会演讲嘉宾

全国项目经理专业人士年度盛会 北京易贝恩项目管理科技有限公司副总经理朱洪泽女士受邀为PMO评论主办的全国项目经理专业人士年度盛会——2024第四届中国项目经理大会演讲嘉宾&#xff0c;演讲议题为“从手动到智能&#xff1a;项目管理系统助力项目经理提升执行效率”。大会将…

python中的copy方法

记录一下python中的浅拷贝copy和深拷贝deepcopy 例题如下&#xff1a; import copyls [1, 2, [3, 4], 5, 6]ls1 ls.copy()ls2 lsls3 copy.deepcopy(ls)ls[2][1] 0ls.pop(1)ls.append([7, 8])print(ls1) #--------慙1慖print(ls2) #--------慙2慖print(ls3) #------…

windows10或11家庭版实现远程桌面连接控制

远程协助是一种Windows工具&#xff0c;允许控制者使用鼠标和键盘远程控制接受者的计算机&#xff0c;从某种程度上讲&#xff0c;这也是Win10家庭版无法远程桌面的一个有效替代方案。 步骤1. 在使用Windows远程协助之前&#xff0c;您需要先更改某些设置&#xff0c;右键单击…

yolov11模型在bdd100k数据集上的应用【代码+数据集+python环境+训练/应用GUI系统】

yolov8/9/10/11模型在bdd100k数据集上的应用【代码数据集python环境训练/应用GUI系统】 yolov8/9/10/11模型在bdd100k数据集上的应用【代码数据集python环境训练/应用GUI系统】 yolov8/9/10/11模型在bdd100k数据集上的应用【代码数据集python环境训练/应用GUI系统】 bdd100k数据…

Vue3项目开发——新闻发布管理系统(九)(完结篇)

文章目录 十一、用户信息管理1、用户基本资料管理1.1 页面设计1.2 封装接口,更新信息2、更换头像2.1 静态结构2.2 选择图片预览2.3 上传头像3、重置密码3.1 页面设计3.2 封装接口,更新密码十二、项目打包十三、系统全部源码下载十一、用户信息管理 用户信息管理包括功能:基…

软件设计师——信息安全

&#x1f4d4;个人主页&#x1f4da;&#xff1a;秋邱-CSDN博客☀️专属专栏✨&#xff1a;软考——软件设计师&#x1f3c5;往期回顾&#x1f3c6;&#xff1a;软件设计师——计算机网络&#x1f31f;其他专栏&#x1f31f;&#xff1a;C语言_秋邱 ​ 一、加密技术与认证技术…