常见排序__总结

文章来自:http://www.iteye.com/topic/1129454

一、概括

1.冒泡排序

(1)基本思想:在一个序列元素中,比较相邻的两个数,如果相邻的数的顺序与想要输出的顺序相反就要进行交换,到序列末尾有序列中的最大值或者最小值在数组的一端。

(2)实例:

(3)优缺点:基本思想是元素之间两两进行比较,一趟排序之后,会有较小的数排在二者比较中前一个,最大的数排在后面,(会有最小的数排在前面,较大的数排在后面)。

缺点1是:每次一趟排序之后只有一个极值能达到真正的位置,比如最小的那个值或者最大的那个值。但是其余元素却不能保证,而且元素之间已经进行了两两交换。

缺点2是:另外一个是,当序列中大部分是有序的时候,剩下的一半还是需要进行迭代比较,虽然没有进行交换,但是却进行了相应的比较;

可以通过对外层循环进行优化,比如当剩余序列都是有序的,我们在内层循环外设置标志,当剩余序列存在逆序对时,标志位1,当剩余序列有序的时候,标志位为0;在内层循环结束,也是进行一趟完整的排序结束后;对标志位进行判断,如果为0,则标识剩下的序列已经是有序的,无需再继续外层循环,直接返回结果。这样做优化了缺点2.

(4)性质:时间复杂度为O(n*n),空间复杂度为O(1),稳定性为稳定。

(5)Java代码实现

 package sorts;

public class 冒泡排序 {
    public static void main(String[] args) {
        int a[]={49,38,65,97,76,13,27,49,78,34,12,64,5,4,62,99,98,54,56,17,18,23,34,15,35,25,53,51};
        changeSort(a);
    }

    private static void changeSort(int[] a) {
        int temp;
     int k = a.length-1;
     int flag = 0;
     int pos = 0;
for(int i=0; i<a.length; i++){
        // 是否发生交换标志
        flag = 0;
for(int j=0;j<k; j++){ if(a[j]>a[j+1]){ temp = a[j]; a[j] = a[j+1]; a[j+1] = temp;
            // *发生了交换 (优化点1)
            flag = 1;
            // *下次循环的最大坐标更新,这样做优化了比较次数(优化点2)
            pos = j;
} }
      // 把更新后的最大的可比较坐标放到内层循环的判断条件中
      k = pos;
      // 如果这一轮的内层循环之后没有发生交换,说明序列已经有序无序继续进行外层循环,返回结果即可。
      if(flag == 0){
      return;
}
}
    // 打印数据排序后的结果
for(int k=0; k<a.length; k++) System.out.println(a[k]); } }

2.快速排序

http://blog.csdn.net/insistgogo/article/details/7785038

(1)基本思想:

1.选择一个哨兵(序列中的第一个元素),序列中有两个标识元素分别指向序列的头部和尾部,两标识分别向中间归拢;

2.首先从序列末尾开始找小于哨兵的元素,找到元素和低位指针所指元素进行交换;

3.然后再从左向中找大于哨兵的元素然后交换高位指针所指向的元素;

4.循环以上的步骤,直到两个指针标识碰头完成一次排序;

5.然后再递归,被分开的子序列进行同样的操作。直到子序列中只剩下少于两个元素,停止递归,完成排序。

(2)实例:

(3)优缺点

优点:当基准选取的比较合适的时候,算法的效率比较可观;

缺点:当序列基本有序,基准选取不当,算法的性能会恶化到O(n*n);

优化:(三数取中+插排+聚集相等元素+尾递归)

  循环过程优化

  1)当待排序序列的长度分割到一定大小的时候,使用插入排序(适合序列大多有序);

  2)在一次分割结束后,可以把与key相等的元素聚在一位,继续下次分割时,不用再对key相等元素进行分割(针对序列有大量重复元素的的情况)

  3)尾递归的优化,当基准不合适,分割的两个子序列就会极不平衡,递归的深度就会很大。采用当递归深度很大的时候采用 

  基准点优化

  1)采用三数取中进行优化(取首尾元素和中间元素三个数的中位数)

  2)采用随机坐标选取随机的基准

