常用排序算法

排序算法总览

算法时间复杂度空间复杂度稳定性
冒泡排序O(n^2)O(1)稳定
快速排序O(nlogn)O(logn)不稳定
直接选择排序O(n^2)O(1)不稳定
直接插入排序O(n^2)O(1)稳定
希尔排序O(nlogn~n^2)O(1)不稳定
归并排序O(nlogn)O(1)稳定
堆排序O(nlogn)O(1)不稳定
计数排序O(n+k)O(n+k)稳定
桶排序O(n)O(n+bucket_num)稳定
基数排序O(n*bucket_num)O(n*bucket_num)稳定

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

一、内部排序:所有数据已经读入内存,在内存中进行排序的算法,排序过程中不需要对磁盘进行读写

1、冒泡排序

基本思想:重复的遍历待排序的一组数字,依次比较两个相邻的元素,若它们的顺序错误则将它们调换一下位置,直至没有元素再需要交换为止,完成一次即可将最大元素放在最右边。

public class BubbleSort {
    public static int[] bubbleSort(int[] array){
        if(array.length==0) return array;
        for(int i=0;i<array.length;i++){
            for(int j=0;j<array.length-i-1;j++){
                //将最大的元素交换到最右边
                if(array[j+1]<array[j]){
                    int temp=array[j+1];
                    array[j+1]=array[j];
                    array[j]=temp;
                }
            }
        }
        return array;
    }
}

2、快速排序

基本思想:选择一个元素作为基准,通过一趟排序将待排序列表分割成独立的两部分,其中一部分的所有元素都比另一部分小,然后再按此方法将独立的两部分分别继续重复进行此操作,这个过程我们可以通过递归实现,从而达到最终将整个列表排序的目的

public class QuickSort {

    public static void quickSort(int[] array,int low,int high){
        if(low<high){
            //寻找基准数据的正确索引
            int index=getIndex(array,low,high);
            //分治
            quickSort(array,low,index-1);
            quickSort(array,index+1,high);
        }
    }

    public static int getIndex(int[] array,int low,int high){
        //基准数据
        int tmp=array[low];
        while(low<high){
            //当队尾的元素大于等于基准数据时,向前挪动high指针
            while(low<high&&array[high]>=tmp){
                high--;
            }
            // 如果队尾元素小于tmp了,需要将其赋值给low
            array[low]=array[high];
            // 当队首元素小于等于tmp时,向前挪动low指针
            while(low<high&&array[low]<=tmp){
                low++;
            }
            // 当队首元素大于tmp时,需要将其赋值给high
            array[high]=array[low];
        }
        // 跳出循环时low和high相等,此时的low或high就是tmp的正确索引位置
        array[low]=tmp;
        return low;
    }

}

3、直接选择排序

基本思想:先在待排序列表中找到最小(大)的元素,把它放在起始位置作为已排序序列;然后,再从剩余待排序序列中找到最小(大)的元素放在已排序序列的末尾,以此类推,直至完毕

public class SelectionSort {
    public static int[] selectionSort(int[] array){
        if(array.length==0) return array;
        for(int i=0;i<array.length;i++){
            int minIndex=i;
            for(int j=i;j<array.length;j++){
                if(array[j]<array[minIndex])
                    minIndex=j;
            }
            int temp=array[minIndex];
            array[minIndex]=array[i];
            array[i]=temp;
        }
        return array;
    }
}

4、直接插入排序

基本思想:对于未排序元素,在已排序序列中从后向前扫描,找到相应位置把它插入进去;在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为新元素提供插入空间

public class InsertionSort {
    public static int[] insertionSort(int[] array){
        if(array.length==0) return array;
        int current;
        for(int i=0;i<array.length-1;i++){
            current=array[i+1];
            int preIndex=i;
            while(preIndex>=0&&current<array[preIndex]){
                array[preIndex+1]=array[preIndex];
                preIndex--;
            }
            array[preIndex+1]=current;
        }
        return array;
    }
}

5、希尔排序

基本思想:将待排序列表按下标的一定增量分组(比如增量为2时,下标1,3,5,7为一组,下标2,4,6,8为另一组),各组内进行直接插入排序;随着增量的越来越小,每组所包含的数字序列越来越多,当增量减至1时,整个序列被分成一个组,排序就完成了

public class ShellSort {
    public static int[] shellSort(int[] array){
        int len=array.length;
        int temp,gap=len/2;
        while(gap>0){
            for(int i=gap;i<len;i++){
                temp=array[i];
                int preIndex=i-gap;
                while(preIndex>=0&&temp<array[preIndex]){
                    array[preIndex+gap]=array[preIndex];
                    preIndex-=gap;
                }
                array[preIndex+gap]=temp;
            }
            gap/=2;
        }
        return array;
    }
}

6、归并排序

基本思想:递归的将两个已排序的序列合并成一个序列

