java 快速排序
来源:互联网 发布:德勤gdc和德勤区别知乎 编辑:程序博客网 时间:2024/05/12 04:46
- 说来感到惭愧,昨天看别人的博客上面一一讲了一些算法,其实这些算法在大学都学过,不过几乎全部忘记了。虽然现在做java上层开发基本上用不到算法,但是还是感觉算法是一种思想,是一种灵魂,所以又不仅翻开了严蔚敏老师的数据结构,一个一个把以前忘记的算法实现一遍。
快速排序的基本思想:
通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分关键字小,则分别对这两部分继续进行排序,直到整个序列有序。
先看一下这幅图:
把整个序列看做一个数组,把第零个位置看做中轴,和最后一个比,如果比它小交换,比它大不做任何处理;交换了以后再和小的那端比,比它小不交换,比他大交换。这样循环往复,一趟排序完成,左边就是比中轴小的,右边就是比中轴大的,然后再用分治法,分别对这两个独立的数组进行排序。
- public int getMiddle(Integer[] list, int low, int high) {
- int tmp = list[low]; //数组的第一个作为中轴
- while (low < high) {
- while (low < high && list[high] >= tmp) {
- high--;
- }
- list[low] = list[high]; //比中轴小的记录移到低端
- while (low < high && list[low] <= tmp) {
- low++;
- }
- list[high] = list[low]; //比中轴大的记录移到高端
- }
- list[low] = tmp; //中轴记录到尾
- return low; //返回中轴的位置
- }
递归形式的分治排序算法:- public void _quickSort(Integer[] list, int low, int high) {
- if (low < high) {
- int middle = getMiddle(list, low, high); //将list数组进行一分为二
- _quickSort(list, low, middle - 1); //对低字表进行递归排序
- _quickSort(list, middle + 1, high); //对高字表进行递归排序
- }
- }
- public void quick(Integer[] str) {
- if (str.length > 0) { //查看数组是否为空
- _quickSort(str, 0, str.length - 1);
- }
- }
编写测试方法:看一下打印结果吧:- public class TestMain {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- Integer[] list={34,3,53,2,23,7,14,10};
- QuicSort qs=new QuicSort();
- qs.quick(list);
- for(int i=0;i<list.length;i++){
- System.out.print(list[i]+" ");
- }
- System.out.println();
- }
- }
2 3 7 10 14 23 34 53
这样就排序好了,快速排序是对冒泡排序的一种改进,平均时间复杂度是O(nlogn)。
快速排序作为一种高效的排序算法被广泛应用,SUN的JDK中的Arrays.sort 方法用的就是快排。
快排采用了经典的分治思想(divide and conquer):
Divide:选取一个基元X(一般选取数组第一个元素),通过某种分区操作(partitioning)将数组划分为两个部分:左半部分小于等于X,右半部分大于等于X。
Conquer: 左右两个子数组递归地调用Divide过程。
Combine:快排作为就地排序算法(in place sort),不需要任何合并操作
可以看出快排的核心部分就是划分过程(partitioning),下面以一个实例来详细解释如何划分数组(图取自于《算法导论》)
初始化:选取基元P=2,就是数组首元素。i=1,j=i+1=2 (数组下标以1开头)
循环不变量:2~i之间的元素都小于或等于P,i+1~j之间的元素都大于或等于P
循环过程:j从2到n,考察j位置的元素,如果大于等于P,就继续循环。如果小于P,就将j位置的元素(不应该出现在i+1~j这个区间)和i+1位置(交换之后仍在i+1~j区间)的元素交换位置,同时将i+1.这样就维持了循环不变量(见上述循环不变量说明)。直到j=n,完成最后一次循环操作。
要注意的是在完成循环后,还需要将i位置的元素和数组首元素交换以满足我们最先设定的要求(对应图中的第i步)。细心的读者可能会想到另一种更直白的分区方法,即将基元取出存在另一相同大小数组中,遇到比基元小的元素就存储在数组左半部分,遇到比基元大的元素就存储在数组右半部分。这样的操作复杂度也是线性的,即Theta(n)。但是空间复杂度提高了一倍。这也是快排就地排序的优势所在。
123456789101112131415161718192021222324252627282930313233343536public
class
QuickSort {
private
static
void
QuickSort(
int
[] array,
int
start,
int
end)
{
if
(start<end)
{
int
key=array[start];
//初始化保存基元
int
i=start,j;
//初始化i,j
for
(j=start+1;j<=end;j++)
if
(array[j]<key)
//如果此处元素小于基元,则把此元素和i+1处元素交换,并将i加1,如大于或等于基元则继续循环
{
int
temp=array[j];
array[j]=array[i+1];
array[i+1]=temp;
i++;
}
}
array[start]=array[i];
//交换i处元素和基元
array[i]=key;
QuickSort(array, start, i-1);
//递归调用
QuickSort(array, i+1, end);
}
}
public
static
void
main(String[] args)
{
int
[] array=
new
int
[]{11,213,134,44,77,78,23,43};
QuickSort(array, 0, array.length-1);
for
(
int
i=0;i<array.length;i++)
{
System.
out
.println((i+1)+
"th:"
+array[i]);
}
}
Java
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126classQuick
{
publicvoidsort(intarr[],intlow,inthigh)
{
intl=low;
inth=high;
intpovit=arr[low];
while
(l<h)
{
while
(l<h&&arr[h]>=povit)h--;
if
(l<h){
inttemp=arr[h];
arr[h]=arr[l];
arr[l]=temp;
l++;
}
while
(l<h&&arr[l]<=povit)l++;
if
(l<h){
inttemp=arr[h];
arr[h]=arr[l];
arr[l]=temp;
h--;
}
}
print(arr);
System.out.print(
"l="
+(l+
1
)+
"h="
+(h+
1
)+
"povit="
+povit+
"\n"
);
if
(l>low)sort(arr,low,h-
1
);
if
(h<high)sort(arr,l+
1
,high);
}
}
/*//////////////////////////方式二////////////////////////////////*/
更效率点的代码:
public
<TextendsComparable<?superT>>
T[]quickSort(T[]targetArr,intstart,intend)
{
inti=start+
1
,j=end;
Tkey=targetArr[start];
SortUtil<T>sUtil=newSortUtil<T>();
if
(start>=end)
return
(targetArr);
/*从i++和j--两个方向搜索不满足条件的值并交换
*
*条件为:i++方向小于key,j--方向大于key
*/
while
(
true
)
{
while
(targetArr[j].compareTo(key)>
0
)j--;
while
(targetArr[i].compareTo(key)<
0
&&i<j)i++;
if
(i>=j)
break
;
sUtil.swap(targetArr,i,j);
if
(targetArr[i]==key)
{
j--;
}
else
{
i++;
}
}
/*关键数据放到‘中间’*/
sUtil.swap(targetArr,start,j);
if
(start<i-
1
)
{
this
.quickSort(targetArr,start,i-
1
);
}
if
(j+
1
<end)
{
this
.quickSort(targetArr,j+
1
,end);
}
returntargetArr;
}
/*//////////////方式三:减少交换次数,提高效率/////////////////////*/
private
<TextendsComparable<?superT>>
voidquickSort(T[]targetArr,intstart,intend)
{
inti=start,j=end;
Tkey=targetArr[start];
while
(i<j)
{
/*按j--方向遍历目标数组,直到比key小的值为止*/
while
(j>i&&targetArr[j].compareTo(key)>=
0
)
{
j--;
}
if
(i<j)
{
/*targetArr[i]已经保存在key中,可将后面的数填入*/
targetArr[i]=targetArr[j];
}
/*按i++方向遍历目标数组,直到比key大的值为止*/
while
(i<j&&targetArr[i].compareTo(key)<=
0
)
/*此处一定要小于等于零,假设数组之内有一亿个1,0交替出现的话,而key的值又恰巧是1的话,那么这个小于等于的作用就会使下面的if语句少执行一亿次。*/
{
i++;
}
if
(i<j)
{
/*targetArr[j]已保存在targetArr[i]中,可将前面的值填入*/
targetArr[j]=targetArr[i];
}
}
/*此时i==j*/
targetArr[i]=key;
if
(i-start>
1
)
{
/*递归调用,把key前面的完成排序*/
this
.quickSort(targetArr,start,i-
1
);
}
if
(end-j>
1
)
{
/*递归调用,把key后面的完成排序*/
this
.quickSort(targetArr,j+
1
,end);
}
}
- 排序-快速排序-Java
- java排序之快速排序
- Java排序算法 快速排序
- 快速排序(java排序)
- java 快速排序,冒泡排序
- 算法:排序----Java快速排序
- 【交换排序】快速排序--Java
- java 插入排序+快速排序
- Java 排序之 快速排序
- java排序之快速排序
- Java排序算法:快速排序
- Java排序-快速排序
- Java排序算法:快速排序
- Java排序算法--》快速排序
- JAVA排序算法---快速排序
- Java 快速排序 归并排序
- Java排序算法--快速排序
- java 冒泡排序、快速排序
- 20060613-Spatial transformations: findbounds
- 水的起源与作用
- Android之TextView属性列表
- 获取网页内容
- spring的优点 ioc aop
- java 快速排序
- ExtJS4学习笔记四--图片上传
- 水是什么时候形成的
- mysql 游标的使用总结
- android虚拟机启动失败
- tomcat 单独启动问题(一闪而过)
- jeecg分析意见
- 多线程系列
- 编译vs2008的samples程序总是跳过