(4)性质

(5)Java代码实现

package sorts;

public class 快速排序 {
    public static void main(String[] args) {
        int a[]={49,38,65,97,76,13,27,49,78,34,12,64,5,4,62,99,98,54,56,17,18,23,34,15,35,25,53,51};
        quick(a);
        for(int i=0; i<a.length;i++){
            System.out.println(a[i]);
        }
    }
    
    private static void quick(int[] a2) {
        //查看数组是否为空
        if(a2.length>1){
            _quickSort(a2,0,a2.length-1);
        }
    }
    //递归对子序列进行排序
    private static void _quickSort(int[] list, int low, int high) {
        if(low<high){
            int middle = getMiddle(list,low,high);
        // 比较左半边 _quickSort(list,low,middle
-1);
        // 比较右半边 _quickSort(list,middle
+1,high); } }   // 获取碰撞点也就是中间点 private static int getMiddle(int[] list, int low, int high) {
      // 默认第一个值为基准点
int tmp = list[low];
      // 从传入序列末尾开始查询,找到小于基准点的元素
while(low<high){ while(low<high && list[high]>=tmp){ high--; }
        // 找到小于基准点的元素,把该元素放到基准点的当前位置 list[low]
= list[high];
        // 从传入序列开始位置向右寻找大于基准点的元素
while(low<high && list[low]<=tmp){ low++; }
        // 找到大于基准点的元素,把该元素放到基准点的位置 list[high]
=list[low]; }
      // 返回基准点元素的最后位置,该位置就是最终序列的分割点 list[low]
=tmp; return low; } }

 1.直接插入排序

(1)基本思想:把要进行排序的元素插入到有序的序列中,

1.首先从序列头部开始(第二个元素)作为待插入元素,

2.然后从后向前依次和该元素前面的序列中的元素进行比较,找到第一个小于(大于)待插入元素的位置;

3.用较大(较小)的那个元素覆盖待插入元素,然后再进行比较只要比待插入元素大的元素都要向后移动;

3.找到等于或者小于待插入元素的位置后停止该轮的内部循环;外循环整个2至n之间的元素;

(2)实例

(3)特点

(4)性质

(5)java代码实现

package sorts;public class 直接插入排序 {

public
static void main(String[] args) { int a[]={49,38,65,97,76,13,27,49,78,34,12,64,5,4,62,99,98,54,56,17,18,23,34,15,35,25,53,51}; for(int i=0;i<insertSort(a).length;i++){ System.out.println(a[i]); } } private static int[] insertSort(int[] a) { if(a == null || a.length<2){ return a; } int temp = 0; for(int i=1; i<a.length; i++){
        int j=i-1;
        temp=a[i];
         //移动待插入元素前 比其大的元素到后一个元素的位置;
        for(;j>=0&&temp<a[j];j--){  a[j+1]=a[j]; }
        //插入元素到其前面的位置
       a[j+1]=temp; } }
return a; } }

 2.折半插入排序

(1)基本思想:和直接插入排序的不同在于找到插入位置时,使用的折半查找的方法;

(2)例子

(3)性质

(4)特点

(5)Java实现

package sorts;

public class 折半插入排序 {
    public static void main(String[] args) {
        int a[]={49,38,65,97,76,13,27,49,78,34,12,64,5,4,62,99,98,54,56,17,18,23,34,15,35,25,53,51};
//        int a[]={13,27,38,49,49,65,76,78,97};
        for(int i=0;i<insertSort(a).length;i++){
            System.out.println(a[i]);
        }
        
//        insertSort1(a,13);
    }

