排序算法(java)

1.交换排序

1.1冒泡排序

进行arr.length-1轮排序,第一轮排序比较arr.length-1次,第一轮完毕后,最大的数字排好在最后;第二轮排序比较arr.length-2次,第二大的数字排好在倒数第二;第arr.length-1轮排序,比较一次,第二小的元素在arr[1]的位置上,最小的元素交换到arr[0]。

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

1.2快速排序

取数组第一个元素为基元素,大致把其余元素分成两个区间,比基数大的排在右边,比基数小的排在左边,这两个区间的数组分别继续这样递归,最终排好序。

public static void quickSort(int[] arr, int start, int end) {
        if(start < end) {
            // 把数组中的首位数字作为基准数
            int stard = arr[start];//4
            // 记录需要排序的下标
            int low = start;//0
            int high = end;//5
            // 循环找到比基准数大的数和比基准数小的数
            while(low < high) {
                // 右边的数字比基准数大
                while(low < high && arr[high] >= stard) {//6>4
                    high--;//[4]
                }
                //2<4
                // 使用右边的数替换左边的数
                arr[low] = arr[high];//2 9 5 1 2 6
                // 左边的数字比基准数小
                while(low < high && arr[low] <= stard) {//2<4
                    low++;//1
                }
                // 使用左边的数替换右边的数
                arr[high] = arr[low];//2 9 5 1 9 6
            }
            // 把标准值赋给下标重合的位置
            arr[low] = stard;
            // 处理所有小的数字
            quickSort(arr, start, low);
            // 处理所有大的数字
            quickSort(arr, low + 1, end);
        }
    }

2.插入排序

2.1直接插入排序

对于一个序列,选定一个下标,认为在这个下标之前的元素都是有序的。将下标所在的元素插入到其之前的序列中。接着再选取这个下标的后一个元素,继续重复操作。直到最后一个元素完成插入为止。我们一般从序列的第二个元素开始操作。

public static void insertSort(int[] arr) {
        for(int i = 1; i < arr.length; i++) {
            if(arr[i] < arr[i - 1]) {//要插
                int j;
                int temp = arr[i];//记录要插入的元素
                //寻找要插入的位置,慢慢移动前面排序好的元素
                for(j = i - 1; j >= 0 && arr[j] > temp; j--) {
                    // 前一个数字赋给后一个数字
                    arr[j + 1] = arr[j];
                }
                arr[j + 1] = temp;//插入元素
            }
        }
    }

2.2希尔排序

某些情况下直接插入排序的效率极低。比如一个已经有序的升序数组,这时再插入一个比最小值还要小的数,也就意味着被插入的数要和数组所有元素比较一次。我们需要对直接插入排序进行改进。

怎么改进呢?前面提过,插入排序对已经排好序的数组操作时,效率很高。因此我们可以试着先将数组变为一个相对有序的数组,然后再做插入排序。

希尔排序能实现这个目的。希尔排序把序列按下标的一定增量(步长)分组,对每组分别使用插入排序。随着增量(步长)减少,一直到一,算法结束,整个序列变为有序。因此希尔排序又称缩小增量排序。

一般来说,初次取序列的一半为增量,以后每次减半,直到增量为一。

public void shellSort(int[] arr) {
    // gap 为步长,每次减为原来的一半。
    for (int gap = arr.length / 2; gap > 0; gap /= 2) {
        // 对每一组都执行直接插入排序
        for (int i = 0 ;i < gap; i++) {
            // 对本组数据执行直接插入排序
            for (int j = i + gap; j < arr.length; j += gap) {
                // 如果 a[j] < a[j-gap],则寻找 a[j] 位置,并将后面数据的位置都后移
                if (arr[j] < arr[j - gap]) {
                    int k;
                    int temp = arr[j];
                    for (k = j - gap; k >= 0 && arr[k] > temp; k -= gap) {
                        arr[k + gap] = arr[k];
                    }
                    arr[k + gap] = temp;
                }
            }
        }
    }
}

 3.选择排序

3.1简单选择排序

public static void selectSort(int[] arr) {
        for (int i = 0; i < arr.length-1; i++) {
            int minIndex = i;
            //i+1开始,依次与arr[i]比较找出最小元素下标
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[j] < arr[minIndex]) {
                    // 记录最小的数的下标
                    minIndex = j;
                }
            }
            //最小元素和第i个元素交换
            if (i != minIndex) {
                int temp = arr[i];
                arr[i] = arr[minIndex];
                arr[minIndex] = temp;
            }
        }
    }

 3.2堆排序

