Java实现各种排序算法

1 排序算法分类

在这里插入图片描述

2 算法实现

2.1 冒泡排序

public class BubbleSortDemo {
    static final int SIZE = 30;

    public static void bubbleSort(int[] array) {
        int temp;
        for (int i = 1; i < array.length; i++) {
            for (int j = 0; j < array.length - i; j++) {
                // 将数组升序排序
                if (array[j] > array[j+1]) {
                    temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] array = new int[SIZE];

        System.out.println("排序前的数组为:");
        for (int i = 0; i < SIZE; i++) {
            // 产生 100~199 之间的随机数
            array[i] = (int) (100 + Math.random() * 100);
            System.out.printf("%d ", array[i]);
            if ((i + 1) % 10 == 0)
                System.out.println();
        }

        bubbleSort(array);
        System.out.println("排序后的数组为:");
        for (int i = 0; i < SIZE; i++) {
            System.out.printf("%d ", array[i]);
            if ((i + 1) % 10 == 0)
                System.out.println();
        }
    }
}
排序前的数组为:
108 163 175 158 102 116 137 136 163 153 
196 190 156 167 189 191 188 178 187 169 
排序后的数组为:
102 108 116 136 137 153 156 158 163 163 
167 169 175 178 187 188 189 190 191 196 

Process finished with exit code 0

2.2 选择排序

  选择排序算法也是比较简单的排序算法,其思路比较直观。选择排序算法在每一步中选取最小值来重新排列,从而达到排序的目的。
  选择排序算法通过选择和交换来实块排序,其排序流程如下:

  1. 首先从原始数组中选择最小的1个数据,将其和位于第1个位置的数据交换。
  2. 接着从剩下的 n-1 个数据中选择次小的 1 个数据,将其和第2个位置的数据交换。
  3. 然后不断重复上述过程,直到最后两个数据完成交换。至此,便完成了对原始数组的从小到大的排序。
public class SelectSortDemo {
    static final int SIZE = 20;
    public static void selectSort(int[] array) {
        int index, temp;

        for (int i = 0; i < array.length - 1; i++) {
            index = i;
            for (int j = i + 1; j < array.length; j++) {
                // 若剩余 n-(i+1) 个数据中有小于 a[i]的,则记录该元素下标
                if (array[j] < array[index])
                    index = j;
            }

            if (index != i) {
                temp = array[i];
                array[i] = array[index];
                array[index] = temp;
            }
        }
    }

    public static void main(String[] args) {
        int[] array = new int[SIZE];

        System.out.println("排序前的数组为:");
        for (int i = 0; i < SIZE; i++) {
            // 产生 100~199 之间的随机数
            array[i] = (int) (100 + Math.random() * 100);
            System.out.printf("%d ", array[i]);
            if ((i + 1) % 10 == 0)
                System.out.println();
        }

        selectSort(array);
        System.out.println("排序后的数组为:");
        for (int i = 0; i < SIZE; i++) {
            System.out.printf("%d ", array[i]);
            if ((i + 1) % 10 == 0)
                System.out.println();
        }
    }
}

排序前的数组为:
117 118 164 136 182 143 169 181 120 159 
131 116 149 138 140 166 166 141 138 158 
排序后的数组为:
116 117 118 120 131 136 138 138 140 141 
143 149 158 159 164 166 166 169 181 182 

Process finished with exit code 0

2.3 插入排序

  1. 将一组数据分成两组,分别为有序组、无序组(一般将数据第一个元素视为有序组,剩余为无序组)。
  2. 取出无序组第一个元素,按从后向前的顺序,与有序组的元素逐个比较,插入到有序组合适位置。
  3. 重复步骤 2, 直至无序组元素为空。

插入排序对小规模数据或基本有序数据比较有效。数据有序程度越高,越有效

public class InsertSortDemo {
    static final int SIZE = 20;
    public static void insertSort(int[] array) {
        int temp = 0, j;
        for (int i = 1; i < array.length; i++) {
            temp = array[i];            // 从无序组取出第一个元素
            j = i - 1;                  // i-1 即为有序组最后一个元素(与待插入元素相邻)的下标
            // 按从后向前的顺序,将待插入元素与有序组元素逐个比较
            while (j >=0 && temp < array[j]) {
                array[j+1] = array[j];  // 若不是合适位置,有序组元素向后移动
                j--;
            }
            array[j+1] = temp;          // 找到合适位置,将元素插入
        }
    }

    public static void main(String[] args) {
        int[] array = new int[SIZE];

        System.out.println("排序前的数组为:");
        for (int i = 0; i < SIZE; i++) {
            // 产生 100~199 之间的随机数
            array[i] = (int) (100 + Math.random() * 100);
            System.out.printf("%d ", array[i]);
            if ((i + 1) % 10 == 0)
                System.out.println();
        }

        insertSort(array);
        System.out.println("排序后的数组为:");
        for (int i = 0; i < SIZE; i++) {
            System.out.printf("%d ", array[i]);
            if ((i + 1) % 10 == 0)
                System.out.println();
        }
    }
}


排序前的数组为:
196 112 168 103 158 193 153 159 142 148 
128 167 116 148 158 170 173 168 197 112 
排序后的数组为:
103 112 112 116 128 142 148 148 153 158 
158 159 167 168 168 170 173 193 196 197 

Process finished with exit code 0

2.4 希尔排序(shell排序/缩小增量排序)

先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的),对每个子序列进行直接插入排序。然后缩减增量对子序列进行排序…,直至增量为 1 时,将整个元素序列变为有序。

