排序算法

排序算法

1、 冒泡排序

public class BubbleSort {

    /**
     * 普通版本
     */

    public static void sort(int[] arr){
        int tem;
        for (int i = 0; i <= arr.length-2; i++) {
            for (int j = 0; j <= arr.length-2-i; j++) {
                if (arr[j]>arr[j+1]){
                    tem = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = tem;
                }
            }
        }
    }

    /**
     * 优化版本
     * 如果在一趟比较中,发现没有进行过交换,则说明序列有效,设置一个flag判断是否进行过交换从而减少不必要的比较
     */
    public static void sort2(int[] arr){
        int tem;
        boolean flag = false;
        for (int i = 0; i <= arr.length-2; i++) {
            for (int j = 0; j <= arr.length-2-i; j++) {
                if (arr[j]>arr[j+1]){
                    flag = true;
                    tem = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = tem;
                }
            }
            if (!flag)
                break;
            else
                flag = false;
        }
    }
}

2、选择排序

/**
 *  * 选择排序__从小到大
 */
public class SelectSort {

    public static void sort(int[] arr){
        int index_min;
        int value_min;
        for (int i = 0; i <= arr.length-2 ; i++) {
            index_min = i;
            value_min = arr[i];
            for (int j = i+1; j <= arr.length-1 ; j++) {
                if (arr[j] < value_min){
                    index_min = j;
                    value_min = arr[j];
                }
            }
            arr[index_min] = arr[i];
            arr[i] = value_min;
        }
    }
}

3、插入排序

/**
 * 插入排序--从小到大
 */
public class InsertSort {

    public static void sort(int[] arr){
        for (int i = 1; i <= arr.length-1 ; i++) {
            int cur_value = arr[i]; //记录待插入的数
            int cur_index = i;  // 以当前待插入数的索引为起点,将前面的比 待插入数 大的的数后移
            while (cur_index-1 >= 0  && cur_value < arr[cur_index-1] ){
                arr[cur_index] = arr[cur_index-1];
                cur_index--;
            }
            arr[cur_index] = cur_value; //插入到合适的位置
        }
    }
}

4、希尔排序

希尔提出的一种排序算法,也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。

/**
 * 希尔排序 -- 从小到大
 */
public class ShellSort {
    //对有序序列在插入时采用【交换】法
    public static void sort(int[] data) {
        for (int i = data.length / 2; i > 0; i = i / 2) {  //步长
            for (int j = i; j <= data.length - 1; j++) {   
                for (int k = j; k - i >= 0; k = k - i) {
                    if (data[k] < data[k - i]) {
                        int tmp = data[k];
                        data[k] = data[k - i];
                        data[k - i] = tmp;
                    }
                }
            }
        }
    }

    //对有序序列在插入时采用【移位】法,效果更佳!
    public static void sort2(int[] data) {
        for (int i = data.length / 2; i > 0; i = i / 2) {  //步长
            for (int j = i; j <= data.length - 1; j++) {
                int insert_value = data[j];
                int insert_index = j;
                while (insert_index-i >= 0 && insert_value < data[insert_index-i]) {
                    data[insert_index] = data[insert_index-i];
                    insert_index = insert_index - i;
                }
                data[insert_index] = insert_value;
            }
        }
    }
}

5 、快速排序(参考慕课网bobo老师的算法课程)

5.1 以第一个元素作为标定点的快速排序

在这里插入图片描述

1: 将第一个元素v作为标定点,从第二个元素开始逐个遍历所有元素 ;
2: 初始 j = l ,i = l + 1 ;
3: 如果 i 索引对应的 e 大于 v ,则只需 i 后移一位 即可;
4: 如果 i 索引对应的 e 小于 v ,则需要 j 后移一位,再交换 j 和 i 索引的数据 ;

在这里插入图片描述

5: 最后遍历完所有元素,只需交换 l 和 j 索引的数据即可;
6: 然后重复这个过程sort( l , j - 1 ) 、sort( j+1 , j+1,r )

public class QuickSort1 {
    
    public static void sort(int[] data , int l,int r){ //初始调用 sort(int[] data , 0, data.length-1)
           if (l>=r)
               return;
           int p = partition(data,l,r);
           sort(data,l,p-1);
           sort(data,p+1,r);

    }

