1 交换排序
a 冒泡排序-----稳定
标准的冒泡排序:
ints是一个整型数组
for(int i=0;i<ints.length-1;i++){//比较的轮数,n个数需要比较n-1轮,每一轮冒出一个"泡"
for(int j=0;j<ints.length-1-i;j++){//每一轮在最后一个位置产生一个最大或最小的数
if(ints[j]<ints[j+1]){//如果符合条件,两相邻的数交换位置
int temp;
temp=ints[j+1];
ints[j+1]=ints[j];
ints[j]=temp;
}
}
}
这个算法不好,试想当原来的数已经排好序时,仍然需要比较n-1轮!
改进的冒泡排序:
Boolean swapped;//用于标记是否交换过
do{
swapped=false;
for(int i=0;i<ints.length-1;i++){
if(ints<ints){
int temp;
temp=ints;
ints=ints;
ints=temp;
swapped=true;
}
}
}while(swapped);
因此,改进的冒泡排序最好情况的时间复杂度是O(1).最坏情况需要比较的次数为:1+2+3+...+ n-1=n(n-1)/2,需要移动的次数为:比较次数*3.最坏情况的时间复杂度为O(n的平方).由于两个元素相等就不需要交换,因此此算法是一种稳定的排序算法.不管有多少数,交换时只需temp一个临时变量空间,因此它的空间复杂度为O(1).
b 快速排序----不稳定
快速排序的基本思想
设当前待排序的无序区为R[low..high],利用分治法可将快速排序的基本思想描述为:
①分解:
在 R[low..high]中任选一个记录作为基准(Pivot),以此基准将当前无序区划分为左、右两个较小的子区间R[low..pivotpos- 1)和R[pivotpos+1..high],并使左边子区间中所有记录的关键字均小于等于基准记录(不妨记为pivot)的关键字 pivot.key,右边的子区间中所有记录的关键字均大于等于pivot.key,而基准记录pivot则位于正确的位置(pivotpos)上,它无 须参加后续的排序。
注意:
划分的关键是要求出基准记录所在的位置pivotpos。划分的结果可以简单地表示为(注意pivot=R[pivotpos]):
R[low..pivotpos-1].keys≤R[pivotpos].key≤R[pivotpos+1..high].keys
其中low≤pivotpos≤high。
②求解:
通过递归调用快速排序对左、右子区间R[low..pivotpos-1]和R[pivotpos+1..high]快速排序。
③组合:
因为当"求解"步骤中的两个递归调用结束时,其左、右两个子区间已有序。对快速排序而言,"组合"步骤无须做什么,可看作是空操作。
public static void quickSort(int[] a,int low,int high){
int i,j;
int temp;//临时存放参考数
i=low;j=high;
temp=a[low];
while(i<j){
while(i<j&&a[j]>=temp){//在数组的右端扫描,执行完会j会指向比参考数temp小的数
--j;
}
if(i<j){
a=a[j];//把这个小的数移到参考数的位置
++i;//i向后移一位
}
while(i<j&&a[i]<temp){//在数组的左端扫描,执行完会i会指向比参考数temp大的数
++i;
}
if(i<j){
a[j]=a[i];//把这个大的数移到参考数的位置
--j;//j向前移一位
}
}
a[i]=temp;//一轮结束,把参考数移到它该在的位置
if(low<i) quickSort(a,low,i-1);//对左端子集合递归
if(i<high) quickSort(a,j+1,high);//对右端子集合递归
}
快速排序算法是冒泡算法的一个变体.冒泡排序每一轮冒出的那个泡总是在子集的最后一个位置,它把原集合分成一个单分支二叉树,在最坏的情况下,快速排序也是这种情况.但最好的情况下就把原集合分成一个二叉树,它的深度为Ln(n),而轮数是不变的n-1轮(同冒泡),所以时间复杂度变为nLn(n).由于递归需要堆栈空间临时保存调用参数,递归的次数为树的深度,所以空间复杂度为O(Ln(n)),最坏情况下为O(n).两个相邻大小一样的数,后面的有可能跳到前面去,因此此算法不稳定.