数据结构与算法之排序: 计数排序 (Javascript版)

这篇具有很好参考价值的文章主要介绍了数据结构与算法之排序: 计数排序 (Javascript版)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

排序

  • 排序:把某个乱序的数组变成升序或降序的数组 (这里用数组来做举例)

计数排序

  • 核心思想:通过计数而非比较来进行排序,借助数组下标本身就是有序的原理实现
  • 适用范围:较小的非负整数序列和最小值和最大值之间的数字范围比较合适
  • 基数排序需要新增一个计数数组(待累计数组)
    • 计数数组:即新的临时的初始化数组用于计数,需要找到原始数组中最大的值,再加1,才是这个数组的长度,存储 [0 ~ max]
    • 累计数组:是由计数数组计算后得到的,下标是原数组的值,元素值是累计的个数,即重复的个数

算法实现

1 )基础版本

function countSort(list: number[]) {
	const len: number = list.length;
	if (len < 2) return;
	// 获取最大值
	// const max = Math.max.apply(null, list);
	const max: number = Math.max(...list);
	// 初始化计数数组
	const countList: number[] = new Array(max + 1).fill(0); // 数组大小,存放[0~max]
	// 对原数组的数据在计数数组中累计,原数组的值是计数数组的下标
	let i: number;
	for(i = 0; i < len; i++) countList[list[i]]++; // 遍历完成后计数数组就变成了累计数组
	// 基于累计数组生成排好序的数组,这里利用了下标即是有序的原理
	i = 0; // 重新使用i
	for(let j: number = 0; j < max + 1; j++) {
    	// 内层循环不会到n, 一般是常数级别的,但是这样写似乎也不是太好,嵌套会增加时间复杂度
		for (let k: number = 0; k < countList[j]; k++) list[i++] = j; // 这里j就是下标,其实就是原数组的值
	}
}

const list = [2, 3, 8, 7, 1, 2, 2, 2, 7, 3, 9, 8, 2, 1, 4, 2, 4, 6, 9, 2];
countSort(list);
console.log(list); // [1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4, 6, 7, 7, 8, 8, 9, 9]

2 )优化版本:优化时间复杂度(空间换时间)

function countSort(list: number[]): number[] {
	const len: number = list.length;
	if (len < 2) return list;
	// 获取最大值和最小值
	// const max = Math.max.apply(null, list);
	// const min = Math.min.apply(null, list);
	const max: number = Math.max(...list);
	const min: number = Math.min(...list);
	// 初始化计数数组
	const countList: number[] = new Array(max - min + 1).fill(0);
	// 对原数组的数据在计数数组中累计,原数组的值是计数数组的下标
	for(let i: number = 0; i < len; i++) countList[list[i] - min]++; // 遍历完成后计数数组就变成了累计数组
	// 基于累计数组生成排好序的数组,这里利用了下标即是有序的原理
	let result: number[] = [];
	for(let j: number = 0; j < max + 1; j++) {
		if (!countList[j]) continue;
		countList[j] > 1 ? (result = result.concat(new Array(countList[j]).fill(j + min))) : result.push(j + min);
	}
	return result; // 替换
}

const list: number[] = [102, 103, 108, 107, 101, 102, 102, 102, 107, 103, 109, 108, 102, 101, 104, 102, 104, 106, 109, 102];
const result: number[] = countSort(list);
console.log(result); // [101, 101, 102, 102, 102, 102, 102, 102, 102, 103, 103, 104, 104, 106, 107, 107, 108, 108, 109, 109]

3 )优化版本:优化时间复杂度(空间换时间) + 优化存储空间

