快速排序和归并排序



1,快速排序

快速排序通过分割值列,然后递归的对两个部分进行排序,从而实现对值列的排序。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序

首先任意选取一个数据(通常选用第一个数据)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序


怎么实现呢?---

它的关键在于完成一趟快排后,基准元素在哪个位置,每次都选取一个分割列的第一个元素作为基准元素,来寻找用它来分割排序列后它自己所处的位置,编写一个

int  findPartition(data,min,max)方法,用于使用data[min]作为基准元素把data[min]到data[max]分割为两个部分,并返回分割以后基准元素自己所在的位置索引。


在主函数里这样来调用:

 
  
public static void quickSort(Comparable[] data, int min, int max) {

int mid;
if (min < max)
{
mid
= findPartition(data,min,max);
quickSort(data,min,mid
- 1 );
quickSort(data,mid
+ 1 ,max);
}

}



理解递归过程:::if(min < max),实际上就是要将值列分割为单一的元素(递归的最深一层),在得到基准元素的位置min后,基准元素在数组中的位置就最终确定,剩下只对左右两侧的分割列
排序:quickSort(data,min,mid-1); quickSort(data,mid+1,max);

下面来看最重要的实现部分,如何实现findPartition函数:

将第一个元素作为基准元素不要动,设两个指针left和right,left从左往右移动寻找比基准元素大的数,right从右往左移动寻找比基准元素小的数,当它们都找到对应的left和right的位置时,交换
这两个元素。重复这个过程,直到right < left.这时,除基准元素外,从min+1到right的元素都比基准元素小,left到max的元素都比基准元素大。形成了这样一个序列:

基准元素(data[min])  ....比基准元素小的......   data[right]    |    data[left]  ......比基准元素大的 

交换基准元素和data[right]即可得到  以基准元素为分割线的 左右两个分割列。

这里我把  排序递归的调用  与   分割值列和寻找分割位置(核心部分) 分开写成两个方法,为了显得逻辑更加清楚。


完整代码   :

 
  
// 快排:快排是一个递归的过程!!!!
public static void quickSort(Comparable[] data, int min, int max) {

int mid;
if (min < max)
{
mid
= findPartition(data,min,max);
quickSort(data,min,mid
- 1 );
quickSort(data,mid
+ 1 ,max);
}

}

// 快排的支持方法
public static int findPartition(Comparable[] data, int min, int max){
int left,right;
Comparable temp,partitionelement;

left
= min;right = max;
partitionelement
= data[min]; // partitionelement 是data[min]的一个副本

while (left < right)
{
while (data[left].compareTo(partitionelement) <= 0 && left < right)
left
++ ;
while (data[right].compareTo(partitionelement) > 0 )
right
-- ;
if (left < right)
{
temp
= data[left];
data[left]
= data[right];
data[right]
= temp;
}
}

temp
= data[min];
data[min]
= data[right];
data[right]
= temp;

// 错错误的写法,要的是把第一个元素和data[right]交换,而不是它的副本
// temp = data[right];
// data[right] = partitionelement;
// partitionelement = temp;

return right;
}




2,归并排序


归并排序也是一个递归的实现,其关键在于合并两个有序集:

void merge(Comparable[] data,int min,int mid,int max)

数组data从min到mid有序,从mid+1到max有序,合并这两部分,使得从min到max有序,且这个有序序列仍然放在data的从min到max下标里

这样来递归调用:


 
  
// 归并排序:归并是一个递归的过程,其关键部分在于合并2个有序集
public static void mergSort(Comparable[] data, int min, int max){
int mid = (min + max) / 2 ;
// 递归的过程
if (max > min){
mergSort(data,min,mid);
mergSort(data,mid
+ 1 ,max);
merge(data,min,mid,max);
}
}



理解递归的过程:每次merg一次, 在data原来的那一段位置上把data排好序了,不影响其他位置

下面来实现关键部分,在data里合并min到mid,mid+1到max两个有序集,合并后位置仍放到min到max上, 借助于申请一个与data大小相等的空间!!!

这个地方开始没有想清楚:应该是Comparable[] temp = new Comparable[max+1];

而不是Comparable[] temp = new Comparable[max - min + 1];想一想为什么?

每递归一次就会调用一次merge,实际上temp每次都会重新分配一下,利用temp把data从min到max排好序,并放在data的这一段位置上


完整代码:

 
  
// 归并排序:归并是一个递归的过程,其关键部分在于合并2个有序集
public static void mergSort(Comparable[] data, int min, int max){
int mid = (min + max) / 2 ;
// 递归的过程
if (max > min){
mergSort(data,min,mid);
mergSort(data,mid
+ 1 ,max);
merge(data,min,mid,max);
}
}

// 支持方法:合并2个有序集,借助于申请一个与两个有序集大小之和相等的空间。
public static void merge(Comparable[] data, int min, int mid, int max){
Comparable[] temp
= new Comparable[max + 1 ];
// Comparable[] temp = new Comparable[max - min + 1];错误!!!
int begin1 = min;
int begin2 = mid + 1 ;
int index = min; // 注意index是从min开始的!!!

while (begin1 <= mid && begin2 <= max){
if (data[begin1].compareTo(data[begin2]) < 0 )
temp[index
++ ] = data[begin1 ++ ];
else
temp[index
++ ] = data[begin2 ++ ];
}

if (begin1 > mid)
for ( int i = begin2;i <= max;i ++ )
temp[index
++ ] = data[i];
if (begin2 > max)
for ( int i = begin1;i <= mid;i ++ )
temp[index
++ ] = data[i];

for ( int i = min;i <= max;i ++ ) // 对应的位置!!!!
data[i] = temp[i];
}

}


这个程序调试了半天才好,主要还是没把递归的过程想清楚,data在每次递归的过程中不能被覆盖,它排好了序的部分就不能动了!!!

另外就是temp申请空间这个地方,是max+1,而不是max-min+1的大小,否则会导致数组越界

刚才代码效果乱了,悲剧 ,又重新复制了一遍,耽误了半天时间。


好了,有空再写个性能的比较总结。






转载于:https://www.cnblogs.com/jmzz/archive/2011/04/29/2032363.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值