leetcode912 排序算法总结

解法一:插入排序:

最经典的算法,常数时间非常少,有效练手

核心思想有点像dp,最长上升子序列(顺便膜拜下二分做法)处理好前面后,处理当前元素,做一次遍历即可。

进阶问题:选择排序是一种两两比较的贪心算法,为什么小数组用插入排序,不用选择排序

  1. 插入排序有dp思想,左边是已排序数组,期望中可以减少比较数目,平均比一半就能找到自己的位置,而选择排序是固定次数,比较多
  2. 具有排序稳定性,是一种更为普适的算法,选择排序不具有这种特性。如数组【2,2,1],会导致两个2位置互换。
cpp 复制代码
class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        int n = nums.size();
        for(int i=1;i<n;i++)
        {
            int tmp = nums[i];
            int j = i-1;
            for(j=i-1;j>=0;j--)
            {
                if(nums[j] > tmp)nums[j+1] = nums[j];
                else break;
            }

            nums[j+1] = tmp;
        }

        return nums;
    }
};

解法二:归并排序

归并排序是分治算法的一种,必须要用到额外数组,可以用一个数组代替。和回溯类似,均匀处理两边后返回,再用双指针对两边排好序的数组进行处理

用到的两个高阶技巧:

  1. 排序稳定性:在合并两个有序数组阶段,用了nums[p1] <= nums[p2],这是为了保证相等的元素位置不发生变化,这样在多维度排序时,不会导致前一个尺度发生变化,是排序中一种非常重要的特性。
  2. 使用插入排序减少常数时间开销:在小数据量时调用,会有一些好处。
cpp 复制代码
typedef vector<int> V;

class Solution {
public:
    void insert_sort(V& nums, int l, int r)
    {
        int n = r-l+1;
        if(n <= 1)return;

        for(int i=l+1;i<=r;i++)
        {
            int j = i-1, tmp = nums[i];
            while(j>=l && nums[j] > tmp)
            {
                nums[j+1] = nums[j];
                j--;
            }
            nums[j+1] = tmp;
        }
    }

    void merge_sort(V& nums, int l, int r, V&tmp)
    {
        int len = r-l+1;
        if(len <= 10)
        {
            insert_sort(nums, l, r);
            return;
        }

        int mid = l + (r-l)/2;
        merge_sort(nums, l, mid, tmp);
        merge_sort(nums, mid+1, r, tmp);

        int p1 = l, p2 = mid+1, p_tmp = l;

        while(p1 <= mid && p2 <= r)
        {
            if(nums[p1] <= nums[p2])
            {
                tmp[p_tmp++] = nums[p1++];
            }
            else tmp[p_tmp++] = nums[p2++];
        }

        while(p1 <= mid)tmp[p_tmp++] = nums[p1++];
        while(p2 <= r)tmp[p_tmp++] = nums[p2++];

        for(int i=l;i<=r;i++)nums[i] = tmp[i];
    }

    vector<int> sortArray(vector<int>& nums) {
        int n = nums.size();
        V tmp(n, 0);

        merge_sort(nums, 0, n-1, tmp);

        return nums;
    }
};

解法三:快速排序

1 双路快排

这里小数据量没有切插入排序,实际也是可以切的,懒得写了(狗头)

这个算法样例会卡在一个2,一堆3(可能几万个)的情况,这种情况下,快排会退化到O(N方),所以我们要考虑一种更为优化的算法,即三路快排。

cpp 复制代码
typedef vector<int> V;

class Solution {
public:
    void moveMedian(V& nums, int l, int r)
    {
        int mid = l + (r-l)/2;
        if(nums[l] >= nums[r] && nums[l] <= nums[mid])swap(nums[l], nums[r]);
        else if(nums[l] <= nums[r] && nums[l] >= nums[mid])swap(nums[l], nums[r]);
        else if(nums[mid] <= nums[l] && nums[mid] >= nums[r])swap(nums[mid], nums[r]);
        else if(nums[mid] >= nums[l] && nums[mid] <= nums[r])swap(nums[mid], nums[r]);
       
    }

