`
javasalatu
  • 浏览: 777144 次
  • 性别: Icon_minigender_2
  • 来自: 北京
博客专栏
96df99eb-e89d-3228-9c8e-967fc745ec52
程序员的自我经营之道
浏览量:7874
文章分类
社区版块
存档分类
最新评论

一个线性时间下的原地置换排序算法

 
阅读更多

版权所有,转载请注明出处,谢谢!

一个线性时间下的原地置换排序算法

排序算法

现有的线性时间排序(时间复杂度为ο(n)算法,比如计数排序,基数排序,桶排序等,虽然在时间复杂度上都能保持线性,但不能进行原地置换排序,需要额外的辅助空间.一个算法能否同时做到时间复杂度为ο(n),空间复杂度为Ω(1)呢?

我们知道,在现有的常规线性算法(计数排序,桶排序,基数排序等)当中,计数排序和桶排序因为不是原地置换排序,因此都需要额外的辅助空间来完成排序,因此无法达到上述目的,而基数算法本身是一个复合算法,如果能保证其位排序的算法能做到空间复杂度为Ω(1)和时间复杂度ο(n),那显然可以达到上述要求的算法目标.对于基数排序中的位排序要求必须具有稳定性.既要满足线性时间,常量空间,并且稳定的算法,在现有常用算法中还没有这种算法可以达到这种要求。

我们选基数排序作为实现这个算法目标的基本算法,由于我们进行的是整数排序,基数排序的位就是整数二进制表示里的位,而对于整数的二进制形式,每位上的数就只有0和1(这是个很有利的因素),而对0和1序列排序做到线性时间,只需要利用0作为基元,做一次快排划分即可,快排划分的算法时间复杂度是ο(n),而且也是原地置换排序,空间上的要求可以满足(Ω(1)),但问题在于对于基数排序,要求其位排序必须是稳定排序,而快排划分显然不是稳定的。但如果我们能找到一种方法将这种不稳定带来的影响消除,使得其不影响最终结果,问题就解决了。那么而怎样让快排划分不影响最终结果呢?

我们知道基数排序有两种基本的形式,一种是从低位到高位进行排序,一种是从高位到低位进行,经过分析发现,如果采用从高位到低位的方式进行,是可以让快排划分不影响到最终结果的,我们考虑一般情况,设B(k),B(k-1)….,B(1)表示一个基数排序所有的位(对于32位整数k=32),对第i位进行排序,而前m位(m=k-i-1)位已经排好序B(k)…B(i+1),在进行i位排序时,如果我们的快排划分只针对前m位相同的情况进行,则快排划分的结果显然不会影响最终结果.这样对于第i位上的一次快排,就可以转换成若干个小区间的快排划分(用前m位数进行划分,前m位相同的为一个区间),而且由于前m位已经排好序,那么显然前m位相同的都必然紧挨在一起,这种情况下虽然一次快排划分变成了p次快排划分(p<n),但整体上的时间复杂度并没有增加,虽然在具体的算法技巧处理时会考虑索引回溯,但时间复杂度不会改变,因为最极端的回溯小于等于n,因此时间复杂度最多为2n.而对于整数而言,前m位的获取和比较都非常快,所以在进行时间复杂度分析时可以不考虑,所以整体上算法每位上排序时间复杂度还是为32n到64n之间,而回溯通过一定技巧处理也可以消除,那么算法的时间复杂度就可以小于32n.由于这种位算法空间复杂度为Ω(1),因此整个算法满足前面提到的算法目标。

下面是具体的C#语言下的算法实现(为了减少交换次数,这里位排序不是严格按照快排划分进行,而是针对0和1的特点,用一个指针i指向序列第1个1,遍历指针在前,遇到0就与i交换,i随后右移一位,这可以大大减少交换次数):

private void BitSortAndDelRepeatorsA(int[] A)

{

//获取数组长度

int theN = A.Length;

//从高位到低位开始排序,这里从31位开始,32位是符号位不考虑,

//或者单独考虑。

for (int i = 31; i >= 1; i--)

{

//当前排序之前的值,只有该值相同才进行快排分组,如果不相同,

//则重新开始另外一次快排

//这很关键,否则快排的不稳定就会影响最后结果.

int thePrvCB = A[0] >>(i) ;

//快排开始位置,会变化

int theS = 0;

//快排插入点

int theI = theS-1;

//2进制基数,用于测试某一位是否为0

int theBase = 1 << (i-1);

//位基元始终为0,

int theAxBit = 0;

//分段快排,但总体上时间复杂度与快排分组一样.

for (int j = 0; j < theN; j++)

{

//获取当前数组值的前面已拍过序的位数值。

int theTmpPrvCB = A[j]>> (i);

//如果前面已排过的位不相同,则重新开始一次快排.

if (theTmpPrvCB !=thePrvCB)

{

theS = j;

theI = theS - 1;

theAxBit = 0;

thePrvCB = theTmpPrvCB;

j--;//重新开始排,回朔一位.

continue;

}

//如果前面的数相同,则寻找第1个1,thI指向其

//如果相同,则按快排处理

int theAJ = (A[j] &(theBase)) > 0 ? 1 : 0;

//如果是重新开始排,则寻找第1个1,并人theI指向其.这可以

//减少交换,加快速度.

if (theI < theS)

{

if (theAJ == 0)

{

continue;

}

theI = j;//Continue保证J从theI+1开始.

continue;

}

//交换.

if (theAJ <= theAxBit)

{

int theTmp = A[j];

A[j] = A[theI];

A[theI] = theTmp;

theI++;

}

}

}

}

算法分析

时间复杂度虽然有32*n,但由于32是个常数,因此整个算法的时间复杂度还是ο(n),空间复杂度方面,算法中只是利用了有限的几个交换变量(<20),因此空间复杂度为Ω(1)。但算法的稳定性方面,由于其位算法是不稳定的,因此整个算法也是不稳定的。

该算法经过我大量测试,其复杂度符合算法分析结果。

分享到:
评论

相关推荐

    线性时间排序算法

    **计数排序**是一种特殊的线性时间排序算法,适用于输入数据范围较小的情况。该算法假设输入元素为m个整数,这些整数的范围在[1, k]之间。计数排序的主要步骤包括: 1. **初始化计数数组**:创建一个大小为k的计数...

    线性时间选择算法实现

    这通常优于其他如排序算法(如快速排序或归并排序),它们虽然在平均或最好情况下具有较低的时间复杂度,但在最坏情况下可能达到O(n log n)。线性时间选择算法不依赖于排序,而是通过一系列迭代步骤直接找到目标元素...

    线性时间选择算法(附完整的代码,结合例题详细解析) 全套资源已打包好,求抱走!!!

    给定一个包含n个元素的一维线性序列a[p:r],从这n个元素中找出第k小的元素,1。设a[0:14]={2,9,11,3,14,7,10,8,15,4,13,1,6,5,12},k = 8,采用线性时间选择算法解决该问题。 1)写出算法实现代码并截屏程序运行结果...

    三种线性排序算法Java实现

    本资源提供的Java实现包括了三种线性排序算法:桶排序(Bucket Sort)、基数排序(Radix Sort)和计数排序(Counting Sort)。这三种算法在特定条件下可以达到线性的平均或最好时间复杂度,效率相对较高。 1. **桶...

    线性时间排序算法.pdf

    线性时间排序算法主要指的是那些能在输入数据规模为n的情况下,以O(n)的时间复杂度完成排序的算法。其中,计数排序(Counting Sort)是这类算法的一个典型代表,尤其适用于特定条件的数据集。 计数排序是一种非比较...

    MIT算法导论公开课之课程笔记 5.线性时间排序.rar

    此外,还有一种名为 radix sort 的线性时间排序算法,适用于整数排序。基数排序基于位运算,将整数按位分割,然后从最低位到最高位依次进行排序。通过多轮排序,最终可以实现整体的线性时间复杂度。 在实际应用中,...

    线性时间排序.pptx

    这一理论下界意味着在纯比较的基础上,无法找到一个在所有情况下都能在O(n)时间内完成的排序算法。然而,存在非比较排序算法,如计数排序,可以达到线性时间复杂度。 计数排序是一种适用于特定场景的排序方法,它不...

    线性时间排序培训课件.pptx

    线性时间排序,顾名思义,是一种在理想条件下能达到线性时间复杂度的排序算法。简而言之,这意味着在最理想的情况下,这类算法的运行时间与输入数据的量级呈线性关系,即O(n),而非传统的比较排序的O(nlogn)。这样的...

    《算法设计与分析》实验报告:实验二(线性选择问题)

    在本实验中,我们通过在快速排序算法的基础上进行改进,实现了一个线性时间选择算法,并通过大量实验来分析其时间复杂性。该问题的核心在于从n个元素中找到第k小的元素,这在数据挖掘、统计分析等多个领域有着广泛的...

    C++线性时间的排序算法分析

    前面的文章已经介绍了几种排序算法,如插入排序(直接插入排序,折半插入...“定理:对于含n个元素的一个输入序列,任何比较排序算法在最坏情况下,都需要做Ω(nlgn)次比较。” 也就是说,比较排序算法的运行速度不会

    排序算法 sort 经典

    在计算机科学领域,排序算法是数据处理的核心技术之一,它用于将一组无序的数据元素按照特定的顺序排列。本文将深入探讨排序算法及其在数组中的应用,特别关注"sort"这个关键字,它通常指的是排序的过程。 一、排序...

    各种排序算法合集

    本合集包含多种经典的排序算法,每种算法都是通过继承自一个基类并实现其具体逻辑来实现的。下面,我们将详细讨论这些排序算法及其原理。 1. 冒泡排序(Bubble Sort):冒泡排序是最基础的排序算法之一,通过不断...

    Java各种排序算法(含代码)

    堆排序的时间复杂度为O(n log n),是原地排序,但不是稳定的排序算法。 4)**归并排序**:归并排序是分治策略的一种体现,将数组分为两半,分别进行排序,然后合并两个已排序的子数组。时间复杂度稳定在O(n log n)...

    各种排序算法比较(java实现)

    桶排序是一种分布式排序算法,它将数据分到有限数量的桶里,每个桶再分别排序,最后把所有桶中的数据合并。桶排序假设输入数据服从均匀分布,如果数据均匀分布在各个桶中,那么桶排序的时间复杂度可以达到线性的O(n...

    C++实现希尔、快速、堆排序、归并排序算法

    堆排序,是由Napoleon Goldstein在1960年代提出的一种排序算法。它利用了完全二叉树的特性,构建一个大顶堆或小顶堆,然后将堆顶元素与最后一个元素交换,调整堆,再将堆的大小减一,重复这个过程,直到堆的大小为1...

    Java排序算法大全

    7. 计数排序、桶排序和基数排序:这些是线性时间复杂度的非比较排序算法,适用于特定类型的数据,如非负整数。 除此之外,还有一些高级的排序算法,如Timsort(Python和Java的默认排序算法)、Introsort(快速排序...

    十大排序算法.pdf

    归并排序的实现需要递归调用和额外的存储空间,其时间复杂度在最好、平均和最坏情况下均为O(nlogn),它是一个稳定的排序算法。 6. 堆排序 堆排序是利用堆这种数据结构设计的一种排序算法。堆是一个近似完全二叉树的...

    分治法实现线性时间选择

    在这里,我们将使用分治法来实现线性时间选择,也就是选择一个数组中的第k小数。 分治法的基本思想 分治法的基本思想是将一个大问题分解成多个小问题,然后逐个解决每个小问题。这里,我们可以将选择第k小数的问题...

Global site tag (gtag.js) - Google Analytics