排序算法总结

冒泡排序

  1. 原理
      冒泡排序是一个简单的排序算法, 它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来,如果没有可交换的元素,则排序完成
  2. 算法过程
     1) 从序列首部开始,比较每对相邻元素,如果它们的顺序与要排序的顺序相反,则交换这连个数的位置
     2) 1执行完后,最大或最小的元素会移动到序列尾部,然后再次从序列首部开始,执行1过程
     3) 当没有元素需要交换位置是,排序结束
  3. 算法图解
    在这里插入图片描述
    在这里插入图片描述
  4. java代码实现
public class BubbleSort {

    public static void bubbleSort(int[] arr) {
        if (arr == null || arr.length < 2){
            return;
        }
        boolean flag;
        for (int i = 1; i < arr.length; i++){
            flag = false;
            for (int j =0; j< arr.length - i; j++){
                // 相邻两个数,前面比后面大,则交换位置
                if (arr[j] > arr[j+1]){
                    flag = true;
                    swap(arr, j, j+1);
                }
            }
            if (!flag){
                // 如果没有元素被交换,则代表已经排好序,结束循环
                break;
            }
        }
    }

    public static void bubbleSort2(int[] arr){
        if (arr == null || arr.length<2){
            return;
        }
        boolean flag;
        for(int e = arr.length-1; e>0 ; e--){
            flag = false;

            for(int j=0; j<e; j++){
                if(arr[j] > arr[j+1]){
                    flag=true;
                    swap(arr, j, j+1);
                }
            }

            if(!flag){
                break;
            }
        }
    }
    public static void swap(int[] arr, int i, int j){
        // 用异或操作实现两个数值的交换
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
    }
    public static void main(String[] args){
        int[] arr_test = {5,2,6,3,2,7,2,4,7};

//        bubbleSort(arr_test);
        bubbleSort2(arr_test);

        for (int j : arr_test) {
            System.out.println(j);
        }
    }
}
  1. 冒泡排序的优化
      设置一个flag变量,用来记录在一遍的扫描中,是否发生了元素位置的交换,如果没有发生,则代表已经排好序,没有必要再进行下一遍扫描,可以提前结束循环。

选择排序

  1. 原理
      第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。

  2. 算法过程

  • 第一趟:选出第一个元素后面所有元素的最小值,如果这个最小值比第一个元素小,则交换它们
  • 第二趟:选出第二个元素后面所有元素的最小值,如果这个最小值比第二个元素小,则交换它们
  • ······
  • 第n-1趟:如果第n个元素比第n-1个元素小,则交换它们
  1. 算法图解
    在这里插入图片描述
  2. java代码实现
public class SelectSort {

    public static void selectSort(int[] arr){

        if (arr==null || arr.length < 2){
            return;
        }
        int min_num, index;
        for (int i=1; i<arr.length; i++){
            min_num = arr[i];
            index = i;
            for (int j=i; j<arr.length; j++){
                if(arr[j]<min_num){
                    min_num = arr[j];
                    index = j;
                }
            }
            if(min_num<arr[i-1]){
              swap(arr, i-1, index);
            }
        }
    }
}

插入排序

  1. 算法原理

  它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

  1. 算法过程
    1)从第一个元素开始,该元素可以认为已经被排序
    2)取出下一个元素,如果它比已排序的元素序列的最后一个元素大,执行步骤3,否则从已排序的元素序列的最后向前扫描,寻找比第一个比它小的元素,将它插入这个元素的后面,如果没找到,则将它插到序列首部
    3)取出已排序序列的下一个元素,重复步骤2
    4)重复步骤2~3,直至完成排序

  2. 算法图解
    在这里插入图片描述

  3. java代码实现

    public static void insetSort(int[] arr){

        if(arr == null || arr.length < 2){
            return;
        }
        for(int i = 1; i< arr.length; i++){

            for(int j = i-1; j>=0; j--){
                if(arr[j+1] < arr[j]){
                    swap(arr, j, j+1);
                }
                else{
                	break;
            }
        }
    }

归并排序

  1. 算法原理
      归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。

  2. 算法步骤
    1)将待排序序列从中间等分成左右连个子序列
    2)使用归并排序递归处理左序列
    3)使用归并排序递归处理右序列
    4)将左右两个有序子序列合并成一个完成的有序序列

  3. 算法图解
    在这里插入图片描述

  4. java代码实现, 递归实现

public class MergeSort {
    public static void merge(int[] arr, int L, int M, int R){
        int idx1 = L;
        int idx2 = M + 1;
        int i = 0;
        int[] help = new int[R - L + 1];

        while(idx1 <= M && idx2 <= R){
            if(arr[idx1] <= arr[idx2]){
                help[i] = arr[idx1];
                idx1++;
            }else{
                help[i] = arr[idx2];
                idx2++;
            }
            i++;
        }
        while(idx1<=M){
            help[i++] = arr[idx1++];
        }
        while(idx2<=R){
            help[i++] = arr[idx2++];
        }

        for(i = 0; i < help.length; i++){
            arr[L+i] = help[i];
        }
    }

    public static void process(int[] arr, int L, int R){

        if(L == R){
            return;
        }
        int mid = L + ((R - L) >> 1);

        process(arr, L, mid);
        process(arr, mid+1, R);

        merge(arr, L, mid, R);
    }

