排序算法总结

1.绪论

排序是算法题中常见的操作,本文将介绍各种排序算法的思想和实现。

2. 选择排序

2.1 思想

选择排序其实就是从左往右遍历,假设当前处理的位置为i,则i左侧为已经拍好序的元素,i右侧为待排序的元素,每次从待排序中元素选择出最大值,与i位置进行交换。

可以看出选择排序每次处理,左边已经是最终排序的元素,右侧是待排的元素,只需要从待排元素中的找到最小值放到i处就可以了。

2.2 代码

java 复制代码
private void  selectedSortedAlgorithm(int nums[]){
        //i为正在处理的元素,应该放入剩余元素最小值
        for (int i = 0; i < nums.length - 1; i++) {
            //寻找[i,nums.length)中的最小值
            int minIndex = i;
            for (int j = i; j < nums.length - 1; j++) {
                if(nums[j] < nums[minIndex]) {
                    minIndex = j;
                }
            }
            //交换元素
            swap(nums, i, minIndex);
        }
    }

    private void swap(int[] nums, int i, int minIndex) {
        int temp = nums[minIndex];
        nums[minIndex] = nums[i];
        nums[i] = temp;
    }

选择排序的时间复杂度为O(nlogn)。

3.插入排序

3.1 思想

插入拍序其实就是从左向右遍历,假设当前轮待处理元素为i,则[0,i)为局部有序的元素,[i,length - 1)为待处理元素。所以将i元素放到[0,i)的正确位置上即可。

可以看出,插入排序,i左侧[0,i)为局部有序的序列,他们的顺序并不是最终顺序。

3.2 代码

java 复制代码
  private void insertSortedAlgorithm(int nums[]){
        for (int i = 1; i < nums.length - 1; i++) {
            //i为当前处理的元素
            for(int j = i; j > 0 ; j--) {
                if(nums[j] < nums[j - 1]) {
                    swap(nums,j,j-1);
                }
            }
        }
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[j];
        nums[j] = nums[i];
        nums[i] = temp;
    }

插入排序的时间复杂度为O(nlogn)。

4. 归并排序

4.1 思想

归并排序其实是利用分治的思想,每次将区间平分为左右两个区间。只要左右区间为有序的区间,利用merge操作(即将两个有序数合并成一个有序数组),便能保证最后得到的区间是有序的。可以看出,利用分支的思想,最后得到的是合并两个数字,这两个数字本身就是有序的。

4.2 代码

java 复制代码
    public int[] sortArray(int[] nums) {
        // 假设现在有两个数组
        sortArrayIncr(nums, 0, nums.length - 1);
        return nums;
    }

    // 将[l,r]的元素进行排序
    private void sortArrayIncr(int[] nums, int l, int r) {
        if (l >= r) {
            return;
        }
        // 左边元素排序
        int p = (l + r) / 2;
        // 对[l,p]进行排序
        sortArrayIncr(nums, l, p);
        // 对[p+1,r]进行排序
        sortArrayIncr(nums, p + 1, r);
        // merge操作[l,p],[p+1,r]
        merge(nums, l, p, r);
    }

    private void merge(int[] nums, int l, int p, int r) {
        int arr[] = new int[r - l + 1];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = nums[l + i];
        }
        // [0,p-l]和[p-l+1, r]
        int m = 0;
        int n = p - l + 1;
        int k = l;
        for (; k <= r; k++) {
            if (m > p - l) {
                nums[k] = arr[n++];
            } else if (n > r - l) {
                nums[k] = arr[m++];
            } else if (arr[n] > arr[m]) {
                nums[k] = arr[m++];
            } else {
                nums[k] = arr[n++];
            }
        }

    }

5.快速排序

5.1 思想

快速排序其实也是采用分治的思想,首先获取一个元素e,然后采用partition操作,将整个数组中比元素e小的元素放在左边,比元素e大的元素放在右边。然后递归,左边执行该操作,右边也执行该操作,最后便能排好序。

那partition操作是如何进行的呢,如图所示

假设的[l+1,p)<=e,此时p表示下一个要处理的元素,[q,r]的元素都大于e。所以如果待处理元素arr[p]<=e的话,直接p++,如果arr[p]>e的话,直接交换arr[q-1]和arr[p]。

5.2 代码

java 复制代码
class Solution {
    public int[] sortArray(int[] nums) {
        quickSort(nums, 0, nums.length - 1);
        return nums;
    }