    private static int partition(int[] data ,int l, int r) {
        int v = data[l];
        int j = l;
        for (int i = l+1; i <= r; i++) {
            if (data[i] < v){
                j++;
                swap(data,i,j); //进行步骤4的交换
            }
        }
        swap(data,l,j); //进行步骤5的交换
        return j;
    }
    
    //定义一个交换元素的方法
    private static void swap(int[] data, int i, int j) {
        int tmp = data[i];
        data[i] = data[j];
        data[j] = tmp;
    }
}
5.2 随机选取标定点的快速排序

对于一个近乎有序数组,如果选取第一个元素作为标定点,算法时间复杂度会达到O(n2)级别。使用随机标定点,可以解决这个问题。
只需要在原来算法的基础上,在partition方法中生成一个随机标定值,然后 l 和 rand 索引对应的值就行

public class QuickSort2 {

    public static void sort(int[] data , int l,int r){ //初始调用 sort(int[] data , 0, data.length-1)
           if (l>=r)
               return;
           int p = partition(data,l,r);
           sort(data,l,p-1);
           sort(data,p+1,r);

    }

    private static int partition(int[] data ,int l, int r) {
        //****************************************************************
        int rand  = (int) (Math.random()*(r-l+1)+l);// 生成一个随机标定值
        swap(data,l,rand);
        //****************************************************************
        int v = data[l];
        int j = l;
        for (int i = l+1; i <= r; i++) {
            if (data[i] < v){
                j++;
                swap(data,i,j); //进行步骤4的交换
            }
        }
        swap(data,l,j); //进行步骤5的交换
        return j;
    }

    //定义一个交换元素的方法
    private static void swap(int[] data, int i, int j) {
        int tmp = data[i];
        data[i] = data[j];
        data[j] = tmp;
    }
}
5.3 双路快速排序

随机标定点虽然解决了有序数组的排序时间复杂度高的问题,但对于几乎所有元素都相等的一个数组,随机标定点是没有任何意义的,此时双路排序算法可以解决这个问题。

在这里插入图片描述

左边进行循环找到一个 <v 的元素,右边进行循环找到一个 >v 的元素,进行交换

在这里插入图片描述

交换完毕,将i向后移动一位,j 向前移动一位


public class QuickSortTwoWay {

    public static void sort(int[] data , int l,int r){ //初始调用 sort(int[] data , 0, data.length-1)
           if (l>=r)
               return;
           int p = partition(data,l,r);
           sort(data,l,p-1);
           sort(data,p+1,r);
    }

    private static int partition(int[] data ,int l, int r) {
        int rand  = (int) (Math.random()*(r-l+1)+l);// 生成一个随机标定值
        swap(data,l,rand);

        int v = data[l];
        int i = l+1;
        int j = r;

        while (true){
            while (i <= r && data[i] < v)
                i++;
            while (j >= l+1 && data[j] > v)
                j--;
            if (j < i) //考虑边界情况
                break;
            swap(data,i,j);
            i++;
            j--; // 如果现在是 i j 的相邻顺序,经过i++,j--,变成 j i 的相邻顺序
        }
        swap(data,l,j);
        return j;
    }

    //定义一个交换元素的方法
    private static void swap(int[] data, int i, int j) {
        int tmp = data[i];
        data[i] = data[j];
        data[j] = tmp;
    }
}

关于边界情况的考虑


如果是左边已经找到了一个 > v 的元素,退出来循环
但是右边全部是 >v 的元素,于是j继续移动,直到移动到i的前一位才找到 < v 的元素,也退出循环


还有可能左边没有找到 > v 的元素 ,直到找到 j 或 j 的后一位才找到
而此时如果是 j 的后一位,就说明当前j指向的是一个 < v 的元素,所以下一步 j 的循环判断 j 并不做任何移动
而此时如果是 j 位,就说明当前j指向的是一个 > v 的元素,所以下一步 j 的循环判断 j 会向前移动一次


此时变成 j i 的相邻顺序,但此时已经说明所有的元素都已遍历,且不需要任何的交换,直接break,
而此时的 j 指向的是最 < v 的序列中的最后一个元素,交换 l , j 对应的值即可

5.4 三路快速排序

在这里插入图片描述