    private static int insertSort1(int[] a,int data) {
      // 初始化左中右三个指针位置
int low = 0; int high = a.length-1; int middle = high/2;
      // 循环直到左右指针位置碰撞
while(high>=low){
        // 如果基准元素和中间指针位置元素相等,结束循环,返回该位置
if(data==a[middle]){ break; }
        // 如果基准元素在右半边范围,更新左指针为中间指针位置加一
if(data > a[middle]){ low = middle+1; middle = (low+high)/2; }
        // 如果基准为元素在左半边范围,更新右指针为中间指针位置减一
if(data<a[middle]){ high = middle-1; middle = (low+high)/2; } } return middle; } private static int[] insertSort(int[] a) { if(a == null || a.length<2){ return a; } int temp = 0; int index = 0; for(int i=1; i<a.length; i++){ //从第二个元素a[1]开始遍历元素; temp = a[i]; index = getBinaryIndex(0,i,a[i], a); //从序列头开始遍历依次移动i-low个元素;当a[low]=a[i-1]时说明a[i]为最大值,放在有序子序列的末尾; for(int j = i-1; j>=index; j--){ a[j+1] = a[j]; } a[index] = temp; } return a; } private static int getBinaryIndex(int low, int high,int data,int[] a) { int middle = high; //利用折半查找,找到a[i]应在位置返回low指针,应为low是肯定小于a[i]的位置, while(low<=high){ middle=(low+high)/2; if(data == a[middle]){ break; } if(data > a[middle]){ low = middle+1; } if(data<a[middle]){ high = middle-1; } } return low; } }

 3.希尔排序

(1)基本思想:算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的下标相差d,

然后比较i和i+d所对应的的元素的大小,如果后者小于前者,就要交换 这两个元素;逐渐增大i到d结束;这是整个内循环的过程,循环结束对应的i和i+d位置的元素都是i<i+d;

接下来,比较开始缩小d的范围到d/2,循环上一个步骤;

当d逐渐缩小时,i位置的元素会和多个元素比较(i+d,i+2d,i+3d。。。。);

(2)例子

(3)性质

(4)特点

(5)Java实现

package sorts;

public class 希尔排序 {
    public static void main(String[] args) {
        int a[]={49,38,65,97,76,13,27,49,78,34,12,64,5,4,62,99,98,54,56,17,18,23,34,15,35,25,53,51};
        for(int i=0;i<shellSort(a).length;i++){
            System.out.println(a[i]);
        }
    }

    private static int[] shellSort(int[] a) {
        double d1 = a.length;
        int temp = 0;
        while(true){
        // 第一层循环,从序列的一半长度d一直循环到该长度为1,达到基准条件 d1
= Math.ceil(d1/2); int d = (int)d1;
        // 第二层循环,在d范围内比较其后面紧临的一个d范围元素
for(int x=0; x<d; x++){
          // 第三层循环,比较i和i+d元素的大小
for(int i=x+d;i<a.length;i+=d){ int j=i-d; temp = a[i];
            //
for(;j>=0&&temp<a[j];j-=d){ a[j+d]=a[j]; } a[j+d]=temp; } }
      // 基准条件 当跨域只有一个元素的时候停止比较
if(d==1){ break; } } return a; } }

1.简单选择排序

(1)基本思想:和冒泡的不同是,两个算法的交换次数,冒泡排序是每次比较后条件成立都会进行交换,而选择排序只是重新记录了较小元素的下标;待循环结束;

直接替换a[i]的值;

(2)例子

(3)性质

(4)特点

(5)java实现

package sorts;

public class 简单选择排序 {
    public static void main(String[] args) {
        int a[]={49,38,65,97,76,13,27,49,78,34,12,64,5,4,62,99,98,54,56,17,18,23,34,15,35,25,53,51};
        changeSort(a);
    }

    private static void changeSort(int[] a) {
        int point;
        for(int i=0; i<a.length; i++){
            int temp = a[i];
            point = i;
            for(int j=i+1; j<a.length; j++){
                if(a[point]>a[j]){
                    point = j;
                }
            }
            a[i]= a[point];
            a[point] = temp;
        }
        
        for(int k=0; k<a.length; k++)
            System.out.println(a[k]);
    }
}

2.堆排序

(1)基本思想:不断构造堆,交换堆顶与末尾未排序的每一个元素,直到全部都排完。

应用到topK问题:

  创建一个大小为k的数组,对该数组构造堆,如果是求解最大的k个数,构建的是小顶堆,反之构建的是大顶堆。

  遍历数组中剩余的元素,依次与构建好的堆顶元素比较,比堆顶元素还要小的元素无需置换,如果堆顶元素被置换之后,需要调整堆,找到堆内最小的元素放到堆顶。

堆的定义:具有n个元素的序列(h1,h2,....,hn),当且仅当满足(hi>h2i,hi>=2i+1)或(hi<=h2i,hi<2i+1)时;

(2)实例

(3)性质

(4)特点

(5)java实现

package sorts;

import java.util.Arrays;

public class 堆排序 {
    public static void main(String[] args) {
        int a[]={49,38,65,97,76,13,27,49,78,34,12,64,5,4,62,99,98,54,56,17,18,23,34,15,35,25,53,51};
        heapSort(a);
    }

    private static void heapSort(int[] a) {
        int arrayLength = a.length;
        //循环建堆
        for(int i=0; i<arrayLength; i++){
            //建堆
            buildMaxHeap(a,arrayLength-1-i);
            //交换堆顶和最后一个元素
            swap(a,0,arrayLength-1-i);
            System.out.println(Arrays.toString(a));
        }
    }

    private static void buildMaxHeap(int[] data, int lastIndex) {
        //从lastIndex处节点的父节点开始
        for(int i=(lastIndex-1)/2; i>=0; i--){
            //k保存正在判断的节点
            int k=i;
            //如果当前k节点的子节点存在
            while(k*2+1<=lastIndex){
                //k节点的左子节点的索引
                int biggerIndex = 2*k+1;
                //如果biggerIndex小于lastIndex,即biggerIndex+1存在
                if(biggerIndex<lastIndex){
                    //如果右子节点的值较大
                    if(data[biggerIndex]<data[biggerIndex+1]){
                        //biggerIndex总是记录较大子节点的索引
                        biggerIndex++;
                    }
                }
                //如果k节点的值小于其较大的子节点的值
                if(data[k]<data[biggerIndex]){
                    //交换他们
                    swap(data,k,biggerIndex);
                    //奖biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值
                    //k节点的值大于其左右节点的值;
                    k=biggerIndex;
                }else{
                    break;
                }
            }
        }
    }

    private static void swap(int[] data, int i, int j) {
        int tmp = data[i];
        data[i] = data[j];
        data[j] = tmp;
    }
}
public class HeapDemo {
    public static void main(String[] args){     
        //最小堆实现从大到小排序
        int[] arrSort = {90,3,60,6,7,2,89,100,33,5};        
        MinHeap heap1 = new MinHeap(arrSort);
        for(int i = arrSort.length - 1;i > 0; i --){
            //把最小的元素移到末尾
            heap1.swap(0, i);
            //重新构造最小堆
            heap1.buildHeap(i);
        }
        //打印排序后的数组
        for(int i = 0; i < arrSort.length;i ++){
            System.out.println(arrSort[i]);
        }       
        
        //最小堆实现topK
        int[] arrTopK = {90,44,60,6,71,2,38,13,33,5};
        int k = 3;
        //创建一个大小为k的数组
        int[] data = new int[k];
        for(int i = 0; i < k; i ++){
            data[i] = arrTopK[i];
        }       
        MinHeap heap = new MinHeap(data);       
        for(int i = k; i < arrTopK.length; i ++){
            //如果大于堆顶的元素则替换顶并重新构造
            if(arrTopK[i] > heap.getRoot()) heap.setRoot(arrTopK[i]);           
        }       
        //打印最大的k个数
        for(int i = 0; i < k; i ++){
            System.out.println(data[i]);
        }
    }

    //最小堆类
    static class MinHeap{
        private int[] data;
        public MinHeap(int[] data){
            this.data = data;
            buildHeap(data.length);
        }

        public void buildHeap(int heapSize){
            for(int i = heapSize / 2 - 1; i >= 0;i --){
                heapify(i, heapSize);
            }
        }

        public void heapify(int index,int heapSize){
            int right = right(index);

            int left = left(index);

            int min = index;

            if(right < heapSize && data[right] < data[min]) min = right;

            if(left < heapSize && data[left] < data[min]) min = left;

            if(min == index) return;

            swap(index,min);

            heapify(min,heapSize);
        }

        private int right(int index){
            //右儿子的下标
            return (index + 1) << 1;
        }

        private int left(int index){
            //左儿子的下标
            return ((index + 1) << 1) - 1;
        }

        public void swap(int index1,int index2){
            int tmp = data[index1];
            data[index1] = data[index2];
            data[index2] = tmp;
        }

        public int getRoot(){
            return data[0];
        }

        public void setRoot(int root){
            data[0] = root;
            heapify(0, data.length);
        }
    }
}

1.归并排序

(1)基本思想:

将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。

然后再把有序子序列合并为整体有序序列。

(2)实例

(3)性质

(4)特点

(5)java实现

package sorts;

import java.util.Arrays;

import org.junit.Test;
public class 归并排序 { static int a[]={49,38,65,97,76,13,27,49,78,34,12,64,5,4,62,99,98,54,56,17,18,23,34,15,35,25,53,51}; public static void 归并排序1(){ sort(a,0,a.length-1); for(int i=0; i<a.length; i++){ System.out.println(a[i]); } } @Test public void ru(){ this.归并排序1(); } private static void sort(int[] data, int left, int right) { if(left<right){ //找出中间索引 int center = (left+right)/2; //对左边数组进行递归 sort(data,left,center); //对右边数组进行递归 sort(data,center+1,right); merge(data,left,center,right); } } private static void merge(int[] data, int left, int center, int right) { int [] tmpArr = new int[data.length]; int mid = center + 1; //third记录中间数组的索引 int third = left; int tmp = left; while(left<=center&&mid<=right){ //从两个数组中取出最小的放入中间数组 if(data[left]<=data[mid]){ tmpArr[third++]=data[left++]; }else{ tmpArr[third++]=data[mid++]; } } //剩余部分依次放入中间数组 while(mid<=right){ tmpArr[third++]=data[mid++]; } while(left<=center){ tmpArr[third++]=data[left++]; } //将中间数组中的内容复制回原数组 while(tmp<=right){ data[tmp]=tmpArr[tmp++]; } System.out.println(Arrays.toString(data)); } }

 2.基数排序

(1)基本思想:将所有有待比较的数值(正整数)统一为同样的数位长度,数位较短的说在前面补零,然后,从最低位开始进行一次排序;

这样从最低位排序到最高位排序完成以后,数列就变成一个有序的序列;

(2)实例:

(3)性质:

(4)特点:

(5)java实现

package sorts;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

public class 基数排序 {
    static int a[]={49,38,65,97,76,13,27,49,78,34,12,64,5,4,62,99,98,54,101,56,17,18,23,34,15,35,25,53,51};
    
    @Test
    public  void 基数排序1(){
        sort(a);
        for(int i=0; i<a.length; i++){
            System.out.println(a[i]);
        }
    }
    
    private static void sort(int[] array) {

        //首先确定排序的趟数
        int max=array[0];
        for(int i=0; i<array.length; i++){
            if(array[i]>max){
                max=array[i];
            }
        }
        
        int time=0;
        //判断位数
        while(max>0){
            max/=10;
            time++;
        }
        
        //建立10个队列
        List<ArrayList> queue = new ArrayList<ArrayList>();
        for(int i=0; i<10; i++){
            ArrayList<Integer> queue1 = new ArrayList<Integer>();
            queue.add(queue1);
        }
        
        //进行time次分配和收集
        for(int i=0; i<time;i++){
            
            //分配数组元素
            for(int j=0;j<array.length; j++){
                //得到数字的第time+1位数
                int x=array[j]%(int)Math.pow(10, i+1)/(int)Math.pow(10, i);
                ArrayList<Integer> queue2 = queue.get(x);
                queue2.add(array[j]);
                queue.set(x, queue2);
            }
            
            int count = 0;//元素计数器
            
            //收集队列元素
            for(int k=0; k<10; k++){
                while(queue.get(k).size()>0){
                    ArrayList<Integer> queue3 = queue.get(k);
                    array[count]=queue3.get(0);
                    queue3.remove(0);
                count++;
                }
            }
        }
    }
}
原文地址:https://www.cnblogs.com/jinb/p/7062399.html