不会二叉树暂且不学

4.归并排序

整型数组排序:

public static void merge(int[] arr, int low, int middle, int high) {
        // 用于存储归并后的临时数组
        int[] temp = new int[high - low + 1];
        // 记录第一个数组中需要遍历的下标
        int i = low;
        // 记录第二个数组中需要遍历的下标
        int j = middle + 1;
        // 记录在临时数组中存放的下标
        int index = 0;
        // 遍历两个数组,取出小的数字,放入临时数组中
        while (i <= middle && j <= high) {
            // 第一个数组的数据更小
            if (arr[i] <= arr[j]) {
                // 把更小的数据放入临时数组中
                temp[index] = arr[i];
                // 下标向后移动一位
                i++;
            } else {
                temp[index] = arr[j];
                j++;
            }
            index++;
        }
        // 处理剩余未比较的数据
        while (i <= middle) {
            temp[index] = arr[i];
            i++;
            index++;
        }
        while (j <= high) {
            temp[index] = arr[j];
            j++;
            index++;
        }
        // 把临时数组中的数据重新放入原数组
        for (int k = 0; k < temp.length; k++) {
            arr[k + low] = temp[k];
        }
    }

    public static void mergeSort(int[] arr, int low, int high) {
        int middle = (high + low) / 2;//2
        if (low < high) {
            // 处理左边数组
            mergeSort(arr, low, middle);
            // 处理右边数组
            mergeSort(arr, middle + 1, high);
            // 左右两边分别排序好后归并
            merge(arr, low, middle, high);
        }
    }

⭐通用对象数组排序(实现Comparable接口):

public static void mergeSort(Comparable[] a) {//分组
        int left = 0;
        int right = a.length - 1;
        mergeSort(a, left, right);
}

public static void mergeSort(Comparable[] a, int left, int right) {//多次分组
        if (left < right) {
            int middle = (left + right) / 2;
            mergeSort(a, left, middle);
            mergeSort(a, middle + 1, right);
            merge(a, left, middle, right);
        }
}

public static void merge(Comparable[] a, int left, int middle, int right) {//多次归并
        int p = 0;
        int p1 = left;
        int p2 = middle + 1;
        Comparable[] assist = new Comparable[right - left + 1];
        while (p1 <= middle && p2 <= right) {
            if (a[p1].compareTo(a[p2]) < 0) {
                assist[p++] = a[p1++];
            } else {
                assist[p++] = a[p2++];
            }
        }
        while (p1 <= middle) {
            assist[p++] = a[p1++];
        }
        while (p2 <= right) {
            assist[p++] = a[p2++];
        }
        for (int i = 0; i < assist.length; i++) {
            a[i+left] = assist[i];
        }
}

 5.基数排序

基数排序的原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。为此需要将所有待比较的数值统一为同样的数位长度,数位不足的数在高位补零。

public static void radixSort(int[] arr) {
    // 存放数组中的最大数字
    int max = Integer.MIN_VALUE;
    for (int value : arr) {
        if (value > max) {
            max = value;
        }
    }
    // 计算最大数字是几位数
    int maxLength = (max + "").length();
    // 用于临时存储数据
    int[][] temp = new int[10][arr.length];
    // 用于记录在 temp 中相应的下标存放数字的数量
    int[] counts = new int[10];
    // 根据最大长度的数决定比较次数
    for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
        // 每一个数字分别计算余数
        for (int j = 0; j < arr.length; j++) {
            // 计算余数
            int remainder = arr[j] / n % 10;
            // 把当前遍历的数据放到指定的数组中
            temp[remainder][counts[remainder]] = arr[j];
            // 记录数量
            counts[remainder]++;
        }
        // 记录取的元素需要放的位置
        int index = 0;
        // 把数字取出来
        for (int k = 0; k < counts.length; k++) {
            // 记录数量的数组中当前余数记录的数量不为 0
            if (counts[k] != 0) {
                // 循环取出元素
                for (int l = 0; l < counts[k]; l++) {
                    arr[index] = temp[k][l];
                    // 记录下一个位置
                    index++;
                }
                // 把数量置空
                counts[k] = 0;
            }
        }
    }
}

所有排序的复杂度

排序法平均时间最差情形稳定度额外空间备注
冒泡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是基数(个十百)

希尔O(nlogn)O(ns) 1<s<2不稳定O(1)s是所选分组
快速O(nlogn)O(n2)不稳定O(nlogn)n大时较好
归并O(nlogn)O(nlogn)稳定O(1)n大时较好

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值