TypeScript 算法手册 【基数排序】

news2024/10/4 18:27:49

文章目录

    • 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 基数排序定义

基数排序是一种非比较性的整数排序算法,它的核心思想是"按位排序"。想象你是一位军需官,需要整理一大批军需物资。这些物资的编号是由多位数字组成的,比如"23145"、"10234"等。你采用这样的策略:先按物资编号的最后一位数字排序,然后是倒数第二位,倒数第三位,以此类推,直到第一位。每一轮排序后,物资的顺序会越来越接近最终的正确顺序。这就像是你在一步步地将物资放到正确的仓库位置上,最终形成一个完美有序的军需仓库,这就是基数排序的基本思想。

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

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

  const max = Math.max(...arr);
  let exp = 1;
  const output = new Array(arr.length).fill(0);

  while (max / exp > 0) {
    const count = new Array(10).fill(0);

    for (let i = 0; i < arr.length; i++) {
      count[Math.floor(arr[i] / exp) % 10]++;
    }

    for (let i = 1; i < 10; i++) {
      count[i] += count[i - 1];
    }

    for (let i = arr.length - 1; i >= 0; i--) {
      output[count[Math.floor(arr[i] / exp) % 10] - 1] = arr[i];
      count[Math.floor(arr[i] / exp) % 10]--;
    }

    for (let i = 0; i < arr.length; i++) {
      arr[i] = output[i];
    }

    exp *= 10;
  }

  return arr;
}

1.2 基数排序特点

  1. 线性时间复杂度:基数排序的时间复杂度为O(d(n+k)),其中d是最大数的位数,n是数组长度,k是基数(通常为10)
  2. 非比较排序:基数排序不通过比较元素来排序,而是通过分配和收集的过程
  3. 稳定性:基数排序是稳定的排序算法
  4. 适用范围:适用于整数排序,特别是当整数的位数不是很大的时候

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

2.1 找出数组中的最大值

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

这就像军需官找出所有军需物资编号中最大的那一个。这个最大编号将决定我们需要进行多少轮分类整理。

2.2 从最低位开始,对每一位进行计数排序

let exp = 1;
while (max / exp > 0) {
  // 进行一轮计数排序
  // ...
  exp *= 10;
}

这个过程就像军需官从军需物资编号的个位数开始,一位一位地进行排序,直到处理完最高位。

2.3 对某一位数进行计数排序

const count = new Array(10).fill(0);

for (let i = 0; i < arr.length; i++) {
  count[Math.floor(arr[i] / exp) % 10]++;
}

for (let i = 1; i < 10; i++) {
  count[i] += count[i - 1];
}

for (let i = arr.length - 1; i >= 0; i--) {
  output[count[Math.floor(arr[i] / exp) % 10] - 1] = arr[i];
  count[Math.floor(arr[i] / exp) % 10]--;
}

这个步骤就像军需官为当前处理的位数准备了10个箱子(对应0-9这10个数字)。他先统计每个箱子里应该有多少件军需物资,然后从后往前,将每件物资放入对应的箱子。这样可以保证排序的稳定性,确保相同编号的物资保持原有的相对顺序。

2.4 将排序结果复制回原数组

for (let i = 0; i < arr.length; i++) {
  arr[i] = output[i];
}

这个步骤就像军需官完成了一轮排序后,将军需物资按新的顺序重新排列在仓库中。

3. 基数排序的优化

3.1 处理负数

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

  const positives = arr.filter(num => num >= 0);
  const negatives = arr.filter(num => num < 0).map(num => -num);

  const sortedPositives = radixSort(positives);
  const sortedNegatives = radixSort(negatives).reverse().map(num => -num);

  return [...sortedNegatives, ...sortedPositives];
}

这个优化版本就像军需官遇到了一些特殊的负数编号的军需物资。他将军需物资分成两组:正数编号和负数编号。对负数编号取绝对值后进行排序,最后再将结果反转并恢复负号。

3.2 字符串排序

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

  const maxLength = Math.max(...arr.map(str => str.length));
  const buckets: string[][] = Array.from({ length: 256 }, () => []);

  for (let i = maxLength - 1; i >= 0; i--) {
    for (const str of arr) {
      const char = str[i] || '\0';
      buckets[char.charCodeAt(0)].push(str);
    }

    arr = buckets.flat();
    buckets.forEach(bucket => bucket.length = 0);
  }

  return arr;
}

这个优化版本就像军需官需要整理一批以字母编号的军需物资。他从最后一个字母开始,一直排序到第一个字母。每次排序时,他准备了256个箱子(对应ASCII字符),将军需物资按照当前处理的字母放入对应的箱子中。

案例代码和动态图