    void qsort(V& nums, int l, int r)
    {
        int len = r-l+1;
        if(len <= 1)return;

        // partition
        moveMedian(nums, l, r);
        int pivot = nums[r];
        int ct = l; // 指针记录到第一个不符合条件的位置 或待处理位置

        for(int i=l;i<r;i++)
        {
            if(nums[i] <= pivot)
            {
                swap(nums[i], nums[ct]);
                ct++;
            }
        }
        swap(nums[r], nums[ct]);

        // printf("%d %d %d\n", l, r, ct);

        qsort(nums, l, ct-1);
        qsort(nums, ct+1, r);
    }

    vector<int> sortArray(vector<int>& nums) {
        int n = nums.size();

        qsort(nums, 0, n-1);

        return nums;
    }
};
  1. 三路快排
    三路快排对双路快排的逻辑做了一定改进,但是核心是一样的,右边都是从r-1开始,把大于和小于的都放好后,中间的就自动都是相等的部分了。然后再交换一下,第一个大于的元素,和pivot即可。
    注意难点:不能for循环,因为当右边元素换过来时,这个元素是还没有检查的,所以需要再检查一次。左边的能换过来是因为已经检查过,如果是小于的,那一定是i = p1,不然不会留在那个位置,如果是等于的,那换到中间就对了。
cpp 复制代码
typedef vector<int> V;

class Solution {
public:
    void moveMedian(V& nums, int l, int r)
    {
        int mid = l + (r-l)/2;
        if(nums[l] >= nums[r] && nums[l] <= nums[mid])swap(nums[l], nums[r]);
        else if(nums[l] <= nums[r] && nums[l] >= nums[mid])swap(nums[l], nums[r]);
        else if(nums[mid] <= nums[l] && nums[mid] >= nums[r])swap(nums[mid], nums[r]);
        else if(nums[mid] >= nums[l] && nums[mid] <= nums[r])swap(nums[mid], nums[r]);
       
    }

    void qsort(V& nums, int l, int r)
    {
        int len = r-l+1;
        if(len <= 1)return;

        // partition
        moveMedian(nums, l, r);
        int pivot = nums[r], p1 = l, p2 = r-1;

        int i = l;
        while(i <= p2)
        {
            if(nums[i] < pivot)
            {
                swap(nums[i], nums[p1++]);
                i++;
            }
            else if(nums[i] > pivot)swap(nums[i], nums[p2--]);
            else
            {
                i++;
            }
        }
        
        swap(nums[r], nums[p2+1]);

        qsort(nums, l, p1-1);
        qsort(nums, p2+2, r);
    }

    vector<int> sortArray(vector<int>& nums) {
        int n = nums.size();

        qsort(nums, 0, n-1);

        return nums;
    }
};
相关推荐
@猿程序2 小时前
ShardingSphere自定义分片算法与Redis动态规则加载实战
网络·redis·算法
Share_Shun2 小时前
【定位引导】多点对位算法
算法
炽烈小老头2 小时前
【 每天学习一点算法 2026/03/18】全排列
学习·算法
Book思议-2 小时前
【数据结构实战】判断链表是否有环:快慢指针法(Floyd 判圈算法)
c语言·数据结构·算法·链表
liuyao_xianhui2 小时前
优选算法_位运算_只出现一次的数字3_C++
开发语言·数据结构·c++·算法·leetcode·链表·动态规划
lihao lihao2 小时前
滑动窗口
数据结构·算法
咕叽吧咔2 小时前
LeetBook乐扣题库 142. 环形链表 II
java·数据结构·leetcode·链表
Jordannnnnnnn2 小时前
复试打卡day30
算法
郝学胜-神的一滴2 小时前
贪心策略实战Leetcode 860题:柠檬水找零问题的优雅解法
数据结构·c++·算法·leetcode·职场和发展