    public static void mergeSort(int[] arr){
        if(arr==null || arr.length<2){
            return;
        }

        process(arr, 0, arr.length-1);
    }
    public static void main(String[] args){
        int[] arr_test = {5,2,6,3,2,7,2,4,7};

        mergeSort(arr_test);

        for (int j : arr_test) {
            System.out.println(j);
        }
    }
}
  1. java代码,非递归实现

// 非递归实现
    public static void mergeSort2(int[] arr){

        int mergeSize = 1;
        int N = arr.length;

        while(mergeSize < N){
            int L = 0;
            while(L < N) {

                int M = L + mergeSize - 1;
                // 如果剩余的数不够组成一个左组,则跳出循环
                if (M > N - 1) {
                    break;
                }
                int R = M + Math.min(mergeSize, N - M - 1);
                merge(arr, L, M, R);
                L = R + 1;
            }
            if (mergeSize > N / 2){
                // 防止数组长度过大,出现溢出
                break;
            }
            mergeSize = mergeSize << 1;
        }
    }

快排

  1. partition过程
    问题描述:
      给定给定一个数组和一个数num,把小于等于num的数放到数组的左边,大于num的数放到数组右边。要求额外空间复杂度为O(1),时间复杂度为O(n)。

步骤:

  • 划定一个小于等于区域,初始时小于等于区右边界在-1位置,i=0
  • 然后比较arr[i]与num的大小,如果arr[i] <= num, 则arr[i]和小于等于区下一个位置交换,然后小于等于区右扩一位,i++;
  • 如果arr[i] > num, 则 i++
    在这里插入图片描述
  1. 荷兰国旗问题
    问题描述:
      给定一个数组和一个数num,将小于num的数放左边,等于num的数放中间,大于num的数放右边。这是partition问题的升级版。

步骤:
相比一般的partition问题,这里需要一个小于区和一个大于区。

  • 划定一个小于区和一个大于区,初始时,小于区右边界在-1位置,大于区有边界在数组的第一个越界位置,并且i=0。
  • 比较arr[i]和num的大小,arr[i] 等于num,则不进行任何操作,i++
  • arr[i] 小于num,则arr[i]和小于区的右一个交换,小于区有阔1,i++
  • arr[i] 大于num,则arr[i]和大于区的左一个交换,大于区左阔1,i不变。这里i不变的原因是从右边交换过来的数还没被检查过。
  • 直到 i 等于大于区的边界,算法结束。
    在这里插入图片描述
  1. 快排的java实现
public class QuickSort {

    public static int partition(int[] arr, int L, int R){
        //这里的L和R是数组的index
        if (L > R){
            return -1;
        }
        if (L == R){
            return L;
        }
        int lessEqual = L - 1;
        int i = L;
        while (i < R ){
            if(arr[i] <= arr[R]){
                swap(arr, lessEqual+1, i);
                lessEqual++;
            }
            i++;
        }
        swap(arr, ++lessEqual, R);
        return lessEqual; //返回小于等于区的边界位置, 返回的这个位置的元素一定是arr[R]
    }

    public static void swap(int[] arr, int i, int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static int[] netherlandsFlag(int[] arr, int L, int R){
        //返回等于区的左右两个边界位置,以arr[R]作为划分元素
        if(L > R){
            return new int[] {-1, -1};
        }
        if(L == R){
            return new int[] {L, L};
        }

        int less = L - 1;
        int more = R;
        int i = L;
        while(i < more){
            if(arr[i] == arr[R]){
                i++;
            }else if(arr[i] < arr[R]) {
                swap(arr, i, less + 1);
                i++;
                less++;
            }else{
                swap(arr, i, more - 1);
                more--;
            }
        }
        swap(arr, R, more);
        return new int[] {less+1, more};
    }

    public static void process1(int[] arr, int L, int R){
        // 快排1.0版本,使用partition,和arr[R]作为分界点
        if(L > R){
            return;
        }
        int M = partition(arr, L, R);

        process1(arr, L, M-1); // 左侧范围
        process1(arr, M+1, R); // 右侧范围
    }

    public static void process2(int[] arr, int L, int R){
        // 快排2.0版本,使用netherlandsFlag,和arr[R]作为分界点
        if(L>R){
            return;
        }
        int [] equalArea = netherlandsFlag(arr, L, R);

        process2(arr,0, equalArea[0]-1);
        process2(arr, equalArea[1]+1, R);
    }

    public static void process3(int[] arr, int L, int R){
        // 随机快排,随机选取一个元素作为分割点,时间复杂度最低。
        if(L>R){
            return;
        }
        swap(arr, L + (int)(Math.random() * (R - L + 1 )), R);
        int [] equalArea = netherlandsFlag(arr, L, R);

        process3(arr,0, equalArea[0]-1);
        process3(arr, equalArea[1]+1, R);
    }

    public static void quickSort1(int[] arr){
        if(arr == null|| arr.length < 2){
            return;
        }
        process1(arr, 0, arr.length-1);
    }

    public static void quickSort2(int[] arr){
        if(arr == null|| arr.length < 2){
            return;
        }
        process2(arr, 0, arr.length-1);
    }

    // 随机快排,最经典的快排
    public static void quickSort3(int[] arr){
        if(arr == null|| arr.length < 2){
            return;
        }
        process3(arr, 0, arr.length-1);
    }


    public static void main(String[] args){
        int[] arr_test = {5,2,6,3,2,7,2,4,7,3,8,89};

        quickSort3(arr_test);

        for (int j : arr_test) {
            System.out.println(j);
        }
    }

}

计数排序

基数排序

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值