排序(2)

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

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了。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值