  1. 假设共有 n 个元素,第一次取增量 gap = n / 2,对 gap 个元素序列进行插入排序
  2. 取 gap = gap / 2,对 gap 个元素序列进行插入排序
  3. 重复步骤 2,直至 gap = 1,将整个元素序列变为有序

array[10] = {198, 104, 146, 153, 198, 132, 103, 170, 185, 184}

  1. gap = 10 / 2 = 5
    序列 1 :198, 132 -> 132, 198
    序列 2 :104, 103 -> 103, 104
    序列 3 :146, 170 -> 146, 170
    序列 4 :153, 185 -> 153, 185
    序列 5 :198, 184 -> 184, 198
  2. gap = 5 / 2 = 2
    序列 1 :198, 146, 198, 103, 185 -> 103, 146, 185, 198, 198
    序列 2 :104, 153, 132, 170, 184 -> 104, 132, 153, 170, 184
  3. gap = 2 / 2 = 1
    序列 1 :103 104 132 146 153 185 170 184 198 198

希尔排序适用于大规模且无序的数据

import java.util.Arrays;

public class ShellSortDemo {
    static final int SIZE = 20;

    public static void shellSort(int[] array) {
        int j, temp, k = 0;
        for (int gap = array.length / 2; gap >= 1; gap /= 2) {
            // 对子序列进行插入排序
            for (int i = gap; i < array.length; i++) {
                temp = array[i];            // 取无序组第一个元素,此元素即为待插入元素
                j = i - gap;                // i-gap 为有序组最后一个元素
                // 按从后向前的顺序,将待插入元素与有序组元素逐个比较
                while (j >= 0 && temp < array[j]) {
                    array[j+gap] = array[j];// 若不是合适位置,有序组元素向后移动
                    j -= gap;
                }
                array[j+gap] = temp;        // 找到合适位置,将元素插入
            }

            System.out.print("第" + ++k + "步排序结果:");
            Arrays.stream(array).mapToObj(item -> item + " ").forEach(System.out::print);
            System.out.println();
        }
    }

    public static void main(String[] args) {
        int[] array = new int[SIZE];

        System.out.println("排序前的数组为:");
        for (int i = 0; i < SIZE; i++) {
            // 产生 100~199 之间的随机数
            array[i] = (int) (100 + Math.random() * 100);
            System.out.printf("%d ", array[i]);
            if ((i + 1) % 10 == 0)
                System.out.println();
        }

        shellSort(array);
        System.out.println("排序后的数组为:");
        for (int i = 0; i < array.length; i++) {
            System.out.printf("%d ", array[i]);
            if ((i + 1) % 10 == 0)
                System.out.println();
        }
    }
}

排序前的数组为:
199 136 110 131 193 102 141 102 193 122 
120 175 162 102 117 197 119 105 136 162 
第1步排序结果:120 136 110 102 117 102 119 102 136 122 199 175 162 131 193 197 141 105 193 162 
第2步排序结果:102 119 102 102 117 120 136 105 131 122 197 141 110 136 162 199 175 162 193 193 
第3步排序结果:102 102 102 105 110 119 117 120 131 122 136 136 162 141 175 162 193 193 197 199 
第4步排序结果:102 102 102 105 110 117 119 120 122 131 136 136 141 162 162 175 193 193 197 199 
排序后的数组为:
102 102 102 105 110 117 119 120 122 131 
136 136 141 162 162 175 193 193 197 199 

Process finished with exit code 0

2.5 快速排序

假设有一个由若干数据组成的序列,

  1. 取第一个数据为基准数据 base,指针 i 指向数据基准数据下一数据,指针 j 指向序列最后一个数据;
  2. 当 i < j 时:先递减 j,从后向前搜索小于 base 的数据,再递增 i ,从前向后搜索大于 base 的数据,若找到,则交换;继续递减 j,递增 i 进行同样的操作,直至 i == j
  3. 当 i == j 时:交换 base 和 array[i],则 base 左侧的数据都小于 base,base 右侧的数据都大于base,此时一次排序结束
  4. 以 base 为界限将数据分成两个子序列,对子序列进行同样的操作,直到整个序列有序

array[10] = { 150 107 182 178 183 154 141 138 182 169 }
在这里插入图片描述

import java.util.Arrays;

public class QuickSortDemo {
    static final int SIZE = 20;
    static int k = 0;

