排序(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了。
这是为什么呢,算法的稳定性定义是这样的,交换的数组元素如果越近,那么这个算法的稳定性就越好。快速显然很不适合这种原则,需要做出很大幅度的交换。
- 排序(2)二分排序、快速排序、归并排序
- 排序(2)-选择排序
- 排序(2)---希尔排序
- 排序(2)快速排序
- 排序(2)归并排序(递归、合并排序)
- 排序2:插入排序(折半插入排序)
- 排序3:插入排序(2路插入排序)
- 排序算法(2)—归并排序,快速排序
- acm: 排序--快速排序(2/2)
- 经典排序算法2(插入排序)
- 常用排序算法(2)-选择排序
- 数据结构算法----排序(2)----冒泡排序
- 排序算法(2)-堆排序
- C++排序之快速排序(2)
- 排序(2)直接插入排序
- 排序算法小结(2)选择排序
- 排序算法(2)-直接插入排序
- 排序算法复习(2)-- 选择排序
- 源码一错误
- C++学习代码--return
- 我理解的逻辑地址、线性地址、物理地址和虚拟地址
- 大心脏
- C++学习代码--pointer
- 排序(2)
- 使用函数指针来代替switch语句
- 排序(3)
- 十个糟糕的程序员的行为
- C++学习代码--file
- Android 初始化语言(Android init Language翻译)
- 外企九年-我最终选择放弃
- 在Vim中编辑Qt4
- 浏览器缓存知识收藏