    private void quickSort(int[] nums, int left, int right) {
        if (left >= right) {
            return;
        }
        // 执行partition操作
        int p = partition(nums, left, right);
        // 左边排序
        quickSort(nums, left, p - 1);
        // 右边排序
        quickSort(nums, p + 1, right);

    }

    private int partition(int[] nums, int left, int right) {
        // 随机一个元素和第一个元素交换
        Random random = new Random();
        swap(nums, left, random.nextInt(right - left) + left);
        // 取出待处理元素
        int e = nums[left];
        // [left+1,p) <=e,待处理元素为[p,q)
        int p = left + 1;
        // [q,right] > e
        int q = right + 1;
        // 无待处理元素
        while (p < q) {
            if (nums[p] <= e) {
                p++;
            } else {
                // 交换p和q-1的元素
                q--;
                swap(nums, p, q);
            }
        }
        // 其中p-1为最后一个小于e的元素
        swap(nums, left, p - 1);
        return p - 1;
    }

    private void swap(int[] nums, int p, int q) {
        int num = nums[q];
        nums[q] = nums[p];
        nums[p] = num;
    }
}

5.3 优化

5.3.1 如果数组本来就近乎有序

这个时候,由于递归树会失衡,可能退化为O(n*n)的时间复杂度。所以我们在获取元素e的时候可以不直接获取第一个元素,而是从待排序数组中随机取一个元素,作为元素e。

5.3.2 如果数组有大量重复元素

大量重复元素的话,可以考虑3路快排,即小于e的占一个区间,等于e的占一个区间,大于e的占一个区间。

6.排序思想的应用

快排的partition操作和归并排序的merge操作,是一种很好的思想,在算法题中右很多应用。

6.1 leetcode 215

6.1.1 题目描述

6.1.2 思路

可以利用的快排的partition操作,假设partition操作完成后,会找到第p大的元素,如果k小于p,便在[0,p]的范围内搜索第k大的元素,如果k>p,便在[p+1,nums.length-1]的范围内搜索第k大的元素。

6.1.3 代码

java 复制代码
class Solution {
    public int findKthLargest(int[] nums, int k) {
        return findKthLargest(nums, 0, nums.length - 1, nums.length - k);
    }

    // 寻找[l,r]位置下标为k的元素
    public int findKthLargest(int[] nums, int l, int r, int k) {

        // partition
        int p = partition(nums, l, r);

        if (k == p) {
            return nums[k];
        }
        // [l,p-1]寻找下标为k的元素
        if (k < p) {
            return findKthLargest(nums, l, p - 1, k);
        } else {
            // [p+1,r]寻找k-p-1大元素
            return findKthLargest(nums, p + 1, r, k);
        }

    }

    // 将[l,r]执行partition
    private int partition(int[] nums, int l, int r) {
        Random random = new Random();
        swap(nums, l, random.nextInt(r - l + 1) + l);
        int e = nums[l];
        // [l+1,p) <= e
        int p = l + 1;
        // [q,r] > e
        int q = r + 1;
        while (p < q) {
            if (nums[p] <= e) {
                p++;
            } else {
                q--;
                swap(nums, p, q);
            }
        }
        swap(nums, p - 1, l);
        return p - 1;

    }

    private void swap(int[] nums, int p, int q) {
        int tmp = nums[p];
        nums[p] = nums[q];
        nums[q] = tmp;
    }
}
相关推荐
智驱力人工智能2 小时前
工厂智慧设备检测:多模态算法提升工业安全阈值
人工智能·算法·安全·边缘计算·智慧工厂·智能巡航·工厂设备检测
2501_924731474 小时前
城市路口识别准确率↑31%!陌讯时空建模算法在交通拥堵识别中的突破
人工智能·算法·目标检测·计算机视觉·目标跟踪
熬了夜的程序员5 小时前
【华为机试】208. 实现 Trie (前缀树)
数据结构·算法·华为od·华为
小O的算法实验室7 小时前
2024年ESWA SCI1区TOP,自适应种群分配和变异选择差分进化算法iDE-APAMS,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
不吃洋葱.8 小时前
左子树之和
算法
金融小师妹8 小时前
基于AI量化模型的比特币周期重构:传统四年规律是否被算法因子打破?
大数据·人工智能·算法
数据智能老司机9 小时前
图算法趣味学——最短路径
数据结构·算法·云计算
快去睡觉~9 小时前
力扣109:有序链表转换二叉搜索树
算法·leetcode·链表
gopher_looklook10 小时前
Go并发实战:singleflight 源码解读与二次封装
数据结构·后端·go
是Dream呀10 小时前
YOLOv8深度解析:从架构革新到应用实践
人工智能·算法