算法系列(五)排序算法下篇--如何超越排序算法下界

原创 2016年06月01日 23:57:56

概述

算法系列(四)排序算法中篇--归并排序和快速排序一文中,我们介绍了归并排序和快速排序,最坏的情况下,最快的排序算法的时间复杂度是O(nlogn),是否有更好的算法呢?到目前为止,没有特殊的规则,O(nlogn)已经是最好的排序算法了,也就是说通用排序算法的时间复杂度下界就是O(nlogn)。如果限定一些规则,是可以打破这个下界的。下面说一下尽在O(n)时间内就能实现对数组排序的算法。

基于排序的规则

基于什么样的规则才能突破排序的下界呢?我们需要分析一下排序消耗的时间。排序需要遍历,比较,交换。能否省略其中的一些步骤呢?这就是要定义的规则,通过规则减少排序步骤。下面举一个最简单的例子。
一组待排序的元素仅有1和2,没有其它值,对这组数进行排序。
输入A0,A1,A2,A3......An-1,Ai为1或者2
排序步骤
1、令k=0
2、令i从0到n-1依次取值,如果A[i]=1,k自增1
3、令i从0到k-1依次取值,将A[i]赋值为1
4、令i从k到n-1依次取值,将A[i]赋值为2
这样我们完成了排序,花费的时间为O(n)
之前我们所说的算法都是通过比较元素对来确定顺序,那种排序叫做比较排序。凡是比较排序,通用下界为O(nlogn)
刚才所说的简单例子,是一个简单计数排序。下面详细说明一下

使用基数排序超越排序下界

简单例子每个元素仅有两种可能的取值,扩展一下,如果每个元素有m个不同取值,只要取值是m个连续整数之内的整数,算法是通用的。
首先,通过计算出有多少个元素的排序关键字等于某个值,随后就能就算出有多少个元素的排序关键字小于每个可能的排序。

基本思想

计数排序的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数(此处并非比较各元素的大小,而是通过对元素值的计数和计数值的累加来确定)。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。

计数排序算法详细描述

该算法需要三个基本方法

COUNT-KEY-EQUAL(A,n,m)

输入 A 一个数组,
         n 数组A中的元素个数
         m数组A中元素的取值范围
输出一个数组equal[0......m],是equal[j]等于数组A中元素值为j的元素个数
1、创建一个新数组equal[0......m]
2、令equal数组每个元素都为0
3、i从0到n-1依次取值,每次将equal[A[i]]的值自增1
4、返回equal

COUNT-KEY-LESS(equal,m)

输入值 COUNT-KEY-EQUAL方法对应的值equal,m
输出一个数组less[0......m],less[j]=equal[0]+equal[1]+......+equal[j-1]
1、创建一个新数组less[0...m]
2、令less[0]=0
3、j从1取到m,less[j]=less[j-1]+equal[j-1](这是普通的迭代算法)
4、返回less

REARRANGE(A,less,n,m)

输入 COUNT-KEY-EQUAL COUNT-KEY-LESS方法对应的A,less,n,m
输出 数组B,B中包含A中所有元素,并且已经排好序
1、创建新数组B[0...n-1],next[0.....m]
2、j从0到m依次取值
令next[j]=less[j]+1
3、令i从0到n-1依次取值
key=A[i];index=next[key],B[index]=A[i],next[key]++
4、返回数组B

代码实现

进行了逻辑整合,基本思路相同
package com.algorithm.sort;

/**
 * 计数排序
 * 
 * @author chao
 *
 */
public class CountSort {

	public static void main(String[] args) {
		int[] num = { 1, 1 };
		sort(num);
		for (int i = 0; i < num.length; i++)
			System.out.print(num[i] + " ");
	}

	/**
	 * 计数排序
	 * 
	 * @param num
	 */
	public static void sort(int[] num) {
		int len = num.length;
		int[] orign = new int[len];
		int max = 0;// 我们只对正整数排序
		for (int i = 0; i < len; i++) {
			orign[i] = num[i];
			if (num[i] > max) {
				max = num[i];
			}
		}
		max = max + 1;
		int[] count = new int[max];
		for (int i = 0; i < max; i++) {
			count[i] = 0;
		}
		for (int i = 0; i < len; i++) {
			count[num[i]]++;
		}
		int t1, t2;
		t1 = count[1];
		count[0] = count[1] = 0;
		for (int i = 2; i < max; i++) {
			t2 = count[i];
			count[i] = t1 + count[i - 1];
			t1 = t2;
		}
		int key, index;
		for (int i = 0; i < len; i++) {
			key = orign[i];
			index = count[key];
			num[index] = orign[i];
			count[key]++;
		}
	}
}

复杂度分析

计数排序的复杂性为O(n),但是有空间代价,如果最大数很大的话,空间代价非常大。
还有一种排序叫做基数排序,是基于计数排序的,有约束条件,时间复杂度为O(n)
桶排序,基数排序跟计数排序类似,不再详细说明,堆排序(很常用,在树形算法分析中会再说明)
代码实现可以看github,地址https://github.com/robertjc/simplealgorithm
github代码也在不断完善中,有些地方可能有问题,还请多指教

欢迎扫描二维码,关注公众账号






版权声明:本文为robert原创文章,未经博主允许不得转载。博客地址http://blog.csdn.net/robertcpp

相关文章推荐

简单排序算法的时间下界

插入排序 插入排序是zui

排序算法的下界和如何超越下界(摘自算法基础)

对于排序算法的下界和如何超越下界的讨论

排序算法系列--冒泡泡泡(C++)

不得不说,算法这个东西还是挺让人头疼的。。昨天中午忽然脑袋短路,一时竟想不起以前看过的冒泡算法的原理,看来还是没有理解透,中午躺床上睡不着了。。。。 于是,先把他想明白了再休息吧。。。。 冒泡算法...

【java集合框架源码剖析系列】java源码剖析之java集合中的折半插入排序算法

注:关于排序算法,博主写过【数据结构排序算法系列】数据结构八大排序算法,基本上把所有的排序算法都详细的讲解过,而之所以单独将java集合中的排序算法拿出来讲解,是因为在阿里巴巴内推面试的时候面试官问过...
  • htq__
  • htq__
  • 2016-04-04 16:44
  • 8894

排序算法系列之二叉查找树

排序算法系列之二叉查找树 基本概念   二叉查找树(Binary Search Tree),或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它...

代码系列1:c++实现的7种排序算法

#include using namespace std; void bubbleSort(int list[], int length) { for (int i = length; i > ...

排序算法系列之快排

快排(对于大的、乱数串行一般相信是最快的已知排序) 思想:快速排序是所有排序算法中最高效的一种。它采用了分治的思想:先保证列表的前半部分都小于后半部分,然后分别对前半部分和后半部分排序,这样整个...

重学数据结构系列之——八大排序算法

一、有一种分类 稳定排序:如果线性表中的两个元素 a​i 和 aj​​  满足 i 简单来说就是排序前有两个或多个相等的元素,排序后他们的相对位置不会改变) 不稳定排序:不是上面的情况就是了 ...

排序算法系列第一篇

写程序时,经常会需要对数组进行排序。为了节省上网查找的时间,笔者花费了一些时间,专门对一些排序算法做了分析,并在本文中给出了总结,一方面方便自己日后使用,同时也方便大家用的时候能够在这里就找到合适自己...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)