java十大经典排序算法(算法复杂度及代码实现)

目录

    • 一、排序算法介绍
    • 二、算法复杂度
    • 三、代码实现
      • 3.1 冒泡排序
      • 3.2 选择排序
      • 3.3 插入排序
      • 3.4 希尔排序
      • 3.5 归并排序
      • 3.6 快速排序
      • 3.7 堆排序
      • 3.8 计数排序
      • 3.9 桶排序
      • 3.10 基数排序

一、排序算法介绍

菜鸟教程:https://www.runoob.com/w3cnote/ten-sorting-algorithm.html

  1. 冒泡排序:两两比较,依次将最大值放在最后一位,倒数第二位。。。
  2. 选择排序:找到最小值,与第一位交换,然后找到次小值与第二位交换。。。
  3. 插入排序:分为有序和无序两块,每次将无序中的一个按照位置插入有序中,该位置后的有序值均向后移动一位
  4. 希尔排序:希尔排序也是插入排序的一种,只是对希尔算法做了优化,缩小增量排序,希尔算法有交换法和位移法
  5. 归并排序:该算法是采用分治法
  6. 快速排序:每次定义一个中轴,让将小值放在左侧,大值放在右侧,依次递归左侧和右侧,每次均定义一个中轴
  7. 堆排序:堆分为大顶堆小顶堆,升序使用大顶堆,降序使用小顶堆
  8. 计数排序
  9. 桶排序
  10. 基数排序

二、算法复杂度

三、代码实现

3.1 冒泡排序

两两比较,依次将最大值放在最后一位,倒数第二位。。。

public static void main(String[] args){
     
    int[] arr = {
     2,6,4,20,8,3,10};
    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+1];
                arr[j+1] = arr[j];
                arr[j] = temp;
            }
        }
    }
    System.out.println(Arrays.toString(arr));
    // [2, 3, 4, 6, 8, 10, 20]
}

// 优化冒泡排序
public static void main(String[] args){
     
    int[] arr = {
     2,6,4,20,8,3,10};
    int temp = 0;
    // 当次循环位置是否有改动 true:有 false:无
    boolean flag = false;
    for(int i = 0;i < arr.length - 1;i++){
     
    	flag = false;
        for(int j = 0;j < arr.length - i - 1;j++){
     
         	// 从小到大排序(小于号为从大到小排序)
            if(arr[j] > arr[j+1]){
     
                temp = arr[j+1];
                arr[j+1] = arr[j];
                arr[j] = temp;
                flag = true;
            }
        }
        // 当次循环位置是否没有改动即可提前结束循环
        if(!false){
     
			break;
		}
    }
    System.out.println(Arrays.toString(arr));
    // [2, 3, 4, 6, 8, 10, 20]
}

3.2 选择排序

找到最小值,与第一位交换,然后找到次小值与第二位交换。。。

public static void main(String[] args){
     
    int[] arr = {
     2,6,4,20,8,3,10};
    // 临时变量
    int temp = 0;
    // 最小值下标
    int index = 0;
    for(int i = 0;i < arr.length - 1;i++){
     
        index = i;
        for(int j = i + 1;j < arr.length;j++){
     
        	// 记录最小值的下标
            if(arr[j] < arr[index]){
     
                index = j;
            }
        }
        // 将找到的最小值和i位置所在的值进行交换
        if(i != index){
     
            temp = arr[i];
            arr[i] = arr[index];
            arr[index] = temp;
        }
    }
    System.out.println(Arrays.toString(arr));
    // [2, 3, 4, 6, 8, 10, 20]
}

3.3 插入排序

分为有序和无序两块,每次将无序中的一个按照位置插入有序中,该位置后的有序值均向后移动一位

public static void main(String[] args){
     
	int[] arr = {
     2,6,4,20,8,3,10};
    // 待插入的值
    int temp = 0;
    // 角标
    int index = 0;
    // 循环当前要比较的值
    for (int i = 1; i < arr.length; i++) {
     
        temp = arr[i];
        index = -1;
        // 循环有序列表
        for (int j = i; j > 0; j--) {
     
            if (arr[j - 1] > temp){
     
                index = j;
                arr[j] = arr[j -1];
            }else {
     
                break;
            }
        }
        if (index != -1){
     
            arr[index-1] = temp;
        }
    }
    System.out.println(Arrays.toString(arr));
    // [2, 3, 4, 6, 8, 10, 20]
}

3.4 希尔排序

希尔排序也是插入排序的一种,只是对希尔算法做了优化,缩小增量排序,希尔算法有交换法和位移法

