排序算法总结

冒泡排序

public class Main{
    public void Sort(int[] nums) {
        int i, j;

        for(i = nums.length - 1; i > 0; i--){
            for(j = 0; j < i; j++){
                if(nums[j+1] < nums[j]){
                    nums[j+1] ^= nums[j];
                    nums[j] ^= nums[j+1];
                    nums[j+1] ^= nums[j];
                }
            }
        }
    }
}

每一次循环是比较相邻两个数的大小,小的在前,大的在后,一趟后最后一个数是最大的数。
无论最好还是最坏情况下时间复杂度都是 O ( N 2 ) O(N^2) O(N2),是一种比较低效率的排序方法。

选择排序

public class Main{
    public void Sort(int[] nums) {
        int i, j, min;

        for(i = 0; i < nums.length; i++){
            min = i;
            for(j = i + 1; j < nums.length; j++){
                if(nums[j] < nums[min]){
                    min = j;
                }
            }
            if(min != i) {
                nums[min] ^= nums[i];
                nums[i] ^= nums[min];
                nums[min] ^= nums[i];
            }
        }
    }
}

插入排序

插入排序的思想是假设已经排好了前面一部分,再将后面的元素一个一个插入到排好的序列中,并保持有序。

public class Main{
    public void insertSort(int[] nums) {
        int i, j;

        for(i = 1; i < nums.length; i++){
            for(j = i; j > 0; j--){
                if(nums[j] < nums[j-1]){
                    nums[j] ^= nums[j-1];
                    nums[j-1] ^= nums[j];
                    nums[j] ^= nums[j-1];
                }else{
                    break;
                }
            }
        }
    }
}

插入排序的时间复杂度不稳定,跟原序列的排序情况有关,如果原序列已经是有序(和目标顺序一致)的,那么时间复杂度是 O ( N ) O(N) O(N),而如果是逆序的,则时间复杂度是 O ( N 2 ) O(N^2) O(N2)

归并排序

归并排序的思路是一半一半排序,假设左右两边都已经是有序的了,那么再按大小顺序插入一个新的数组里即可,需要 N N N 次操作,而利用递归来使左右两边达到有序需要 l o g 2 n log_2n log2n 次,因此总的时间复杂度是 O ( N l o g N ) O(NlogN) O(NlogN),又因为需要一个辅助的数组,空间复杂度是 O ( N ) O(N) O(N)

	public void mergeSort(int[] nums) {
        int[] help;

        help = new int[nums.length];
        mergeSortHelper(nums, help, 0, nums.length);
    }

    private void mergeSortHelper(int[] nums, int[] help, int start, int end) {
        int i, j, k, mid;

        if(start + 1 == end)
            return;
        mid= (start + end) / 2;
        mergeSortHelper(nums, help, start, mid);
        mergeSortHelper(nums, help, mid, end);
        i = start;
        j = mid;
        k = start;
        while(i < mid && j < end){
            if(nums[i] < nums[j]){
                help[k++] = nums[i];
                i++;
            }else {
                help[k++] = nums[j];
                j++;
            }
        }
        while(i < mid){
            help[k++] = nums[i++];
        }
        while(j < end){
            help[k++] = nums[j++];
        }
        for(k = start; k < end; k++){
            nums[k] = help[k];
        }
    }

快速排序

快排的大致思路是找到一个中间数,使这个数左边的都小于等于它,右边的数都大于等于它,然后接着递归处理两边的部分。
快排的时间复杂度和原数组里的顺序有关,平均复杂度为 O ( N l o g N ) O(NlogN) O(NlogN),当原数组是有序的时候,时间复杂度达到最差,为 O ( N 2 ) O(N^2) O(N2)

	public void quickSort(int[] nums) {
        quickSortHelper(nums,0, nums.length);
    }

    private void quickSortHelper(int[] nums, int start, int end) {
        int i, j, m, t;

        i = start;
        j = end - 1;
        while(i < j){
            m = i;
            while(i < j && nums[j] >= nums[m]){
                j--;
            }
            while(i < j && nums[i] <= nums[m]){
                i++;
            }
            t = nums[m];
            nums[m] = nums[j];
            nums[j] = nums[i];
            nums[i] = t;
        }
        if(start < i)
            quickSortHelper(nums, start, i);
        if(i < end)
            quickSortHelper(nums, i + 1, end);
    }

非递归版本

	public void quickSort(int[] nums) {
        int i, j, m, t;
        int[] range;
        Stack<int[]> stack;

        stack = new Stack<>();
        stack.push(new int[]{0, nums.length - 1});
        while(!stack.isEmpty()){
            range = stack.pop();
            i = range[0];
            j = range[1];
            while(i < j){
                m = i;
                while(i < j && nums[j] >= nums[m])
                    j--;
                while(i < j && nums[i] <= nums[m])
                    i++;
                t = nums[m];
                nums[m] = nums[j];
                nums[j] = nums[i];
                nums[i] = t;
            }
            if(range[0] < i - 1)
                stack.push(new int[]{range[0], i - 1});
            if(i + 1 < range[1])
                stack.push(new int[]{i + 1, range[1]});
        }
    }

堆排序

由于大根堆具有堆顶是最大值的性质,因此可以根据堆的插入(向上重排)和删除(向下重排)来进行排序。
堆的插入和删除都涉及到堆的重排,时间复杂度是 O ( l o g N ) O(logN) O(logN),需要 N N N 次,因此时间复杂度是 O ( N l o g N ) O(NlogN) O(NlogN)

class Solution {

    public void heapSort(int[] nums) {
        int i, j, next;

        //大根堆
        for(i = 1; i < nums.length; i++){
            next = i;
            //向上重排
            while(next != 0){
                j = (next - 1) / 2;
                if(nums[next] > nums[j]){
                    nums[next] ^= nums[j];
                    nums[j] ^= nums[next];
                    nums[next] ^= nums[j];
                    next = j;
                }else {
                    break;
                }
            }
        }
        //排序
        for(i = nums.length - 1; i > 0; i--){
            nums[i] ^= nums[0];
            nums[0] ^= nums[i];
            nums[i] ^= nums[0];
            //向下重排
            next = 0;
            while(next < i){
                j = 2 * next + 1;
                if(j + 1 < i){
                    j += nums[j] > nums[j+1] ? 0 : 1;
                }
                if(j < i){
                    nums[next] ^= nums[j];
                    nums[j] ^= nums[next];
                    nums[next] ^= nums[j];
                }
                next = j;
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值