1.插入排序
思想:假定x[0…0]是有序的,然后加入x[1],x[2],….,x[n];只要保证每次将待加入x[i]放入正确的位置即可。
方法一:从x[i]开始往前,两两比较,若是前者比后者大,则交换,否则终止
方法二:将x[i]暂存到temp里,两两比较,若是前者比后者大,则用前者覆盖后者,否则跳出循环,然后将temp赋给当前位置
插入排序总结,时间复杂度为O(n^2),如果数组本身就是接近有序的,会快很多
package chapter11;
import java.util.Arrays;
public class InsertionSort {
public static int[] swap(int a,int b){
int temp=a;
a=b;
b=temp;
return new int[]{a,b};//由于Java里不支持指针,所以此处才写的如此麻烦。。。
}
public static void insertionSort0(int arr[]){
for(int i=1;i<arr.length;i++){//比较若前面的比当前的大,两个交换
for(int j=i;j>0&&arr[j]<arr[j-1];j--){
int re[]=swap(arr[j], arr[j-1]);
arr[j]=re[0];
arr[j-1]=re[1];
}
}
}
public static void insertionSort1(int arr[]){
for(int i=1;i<arr.length;i++){
for(int j=i;j>0&&arr[j]<arr[j-1];j--){
int temp=arr[j];
arr[j]=arr[j-1];
arr[j-1]=temp;
}
}
}
public static void insertionSort2(int arr[]){
//比较,若是前面的比当前的大,则用它覆盖后一个数;全部后移后,然后将值直接覆盖指定位置
for(int i=1;i<arr.length;i++){
int temp=arr[i];
int j=i;
for( ;j>0&&temp<arr[j-1];j--){
arr[j]=arr[j-1];
}
arr[j]=temp;
}
}
public static void main(String args[]){
long current=System.currentTimeMillis();
boolean firstIn=true;
// for(int i=0;i<1000000000;i++){
// int arr[]=new int[]{8,7,6,5,4,3,2,1};
// insertionSort0(arr);
// if(firstIn){
// System.out.println(Arrays.toString(arr));
// firstIn=false;
// }
// }
// System.out.println((System.currentTimeMillis()-current)/1000);
// current=System.currentTimeMillis();
// firstIn=true;
for(int i=0;i<1000000000;i++){
int arr[]=new int[]{8,7,6,5,4,3,2,1};
insertionSort1(arr);
if(firstIn){
System.out.println(Arrays.toString(arr));
firstIn=false;
}
}
System.out.println((System.currentTimeMillis()-current)/1000);
current=System.currentTimeMillis();
firstIn=true;
for(int i=0;i<1000000000;i++){
int arr[]=new int[]{8,7,6,5,4,3,2,1};
insertionSort2(arr);
if(firstIn){
System.out.println(Arrays.toString(arr));
firstIn=false;
}
}
System.out.println((System.currentTimeMillis()-current)/1000);
}
}
在我的机器上运行的结果
[1, 2, 3, 4, 5, 6, 7, 8]
84
[1, 2, 3, 4, 5, 6, 7, 8]
66
可见还是有区别的,思路二更有效率,思路一中交换需要三步,而思路而只需要一步,因而更快。
2.快速排序
思路:将大的数组划分成两个小的部分,然后对他们进行递归排序。主题都是使用分治法,主要是划分方法的不同,围绕第一个元素进行划分,将所有小于等于它的数移到左边,所有大于它的数移到右边。
方法一:单向划分,从左向右。
分界值boundaryValue选为最左边的元素,分解下标splitIndex初始化为left,然后进行一个循环,数组下标arrIndex从left+1到right,如果出现数组下标值小于分界值boundaryValue,将分界下标splitIndex加一,然后交换splitIndex和arrIndex对应的值;循环结束后交换splitIndex和left的值。
方法二:单向划分,从右向左。
思想与第一个差不多,将划分元素arr【left】也包括在内了
方法三:双向划分
目的:为了避免所有元素相同情况下,上述的单向划分退化成为O(n^2)
思路:主循环中有两个内循环,内循环一 lIndex从数组的左边寻找大于等于arr[left]的,找到则停止加一操作;内循环二rIndex从数组的右端寻找小于等于arr【left】的,找到则停止减一操作;然后在主循环中判断lIndex与rIndex是否交叉并决定退出主循环还是交换值。最终交换arr[left]与arr[rIndex]
方法四:双向划分加插入排序
因为当循环进行到一定程度时,待排序的部分很小,就没有必要花费很长时间进行双向排序了,直接返回就好。最后的时候对整个数组进行一下插入排序就解决了。
以下是四种方法的源程序
package chapter11;
import java.util.Arrays;
import java.util.Random;
public class QuickSort {
//单向划分,从左向右
public static void quickSort1(int arr[],int left,int right){
if(left>=right)
return;
int boundaryValue=arr[left] ;//分界值
int splitIndex=left;//记录分界下标的变化,《=splitIndex的都是小于分界值boundaryValue ,>splitsplitIndex的元素都是大于等于分界值boundaryValue
int temp=0;
for(int i=left+1;i<=right;i++){
//循环不变式 arr[left+1...splitIndex]<boundaryValue arr[splitIndex+1....right]>=boundaryValue
if(arr[i]<boundaryValue ){
splitIndex++;
temp=arr[splitIndex];
arr[splitIndex]=arr[i];
arr[i]=temp;
}
}
arr[left]=arr[splitIndex];//将分界元素放到对象的分界下标上,交换即可
arr[splitIndex]=boundaryValue ;
quickSort1(arr, left, splitIndex-1);
quickSort1(arr, splitIndex+1, right);
}
//单向划分,从右向左,其实就省了最后的交换划分元素过程
public static void quickSort2(int arr[],int left,int right){
if(left>=right)
return;
int splitIndex=right+1;
for(int arrIndex=right;arrIndex>=left;arrIndex--){
if(arr[arrIndex]>=arr[left]){
splitIndex--;
int temp=arr[splitIndex];
arr[splitIndex]=arr[arrIndex];
arr[arrIndex]=temp;
}
}
quickSort2(arr, left, splitIndex-1);
quickSort2(arr, splitIndex+1, right);
}
//双向划分,lIndex++,直到从左边找到大于等于分界值得元素或者rIndex大于rIndex;
//rIndex--,直到从右边找到的元素小于等于分界值;
//然后判断lIndex,rIndex的大小,若是
public static void quickSort3(int arr[],int left,int right){
// System.out.println("boundaryValue="+arr[left]+ " left="+left+" right="+right);
if(left>=right){
return;
}
int temp=0;
int boundaryValue =arr[left];
int lIndex=left;
int rIndex=right+1;
while(true){
do lIndex++ ; while(lIndex<=rIndex&&arr[lIndex]<boundaryValue);
do rIndex-- ; while(arr[rIndex]>boundaryValue);
if(lIndex>rIndex){
break;
}
temp=arr[lIndex];
arr[lIndex]=arr[rIndex];
arr[rIndex]=temp;
}
arr[left] =arr[rIndex];
arr[rIndex]=boundaryValue ;
// System.out.println("=>"+Arrays .toString(arr));
quickSort3(arr, left, rIndex-1);
quickSort3(arr, rIndex+1, right);
}
public static int generateRandom(int left,int right){
Random random=new Random();
return random.nextInt(right-left+1)+left;
}
public static void insertionSort2(int arr[]){
//比较,若是前面的比当前的大,则用它覆盖后一个数;全部后移后,然后将值直接覆盖指定位置
for(int i=1;i<arr.length;i++){
int temp=arr[i];
int j=i;
for( ;j>0&&temp<arr[j-1];j--){
arr[j]=arr[j-1];
}
arr[j]=temp;
}
}
//双向划分加插入排序
public static void quickSort4(int arr[],int left,int right,int cutOff){
// System.out.println("boundaryValue="+arr[left]+ " left="+left+" right="+right);
if(right-left<cutOff){
return;
}
int temp=0;
int result=generateRandom(left, right);//交换到最左端
// System.out.println("reslut="+result);
temp=arr[left];
arr[left]=arr[result];
arr[result]=temp;
int boundaryValue =arr[left];
int lIndex=left;
int rIndex=right+1;
while(true){
do lIndex++ ; while(lIndex<=rIndex&&arr[lIndex]<boundaryValue);
do rIndex-- ; while(arr[rIndex]>boundaryValue);
if(lIndex>rIndex){
break;
}
temp=arr[lIndex];
arr[lIndex]=arr[rIndex];
arr[rIndex]=temp;
}
arr[left] =arr[rIndex];
arr[rIndex]=boundaryValue ;
// System.out.println("=>"+Arrays .toString(arr));
quickSort4(arr, left, rIndex-1,cutOff);
quickSort4(arr, rIndex+1,right,cutOff);
}
public static void quickSort4( int arr[], int cutOff){
quickSort4(arr, 0,arr.length-1, cutOff);
insertionSort2(arr);
}
public static void main(String args[]){
int arr[]=new int[]{10,13,1,11, 12,8,14,3,4, 15,4,5,12};
// int arr[]={2,2,2,2,2, 2,2,2,2,2};
// quickSort1(arr, 0 ,arr.length-1);
// quickSort3(arr, 0 ,arr.length-1);
quickSort4(arr, 3);
// quickSort2(arr, 0 ,arr.length-1);
System.out.println(Arrays.toString(arr));
}
}
课后习题
2.在上面的方法二已经写了
3.取不同的cutOff值对一个较大的数组进行排序,选择运行时间少的
4.看了后面答案的,但是感觉它的想x[i].bit[depth]会产生数组下标越界,所以在原来的基础上稍微改了一下。思路就是先看字符串长度是否满足本次深度的要求,若是不满足则跳过不予考虑:若是满足了,将当前深度为0的放到前面,为1的放到后面。
package chapter11;
import java.util.Arrays;
public class BitStringSort {
public static void bitStringSort(String arr[],int left,int right,int depth){
if(left>=right){
return;
}
int splitIndex1=left;
for(int arrIndex=left;arrIndex<=right;arrIndex++){
if(arr[arrIndex].length()<depth){//根据bitString长度划分
String temp=arr[arrIndex];
arr[arrIndex]=arr[splitIndex1];
arr[splitIndex1]=temp;
splitIndex1++;
}
}
int splitIndex2=splitIndex1;
for(int arrIndex=splitIndex2;arrIndex<=right&&splitIndex2<=right;arrIndex++){
try {
if(arr[arrIndex].charAt(depth)=='0'){
String temp=arr[arrIndex];
arr[arrIndex]=arr[splitIndex2];
arr[splitIndex2]=temp;
splitIndex2++;
}
} catch (Exception e) {
String temp=arr[arrIndex];
arr[arrIndex]=arr[splitIndex2];
arr[splitIndex2]=temp;
splitIndex2++;
}
}
bitStringSort(arr, splitIndex1, splitIndex2-1, depth+1);
bitStringSort(arr, splitIndex2, right, depth+1);
}
public static void main(String args[]){
String arr[]=new String[10];
String temp="";
for(int i=arr.length-1;i>=1;i-=2 ){
arr[i]=temp+"0";
arr[i-1]=temp+"1";
temp+="1";
}
System.out.println(Arrays.toString(arr));
bitStringSort(arr, 0, arr.length-1, 0);
System.out.println(Arrays.toString(arr));
}
}
运行结果
[11111, 11110, 1111, 1110, 111, 110, 11, 10, 1, 0]
[0, 1, 10, 11, 110, 111, 1110, 1111, 11110, 11111]
6.选择排序
选择排序将数组中所有最小的放在想x[0],然后从1…n中剩下的选择最小的放在x[1],以此类推
public static void chooseSort(int arr[]){
int minIndex;
for(int outerIndex=0;outerIndex<arr.length-1;outerIndex++){
minIndex=outerIndex;
for(int innerIndex=outerIndex+1;innerIndex<arr.length;innerIndex++){
if(arr[innerIndex]<arr[minIndex]){
minIndex=innerIndex;
}
}
int temp=arr[outerIndex];
arr[outerIndex]=arr[minIndex];
arr[minIndex]=temp;
}
}
希尔排序(递减增量排序)请见
http://blog.csdn.net/morewindows/article/details/6668714
9.如何在O(n)时间内找到数组中第k个最小元素的问题。
思路:利用双向划分+插入排序的快速排序,也就是上面的方法四。在此基础上进行改写,只划分我们需要的部分,即arr[k]所在的部分;里面的generateRandom函数与上面的相同
public static void quickSort5(int arr[],int left,int right,int cutOff,int k){
// System.out.println("boundaryValue="+arr[left]+ " left="+left+" right="+right);
if(right-left<cutOff){
return;
}
int temp=0;
int result=generateRandom(left, right);//交换到最左端
//System.out.println("reslut="+result);
temp=arr[left];
arr[left]=arr[result];
arr[result]=temp;
int boundaryValue =arr[left];
int lIndex=left;
int rIndex=right+1;
while(true){
do lIndex++ ; while(lIndex<=rIndex&&arr[lIndex]<boundaryValue);
do rIndex-- ; while(arr[rIndex]>boundaryValue);
if(lIndex>rIndex){
break;
}
temp=arr[lIndex];
arr[lIndex]=arr[rIndex];
arr[rIndex]=temp;
}
arr[left] =arr[rIndex];
arr[rIndex]=boundaryValue ;
//System.out.println("=>"+Arrays .toString(arr));
if(k>rIndex) //只继续划分k所在的部分
quickSort5(arr, rIndex+1,right,cutOff,k);
if(k<rIndex){
quickSort5(arr, left, rIndex-1,cutOff,k);
}
}
public static void quickSort5( int arr[], int cutOff,int k){
quickSort5(arr, 0,arr.length-1, cutOff,k);
insertionSort2(arr);
}
public static void main(String args[]){
int arr[]=new int[1000];
for(int i=0;i<arr.length;i++ ){
arr[i]=1000-i;
}
quickSort5(arr, 40, 51);
System.out.println(Arrays.toString(arr)+"\n=>arr[51]="+arr[51]);
}
11.
在单向划分的基础上更改,令t=arr[left],用splitLeft记录<t的最后一个下标,用splitRight记录==t的最后一个下标。
情况二:当未划分部分arr[arrIndex]<t时
情况二:当未划分部分arr[arrIndex]==t时
循环结束后最后交换一下arr[left]与arr[splitLeft]的值
下面是程序
package chapter11;
import java.util.Arrays;
public class t11 {
//单向划分的改写
public static void quickSort_equalInCenter(int arr[],int left,int right){
if(left>=right){
return;
}
int splitLeft=left;
int splitRight=left;
for(int arrIndex=left+1;arrIndex<=right;arrIndex++){
if(arr[arrIndex]<arr[left]){
int temp=arr[splitRight+1];
for(int i=splitLeft+1;i<=splitRight;i++){
arr[i+1]=arr[i];
}
splitLeft++;
splitRight++;
arr[splitLeft]=arr[arrIndex];
arr[arrIndex]=temp;
}
if(arr[arrIndex]==arr[left]){
int temp=arr[splitRight+1];
splitRight++;
arr[splitRight]=arr[arrIndex];
arr[arrIndex]=temp;
}
}
if(splitLeft>left){
int temp=arr[splitLeft];
arr[splitLeft]=arr[left];
arr[left]=temp;
}
System.out.println(Arrays.toString(arr));
System.out.println("splitLeft="+splitLeft+" splitRight="+splitRight);
quickSort_equalInCenter(arr, left, splitLeft-1);
quickSort_equalInCenter(arr, splitRight+1, right);
}
public static void main(String args[]){
int arr[]={2,1,4,2,5 ,2,4,3,5,3, 2,1};
quickSort_equalInCenter(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
}
运行结果
[1, 1, 2, 2, 2, 2, 4, 3, 5, 3, 5, 4]
splitLeft=2 splitRight=5
[1, 1, 2, 2, 2, 2, 4, 3, 5, 3, 5, 4]
splitLeft=0 splitRight=1
[1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 5]
splitLeft=8 splitRight=9
[1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 5]
splitLeft=6 splitRight=7
[1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 5]
splitLeft=10 splitRight=11
[1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 5]
总结
1.对于算法的描述,画图有时比描述更易懂。
2.理解算法之后,要尽量用几个关键字将算法的主要思路说出来
3.排序算法真的很重要,所以还是多看几遍吧(微笑)
本文详细介绍了几种常见的排序算法,包括插入排序、快速排序等,并通过代码示例展示了不同方法的实现细节及效率对比。

4426

被折叠的 条评论
为什么被折叠?