// 希尔排序交换法
public static void main(String[] args) {
     
  int[] arr = {
     2,6,4,20,8,3,10};
    int temp = 0;
    for (int step = arr.length / 2; step > 0; step /= 2) {
     
        for (int i = step; i < arr.length; i++) {
     
            // 步长为step
            for (int j = i - step; j >= 0; j -= step) {
     
                if (arr[j] > arr[j+step]){
     
                    temp = arr[j];
                    arr[j] = arr[j+step];
                    arr[j+step] = temp;
                }
            }
        }
    }
    System.out.println(Arrays.toString(arr));
    // [2, 3, 4, 6, 8, 10, 20]
}

// 希尔排序位移法
public static void main(String[] args) {
     
    int[] arr = {
     2,6,4,20,8,3,10};
    int temp = 0;
    for (int step = arr.length / 2; step > 0; step /= 2) {
     
        for (int i = step; i < arr.length; i++) {
     
            int j = i;
            temp = arr[i];
            if (arr[j] < arr[j - step]){
     
                while (j-step >= 0 && temp < arr[j - step]){
     
                    arr[j] = arr[j-step];
                    j -= step;
                }
                arr[j] = temp;
            }
        }
    }
    System.out.println(Arrays.toString(arr));
    // [2, 3, 4, 6, 8, 10, 20]
}

3.5 归并排序

该算法是采用分治法

public class MergeSort {
     

    /**
     * 归并算法
     * 合并次数=数组大小-1次
     * @param args
     */
    public static void main(String[] args) {
     
        int[] arr = {
     8,4,5,7,1,3,6,2};
        int[] temp = new int[arr.length];
        test01(arr,0,arr.length -1,temp);
        System.out.println(Arrays.toString(temp));

        // 测试归并排序效率
        // 八百万数据一秒排列完成
        // test02();
    }

    /**
     * 分+合操作
     * @param arr
     * @param left
     * @param right
     * @param temp
     */
    public static void test01(int[] arr,int left,int right,int[] temp){
     
        if (left < right){
     
            // 找到中间索引
            int mid = (left + right) / 2;
            // 向左传递进行分解
            test01(arr,left,mid,temp);
            // 向右传递进行分解
            test01(arr,mid+1,right,temp);
            // 没分解一次就需要合并一次
            merge(arr,left,mid,right,temp);
        }
    }

    /**
     * 合并方法
     * @param arr 要排列的数组
     * @param left 左边索引
     * @param mid 中间索引
     * @param right 右边索引
     * @param temp 临时数组
     */
    public static void merge(int[] arr,int left,int mid,int right,int[] temp){
     
        // 左边初始索引
        int l = left;
        // 右侧初始索引,中间索引为左侧的边界,所以右侧索引初始化的时候需要加一
        int r = mid + 1;
        // temp临时数组初始化索引
        int t = 0;

        // 1、将左右两边有序数组按照规则填充到临时数组
        while (l <= mid && r<= right){
     
            // 当左侧值小于等于右侧值时,将最小值(左侧值)放置在temp中
            if (arr[l] <= arr[r]){
     
                temp[t] = arr[l];
                // 放置之后temp临时数组角标右移,左侧索引右移
                t++;
                l++;
            }else{
     
                // 反之,将最小值(右侧)放置在temp中
                temp[t] = arr[r];
                // 放置之后temp临时数组角标右移,右侧索引右移
                t++;
                r++;
            }
        }

        // 2、当其中一侧全部放置在temp临时数组之后,将另一侧剩余数组按序放置在temp临时数组中
        // 左侧还有剩余数组
        while (l <= mid){
     
            temp[t] = arr[l];
            t++;
            l++;
        }
        // 右侧还有剩余数组
        while (r <= right){
     
            temp[t] = arr[r];
            t++;
            r++;
        }

        // 3、将temp临时数组放置在arr数组中
        // 临时数组索引
        t = 0;
        int t_left = left;
        while (t_left <= right){
     
            arr[t_left] = temp[t];
            t_left++;
            t++;
        }
    }

    /**
     * 测试归并排序效率
     */
    public static void test02(){
     
        int maxSize = 8000000;
        int[] arr = new int[maxSize];
        int[] temp = new int[arr.length];

        for (int i = 0; i < maxSize; i++) {
     
            arr[i] = (int) (Math.random() * 8000000);
        }

        long start = System.currentTimeMillis();
        System.out.println("执行前时间"+System.currentTimeMillis());

        test01(arr,0,arr.length-1,temp);

        long end = System.currentTimeMillis();
        System.out.println("执行后时间"+System.currentTimeMillis());

        System.out.println("程序运行时间: "+(end-start)+"ms");
        System.out.println("程序运行时间: "+(end-start)/1000+"s");
    }
}