function countSort(list: number[]) {
	const len: number = list.length;
	if (len < 2) return list;
	// 获取最大值和最小值
	const max: number = Math.max(...list);
	const min: number = Math.min(...list);
	// 初始化计数数组
	const countList: number[] = new Array(max - min + 1).fill(0);
	// 对原数组的数据在计数数组中累计,原数组的值是计数数组的下标
	for(let i: number = 0; i < len; i++) countList[list[i] - min]++; // 遍历完成后计数数组就变成了累计数组
	// 基于累计数组生成排好序的数组,这里利用了下标即是有序的原理
	let str: string = ''; // 用字符串来处理
	for(let j: number = 0; j < max - min + 1; j++) {
		if (!countList[j]) continue;
		str += countList[j] > 1 ? ((j + min) + ',').repeat(countList[j]) : (j + min) + ',';
	}
	str = str.substring(0, str.length - 1); // 最后一个 , 是多余的
	return str.split(',').map(item => Number(item));
}

const list: number[] = [102, 103, 108, 107, 101, 102, 102, 102, 107, 103, 109, 108, 102, 101, 104, 102, 104, 106, 109, 102];
const result: number[] = countSort(list);
console.log(result); // [101, 101, 102, 102, 102, 102, 102, 102, 102, 103, 103, 104, 104, 106, 107, 107, 108, 108, 109, 109]

4 )优化版本:优化时间复杂度(累计数组变位置数组,基于差值和位置来对应顺序)文章来源地址https://www.toymoban.com/news/detail-738400.html

function countSort(list: number[]) {
	const len: number = list.length;
	if (len < 2) return;
	// 获取最大值和最小值
	const max: number = Math.max(...list);
	const min: number = Math.min(...list);
	// 初始化计数数组
	const countList: number[] = new Array(max - min + 1).fill(0);
	// 初始化目标数组
	const result: number[] = new Array(len);
	// 对原数组的数据在计数数组中累计,原数组的值是计数数组的下标
	for(let i: number = 0; i < len; i++) countList[list[i] - min]++; // 遍历完成后计数数组就变成了累计数组
	// 计算位置: 由上面的累计结果,计算累加后的位置, countList数组从累计数组,变成了位置数组
	for(let j: number = 1; j < max - min + 1; j++) {
		countList[j] += countList[j - 1];
	}
	// 根据累加后的位置, 将原数组中的数据按照位置进行分配,就得到排好序的数组,备注:数据可能重复,使用过的数据记得-1,从后向前遍历更好理解
	for (let k: number = len - 1; k >= 0; k--) {
		const currentIndex = countList[list[k] - min] - 1;
		result[currentIndex] = list[k]; // 将原数组的当前元素整合到 result数组对应的位置之中
		countList[list[k] - min] --; // 使用一次后,累计的数量自减,防止重复使用导致错误
	}
	list.splice(0, len, ...result); // 优化下方式,不用return, 直接替换
}

const list: number[] = [102, 103, 108, 107, 101, 102, 102, 102, 107, 103, 109, 108, 102, 101, 104, 102, 104, 106, 109, 102];
countSort(list);
console.log(list); // [101, 101, 102, 102, 102, 102, 102, 102, 102, 103, 103, 104, 104, 106, 107, 107, 108, 108, 109, 109]

总结

  • 计数排序的适用范围有限:最优版时间复杂度可降低到 O(n), 是最快的排序算法, 但是有两个前提需要满足
    • 1)需要排序的元素必须是非负整数(下标不能是负数和小数)
      • 如果是小数,需要将全部元素同等扩大到整数,之后再回归到小数
      • 一般不考虑这种小数场景
    • 2)排序元素的取值要在一定范围内,并且比较集中(量大,范围小)
  • 满足以上两个条件,才能最大发挥出计数排序的优势, 否则不适用
  • 计数排序的缺点
    • 基础版空间浪费,优化版解决了这个问题
    • 将数组长度定为 max-min+1, 即不仅要找出最大值,还要找出最小值
  • 缺点弥补:根据两者的差来确定计数数组的长度则可弥补之
  • 计数排序是非常稳定的排序算法,但是适用场景确实有限
  • 计数排序是一种桶排序的思想

