Java常见排序算法之冒泡排序、选择排序、插入排序、快速排序

一、冒泡排序(Bubble Sort)

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行,直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。

冒泡排序可以说是最简单的排序算法,想必大多数人都已经熟练掌握,这里分享自己在学习过程中觉得挺不错的两种实现。
写法一:基于常规冒泡写法,加了是否发生交换的判断,来减少冒泡次数

	/**
     * 冒泡排序 升序
     * 优化冒泡次数:通过标记是否发生交换来判断目前数组是否有序
     * @param array 待排序数组
     */
    public static void bubbleSort(int[] array) {
        for (int i = array.length - 1; i > 0; i--) {
            // 是否发生交换
            boolean swapped = false;
            for (int j = 0; j < i; j++) {
                if (array[j] > array[j + 1]) {
                    int temp = array[j + 1];
                    array[j + 1] = array[j];
                    array[j] = temp;
                    swapped = true;
                }
            }
            if (!swapped) {
                // 没有发生交换,则说明数组已有序,停止冒泡
                break;
            }
        }
    }

写法二:通过上一次冒泡中最后一次发生交换的下标,来决定下一次冒泡结束的索引位置;同时也进行是否发生交换的判断,更好的优化排序效率(这个思路本人也第一次接触,感觉非常妙)

    /**
     * 冒泡排序 升序 较优法
     * 优化冒泡次数:冒泡结束的索引位置为0则直接结束
     * 优化比较次数:通过上一次冒泡中最后一次发生交换的下标,来决定下一次冒泡结束的索引位置
     * @param array 待排序数组
     */
    public static void bubbleSort2(int[] array) {
        // 冒泡结束的索引位置
        int end = array.length - 1;
        do {
            // 冒泡最后一次发生交换的下标
            int lastSwapIndex = 0;
            for (int j = 0; j < end; j++) {
                if (array[j] > array[j + 1]) {
                    int temp = array[j + 1];
                    array[j + 1] = array[j];
                    array[j] = temp;
                    lastSwapIndex = j;
                }
            }
            end = lastSwapIndex;
        } while (end != 0);
    }

二、选择排序(Selection sort)

选择排序(Selection sort)是一种简单直观的排序算法。
它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。

我这里选择排序的实现就算是比较中规中矩的,只要理解了它的工作原理思想,想必实现也不在话下。

	/**
     * 选择排序 升序
     * @param array 待排序数组
     */
    public static void selectionSort(int[] array) {
        for (int i = 0; i < array.length - 1; i++) {
            int min = i;
            for (int j = i + 1; j < array.length; j++) {
                if (array[min] > array[j]) {
                    min = j;
                }
            }
            if (min != i) {
                int temp = array[i];
                array[i] = array[min];
                array[min] = temp;
            }
        }
    }

三、插入排序(Insertion Sort)

插入排序(Insertion Sort),一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法。
插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。

	/**
     * 插入排序 升序
     * @param array 待排序数组
     */
    public static void insertionSort(int[] array) {
        // i:待插入的元素下标
        for (int i = 1; i < array.length; i++) {
            int temp = array[i];
            // j:已排序区域尾元素的下标
            int j;
            for (j = i - 1; j >= 0; j--) {
                if (temp > array[j]) {
                    break;
                } else {
                    array[j + 1] = array[j];
                }
            }
            array[j + 1] = temp;
        }
    }

四、快速排序(Quick Sort)

快速排序(Quick Sort)通过多次比较和交换来实现排序,其排序流程如下:
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

快速排序的递归实有两种方式,①单边循环 ②双边循环。下面就来看看两种方式是如何实现快排的吧。
① 单边循环

  • pivot选择最右元素作为基准点元素
  • j指针负责找到比基准点小的元素,一旦找到则与i进行交换
  • i指针维护小于基准点元素的边界,也是每次交换的目标索引
  • 最后基准点与i交换,i即为分区位置
    /**
     * 递归快排 单边循环
     * @param array 待排序数组
     * @param left  待排序分区起始索引
     * @param right 待排序分区结束索引
     */
    public static void quickSort(int[] array, int left, int right) {
        if (left >= right) {
            return;
        }
        int pivot = array[right];
        int i = left;
        for (int j = left; j < right; j++) {
            if (array[j] < pivot) {
                if (j != i) {
                    int tem = array[i];
                    array[i] = array[j];
                    array[j] = tem;
                }
                i++;
            }
        }
        
        if (i != right) {
            array[right] = array[i];
            array[i] = pivot;
        }

        quickSort(array, left, i - 1);
        quickSort(array, i + 1, right);
    }

② 双边循环

  • pivot选择最左元素作为基准点元素
  • rear指针负责从右向左找比基准点小的元素,
  • front指针负责从左向右找比基准点大的元素,
  • 一旦找到二者交换,直至frontrear相交
  • 最后基准点与front(此时frontrear相等)交换,front即为分区位置
	/**
     * 递归快排 双边循环
     * @param array 待排序数组
     * @param left  待排序分区起始索引
     * @param right 待排序分区结束索引
     */
    public static void quickSort2(int[] array, int left, int right) {
        if (left >= right) {
            return;
        }
        int pivot = array[left];
        int front = left;
        int rear = right;
        while (front < rear) {
            while (front < rear && array[rear] > pivot) {
                rear--;
            }
            while (front < rear && array[front] <= pivot) {
                front++;
            }
            if (front < rear) {
                int tem = array[front];
                array[front] = array[rear];
                array[rear] = tem;
            }
        }

        if (front != left) {
            array[left] = array[front];
            array[front] = pivot;
        }

        quickSort2(array, left, front - 1);
        quickSort2(array, front + 1, right);
    }

双边循环注意点:
1.基准点在左边,前后指针查找,需要先rear指针再front指针
2.循环实现前后指针查找时,循环条件一定要加上front < rear

欢迎大家讨论交流,如有错误不当之处,还请读者批评指正。
觉得有收获的话帮忙点个赞吧,

你可能感兴趣的:(面试准备,java,排序算法,算法)