八大排序算法(Java)

本文概述了快速排序(O(nlogn)复杂度,不稳定,空间O(logn))、堆排序(O(nlogn),不稳定,空间O(1))和归并排序(O(nlogn),稳定,空间O(n))的原理、实现与特点。深入理解排序算法在数据结构中的应用。


整理一下学习数据结构算法时遇到的排序问题,将几个经典的算法记录下来。包括冒泡,选择,插入,希尔,快速,归并和基数排序。前三个比较简单,剩下的算法还是有难度的,本文按照先原理后实现的模式来介绍。

快速排序(平均O(nlogn)、最好O(nlogn)、最差O(n2)、不稳定,空间O(logn) )

思想:对于一个数组,定义左右指针分别指向最左和最后的元素,然后定义一个标杆,比如选择最左边的元素。每次快排中,首先从从右往左遍历,直到找到一个比标杆小的元素,把它赋给左指针指向的位置;然后从左往右遍历,直到找到一个比标杆大的元素,把它赋给刚刚右指针停留的位置。重复上述的过程,最后把标杆值赋给左右指针相遇的位置。这样一轮快排下来,就做到了标杆左边都是比他小的,标杆右边都是比他大的。然后对标杆左右子数组递归地调用快排。

class Solution {
    public int[] sortArray(int[] nums) {
        quickSort(nums, 0, nums.length - 1);
        return nums;
    }
    public void quickSort(int[] a, int left, int right){
        if(left >= right) return;
        int temp = a[left];//标杆
        int i = left, j = right;//左右指针
        while(i < j){
            while(i < j && a[j] >= temp) j--;
            a[i] = a[j];
            while(i < j && a[i] <= temp) i++;
            a[j] = a[i];
        }
        a[i] = temp;
        quickSort(a, left, i - 1);
        quickSort(a, i + 1, right);
    }
}

堆排序(平均O(nlogn)、最好O(nlogn)、最差O(nlogn)、不稳定,空间O(1) )

堆排序的理解
思想:堆的每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
数组从逻辑上满足下面的条件,就是一个堆结构,

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
堆排序的基本思想是:将待排序数组构造成一个大顶堆,此时,整个数组的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

class Solution {
    public int[] sortArray(int[] nums) {
        heapSort(nums);
        return nums;
    }

    public void heapSort(int[] nums) {
        int len = nums.length - 1;
        buildMaxHeap(nums, len);
        for (int i = len; i >= 1; --i) {
            swap(nums, i, 0);
            len -= 1;
            maxHeapify(nums, 0, len);
        }
    }
    public void buildMaxHeap(int[] nums, int len) {//构建堆
        for (int i = len / 2; i >= 0; --i) {
            maxHeapify(nums, i, len);
        }
    }
    public void maxHeapify(int[] nums, int i, int len) {
        for (; (i << 1) + 1 <= len;) {
            int lson = (i << 1) + 1;
            int rson = (i << 1) + 2;
            int large;
            if (lson <= len && nums[lson] > nums[i]) {
                large = lson;
            } else {
                large = i;
            }
            if (rson <= len && nums[rson] > nums[large]) {
                large = rson;
            }
            if (large != i) {
                swap(nums, i, large);
                i = large;
            } else {
                break;
            }
        }
    }
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

偷懒写法,用优先队列

class Solution {
    public int[] sortArray(int[] nums) {
        PriorityQueue<Integer> q = new PriorityQueue<>(new Comparator<Integer>(){
            public int compare(Integer o1, Integer o2){
                return o1 - o2;
            }
        });
        for(int i = 0; i < nums.length; i++){
            q.offer(nums[i]);
        }
        for(int i = 0; i < nums.length; i++){
            nums[i] = q.poll();
        }
        return nums;
    }
}

归并排序(平均O(nlogn)、最好O(nlogn)、最差O(nlogn)、稳定、O(n) )

思想:分治。把要排序的数组也分开,方法就是对半分,一直递归地分组,直到不能再分。然后开始治,采用线性合并的方法。需要定义一个辅助数组来存放排序好的部分。

class Solution {
    int[] tmp;
    public int[] sortArray(int[] nums) {
        tmp = new int[nums.length];
        mergeSort(nums, 0, nums.length - 1);
        return nums;
    }

    public void mergeSort(int[] nums, int l, int r) {//递归地分
        if (l >= r) {
            return;
        }
        int mid = (l + r) >> 1;
        mergeSort(nums, l, mid);
        mergeSort(nums, mid + 1, r);
        int i = l, j = mid + 1;
        //开始线性合并
        int cnt = 0;
        while (i <= mid && j <= r) {
            if (nums[i] <= nums[j]) {
                tmp[cnt++] = nums[i++];
            } else {
                tmp[cnt++] = nums[j++];
            }
        }
        while (i <= mid) {
            tmp[cnt++] = nums[i++];
        }
        while (j <= r) {
            tmp[cnt++] = nums[j++];
        }
        for (int k = 0; k < r - l + 1; ++k) {
            nums[k + l] = tmp[k];
        }
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值