    public static void quickSort(int[] array, int left, int right) {
        // 数组长度为 0 或 1 时直接退出
        if (array == null || left > right || (array.length - 1 <= 0))
            return;

        int base = array[left]; // base:基准数据
        int i = left, j = right;// 让 i 指向数据最左侧,让 j 指向数据最左侧
        int temp;
        while (i != j) {
            // 从右向左搜寻小于 base 的数据
            while (array[j] >= base && i < j)
                j--;
            // 从左向右搜寻大于 base 的数据
            while (array[i] <= base && i < j)
                i++;

            // 交换搜寻到的数据
            if (i < j) {
                temp = array[i];
                array[i] = array[j];
                array[j] = temp;
            }
        }

        // 将基准数放到中间的位置(基准数归位)
        array[left] = array[i];
        array[i] = base;
        /*temp = array[i];
        array[i] = array[left];
        array[left] = temp;*/
        // 一次排序结束,此时 base 左侧的数据全小于 base,右侧的数据全大于base

        quickSort(array, left, i - 1); // 快排 base 左侧数据
        quickSort(array, i + 1, right);// 快排 base 右侧数据
    }

    public static void main(String[] args) {
        int[] array = new int[SIZE];

        System.out.println("排序前的数组为:");
        for (int i = 0; i < SIZE; i++) {
            // 产生 100~199 之间的随机数
            array[i] = (int) (100 + Math.random() * 100);
            System.out.printf("%d ", array[i]);
        }

        quickSort(array, 0, array.length - 1);
        System.out.println("\n排序后的数组为:");
        for (int i = 0; i < SIZE; i++) {
            System.out.printf("%d ", array[i]);
        }
    }
}
排序前的数组为:
100 156 179 151 179 175 122 113 106 155 149 173 144 106 147 128 103 172 170 159 
排序后的数组为:
100 103 106 106 113 122 128 144 147 149 151 155 156 159 170 172 173 175 179 179 
Process finished with exit code 0

2.6 堆排序

  堆排序的关键是首先构造堆结构。堆结构是一种树结构,准确地说是一个完全二叉树。在这个树中每个结点对应于原始数据的一个记录,并且每个结点应满足以下条件:

  • 大顶堆:非叶结点的数据 >= 其左、右子结点的数据。
  • 小顶堆:非叶结点的数据 <= 其左、右子结点的数据。

下面将以大顶堆为例进行介绍(一般升序采用大顶堆,降序采用小顶堆):

  1. 从根节点开始,从上到下、从左到右对每个节点编号,起始序号为 0
  2. 构造堆结构。从最后一个非叶节点开始,从右到左,从下到上构造堆。使二叉树满足大顶堆的定义。此时根节点的值最大
  3. 交换根节点与树中最后一个节点的值。此时尾结点的值最大
  4. 排序尾结点,对其余节点执行步骤 2、3。直至数组有序

在这里插入图片描述

import java.util.Arrays;

public class HeapSortDemo {
    static final int SIZE = 20;
    static int k = 0;

    public static void heapSort(int[] array) {
        // 数组长度为 0 或 1 时直接退出
        if (array == null || (array.length - 1 <= 0))
            return;

        // i 初始化值为最后一个非叶节点序号
        for (int i = array.length / 2 - 1; i >= 0; i--) {
            //从最后一个非叶结点开始,从下到上,从右到左调整堆结构
            adjustHeap(array, i, array.length);
        }

        int temp;
        for (int i = array.length - 1; i > 0; i--) {
            // 交换堆顶元素与末尾元素
            temp = array[i];
            array[i] = array[0];
            array[0] = temp;

            // 除末尾元素外,继续调整剩余元素
            adjustHeap(array, 0, i);
        }
    }

    public static void adjustHeap(int[] array, int i, int length) {
        int temp = array[i];
        // 将完全二叉树从根节点开始,从上到下、从左到右对每个节点编号,起始序号为 0(root)
        // 则,序号为 i 的节点:左子树序号为 2*i+1,右子树序号为 2*i+2

        // 从 i 开始调整堆,然后调整 i 的左子树...
        // j 起始为 i 左子树,j 下一次变化为 j 的左子树,以此类推
        for (int j = i * 2 + 1; j < length; j = j * 2 + 1) {
            // 若 i 有右子树,且右子树 > 左子树,则令 j 指向 i 右子树
            if (j + 1 < length && array[j] < array[j + 1])
                j++;

            // 若 i 的子树 > i,则令 i 为较大值
            if (array[j] > temp) {
                array[i] = array[j];
                i = j; // 令 i 指向被改变的子树
            } else {
                break;
            }

            // 将 i 的原始值赋给被改变的子树
            array[i] = temp;
        }
    }

