字符串排序:键索引计数法

简介: 字符串排序:键索引计数法介绍

描述


关于字符串的排序有很多种方式,像《算法》一书中列举的低位优先、高位优先等,其中最先提到的是键索引计数法,它也是其他排序方式的基础,我们先来了解下。


适用性


关于键索引计数法进行字符串排序,并不是全部都适用,因为它的排序算法核心就是通过统计元素出现频次、构建排序因子的索引边界来递进式完成所有元素排序,它主要适合于以下场景:


  • 用于小整数键的算法
    比如数组arr ={1,2,3,4,2,3,4,2,1,3,4,2,3,4},它里面重复的数字比较多,不重复的只有1,2,3,4,这时就可以用此方法。
  • 用于小整数键的分组排序
    还可以应用在基于小整数键的分组排序,比如有字母{a,b,c,d,e,f,g,h},各字母不重复、但是分组重复,其分组序号格式{字母,分组号}描述为{a,1}、{b,2}、{c,1}、{d,2}、{e,3}、{f,4}、{g,4}、{h,1},最终按照分组号进行排序实现的元素排列为a、c、h、b、d、e、f、g
    网络异常,图片无法展示
    |

    综上,我们可以看到按照分组号可以将元素归纳如下:

分组号

元素

1

a、c、h

2

b、d

3

e

4

f、g


步骤


1、频率统计


网络异常,图片无法展示
|

我们可以看到,每个分组出现频次如下:

分组号

元素

元素出现频次

1

a、c、h

3

2

b、d

2

3

e

1

4

f、g

2


统计频次的目的是为了给构建索引提供条件,我们知道所谓键索引计数就是通过索引 + 频次来完成的。索引可以理解为每个分组的排序边界,每个分组的起始索引即较小一侧边界,而每个分组的结束索引即较大一侧边界,如果索引边界为startIndexendIndex,分组内元素出现频次为t,那么每个分组中的索引边界和分组内元素出现频次的关系是endIndex = startIndex + t,每个分组都按照逻辑序号紧紧相邻,较大的分组起始索引与它相邻的第一个较小分组的结束索引一致。


如果将这个场景延展到一个直线上,也可以将索引边界理解为坐标点,将频次理解为步长,或者是坐标点之间的间距


2、构建索引


网络异常,图片无法展示
|

当已知了 间距 ,下面根据间距从0开始计算和整理 索引边界 ,可以参考上图,方法很简单,是从0开始递增每个分组的频次即可。


3、数据分类


网络异常,图片无法展示
|

当建立好索引后,下面开始进行 数据分类 ,说白了这一步就是将元素排序填充的过程,因为已经知道了所有排序分组的起止边界了,也知道每个分组的容量了,接下来就是挨个映射填充就可以完成排序工作了。


分组元素填充后,起始索引递增
这里面的填充技巧的细节点在于,每填充一个分组内元素后,将对应的该分组的起始索引递增1,代表完成了一个分组内地排序,若后续还有同样分组内的元素填充则理所当然填充到该分组的下一个临近位即可。


4、回写数组


这一步是将已排序好的元素重新回写到原数组中即可。


代码实现


  • 纯小整数版


/**

* @author: <a href=mailto:keycasiter@qq.com>guanjian</a>

* @date: 2021/4/8 13:15

* @description:

*/

public class IndexCountSortDemo {

   

   public static void main(String[] args) {

       int[] nums = {2, 3, 4, 1, 2, 4, 3, 1, 2, 2, 1};

       indexCountIndex(nums);

       //打印结果   1 1 1 2 2 2 2 3 3 4 4

   }


   public static void indexCountIndex(int[] nums) {

       int[] count = new int[6];

       //计算频率

       for (int i = 0; i < nums.length; i++) {

           count[nums[i] + 1]++;

       }

       //将频率转化为索引

       for (int i = 1; i < count.length; i++) {

           count[i] = count[i] + count[i - 1];

       }

       //数据分类

       int[] aux = new int[nums.length];

       for (int i = 0; i < nums.length; i++) {

           aux[count[nums[i]]++] = nums[i];

           //aux[count[nums[i]]] = nums[i];

           //count[nums[i]]++;

       }

       //回写数据(这里是打印)

       for (int i = 0; i < nums.length; i++) {

           System.out.print(aux[i] + " ");

       }

   }

}


  • 小整数分组排序元素


/**

* @author: <a href=mailto:keycasiter@qq.com>guanjian</a>

* @date: 2021/4/8 13:15

* @description:

*/

public class IndexCountSortDemo {

   

   public static class Item<T> {