3.6 快速排序

每次定义一个中轴,让将小值放在左侧,大值放在右侧,依次递归左侧和右侧,每次均定义一个中轴

public class QuickSort {
     

    public static void main(String[] args) {
     
        int[] arr = {
     1,2,-3,20,45,28,-4,6};
        // 定义数组中间的值为中轴
        test01(arr,0,arr.length - 1);
		// 定义数组中第一个值为中轴
//        test02(arr,0,arr.length-1);
        System.out.println("数据交换后:"+Arrays.toString(arr));
    }

    /**
     * 定义数组中间的值为中轴
     * @param arr
     * @param left
     * @param right
     */
    public static void test01(int[] arr,int left,int right){
     
        // 左角标
        int l = left;
        // 右角标
        int r = right;
        // 中轴值
        int pivot = arr[(left + right)/2];
        // 临时变量用于交换使用
        int temp = 0;

        // 此循环是为了将比p小的放置在左边,比p大的放置在右边
        while (l < r){
     

            // 在左边找大于等于p的值,找到才退出
            // arr[l]一直循环下去,可以找到p值,所以不存在死循环
            while (arr[l] < pivot){
     
                // 左边角标需要一致右移,所以需要++
                l++;
            }

            // 在右边找小于等于p的值,找到才退出
            // arr[r]一直循环下去,可以找到p值,所以不存在死循环
            while (arr[r] > pivot){
     
                // 左边角标需要一致左移,所以需要--
                r--;
            }

            // 当左边当角标大于等于右边角标时,说明左边已经全部小于p,右边全部大于p
            if (l >= r){
     
                break;
            }

            // 数据交换
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;

            // 交换数据之后arr[l] = pivot时,r--,前移,避免出现数值一样的
            if (arr[l] == pivot){
     
                r--;
            }

            // 交换数据之后arr[r] = pivot时,l++,后移,避免出现数值一样的
            if (arr[r] == pivot){
     
                l++;
            }
        }

        // 当l=r时,必须l++ r--,避免栈溢出
        if (l == r){
     
            l++;
            r--;
        }

        // 向左递归
        if (left < r){
     
            test01(arr,left,r);
        }
        // 向右递归
        if (right > l){
     
            test01(arr,l,right);
        }

    }

    /**
     * 定义数组中第一个值为中轴
     * @param arr
     * @param left
     * @param right
     */
    public static void test02(int[] arr,int left,int right){
     
        // 递归调用
        if (left >= right){
     
            return;
        }

        // 左边滑动角标
        int l = left;
        // 右边滑动角标
        int r = right;
        // 中轴值
        int pivot = arr[l];
        // 临时变量,用于交换数据是使用
        int temp = 0;
        while (l < r){
     
            // 右角标移动
            while (arr[r] >= pivot && l < r){
     
                r--;
            }
            if (l < r){
     
                temp = arr[r];
                arr[r] = arr[l];
                arr[l] = temp;
            }

            // 左角标移动
            while (arr[l] <= pivot && l < r){
     
                l++;
            }
            if (l < r) {
     
                temp = arr[l];
                arr[l] = arr[r];
                arr[r] = temp;
            }
        }

        // 对中轴左侧做重复操作
        test02(arr,left,l-1);
        // 对中轴右侧做重复操作
        test02(arr,l + 1,right);
    }
}

3.7 堆排序

堆分为大顶堆小顶堆,升序使用大顶堆,降序使用小顶堆

public class HeapSort implements IArraySort {
     

    @Override
    public int[] sort(int[] sourceArray) throws Exception {
     
        // 对 arr 进行拷贝,不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

        int len = arr.length;

        buildMaxHeap(arr, len);

        for (int i = len - 1; i > 0; i--) {
     
            swap(arr, 0, i);
            len--;
            heapify(arr, 0, len);
        }
        return arr;
    }

    private void buildMaxHeap(int[] arr, int len) {
     
        for (int i = (int) Math.floor(len / 2); i >= 0; i--) {
     
            heapify(arr, i, len);
        }
    }

    private void heapify(int[] arr, int i, int len) {
     
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        int largest = i;

        if (left < len && arr[left] > arr[largest]) {
     
            largest = left;
        }

        if (right < len && arr[right] > arr[largest]) {
     
            largest = right;
        }

        if (largest != i) {
     
            swap(arr, i, largest);
            heapify(arr, largest, len);
        }
    }

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

}

3.8 计数排序

public class CountingSort implements IArraySort {
     