    public static void main(String[] args) {
        int[] array = new int[SIZE];

        System.out.println("排序前的数组为:");
        for (int i = 0; i < SIZE; i++) {
            // 产生 100~199 之间的随机数
            array[i] = (int) (100 + Math.random() * 100);
            System.out.printf("%d ", array[i]);
        }

        heapSort(array);
        System.out.println("\n排序后的数组为:");
        Arrays.stream(array).mapToObj(e -> e + " ").forEach(System.out::print);
    }
}

排序前的数组为:
114 113 178 147 132 101 121 139 184 116 140 143 187 168 187 127 168 193 147 192 
排序后的数组为:
101 113 114 116 121 127 132 139 140 143 147 147 168 168 178 184 187 187 192 193 
Process finished with exit code 0

2.7 合(归)并排序

  一个待排序的原始数据序列进行合并排序的基本思路是,首先将含有n个结点的待排序数据序列看作由n个长度为1的有序子表组成,将其依次两两合并,得到长度为2的若干有序子表;然后,再对这些子表进行两两合并,得到长度为4的若干有序子表……,重复上述过程,一直到最后的子表长度为,从而完成排序过程。
在这里插入图片描述

import java.util.Arrays;

public class MergeSortDemo {
    static final int SIZE = 20;
    public static void mergeSort(int[] array, int low, int high, int[] temp) {
        if (low < high) {
            int mid = (low + high) / 2;
            mergeSort(array, low, mid, temp);
            mergeSort(array, mid + 1, high, temp);
            merge(array, low, mid, high, temp);
        }
    }

    private static void merge(int[] array, int low, int mid, int high, int[] temp) {
        // j 为左边序列起始索引,k 为右边序列起始索引
        int i = 0, j = low, k = mid + 1;

        // 将两个序列中较小值移入 temp
        while (j <= mid && k <= high) {
            if (array[j] < array[k])
                temp[i++] = array[j++];
            else
                temp[i++] = array[k++];
        }

        // 将左边序列剩余元素移入 temp
        while (j <= mid)
            temp[i++] = array[j++];
        // 将右边序列剩余元素移入 temp
        while (k <= high)
            temp[i++] = array[k++];
        // 用 temp 数组覆盖 array 数组
        for (int l = 0; l < i; l++) {
            array[low + l] = temp[l];
        }
    }

    public static void main(String[] args) {
        int[] array = new int[SIZE];
        int[] temp = new int[array.length];

        System.out.println("排序前的数组为:");
        for (int i = 0; i < SIZE; i++) {
            // 产生 100~199 之间的随机数
            array[i] = (int) (100 + Math.random() * 100);
            System.out.printf("%d ", array[i]);
        }

        mergeSort(array, 0, array.length - 1, temp);
        System.out.printf("\n排序后的数组为:\n%s\n", Arrays.toString(array));
    }
}

排序前的数组为:
179 116 138 161 101 146 196 114 168 118 162 152 106 158 103 173 103 160 188 128 
排序后的数组为:
[101, 103, 103, 106, 114, 116, 118, 128, 138, 146, 152, 158, 160, 161, 162, 168, 173, 179, 188, 196]

Process finished with exit code 0

3 算法执行效率

  在排序算法中还有一个特殊的概念,即稳定排序算法。稳定排序算法主要依照相等的关键字维持记录的相对次序来进行排序。通俗地讲,对于两个有相等关键字的数据 D1 和 D2,在待排序的数据中 D1 出现在 D2 之前,在排序过后的数据中 D1 也在 D2 之前,那么这就是一个稳定排序算法。

  • 冒泡排序算法、插入排序算法、合并排序算法都是稳定排序算法
  • 选择排序算法、Shell排序算法、快速排序算法、堆排序算法都不是稳定排序算法。

  没有一种排序算法是绝对好的,不同的排序算法各有优劣。在实际应用中,需要根据实际的问题来选择合适的排序算法。

  • 如果数据量 n 较小,可采用插入排序法、选择排序法;
  • 如果数据量 n 较大时,应采用时间复杂度为 O( n l o g 2 n nlog_2n ) 的排序方法,如快速排序、堆排序、合并排序。
  • 如果待排序的原始数据呈随机分布,那么快速排序算法的平均时间最短。

在这里插入图片描述

发布了50 篇原创文章 · 获赞 38 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_42250302/article/details/104187184
今日推荐