       private int group;

       private T data;


       public Item(int group, T data) {

           this.group = group;

           this.data = data;

       }


       public int getGroup() {

           return group;

       }


       public void setGroup(int group) {

           this.group = group;

       }


       public T getData() {

           return data;

       }


       public void setData(T data) {

           this.data = data;

       }

   }


   public static void main(String[] args) {

       Item<String>[] arr = new Item[]{

               new Item(1, "a"),

               new Item(2, "b"),

               new Item(1, "c"),

               new Item(2, "d"),

               new Item(3, "e"),

               new Item(4, "f"),

               new Item(4, "g"),

               new Item(1, "h")

       };

       indexCountIndexByItem(arr);

       //打印结果   a c h b d e f g

   }


   public static void indexCountIndexByItem(Item[] arr) {

       int[] count = new int[6];

       //计算频率

       for (int i = 0; i < arr.length; i++) {

           count[arr[i].group + 1]++;

       }

       //将频率转化为索引

       for (int i = 1; i < count.length; i++) {

           count[i] = count[i] + count[i - 1];

       }

       //数据分类

       Item[] aux = new Item[arr.length];

       for (int i = 0; i < arr.length; i++) {

           aux[count[arr[i].group]++] = arr[i];

           // aux[count[arr[i].group]] = arr[i];

           // count[arr[i].group]++;

       }

       //回写数据(这里是打印)

       for (int i = 0; i < arr.length; i++) {

           System.out.print(aux[i].data + " ");

       }

   }

}


总结


键索引计数法是一种对于小整数键排序非常有效却常常被忽略的排序方法。理解它的工作原理是理解字符串排序的第一步,它是一个线性时间级别的排序方法。


参考


字符串排序:键索引计数法
《算法》第4版


相关文章
|
监控 前端开发 JavaScript
前端监控(Frontend Monitoring):保障用户体验的不可或缺工具
前端监控是维护卓越用户体验的关键,它使您能够追踪、分析和解决您的Web应用程序中的性能问题和错误。在本博客中,我们将深入探讨前端监控的重要性、监控的关键指标以及如何实施前端监控来提高您的应用程序的可用性和性能。
865 0
|
存储 机器学习/深度学习 算法
c语言基础知识帮助理解(函数递归详解)
c语言基础知识帮助理解(函数递归详解)
105 0
|
监控 前端开发 安全
SpringCloud Gateway鉴权和跨域解决方案
SpringCloud Gateway鉴权和跨域解决方案
1492 0
|
12月前
|
机器学习/深度学习 算法 数据可视化
机器学习模型中特征贡献度分析:预测贡献与错误贡献
本文将探讨特征重要性与特征有效性之间的关系,并引入两个关键概念:预测贡献度和错误贡献度。
899 3
|
7月前
|
运维 监控 调度
普通人如何用PCDN来赚钱
私有内容分发网络(PCDN)利用分散的终端设备和带宽资源,构建去中心化的内容分发系统。普通人可通过搭建PCDN,利用闲置设备实现低成本、高灵活性的赚钱机会。主要步骤包括硬件准备、选择稳定软件平台、设计网络架构、内容管理和运维监控。盈利模式涵盖提供PCDN服务、广告合作、流量变现及增值服务。通过优化网络配置和设备选择,可最大化收益。尽管存在法律风险和收益波动,但合理搭建和维护能带来可观回报。
12718 0
|
监控 Ubuntu Unix
Linux |Nethogs 监控网络使用情况
Linux |Nethogs 监控网络使用情况
Linux |Nethogs 监控网络使用情况
|
机器学习/深度学习 人工智能 监控
AI安防监控
AI安防监控运用人工智能技术分析视频监控,实现对象识别、追踪和预警,广泛应用在安防、交通和工业等领域。它提升了监控的实时性和准确性,降低了人力成本,但面临误判、隐私泄露和高成本等问题。随着市场需求增长,全球安防监控行业将迎来持续发展,需在提升技术的同时保障个人隐私。
541 0
|
机器学习/深度学习 人工智能 计算机视觉
【YOLOv8-seg】实战一:手把手教你使用YOLOv8实现实例分割
【YOLOv8-seg】实战一:手把手教你使用YOLOv8实现实例分割
6518 0
【YOLOv8-seg】实战一:手把手教你使用YOLOv8实现实例分割
深度解析:短信号码都有那些?他们之间有什么区别?
您的手机上常见的短信号码都有哪些呢,他们直接有什么区别呢,本文将带您一起学习了解哦。
深度解析:短信号码都有那些?他们之间有什么区别?