const numbers = [11, 45, 3, 40, 38, 24, 2, 19];
const sortedNumbers = radixSort(numbers);
console.log(sortedNumbers); // [2, 11, 19, 24, 38, 40, 45]

const strings = ["apple", "banana", "cherry", "date", "elderberry"];
const sortedStrings = radixSortStrings(strings);
console.log(sortedStrings); // ["apple", "banana", "cherry", "date", "elderberry"]

在这里插入图片描述

4. 基数排序的优点

  1. 时间复杂度稳定:基数排序的时间复杂度是线性的,对于特定范围内的整数排序非常高效
  2. 稳定性:基数排序是稳定的排序算法,这在某些应用场景中非常重要
  3. 适合大数据量、取值范围较小的数据排序:当数据量很大但数字的位数不多时,基数排序的优势尤为明显

5. 基数排序的缺点

  1. 空间复杂度高:基数排序需要额外的存储空间来存储临时排序结果
  2. 适用范围有限:基数排序主要适用于整数或可以转化为整数的数据
  3. 不适合位数很多的大数排序:如果数字的位数很多,基数排序可能需要多次遍历,效率会降低

总结

基数排序告诉我们,面对一堆看似杂乱无章的数据,有时候从局部入手,逐步推进,最终也能达到全局有序的效果。这种"分而治之"的思想不仅在排序中有用,在我们日常解决复杂问题时也常常能派上用场。
基数排序的线性时间复杂度和稳定性,使它在特定场景下表现出色。特别是在处理大量整数且位数不多的数据时,基数排序常常能够展现出惊人的效率。然而,它对数据类型的限制和潜在的高空间复杂度,也提醒我们在选择算法时需要权衡利弊,考虑具体的应用场景。

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

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

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

相关文章

Go语言实现随机森林 (Random Forest)算法

在 Go 语言中实现随机森林&#xff08;Random Forest&#xff09;算法通常涉及以下几个步骤&#xff1a; 数据准备&#xff1a;将数据集分为训练集和测试集&#xff0c;确保数据格式适合算法使用。 决策树的构建&#xff1a;随机森林是由多个决策树构成的&#xff0c;首先需要…

MySQL 实验1:Windows 环境下 MySQL5.5 安装与配置

MySQL 实验1&#xff1a;Windows 环境下 MySQL5.5 安装与配置 目录 MySQL 实验1&#xff1a;Windows 环境下 MySQL5.5 安装与配置一、MySQL 软件的下载二、安装 MySQL三、配置 MySQL1、配置环境变量2、安装并启动 MySQL 服务3、设置 MySQL 字符集4、为 root 用户设置登录密码 一…

使用前端三剑客实现一个备忘录

一&#xff0c;界面介绍 这个备忘录的界面效果如下&#xff1a; 可以实现任务的增删&#xff0c;并且在任务被勾选后会被放到已完成的下面。 示例&#xff1a; &#xff08;1&#xff09;&#xff0c;增加一个任务 &#xff08;2&#xff09;&#xff0c;勾选任务 &#xff…

【知乎直答】批量多线程生成原创文章软件-AI智能搜索聚合

【知乎直答】批量多线程生成原创文章软件介绍&#xff1a; 1、知乎发布的全新AI产品“知乎直答”是其AI搜索功能的产品化成果&#xff0c;旨在提升用户的提问、搜索体验以及结果生成和归纳的质量。 2、数据基础&#xff1a;该产品基于知乎平台上的真实问答数据及全网高质量问答…

Chromium 中前端js XMLHttpRequest接口c++代码实现

在JavaScript中发出HTTP请求的主要方式包括&#xff1a;XMLHttpRequest对象、Fetch API、Axios库和各种其他的HTTP客户端库。 本人主要分析下XMLHttpRequest接口在c中对应实现 一、上前端代码 <!DOCTYPE html> <html lang"en"> <head> <meta…

Go基础学习11-测试工具gomock和monkey的使用

文章目录 基础回顾MockMock是什么安装gomockMock使用1. 创建user.go源文件2. 使用mockgen生成对应的Mock文件3. 使用mockgen命令生成后在对应包mock下可以查看生成的mock文件4. 编写测试代码5. 运行代码并查看输出 GomonkeyGomonkey优势安装使用对函数进行monkey对结构体中方法…

Marp精华总结(二)进阶篇

概述 这是Marp精华总结的第二篇&#xff0c;主要补充第一篇未提到的一些内容。 系列目录 Marp精华总结&#xff08;一&#xff09;基础篇Marp精华总结&#xff08;二&#xff09;进阶篇Marp精华总结&#xff08;三&#xff09;高级篇 自适应标题 通过在标题行中插入<!-…

历经十年/头发都快掉光/秘钥生成器终极版/机器码/到期功能限制/运行时间限制/日期防篡改/跨平台