    @Override
    public int[] sort(int[] sourceArray) throws Exception {
     
        // 对 arr 进行拷贝,不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

        int maxValue = getMaxValue(arr);

        return countingSort(arr, maxValue);
    }

    private int[] countingSort(int[] arr, int maxValue) {
     
        int bucketLen = maxValue + 1;
        int[] bucket = new int[bucketLen];

        for (int value : arr) {
     
            bucket[value]++;
        }

        int sortedIndex = 0;
        for (int j = 0; j < bucketLen; j++) {
     
            while (bucket[j] > 0) {
     
                arr[sortedIndex++] = j;
                bucket[j]--;
            }
        }
        return arr;
    }

    private int getMaxValue(int[] arr) {
     
        int maxValue = arr[0];
        for (int value : arr) {
     
            if (maxValue < value) {
     
                maxValue = value;
            }
        }
        return maxValue;
    }

}

3.9 桶排序

public class BucketSort implements IArraySort {
     

    private static final InsertSort insertSort = new InsertSort();

    @Override
    public int[] sort(int[] sourceArray) throws Exception {
     
        // 对 arr 进行拷贝,不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

        return bucketSort(arr, 5);
    }

    private int[] bucketSort(int[] arr, int bucketSize) throws Exception {
     
        if (arr.length == 0) {
     
            return arr;
        }

        int minValue = arr[0];
        int maxValue = arr[0];
        for (int value : arr) {
     
            if (value < minValue) {
     
                minValue = value;
            } else if (value > maxValue) {
     
                maxValue = value;
            }
        }

        int bucketCount = (int) Math.floor((maxValue - minValue) / bucketSize) + 1;
        int[][] buckets = new int[bucketCount][0];

        // 利用映射函数将数据分配到各个桶中
        for (int i = 0; i < arr.length; i++) {
     
            int index = (int) Math.floor((arr[i] - minValue) / bucketSize);
            buckets[index] = arrAppend(buckets[index], arr[i]);
        }

        int arrIndex = 0;
        for (int[] bucket : buckets) {
     
            if (bucket.length <= 0) {
     
                continue;
            }
            // 对每个桶进行排序,这里使用了插入排序
            bucket = insertSort.sort(bucket);
            for (int value : bucket) {
     
                arr[arrIndex++] = value;
            }
        }

        return arr;
    }

    /**
     * 自动扩容,并保存数据
     *
     * @param arr
     * @param value
     */
    private int[] arrAppend(int[] arr, int value) {
     
        arr = Arrays.copyOf(arr, arr.length + 1);
        arr[arr.length - 1] = value;
        return arr;
    }

}

3.10 基数排序

/**
 * 基数排序
 * 考虑负数的情况还可以参考: https://code.i-harness.com/zh-CN/q/e98fa9
 */
public class RadixSort implements IArraySort {
     

    @Override
    public int[] sort(int[] sourceArray) throws Exception {
     
        // 对 arr 进行拷贝,不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

        int maxDigit = getMaxDigit(arr);
        return radixSort(arr, maxDigit);
    }

    /**
     * 获取最高位数
     */
    private int getMaxDigit(int[] arr) {
     
        int maxValue = getMaxValue(arr);
        return getNumLenght(maxValue);
    }

    private int getMaxValue(int[] arr) {
     
        int maxValue = arr[0];
        for (int value : arr) {
     
            if (maxValue < value) {
     
                maxValue = value;
            }
        }
        return maxValue;
    }

    protected int getNumLenght(long num) {
     
        if (num == 0) {
     
            return 1;
        }
        int lenght = 0;
        for (long temp = num; temp != 0; temp /= 10) {
     
            lenght++;
        }
        return lenght;
    }

    private int[] radixSort(int[] arr, int maxDigit) {
     
        int mod = 10;
        int dev = 1;

        for (int i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
     
            // 考虑负数的情况,这里扩展一倍队列数,其中 [0-9]对应负数,[10-19]对应正数 (bucket + 10)
            int[][] counter = new int[mod * 2][0];

            for (int j = 0; j < arr.length; j++) {
     
                int bucket = ((arr[j] % mod) / dev) + mod;
                counter[bucket] = arrayAppend(counter[bucket], arr[j]);
            }

            int pos = 0;
            for (int[] bucket : counter) {
     
                for (int value : bucket) {
     
                    arr[pos++] = value;
                }
            }
        }

        return arr;
    }

    /**
     * 自动扩容,并保存数据
     *
     * @param arr
     * @param value
     */
    private int[] arrayAppend(int[] arr, int value) {
     
        arr = Arrays.copyOf(arr, arr.length + 1);
        arr[arr.length - 1] = value;
        return arr;
    }
}

你可能感兴趣的:(算法)