常用排序算法总结(Java)

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

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值