public class MergeSort {
    public static int[] mergeSort(int[] array){
        if(array.length<2) return array;
        int mid=array.length/2;
        int[] left= Arrays.copyOfRange(array,0,mid);
        int[] right=Arrays.copyOfRange(array,mid,array.length);
        return merge(mergeSort(left),mergeSort(right));
    }
    public static int[] merge(int[] left,int[] right){
        int[] result=new int[left.length+right.length];
        for(int index=0,i=0,j=0;index<result.length;index++){
            if(i>=left.length)
                result[index]=right[j++];
            else if(j>=right.length)
                result[index]=left[i++];
            else if(left[i]>right[j])
                result[index]=right[j++];
            else
                result[index]=left[i++];
        }
        return result;
    }
}

7、堆排序

基本思想:将待排序列表构造成一个最大堆,堆顶元素即为最大值,将堆顶元素与堆尾元素交换,堆大小减1,重复直至堆大小为1

public class HeapSort {

    static int len;

    public static int[] heapSort(int[] array){
        len=array.length;
        if(len<1) return array;
        buildMaxHeap(array);
        while(len>0){
            int temp=array[len-1];
            array[len-1]=array[0];
            array[0]=temp;
            len--;
            adjustHeap(array,0);
        }
        return array;
    }

    public static void buildMaxHeap(int[] array){
        //从最后一个非叶子节点开始向上构造最大堆
        //for循环这样写会更好一点:i的左子树和右子树分别2i+1和2(i+1)
        len= array.length;
        for (int i = (len/2- 1); i >= 0; i--) {
            adjustHeap(array, i);
        }
    }
    public static void adjustHeap(int[] array, int i) {
        int maxIndex = i;
        int left=i * 2 + 1;
        int right=i * 2 + 2;
        //如果有左子树,且左子树大于父节点,则将最大指针指向左子树
        if (left < len && array[left] > array[maxIndex])
            maxIndex = left;
        //如果有右子树,且右子树大于父节点,则将最大指针指向右子树
        if (right < len && array[right] > array[maxIndex])
            maxIndex = right;
        //如果父节点不是最大值,则将父节点与最大值交换,并且递归调整与父节点交换的位置。
        if (maxIndex != i) {
            int temp=array[maxIndex];
            array[maxIndex]=array[i];
            array[i]=temp;
            adjustHeap(array, maxIndex);
        }
    }

}

二、外部排序:内存中无法保存全部数据,需要进行磁盘访问,每次读入部分数据到内存进行排序

8、计数排序

基本思想:对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数(此处并非比较各元素的大小,而是通过对元素值的计数和计数值的累加来确定)。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。

public class CountingSort {
    public static int[] countingSort(int[] array){
        if(array.length==0) return array;
        int bias,min=array[0],max=array[0];
        for(int i=1;i<array.length;i++){
            if(array[i]>max)
                max=array[i];
            if(array[i]<min)
                min=array[i];
        }
        bias=0-min;
        int[] bucket=new int[max-min+1];
        Arrays.fill(bucket,0);
        for(int i=0;i<array.length;i++){
            bucket[array[i]+bias]++;
        }
        int index=0,i=0;
        while(index<array.length){
            if(bucket[i]!=0){
                array[index]=i-bias;
                bucket[i]--;
                index++;
            }else{
                i++;
            }
        }
        return array;
    }
}

9、桶排序

基本思想:桶排序又叫箱排序,它是计数排序的升级版,利用了函数的映射关系将数据分到有限数量的桶里,每个桶再分别进行排序(使用别的排序方法或者递归的继续使用桶方法排序)

public class BubbleSort {
    public static int[] bubbleSort(int[] array){
        if(array.length==0) return array;
        for(int i=0;i<array.length;i++){
            for(int j=0;j<array.length-i-1;j++){
                if(array[j+1]<array[j]){
                    int temp=array[j+1];
                    array[j+1]=array[j];
                    array[j]=temp;
                }
            }
        }
        return array;
    }
}

10、基数排序

基本思想:将所有待比较正整数统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始进行基数为10的计数排序,一直到最高位计数排序完后,数列就变成一个有序序列(利用了计数排序的稳定性)

public class RadixSort {
    public static int[] radixSort(int[] arr){
        if(arr==null||arr.length<2) return arr;
        //1.计算出最大数的位数
        int max=arr[0];
        for(int i=1;i<arr.length;i++){
            max=Math.max(max,arr[i]);
        }
        int maxDigit=0;
        while(max!=0){
            max/=10;
            maxDigit++;
        }

        int mod=10,div=1;
        ArrayList<ArrayList<Integer>> buckerList=new ArrayList<>();
        for(int i=0;i<10;i++){
            buckerList.add(new ArrayList<Integer>());
        }
        for(int i=0;i<maxDigit;i++,mod*=10,div*=10){
            for(int j=0;j<arr.length;j++){
                int num=(arr[j]%mod)/div;
                buckerList.get(num).add(arr[j]);
            }
            int index=0;
            for(int j=0;j<buckerList.size();j++){
                for(int k=0;k<buckerList.get(j).size();k++){
                    arr[index++]=buckerList.get(j).get(k);
                }
                buckerList.get(j).clear();
            }
        }
        return arr;
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值