初始:lt = l ; gt = r+1; i = l+1;
如果当前 i 所指元素等于 v ,则 i 向后移动一位即可
如果当前 i 所指元素小于 v ,则 lt 向后移动一位,交换 lt 和 i 对应的元素,由于交换到 i 位置的值是已知等于v 的,所以 i 向后移动一位
如果当前 i 所指元素大于 v ,则gt 向后移动一位,交换 gt 和 i 对应的元素,由于交换到 i 位置的值是未知的,所以 i 不需要移动,需要下次判断来决定该值合适的位置

在这里插入图片描述

当 i > gt 的时候,说明元素已检索完毕,此刻交换 l 和 lt 位置对应的值,把 v 放在合适的位置

在这里插入图片描述

此刻该段数组已成功分组
然后执行:
sort(data,l,lt-1);
sort(data,gt,r);

public class QuickSortThreeWay {

    public static void sort(int[] data,int l,int r){
        if (l >= r)
            return;
        int lt = l;
        int gt = r+1;
        int i = l+1;

        int rand  = (int) (Math.random()*(r-l+1)+l);// 生成一个随机标定值
        swap(data,l,rand);
        int v = data[l];

        while (i < gt){
            if ( data[i] == v)
                i++;
            else if (data[i] < v){
                swap(data,i,lt+1);
                lt++;
                i++;
            }else {
                swap(data,i,gt-1);
                gt--;
            }
        }
        swap(data,l,lt);

        sort(data,l,lt-1);
        sort(data,gt,r);
    }

    //定义一个交换元素的方法
    private static void swap(int[] data, int i, int j) {
        int tmp = data[i];
        data[i] = data[j];
        data[j] = tmp;
    }
}

6 、归并排序

在这里插入图片描述

public class MergeSort {

    public static void sort(int[] data, int l, int r) {
        if (l >= r)
            return;
        int mid = (l + r) / 2;
        sort(data, l, mid);
        sort(data, mid + 1, r);
        merge(data, l, mid, r);
    }

    private static void merge(int[] data, int l, int mid, int r) {
        int[] tmp = Arrays.copyOfRange(data, l, r + 1); //将data数组中 l 到 r 的元素拷贝到一个临时数组中,下面操作的时候直接改变data数组中的值
        int l_index = l;                                   //左边索引从 l 开始
        int r_index = mid + 1;                             //右边索引从mid+1开始
        for (int i = l; i <= r; i++) {
            if (l_index > mid) {                           //说明左边部分以全部处理完毕
                data[i] = tmp[r_index - l];
                r_index++;
            }
            else if (r_index > r) {                       //说明右边部分以全部处理完毕
                data[i] = tmp[l_index - l];
                l_index++;
            }
            else if (tmp[l_index-l] < tmp[r_index-l]) {    //左边所指元素 < 右边所指元素
                data[i] = tmp[l_index-l];
                l_index++;
            }
            else {                                       //左边所指元素 >= 右边所指元素
                data[i] = tmp[r_index-l];
                r_index++;
            }
        }
    }
}

7 、基数排序

基数排序属于“分配式排序”,又称桶子法,它是通过键值的各个位的值,将要排序的元素分配到某些桶中,达到排序的作用
基数排序是属于稳定性的排序,基数排序法是效率高的稳定性排序
基数排序是桶排序的扩展

public class RadixSort {

    public static void sort(int[] data){

        int max = data[0];
        for (int i = 1; i < data.length; i++) {                   //找出位数最长的数
            if (data[i] > max)
                max = data[i];
        }
        int max_length = (max+"").length();                        //得到最大数的长度

        int[][] bucket = new int[10][data.length];                // 定义10个容量为data.length的桶
        int[] bucket_size = new int[10];                           // 定义一个记录每个桶的存放元素数量的数组

        for (int k = 0; k < max_length ; k++) {                    //根据最大数的位数长度,决定要循环的次数
            for (int d = 0; d < data.length; d++) {               //循环data数组中的数据
                int num = data[d]/(int)Math.pow(10,k)%10;         // 得到当前位数的值
                bucket[num][bucket_size[num]] = data[d];           // 放入对应的桶中
                bucket_size[num]++;                                // 放入元素对应的桶的存放元素数量加1
            }
            int index = 0;
            for (int b = 0; b < 10 ; b++) {                       //把从0号桶至九号桶取出所有元素,对应更新data中的元素排布
                for (int bs = 0; bs < bucket_size[b] ; bs++) {
                    data[index] = bucket[b][bs];
                    index++;
                }
                bucket_size[b] = 0;                               // 取出每个桶的元素,对应的存放元素数量应置为0
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值