到了这里,关于数据结构与算法之排序: 计数排序 (Javascript版)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构与算法】万字剖析八大排序(直接插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序、归并排序、计数排序)

    初窥直接插入排序我们先来看一张动图: 由动图我们可以分析出直接插入排序是从 第二个数据开始遍历 ,与 前面的数据进行比较 如果小于 则让前面的数据向前移动 自己接着向前面的数据比较 直到比较到 大于等于自己的 数据或者 没有数据能进行比较 时停止 插入当前的位

    2023年04月13日
    浏览(43)
  • 【数据结构与算法】JavaScript实现排序算法

    一、大O表示法 大O表示法: 在计算机中采用 粗略的度量 来描述计算机算法的 效率 ,这种方法被称为 “大O”表示法 在 数据项个数 发生改变时, 算法的效率 也会跟着改变。所以说算法A比算法B快两倍,这样的比较是 没有意义 的。 因此我们通常使用 算法的速度 随着 数据

    2024年02月02日
    浏览(38)
  • 【数据结构】——归并排序和计数排序

    🌇个人主页:_麦麦_ 📚今日名言:繁华落尽,我心中仍有花落的声音。一朵,一朵,在无人的山间轻轻飘落。——席慕蓉《桐花》 目录 一、前言 二、正文 1.归并排序 1.1 基本思想 1.2【递归版】具体实现  1.3【递归版】代码部分  1.4【非递归版】具体实现  1.5【非递归版】

    2023年04月15日
    浏览(31)
  • 【数据结构】排序之归并排序与计数排序

    个人主页 : zxctsclrjjjcph 文章封面来自:艺术家–贤海林 如有转载请先通知 在前面的文章中介绍了 插入排序和交换排序,今天来分享的是归并排序和计数排序。 话不多说,正文开始。 归并排序既是内排序也是外排序。 基本思想: 归并排序(MERGE-SORT)是建立在归并操作上的

    2024年01月17日
    浏览(33)
  • 【数据结构】-计数排序

    🎇作者:小树苗渴望变成参天大树 🎉 作者宣言:认真写好每一篇博客 🎊作者gitee:link 如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧! 答应大家的计数排序今天它来了,这也是一个非常巧妙的方法,不通过比较元素的大小就可以排序出来,通过用另一个人数组

    2023年04月17日
    浏览(24)
  • 数据结构——归并排序和计数排序的介绍

    归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列。即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

    2024年02月11日
    浏览(46)
  • 数据结构——lesson13排序之计数排序

    hello hello~ ,这里是大耳朵土土垚~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹 💥 个人主页:大耳朵土土垚的博客 💥 所属专栏:数据结构学习笔记 、排序算法合集 💥对于数据结构顺序表、链表、堆以及排序有疑问的都可以在上面数据结构专栏和排序合集专栏进行

    2024年04月10日
    浏览(34)
  • 【数据结构初阶】八大排序(三)——归并排序&&计数排序

    大家好我是沐曦希💕 往期博客:【数据结构初阶】八大排序(一)——希尔排序堆排序直接插入排序直接选择排序 【数据结构初阶】八大排序(二)——快速排序冒泡排序 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一

    2024年02月03日
    浏览(60)
  • 【C语言】数据结构——排序三(归并与计数排序)

    💗个人主页💗 ⭐个人专栏——数据结构学习⭐ 💫点击关注🤩一起学习C语言💯💫 我们在前面学习了排序,包括直接插入排序,希尔排序,选择排序,堆排序,冒泡排序和快排。 今天我们来讲一讲归并排序和计数排序。 关注博主或是订阅专栏,掌握第一消息。 归并排序的

    2024年01月19日
    浏览(31)
  • 【数据结构】归并排序的两种实现方式与计数排序

    前言:在前面我们讲了各种常见的排序,今天我们就来对排序部分收个尾,再来对归并排序通过递归和非递归的方法进行实现,与对计数排序进行简单的学习。 💖 博主CSDN主页:卫卫卫的个人主页 💞 👉 专栏分类:数据结构 👈 💯代码仓库:卫卫周大胖的学习日记💫 💪关注博

    2024年01月18日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包