写出正确的代码是技术,写出优美的代码时艺术。
前两天在看排序算法,其中看到归并排序的时候,发现一个编码的技巧。
我先复习一下,归并排序先吧。
分析: 归并排序采用分治的思想。将一个大问题分解若干个子问题,子问题与 原问题,解决的方式是一样的,并且子问题是独立的即子问题之间没有关系,最小的子问题是有解的并且好解。这里是不是很想我们学过的递归。
分治是一种解题思路,递归是一种编码技巧。
这里具体时这样实现的,将未排序的数据分为有序的两部分,然后对这两部分进行排序。而对于被分为两部分的数据,再各自当作原问题,按照刚刚的思路,分为两部分有序的数据,直到数据大小唯一的时候。
即:将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。
Take is cheap .Show me the code .
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
int n = 100;
int [] a = new int[n];
for(int i = 0;i<n;i++){
a[i] = (int)(Math.random()*n);
}
System.out.println(Arrays.toString(a));
mergeSort(a,0,a.length-1);
System.out.println(Arrays.toString(a));
}
public static void mergeSort(int[] A,int p ,int r){
/*归并排序是建立在归并操作上的一种有效的排序算法。
该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
将已有序的子序列合并,得到完全有序的序列;
即先使每个子序列有序,再使子序列段间有序
算法描述:
把长度为n的输入序列分成两个长度为n/2的子序列;
对这两个子序列分别采用归并排序;
将两个排序好的子序列合并成一个最终的排序序列。
* */
if(p >= r)
return ;
//防止 (r+p)溢出
int q = p + (r - p)/2;
mergeSort(A,p,q);
mergeSort(A,q+1,r);
merge(A,p,q,r);
}
private static void merge(int[] A, int p, int q, int r) {
int i = p;
int j = q+1;
int []temp = new int[r-p+1];
int index = 0;
while(i <=q && j <=r){
// 需要 <= 等于号保证 稳定性
if(A[i] <= A[j]){
temp[index++] = A[i];
i++;
}else{
temp[index++] = A[j];
j++;
}
}
//判断那边有剩余 只有一边有剩余 或者 都没有, 先假定是 第一部分 有剩余
int start = i;
int end = q;
// 看是不是 第二部分 有剩余
if(j <= r){
start = j;
end = r;
}
//剩余部分拷贝
while(start <= end){
temp[index++] = A[start++];
}
//将临时的数组拷贝到 A的(p,r)中
for( i = 0;i<r-p+1;i++){
A[p+i] = temp[i];
}
}
}
代码其中有一部分
// 取p到r之间的中间位置q,防止(p+r)的和超过int类型最大值
int q = p + (r - p)/2;
看到时候我发现以前自己没有想到过这种问题。
是不是不像平时写的那样 int q = (r+p)/2;
没有考虑到给你的数据大小时合适的,但操作的时候,不自觉的放大了,可能会发生某种问题。
愿我们都能:自身完成确定性增长,面对外界的不确定性,进行“确定增长”。