常用排序算法总结(Java)

来源:互联网 发布:百度统计怎么查看数据 编辑:程序博客网 时间:2024/04/29 19:52

自己学习排序算法的练习总结

https://github.com/pengyuntao/Sort_Algorithms_Java

判断排序算法是否稳定:就是判断原本相等的两个数前后相对位置有没有变化,没有变化就是稳定,变化了就是不稳定。

冒泡排序

需要两两比较,大的数字逐渐移动到后边,很像水中的气泡冒出来,成为冒泡排序。

时间复杂度为O(n^2),稳定的排序算法

public class BubbleSort {    //排序算法稳定 排序后与排序前两个相等的元素前后相对位置没有变化    private void swap(int[] array, int key1, int key2) {        int temp = array[key1];        array[key1] = array[key2];        array[key2] = temp;    }    /**     * 相邻两两比较,大数逐渐冒泡到最右边,内部循环的长度越来越小     *     * @param array     */    private void bubbleSort(int[] array) {        for (int i = 0; i < array.length; i++) {            boolean isSwap = false;            for (int j = array.length - 2; j >= i; j--) {                if (array[j] > array[j + 1]) {                    swap(array, j, j + 1);                    isSwap = true;                }            }            if (!isSwap) {                break;            }        }    }    @Test    public void testCase1() {        int[] arr = {85, 24, 11, 17, 63, 45, 17, 31, 96, 50};        bubbleSort(arr);        System.out.println(Arrays.toString(arr));    }}

快速排序

主要是分治思想,对每个子数组进行快排,直到全排完为止。partition方法作用是把数据分成两部分,小的在左边,大的在右边。有两种写法:一种是两个指针从两端向中间扫描,另一种是两个指针同时从左向右扫描。最终结果是把比枢轴位置的数大的调整到数组右边,比枢轴位置的数小的数调整到数组左边。

时间复杂度为O(nlogn)是不稳定的排序

public class QuickSort {    private void swap(int[] array, int key1, int key2) {        int temp = array[key1];        array[key1] = array[key2];        array[key2] = temp;    }    private int partition1(int[] array, int low, int high) {        // 利用Random随机获取low到high之间的一个值作枢轴索引,并将其与array[high]进行交换        int pivot = new Random().nextInt(high - low) + low;        swap(array, pivot, high);        // 单向扫描:right向右遍历array,当遇到小于pivot的元素,则与left当前指向的元素进行交换,        // 否则直接跳过,一直到达array的最右边right为主动遍历,left为被动遍历        int left = low, right = low;        while (right < high) {            if (array[right] < array[high]) {                // 如果array[r]是array中最大的元素,则right遇到的所有元素都要与left指向的元素进行交换                // 如果left与right相等,则交换是不必要的                if (left != right) {                    swap(array, left, right);                }                left++;                right++;            } else {                // 如果array[r]是array中最小的元素,则left会一直停留在l处                right++;            }        }        // 最终需要将pivot元素换回其排序最终位置,也就是left当前的位置        swap(array, left, high);        return left;    }    private int partition2(int[] array, int low, int high) {        int key = array[low];        while (low < high) {            while (low < high && array[high] >= key) {                high--;            }            swap(array, low, high);            while (low < high && array[low] <= key) {                low++;            }            swap(array, low, high);        }        return low;    }    private void quickSort1(int[] array, int low, int high) {        if (low < high) {            int pivot = partition1(array, low, high);            quickSort1(array, low, pivot - 1);            quickSort1(array, pivot + 1, high);        }    }    private void quickSort2(int[] array, int low, int high) {        if (low < high) {            int pivot = partition2(array, low, high);            quickSort2(array, low, pivot - 1);            quickSort2(array, pivot + 1, high);        }    }    @Test    public void testCase1() {        int[] arr = {85, 24, 11, 17, 63, 45, 17, 31, 96, 50};        quickSort1(arr, 0, arr.length - 1);        System.out.println(Arrays.toString(arr));    }    @Test    public void testCase2() {        int[] arr = {85, 24, 11, 17, 63, 45, 17, 31, 96, 50};        quickSort2(arr, 0, arr.length - 1);        System.out.println(Arrays.toString(arr));    }}

堆排序

从小到大排列,使用大顶堆。依次取出大顶堆的顶部元素,对剩下的元素调整堆。重复上述过程。
堆排序时间复杂度为O(nlogn),是不稳定的排序算法
public class HeapSort {    private void swap(int[] array, int key1, int key2) {        int temp = array[key1];        array[key1] = array[key2];        array[key2] = temp;    }    //i为根节点 2*i+1为左孩子 2*i+2为右孩子 length/2-1为最后一个非叶子节点    /**     * 堆排序     *     * @param array     */    private void heapSort(int[] array) {        for (int i = array.length / 2 - 1; i >= 0; i--) {            adjustHeap(array, i, array.length - 1);        }        for (int i = array.length - 1; i > 0; i--) {            swap(array, 0, i);            adjustHeap(array, 0, i - 1);        }    }    /**     * 调整堆     *     * @param array 数组     * @param start 第一位索引     * @param end   最后一位索引     */    private void adjustHeap(int[] array, int start, int end) {        int root = array[start];        int r = start;        for (int i = start * 2 + 1; i < end; i = i * 2 + 1) {            if (i < end && array[i] < array[i + 1]) {                //选出两个孩子中大的一个                i++;            }            //因为是大顶堆,如果root比孩子大了那么就不用交换了            if (root > array[i]) {                break;            }            //把大的孩子赋值给局部的根元素            array[r] = array[i];            r = i;        }        array[r] = root;//插入到正确的位置    }    @Test    public void testCase1() {        int[] arr = {85, 24, 11, 17, 63, 45, 17, 31, 96, 50};        heapSort(arr);        System.out.println(Arrays.toString(arr));    }}

插入排序

插入排序的思想类似于摸扑克牌,把摸的牌放入对应位置。默认第一个有序,依次把后边无序的向前边有序的元素中插入,使前边有序的序列向后扩展,直到所有的有序为止。

插入排序时间复杂度为O(n^2),是稳定的排序算法。

public class InsertSort {    public void directInsertSort(int[] array) {        for (int i = 1; i < array.length; i++) {            int j = i;            int temp = array[j];            while (j > 0 && temp < array[j - 1]) {                array[j] = array[j - 1];                j--;            }            array[j] = temp;        }    }    @Test    public void testCase1() {        int[] arr = {85, 24, 11, 17, 63, 45, 17, 31, 96, 50};        directInsertSort(arr);        System.out.println(Arrays.toString(arr));    }}

希尔排序

希尔排序的思想是让数组基本逐渐有序,就是按照一定的步长对元素跳着进行插入排序,然后不断的缩小步长直到1为止。例如初始步长为2,对索引为1,3,5位置的元素进行插入排序,然后缩小步长为1,就是对1,2,3,4,5位置元素排序。由于数组逐渐基本有序,所以每次插入的次数会减少,从而提高了效率。注意每次步长序列的选择目前还是个难题,自己适情况而定,但是最后一次排序步长必须为1.
时间复杂度O(nlogn)~O(n^2),不稳定的排序算法。
public class ShellSort {    public void shellSort(int[] array) {        int inc = array.length;        do {            inc = inc / 3 + 1;//增量,最后一趟排序必须为1            for (int i = inc; i < array.length; i++) {//下边代码为对增量序列进行插入排序                int j = i;                int temp = array[j];                while (j >= inc && array[j - inc] > temp) {                    array[j] = array[j - inc];                    j = j - inc;                }                array[j] = temp;            }        } while (inc > 1);    }    @Test    public void testCase1() {        int[] arr = {0, 50, 10, 20, 30, 70, 40, 80, 60};        shellSort(arr);        System.out.println(Arrays.toString(arr));    }}

选择排序

选择排序就是内部循环先找到最小的,然后做一次交换,减少了交换次数。

时间复杂度为O(n^2)

public class SelectionSort {    private void swap(int[] array, int key1, int key2) {        int temp = array[key1];        array[key1] = array[key2];        array[key2] = temp;    }    private void simpleSelectionSort(int[] array) {        for (int i = 0; i < array.length; i++) {            int min = i;            for (int j = i; j < array.length; j++) {                if (array[j] < array[min]) {                    min = j;                }            }            if (min != i) {                swap(array, min, i);            }        }    }    @Test    public void testCase1() {        int[] arr = {85, 24, 11, 17, 63, 45, 17, 31, 96, 50};        simpleSelectionSort(arr);        System.out.println(Arrays.toString(arr));    }}

归并排序

归并其实就是先递归分成多部分,直到分成两两比较排序,然后在一点点合并排序。分治思想。

时间复杂度是O(nlogn),稳定的排序算法,缺点需要O(n)的辅助空间。

public class MergeSort {    public void mergeSort(int[] array) {        if (array == null) {            return;        }        mergeSort(array, new int[array.length], 0, array.length - 1);    }    private void mergeSort(int[] array, int[] temp, int start, int end) {        if (start < end) {            int pivot = (start + end) / 2;            mergeSort(array, temp, start, pivot);            mergeSort(array, temp, pivot + 1, end);            merge(array, temp, start, pivot, end);        }    }    private void merge(int[] array, int[] temp, int start, int pivot, int end) {        int i = start, j = pivot + 1, k = start;        while (i <= pivot && j <= end) {            if (array[i] <= array[j]) {                temp[k++] = array[i++];            } else {                temp[k++] = array[j++];            }        }        while (j <= end) {            temp[k++] = array[j++];        }        while (i <= pivot) {            temp[k++] = array[i++];        }        for (int n = start; n <= end; n++) {            array[n] = temp[n];        }    }    @Test    public void testCase1() {        int[] arr = {50, 10, 20, 30, 70, 40, 80, 60};        mergeSort(arr);        System.out.println(Arrays.toString(arr));    }    @Test    public void testCase2() {        int[] arr = {85, 24, 11, 17, 63, 45, 17, 31, 96, 50};        mergeSort(arr);        System.out.println(Arrays.toString(arr));    }}

桶排序 

桶排序是直接利用一个函数把当前元素映射到指定的桶中,有种哈希的感觉。

基数排序

分别对数字的每一位进行排序

计数排序

准备带排列范围内的数量的桶,并按照范围内的数字编号,初始化为0,遍历待排序的数组,遇到一个数字,对应的桶中数字加1,然后按桶顺序及其内部统计的次数来输出排序数字。

2 0
原创粉丝点击