排序(2)

来源:互联网 发布:maven 书籍 推荐 知乎 编辑:程序博客网 时间:2024/06/16 20:51

冒泡说到底,是一种非常低级的排序方式,为什么,写一个主函数来见识一下就知道了。。

public static void main(String args[]){int a[]=GenerateNumber(100000, 100);long s=System.currentTimeMillis();bubbleSort(a);long e=System.currentTimeMillis();System.out.println("execute time:"+(e-s));}

下面的一数据比较:10000 ,execute time:172

                        10*10000, execute time:15391

                        100*10000, 机器无尽的燃烧中

还有什么选择排序,简单排序之类的,就不作一一介绍,没有必要去研究,都是差不多的原理。

level2:下面即将登场的是的快速排序,如其名,快,但是有其缺点,就是不稳定,用于数字排序比较好,

在字符串排序方面,归并要好的多,先不多说这些,介绍一下原理。

首先需要设置一个哨兵p,也就是存储每次排序后的关键字位置,

53,80,86,96,92,30,14,25,70,75

 因为快速是基于分治法的一种,拿一个block来说明一下这种partition过程。start,end为首尾数组地址

哨兵一般是取block第一个。53,与最后一个来做比较,如果最后一个大于p,把尾指针向前移,end--,

指向前一个,这里就是53和75比较,显然75要大,

往前,然后起到20,这时候把25和53交换,即block[start]=block[end]

这里要注意,p是一个临时变量,这里的交换是交换的实际地址,也就是p不会变的,所以才叫哨兵。

交换后再从start开始,这时候block[start]现在已经是25了,因为已经交换过了,再和p比较,如果小,

start++,向前进,直至80,比p大,交换,

然后再从end往前推。。。最后肯定会到一个位置,好像夹逼准则一样,然后这个位置就是我们需要的关键位置。

程序如下:

 private static int partition(int a[],int s,int e){    int key=a[s];  while(s<e){   while(s<e && a[e]>=key)    e--;   a[s]=a[e];   while(s<e && a[s]<=key)    s++;   a[e]=a[s];  }  a[s]=key;  return s; }

这时候只是一个分治的一个小方法,剩下的事情就是我们向上看了,写大方法

 public static void Qsort(int a[],int s,int e){  if(s<e){   int privot=partition(a, s, e);   Qsort(a, s, privot-1);   Qsort(a, privot+1, e);   } }

OK。。递归,简单清楚,不需要多说,就是一个将整体数组分块成n份进行整理。。

如果不清楚,可以用排序1里面的AlgorithmAnimation来进行调试一下,看下数据走向是如何的。

看下level2和level1的差别。。看差别,再说为什么。。

数据:  10*10000 ,execute time:78

          100*10000,Exception in thread "main" java.lang.StackOverflowError

为什么,递归的伤,因为函数体是存储在全局数据区的,即栈空间,直至生命周期结束才over,

递归是一个超耗空间的东西,将其改造一下,写成非递归。

改造一下,改为非递归的:

public static void noQsort(int[] a,int s,int e){Stack s1=new Stack();Stack s2=new Stack();s1.push(s);s2.push(e);int tl,th,p;while(!s1.isEmpty() && !s2.isEmpty()){tl=(Integer)s1.pop();th=(Integer)s2.pop();if(tl>=th) continue;p=partition(a, tl, th);s1.push(tl);s2.push(p-1);s1.push(p+1);s2.push(th);}

递归到非递归的最终技巧也就是循环用栈,不过java的栈是保存java数据,其实如果是基础数据的话,自己可以写一个数组栈会更快,这里用的jdk的工具栈,

也就是两个同步栈s1,s2,,把递归中的同一方法体的参数用同一个栈给保存住,然后再循环放入方法体中执行后得到的结果进行一个选择,

s1对应递归方法的Qsort(a, s, privot-1); s2 对应着 Qsort(a, privot+1, e); 
直至两个为空为止,好,再看下效果。。

 这次没有栈空间耗尽,10*10000 ,execute time:218

                           100*10000,execute time:8235,没有堆耗尽。

                           1000*10000,漫长等待。

可以看到10W级的数据时,速度还慢了,原因很简单,因为java的stack是继承于vector这个老工具的,vector性能就不用说了。。除线程安全外填充因子是100%,

性能不好是正常的,自己写个数组栈肯定要好很多。

1000W数据的时候,不行了,为什么?

原因如下,从空间复杂度来理解一个算法的稳定性,从快速的原理来看,先从头找,再从尾找,这样的后果是,最坏的情况就是超坏的,

也就是我们每次都会交换一遍,和level1的级别一样,n2,但是如果情况很好的话,那么一个数组我只需要nlgn次就OK了。

这是为什么呢,算法的稳定性定义是这样的,交换的数组元素如果越近,那么这个算法的稳定性就越好。快速显然很不适合这种原则,需要做出很大幅度的交换。

原创粉丝点击