一、项目介绍 1.0 前言说明 标题一点都不夸张&#xff0c;从第一版的秘钥生成器到今天这个版本&#xff0c;确实经历了十年的时间&#xff0c;最初的版本做的非常简陋&#xff0c;就是搞了个异或加密&#xff0c;控制运行时间&#xff0c;后面又增加设备数量的控制&#xff0…

JavaFX加载fxml文件几种方法

环境&#xff1a;idea&#xff0c;maven创建JavaFX工程 工程目录如下&#xff1a; MusicPlayer.java package cn.com;import java.io.IOException;import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.geometry.Insets; import javafx.geo…

目标检测 Deformable DETR(2021)详细解读

文章目录 前言整体网络架构可变形注意力模块backbone生成多尺度特征多尺度位置编码prediction heads两个变体 前言 为解决DETR attention的计算量大导致收敛速度慢、小目标检测效果差的问题&#xff1a;提出了Deformable Attention&#xff0c;其注意力模块只关注一个query周围…

ML 系列: (10)— ML 中的不同类型的学习

一、说明 我们之前将机器学习方法分为三类&#xff1a;监督学习、无监督学习和强化学习。机器学习方法可以分为不同的类型&#xff0c;我们将在下面讨论最重要的类型。 二、懒惰学习与急切学习 预先学习的工作原理是使用训练数据构建模型&#xff0c;然后使用此模型评估测试数据…

STM32F103C8----3-3 蜂鸣器(跟着江科大学STM32)

一&#xff0c;电路图 &#xff08;接线图&#xff09; 面包板的的使用请参考&#xff1a;《面包板的使用_面包板的详细使用方法-CSDN博客》 二&#xff0c;目的/效果 3-3 蜂鸣器 三&#xff0c;创建Keil项目 详细参考&#xff1a;《STM32F103C8----2-1 Keil5搭建STM32项目模…

MySQL 中的 EXPLAIN 命令详解

在 MySQL 数据库中&#xff0c;EXPLAIN命令是一个非常强大的工具&#xff0c;它可以提供关于 SQL 查询执行计划的关键信息。理解这些信息对于优化查询性能至关重要。本文将详细介绍 MySQL 中的EXPLAIN命令提供的关键信息。 一、什么是 EXPLAIN 命令 EXPLAIN命令用于获取 MySQ…

Java多态(向上转型、动态绑定)+结合题目理解原理

第一次尝试使用markdowm写博客哈 文章目录 1.多态的引入2.重写和重载3.避免在构造方法里面去调用重写4.向上转型和向下转型5.让你真正明白什么是多态6.通过一些习题进行理解 1.多态的引入 首先说一下&#xff0c;这个想要使用多态需要我们满足的条件&#xff0c;然后具体的进行…

进程概念(冯诺依曼体系结构、操作系统、进程)-- 详解

目录 一、冯诺依曼体系结构1、概念2、硬件层面的数据流3、关于冯诺依曼的知识点强调4、CPU 工作原理5、补充&#xff08;CPU 和寄存器、高速缓存以及主存之间的关系&#xff09; 二、操作系统&#xff08;Operating System&#xff09;1、概念2、定位3、设计 OS 的目的4、如何理…

Linux高级编程_28_进程

文章目录 进程并行与并发单道与多道程序进程控制块(PCB)了解PCB存储位置进程号&#xff1a;进程号&#xff1a;&#xff08;PID&#xff09;进程组号&#xff1a;&#xff08;PGID&#xff09;父进程号&#xff1a;&#xff08;PPID&#xff09; fork函数 多进程创建进程状态进…

基于vue框架的大学生勤工俭学咨询服务系统的设计与实现60uw9(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;大学生,企业,招聘信息,在线咨询,咨询回复,职位应聘 开题报告内容 基于Vue框架的大学生勤工俭学咨询服务系统的设计与实现 开题报告 一、研究背景 随着高等教育的普及与就业市场的竞争加剧&#xff0c;大学生勤工俭学已成为一种普遍现…

<<机器学习实战>>1-9节笔记

2.前言与导学 从关注算法的分类与特性到关注算法适合解决哪类问题 很多经典算法不再有效&#xff0c;但特征工程、集成学习越来越有效&#xff0c;和深度学习分别适合于不同领域 3、基本概念 如果预测目标是离散的&#xff0c;则是分类问题&#xff0c;否则回归 机器学习相比…

【AIGC】ChatGPT开发者必备:如何获取 OpenAI 的 API Key

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;API Key的重要性&#x1f4af;获取API Key的基本步骤&#x1f4af;定价策略和使用建议&#x1f4af;小结 &#x1f4af;前言 在现代应用开发中&#xff0c;获取OpenAI的…