快速排序算法_基于位运算的快速排序算法

2f585902ccc8ac5c02db0eb5bb21d651.png

前言

如果你准备看这篇文章,我就当你是懂快速排序算法原理的。

下面是我在2018年10月3日想到的基于二进制位运算对正整数进行的一种快速排序算法,目前的代码只能对正整数进行有效的排序,当然,稍微修改一下就可以支持负数的排序了。后面会给出我用Julia语言实现的没有经过优化的代码和一些测试结果。如果您对此能有什么改进或者能帮吗优化一下代码,请告知我一下,我会非常感谢您。下面是此算法的思想和原理。

2是一个神奇的数字,可以用来做很多东西,二叉树,二分搜索,快速幂算法以及很多分治算法都是在2这个神奇的数字的帮助才能达到他们的优雅和高效率。

利用二进制,我们也可以进行快速排序。

如果要对一列数字进行排序,你会怎样去做呢?学过快速排序的同学应该会立即想到快速排序。先选一个数字,然后从两边开始,分别把小于它的数字交换到它的左边,把大于它的数字交换到它右边,完成这一过程,其右边的数字全体大于等于它,左边的数字全部小于等于它。然后分别对其左边和右边递归地运用此方法(快速排序)进行排序,当排序的长度小于2时,递归过程完成了,这一列数字也就变成有序的了。

这种最基本的快排算法是不是也散发着二分的思想?如果它再二一点会怎样?那就是我下面想要谈论的基于二进制的快速排序算法了。哈哈,我给它加入了二进制。

其实除开二进制的位运算,这个算法跟快速排序算法基本上是一样的,所以我暂且称之为BQuickSort(Binary or Bit)。

算法的基本思想

对每一个二进制位,把该位是1的数字划分到右边,是0的划分到左边。高位优先,通过交换来进行。

实现

在算法的实现上我采用了按位与运算,先用

的时间复杂度求出其中最大的数字,然后对最大的数字进行一次简单的flp2运算(把其二进制最高位之外的bit全部置0,这里的最高位并非64或32等,而是左边第一个不为0的bit),获得一个为2的n次幂的整数变量,我们暂且把它命名为shift。

每一次所做的就是用这个shift与一个要排序的区间段进行按位与运算运算,将右边运算结果为0的数字同左边运算结果不为0的数字进行交换,并返回交换之后的分割位置。然后递归地将同样的算法运用于分割位置的左边和右边,只需将shift右移一位即可。递归结束的条件是区间宽度小于1或者shift等于0。

对于一次部分排序,我写了两个函数,一个像快速排序那样从两边向中间行进,是不稳定的算法,另一个只从左边行进,应该是稳定的算法。


演示

接下来我将演示对下列所示的整数序列进行的BQuickSort。

bf7de78cfbc06f3b4c35be41aa363e35.png

在这里最大的数字是10(二进制为1010),通过对10进行flp2运算,我们将获得一个数字8(二进制为1000)。

将shift(这里为8)同每一个数字进行按位与运算并把运算结果不为0的数字交换到右边,最后我们返回一个整数索引10,它记录了一次排序之后的分割线(分割线左边的数字都小于8,同8进行按位与运算为0;右边的数字都大于等于8,同8进行按位与运算不为0)。

c22a7ed2d42a4fe02bafd291e783961f.png

鉴于分割线及其右边的数字都大于左边的数字,下面值演示对左边的数字进行的排序,同样的方法适用于右边。

将shift右移一位(结果为4),然后与分割线左边的数字(前9个)进行与上面一样的部分排序操作,我们将得到下面这张图所示的序列。

0efebef9ae7ebd2e8e7c8f7f827dd041.png

再对前3个数字进行shift为2的部分排序,1将被交换到左边,此时前3个数已经有序,但是递归其实尚未结束,下一次递归是shift为1的一次部分排序。继续对第4~9个数字执行同样的部分排序,最后我们将会得到前九个数字的有序排列。之后再对第10~12个数字进行同样的部分排序,我们就会得到一个所有数字的有序排列。

44c1de4ef5379ad662cbd1abf8b1e3bb.png

e10865ea2c9f797c840b5d24b831c25b.png

更新:以前随便写的文章,用语有点轻佻。我以前以为利用被注释了的那个bqpartition函数,写出来的快速排序是稳定的,现在我不这么认为。我现在认为它是不稳定的,形式化证明应该不难,用计算机也很好验证,用那个bqpartition来做基数排序,以0,1为基数,排序结果不正确,所以它是不稳定的。其次,可以轻易举出这么一个例子:对于这样的序列[1, 1, 2],一次bqpartition将会得到这样的结果,[2, 1, 1],两个1的位置被交换了,所以它是不稳定的。没有被注释的那个bqpartition要快一点,因为被注释掉了的那个bqpartition对于一个数,可能要交换不止一次才能到达最后位置。(这篇文章的代码,以前是用Julia语言实现的,没有改变语义,仅仅改变语法,Julia实现的比C语言实现(未经优化)的略快一些,现在为了读者方便,改用C语言实现的。除了这个引用块里面的更新说明和代码更改,其他一切保持原样(纵有不足之处))。

算法实现代码

BqSort排序算法的C语言实现

// bqsort.c

各种排序算法的测试代码及测试结果(经过gcc -O3编译优化)见

各种排序算法效率简单比较​github.com
b6e5528cf4c79ceb8cced745d9e9bd11.png

。可见,对于正整数的排序,BqSort的速度基本上算是最快的了,尤其是有大量重复数字的时候,虽然这时Quick3Way(三路快排)也很快。不过BqSort可以保证O(nlogn)的最坏情况,而三路快排很容易会陷入O(N^2)的时间复杂度,比如N个不同的数正序或逆序排列。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值