写在半年后:
回头再看自己总结的这个排序实在是太挫了,可以用惨不忍睹来形容。于是就想重新整理一下。然而在整理思路的时候,发现了这么一篇博客:(点击可以打开)
我最多也就写成这个样。。不转载了。。
本文下面就常用的 冒泡排序、快速排序、简单选择排序、直接插入排序给出代码示例。
排序大的分类可以分为两种:内排序和外排序。在排序过程中,全部记录存放在内存,则称为内排序,如果排序过程中需要使用外存,则称为外排序。下面讲的排序都是属于内排序。
内排序有可以分为以下几类:
(1)、插入排序:直接插入排序、二分法插入排序、希尔排序。
(2)、选择排序:简单选择排序、堆排序。
(3)、交换排序:冒泡排序、快速排序。
(4)、归并排序
(5)、基数排序
关于各种排序算法的稳定性:
选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,
冒泡排序、插入排序、归并排序和基数排序都是稳定的排序算法。
各排序法的时间复杂度:(此表转载)
排序法 | 平均时间 | 最差情形 | 稳定度 | 额外空间 | 备注 |
冒泡 | O(n2) | O(n2) | 稳定 | O(1) | n小时较好 |
交换 | O(n2) | O(n2) | 不稳定 | O(1) | n小时较好 |
选择 | O(n2) | O(n2) | 不稳定 | O(1) | n小时较好 |
插入 | O(n2) | O(n2) | 稳定 | O(1) | 大部分已排序时较好 |
基数 | O(logRB) | O(logRB) | 稳定 | O(n) | B是真数(0-9), R是基数(个十百) |
Shell | O(nlogn) O(n^1.25) ??? | O(ns) 1<s<2 | 不稳定 | O(1) | s是所选分组 |
快速 | O(nlogn) | O(n2) | 不稳定 | O(nlogn) | n大时较好 |
归并 | O(nlogn) | O(nlogn) | 稳定 | O(1) | n大时较好 |
堆 | O(nlogn) | O(nlogn) | 不稳定 | O(1) | n大时较好 |
例:
冒泡排序法:
import java.util.ArrayList;
import java.util.Random;
public class AllSort {
public static void main(String []args){
ArrayList<Integer> a=new ArrayList<Integer>();
Bubble bubble=new Bubble();
bubble.sort(a);
//Select select=new Select();
//select.sort(a);
for(int l=1;l<=a.size();l++){
System.out.print(a.get(l-1)+" ");
if(l%10==0){
System.out.println();
}
}
}
}
class Bubble{
public void sort(ArrayList<Integer> ar){
Random rd=new Random(100);
for(int i=0;i<100;i++){
ar.add(rd.nextInt(1000));
}
//System.out.println(a.get(0));
for(int j=0;j<ar.size()-1;j++){
for(int k=0;k<ar.size()-j-1;k++){
//将最小的数冒泡到最后
if(ar.get(k+1)>ar.get(k)){
int temp=ar.get(k+1);
ar.set(k+1, ar.get(k));
ar.set(k, temp);
}
}
}
}
}
快速排序法:
package problemSet0326;
public class QuickSort {
public static void main(String []args){
int a[]=new int[100];
for(int i=0;i<100;i++){
a[i]=(int)(Math.random()*1000);
}
Quick quick=new Quick();
quick.sort(a,0,a.length-1);
for(int l=1;l<=100;l++){
System.out.print(a[l-1]+" ");
if(l%10==0){
System.out.println();
}
}
}
}
class Quick{
public int partition(int arr[],int l, int r){
int i=l,j=r,key=arr[i];
while(i<j){
while(i<j&&key<=arr[j])j--;
if(i<j){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
i++;
}
while(i<j&&arr[i]<=arr[j])i++;
if(i<j){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
j--;
}
}
return i;
}
public void sort(int[]a,int l,int r){
if(l<r){
int key=partition(a, l, r);
sort(a,l,key-1);
sort(a,key+1,r);
}
}
}
选择排序法分为选择排序法和堆排序法。
选择排序法:
class Select{
public void sort(ArrayList<Integer> ar){
Random rd=new Random(47);
for(int i=0;i<100;i++){
ar.add(rd.nextInt(1000));
}
for(int j=0;j<ar.size()-1;j++){
int minIndex=j;
for(int k=j+1;k<ar.size();k++){
if(ar.get(k)<ar.get(minIndex)){
minIndex=k;
}
}
int temp=ar.get(j);
ar.set(j, ar.get(minIndex));
ar.set(minIndex, temp);
}
}
}
插入排序法分为插入排序法,谢耳排序法,二叉树排序法。
插入排序法:
class Insert{
public void sort(ArrayList<Integer> ar){
//Random rd=new Random(100);
for(int i=0;i<100;i++){
ar.add((int)(Math.random()*1000));
}
for(int j=1;j<ar.size();j++){
int insertVal=ar.get(j);
int index=j-1;
while(index>=0&&ar.get(index)>insertVal){
//将index处的值向后移动
ar.set(index+1, ar.get(index));
index--;
}
//将insertVal插入到适当位置(如果有变动即向前变动)
ar.set(index+1, insertVal);
}
}
}
插入排序的另一种写法:
//插入排序
class InsertSort {
public void sort(int[] a){
for(int i=1;i<a.length;i++){
int temp=a[i];
int j;
for(j=i-1;j>=0;j--){
if(a[j]>temp){
a[j+1]=a[j];
}else{
break;
}
}
a[j+1]=temp;
}
}
}
虽然只有两层循环,但是其中的思路还是略有点复杂的。
1.temp这个变量不能少,也就是说不能直接把a[i]拿去和a[j]比较,因为a[i]的值在内层循环的过程中是变化不固定的,所以要有个临时变量来存储a[i]处的初始值,即要寻找插入位置元素的值。
2.int j需要再外层循环中声明,因为每次i加一,即为将a[i]插入到此次循环应当的位置,最后一步应为a[j+1]的值temp插入到应当位置。
3.外层循环从1开始,因为插入排序的思想是每次外层循环均默认左侧的值已排好序。那么一个元素自然是排好序的无需再排。
排序算法的选择
1.数据规模较小
(1)待排序列基本序的情况下,可以选择直接插入排序;
(2)对稳定性不作要求宜用简单选择排序,对稳定性有要求宜用插入或冒泡
2.数据规模不是很大
(1)完全可以用内存空间,序列杂乱无序,对稳定性没有要求,快速排序,此时要付出log(N)的额外空间。
(2)序列本身可能有序,对稳定性有要求,空间允许下,宜用归并排序
3.数据规模很大
(1)对稳定性有求,则可考虑归并排序。
(2)对稳定性没要求,宜用堆排序
4.序列初始基本有序(正序),宜用直接插入,冒泡