优选算法【专题七:分治】

目录

1、75.颜色分类

2、912.排序数组

3、215.数组中第k个最大元素

[4、剑指Offer 40. 最小的k个数](#4、剑指Offer 40. 最小的k个数)

5、分治---归并排序

[6、LCR 170.交易逆序对的总数](#6、LCR 170.交易逆序对的总数)

7、315.计算右侧小于当前元素的个数

8、439.翻转对


分治:分而治之

将一个大问题,转化为若干个相同或相似的小问题,然后在子问题的基础上再把它划分为更小的相同或相似的小问题。

1、75.颜色分类

数组划分三块、随机选择基准元素

解法:思想是"双指针" 但是比双指针多了一个指针 ---"三指针"

cpp 复制代码
class Solution 
{
public:
    void sortColors(vector<int>& nums) 
    {
        int n = nums.size();
        int left = -1, right = n, i = 0;//left指向负一 right指向n 
        while(i < right)
        {
            if(nums[i] == 0) swap(nums[++left],nums[i++]);
            else if(nums[i] == 1) i++;
            else swap(nums[--right], nums[i]);
        }
    }
};

2、912.排序数组

面试的时候可能会手撕快排】

利用数组划分实现快速排序

快速排序---算一个基准元素,左边的小于等于key,右边的大于key ,key的位置就是他排序后的位置。 然后,再对左边的区间,选一个基准元素(key1),元素的左边为小于等于这个基准元素(key1)的,右边为大于这个基准元素(key1)的。 右边区间和左边区间的做法相同。 快排最重要的就是数据划分这一步。-----但是数组重复的话,时间复杂度就变为O(n^2)

1、用数组分三块的思想来实现快排

将数组分为小于key,等于key,大于key。 等于key的区间就是已经确定了,再去小于key和大于key的区间排序。

用指针i、left、right 三个指针,用i遍历整个数组,left指针标记小于key的区域最右边,right标记大于key区域的最左边

数组重复的话,只需一次就可以解决问题。相比于数组划分两次是超级快的。---时间复杂度是O(n)

如何优化:用随机的方式选择基准元素。

cpp 复制代码
class Solution 
{
public:
    vector<int> sortArray(vector<int>& nums) 
    {
        srand(time(NULL));
        qsort(nums, 0, nums.size() - 1);
        return nums;
    }
    //快排
    void qsort(vector<int>& nums, int l, int r)//l是数组0位置, r是数组最后一个位置
    {
        if(l > r) return;
        //数组分三块
        int key = getRandom(nums, l, r);//随机的方式生成基准元素
        int i = l, left = l - 1,right = r + 1;
        while(i < right)//i=right时说明元素已经被扫描完了
        {
            if(nums[i] < key) swap(nums[++left], nums[i++]);
            else if(nums[i] == key) i++;
            else swap(nums[--right] , nums[i]);
        }
        //到此数组已经被成功的分为三块
        //[l,left][left + 1, right - 1][right, r]
        //再继续递归
        qsort(nums, l, left);//递归左边
        qsort(nums, right, r);//递归右边
    }
    int getRandom(vector<int>& nums, int left,int right)
    {
        int r = rand();
        return nums[r % (right - left + 1) + left];//生成随机基准元素的公式
    }
};

3、215.数组中第k个最大元素

这个题其实是一个TOP K 问题

top k问题的几种问法: 1、第K大 2、第K小,(返回1个元素)

3、前K大 4、前K小 (数组排完序之后找出前K个大或者小的元素)

不管什么问法,涉及到TOP K问题,他的解法就是固定的。

堆排序 和 快速选择算法

快速选择算法就是基于快排去实现的

堆排序解决问题:

cpp 复制代码
class Solution
{
public:
    int findKthLargest(vector<int>& nums, int k) 
    {
            //建一个k个元素的小堆
            priority_queue<int, vector<int>, greater<int>> 
            pq(nums.begin(), nums.begin()+k);

            for(int i = k; i < nums.size(); ++i)
            {
                if(nums[i] > pq.top())
                {
                    pq.pop();
                    pq.push(nums[i]);//将 nums[i] 插入堆中,保持堆的大小为 k,因为小顶堆中最小的元素总是堆顶元素,所以这样可以确保堆中始终保存的是当前找到的前 k 大的元素。
                }
            }

            return pq.top();
    }

    
};

快速选择算法:时间复杂度是O(N)

快速选择算法是基于快排来实现的。首先利用选择出来基准元素将数组分三块,然后根据每一块元素的个数来和k进行比较。因为我们要找第k大,所以现从大的部分写这样好写,当c>=k,也就是在大数的区域的数的个数比K值大,那就当然啦,我们就去right到f区间去找就好了。下面两个情况意思相同。【根据数量划分,只用去某一个区间去寻找最终结果】

cpp 复制代码
class Solution
{
public:
    int findKthLargest(vector<int>& nums, int k) 
    {
        srand(time(NULL));//种一个随机数的种子
        return qsort(nums, 0, nums.size() - 1, k);
    }
    int qsort(vector<int>& nums, int l, int r, int k)
    {
        if(l == r) return nums[l];
        //1、随机选择基准元素
        int key = getRandom(nums, l, r);//在l到r这段区间上给我随机返回一个数就可以
        //2、根据基准元素将数组分三块
        int left = l - 1, right = r + 1, i = l;//left和right先指向-1和最后一个元素的下一个
        while(i < right)
        {
            if(nums[i] < key) swap(nums[++left], nums[i++]);
            else if(nums[i] == key) i++;
            else swap(nums[--right], nums[i]);
        }
        //3、分情况讨论去哪个区间去寻找的问题
        int c = r - right + 1;
        int b = right - 1 -(left + 1) + 1;
        if(c >= k) return qsort(nums, right, r, k);
        else if(b + c >= k) return key;
        else return qsort(nums, l , left, k - b -c);//去l到left区间里面去找k-b-c大的元素
    }
    int getRandom(vector<int>& nums, int left, int right)
    {
        return nums[rand() % (right - left + 1) + left];//right - left + 1是一个偏移量,偏移量+left找到一个下标
    }
};

给qsort函数传进行一个l和r,代表数组的开始和结束位置。

但是在进行快排的时候,left指向数组的-1,right指向数组最后一个元素的下一个位置。

1、取基准元素 2、利用快排思想将数组分三块 3、分情况讨论 再去进行递归

4、剑指Offer 40. 最小的k个数

解法一:排序--调用容器,直接进行排序,排完序之后把最小的数拿出来就好了。【时间复杂度 NlogN】

解法二:利用堆。 找最小的k个数,我们可以创建大小为k的大根堆。【时间复杂度 Nlogk】

解法三:快速选择算法。【时间复杂度N】

所以来说,快速选择算法的时间复杂度是最优的

当分类讨论第三种情况的时候,我们直接就去大于key的区间去找k-a-b个较小的元素就好了,这个时候其实a和b区间还是没有排序的,我们其实就不用排序,直接输出就好了。【我们选择基准元素的时候是等概率划分的】

cpp 复制代码
class Solution
{
public:
    vector<int> getLeastNumbers(vector<int>& nums, int k) 
    {
        srand(time(NULL));
        qsort(nums, 0, nums.size() - 1, k);
        return {nums.begin(), nums.begin() + k};
    }

    void qsort(vector<int>& nums, int l, int r, int k)
    {
        if(l >= r) return;
        //1、随机选择一个基准元素+数组分三块
        int key = getRanddom(nums, l, r);
        int left = l - 1, right = r + 1, i = l;//i=l去遍历数组
        while(i < right)
        {
            if(nums[i] < key) swap(nums[++left], nums[i++]);
            else(nums[i] == key) i++;
            else swap(nums[right--], nums[i]);
        }
        //现在数组已经分为三块进行分类讨论
        int a = left - l + 1;
        int b = right - 1 - (left + 1) + 1;
        if(a > k) return qsort(nums, l, left, k);
        else if(a + b >= k) return;
        else qsort(nums, right, r, k - a - b);

    }
    int getRanddom(vector<int>& nums, int l, int r)
    {
        return nums[rand() % (r - l + 1) + l];
    }
};

5、分治---归并排序

用排序数组这道题讲归并排序。

归并排序的原理:

根据中间点mid把数组分成两部分,然后先把左边部分排序,排左边部分的时候相当于又是一个排序,一直往下分开,知道数组只有一个元素的时候,就向上返回。去右边排序,再向上返回,当两边都排完序之后,就合并两个有序数组,那么这一层就变为一个有序数组了,然后再返回到上一层,直到返回到第一层,那么数组的左边就已经排完序了。右边排序和左边排序的方法相同。

快排:

快排和归并排序算法其实是非常类似的,只是处理数组的时机不同。

归并排序是:先排左边,再排右边,然后再合并两个。就相当树的后序遍历。

快排:先把数组分三块,然后再把左边的分三块,再把右边的分三块。(先把分好,然后去左边分,分好再分右边)

cpp 复制代码
class Solution 
{
    vector<int> tmp;  // 1. 定义私有成员变量tmp:临时数组,用于合并两个有序子数组时暂存数据
public:
    // 2. 对外暴露的排序接口函数,输入待排序数组nums,返回排序后的数组
    vector<int> sortArray(vector<int>& nums) 
    {
        tmp.resize(nums.size());  // 2.1 初始化临时数组大小,和待排序数组nums一致
        mergeSort(nums, 0, nums.size() - 1);  // 2.2 调用归并排序核心函数,排序整个数组(区间[0, nums.size()-1])
        return nums;  // 2.3 返回排序后的数组 ---我们用上面的函数将nums排完序,再返回nums数组就好了
    }

    // 3. 归并排序核心函数:递归排序nums中[left, right]区间的元素
    void mergeSort(vector<int>& nums, int left, int right)
    {
        // 3.1 递归终止条件:区间只有1个元素(left==right)或无元素(left>right),无需排序
        if(left >= right) return;
        
        // 3.2 划分区间:计算中间点mid,把[left, right]拆分为[left, mid]和[mid+1, right]
        // (left + right) >> 1 等价于 (left + right)/2,位运算效率更高(题目保证无溢出)
        int mid = (left + right) >> 1;
        
        // 3.3 递归排序左半区间[left, mid]
        mergeSort(nums,left, mid);
        // 3.4 递归排序右半区间[mid+1, right]
        mergeSort(nums,mid + 1, right);
        
        // 3.5 合并两个有序子数组:[left, mid]和[mid+1, right]都是有序的,合并为整体有序
        int cur1 = left;    // cur1:左子数组的起始指针(指向[left, mid]的当前元素)
        int cur2 = mid + 1; // cur2:右子数组的起始指针(指向[mid+1, right]的当前元素)
        int i = 0;          // i:临时数组tmp的下标指针(记录当前填充位置)
        
        // 3.5.1 双指针遍历两个子数组,按升序填充到tmp中
        while(cur1 <= mid && cur2 <= right)
        {
            // 取较小的元素放入tmp,同时移动对应指针
            tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++];
        }
        
        // 3.5.2 处理左子数组剩余元素(如果cur1还没遍历完[left, mid])
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        // 3.5.3 处理右子数组剩余元素(如果cur2还没遍历完[mid+1, right])
        while(cur2 <= right) tmp[i++] = nums[cur2++];
        
        // 3.6 把tmp中合并好的有序数据,复制回原数组nums的[left, right]区间
        for(int i = left; i <= right; i++)
        {
            // tmp的下标是从0开始的,nums的区间是[left, right],所以用i-left映射tmp的下标
            nums[i] = tmp[i - left];
        }
    }
};

【保留草稿】

理解:这个归并排序对数组的处理时机就相当于树的后续遍历,也就决定了递归的顺序。即,先递归左边再递归右边,然后处理跟【就会决定代码写的顺序】

cpp 复制代码
// 3.5.2 处理左子数组剩余元素(如果cur1还没遍历完[left, mid])
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        // 3.5.3 处理右子数组剩余元素(如果cur2还没遍历完[mid+1, right])
        while(cur2 <= right) tmp[i++] = nums[cur2++];
        

解释:首先肯定这两个while循环只能进去一个,那么为什么没有移动完的指针,的那边指针后面的元素可以直接放到tmp呢?答案是:因为如果递归到最后的话比如说只有两个元素的时候,判断哪个小直接放进tmp数组中,另一边剩的元素直接放进去,然后就会把这个升序排序的两个元素给返回上一层,那么对于这一层的左边其实就已经是升序的了。那么右边会和左边一样,也会返回同样升序的数组,再对这一行的两个升序的数组进行合并,不就是对元素进行逐一比较谁小谁放进tmp,然后指针没有移动到最后的那一边,后面的肯定是要比另一边的所有大的,所以直接放到tmp中就好喽~

6、LCR 170.交易逆序对的总数

解法一:暴力枚举

拿一个数,在后面这段区间里面找找有多少比他小,再固定下一个数,找找有多少个数比他小。

两层for循环即可(肯定会超时)

解法二:归并排序

思维流程---如何想到要用归并排序

1、左半部分->右半部分->左一右一

求逆序对的时候,将数组分为两部分,左边和右边,先求左边有几组逆序对(eg:a对),再求右边有几组逆序对(b个),再在左边选一个数,右边选一个数,看有几组逆序对(c个)。

那么,a + b + c就是总的逆序对个数 【其实本质上还是一个暴力枚举】

2)左半部分-->左排序--->右半部分--->右排序--->左一右一

先将数组按中间值分为两部分,左边挑出来a个,把左部分排有序(这个时候是不影响挑出出来的a组的,因为是在挑完之后排序的),右边挑出来b个,然后排序,然后一左一右挑,虽然两边已经被排完序了,但是这个时候挑并不会影响挑出来的组数,因为左右挑我们是不关心左边右边的顺序的。

我们可以通过前两步分析出来,这道题是用分治的方法来解决的。继续分析又得知,如果数组有序的话我们就可以统计出来一大堆

我们为了让归并递归的算法统一,所以给左一右一也加一个排序

策略一:找出该数之前,有多少个比我大 这个策略应该使用升序

策略二:找出该数之后有多少个数比我小,这个策略应该使用降序

这道题其实用策略一的升序和策略二的降序都可以实现。

下面代码使用升序来解决(找出该值前面所有比他大的)

cpp 复制代码
class Solution 
{
    //使用归并排序,所以我们来一个辅助数组,来实现两个有序数组的合并
    int tmp[50010];
public:
    int reversePairs(vector<int>& nums) 
    {
        return mergeSort(nums, 0, nums.size() - 1);//通过这个函数直接返回逆序对的个数
    }
    int mergeSort(vector<int>& nums, int left, int right)
    {
        int ret = 0;
        //处理边界情况 -- 没有逆序对,直接返回0
        if(left >= right) return 0;
        //1、找中间点将数组分为两部分
        int mid = (left + right) >> 1;
        //数据被我们划分为:[left , mid] [mid + 1, right]
        //2、左边的个数 + 排序 + 右边的个数 + 排序
        ret += mergeSort(nums, left, mid);
        ret += mergeSort(nums, mid + 1, right);
        //此时,逆序对的个数已经找到了,并且已经排序了
        //3、一左一右的个数
        int cur1 = left, cur2 = mid + 1, i = 0;//i = 0帮助我们来遍历辅助数组
        //策略一:找前面比我大的数(升序)
        while(cur1 <= mid && cur2 <= right)
        {
            if(nums[cur1] <= nums[cur2]) 
            {
                tmp[i++] = nums[cur1++];
            }
            else //nums[cur1] > nums[cur2] --升序cur1值第一次大于cur2值,cur1后面的值全都比cur大
            {
                ret += mid - cur1 + 1;
                tmp[i++] = nums[cur2++];//完成排序和cur指针移动---因为我们排升序,所以先把小的放到数组
            }
        }
        //处理一下排序 //把剩的那个放到数组中
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        while(cur2 <= right) tmp[i++] = nums[cur2++];
        //把辅助数组上的元素覆盖到原数组
        for(int j = left; j <= right; j++)
        {
            nums[j] = tmp[j - left];
        }
        return ret;
    }
};

先合并5、6 返回给父节点,再合并7、8返回给父节点,再合并3、4返回给父节点

cpp 复制代码
if(nums[cur1] <= nums[cur2])//这个不满足条件,谁小谁移动,cur1++ 
{
    tmp[i++] = nums[cur1++];
}

【代码解释】为什么当cur1的值小于等于cur2的值就能把cur1的值直接放进tmp数组中?难道不会出现cur2后面还有元素比cur2的值更小吗(这个后面指的是[cur2, right]这个区间,即要进行合并的部分,这只是数组的一部分。

肯定不会出现,因为,要进行合并的两个数组肯定是先进行过排序的【因为在递归中,先进行判断看不是符合逆序对的要求,再进行将小的先放到tmp数组,返回过来要进行合并的两个区间的数都是已经经过排序的!!!这点很重要。 每次要进行数组合并之前,都会拿到nums数组,只是每次要合并的区间不一样,区间是不断扩大的,父节点要进行合并操作的数组范围肯定是包含了两个孩子的区域的,只是将两个区域的顺序重新进行排序(升序),其实在排序过程中,就会完成左右区间各取一个时的逆序对的取值。

7、315.计算右侧小于当前元素的个数

题目意思:找出这个位置的后面有多少个数比我小

这个不就是我们上一道题的策略二吗,找出该值后面比他小的数,因此,我们知道用降序来解决这道题比较好。(前面大,后面小)--- 那么救谁大谁移动

nums[cur1] <= nums[cur2] :因为要找的是cur2的值小,这个就是不符合条件的,所以我们没有在后面没有找到比这个值小的元素,所以cur2++

nums[cur1] > nums[cur2] :那么就是找到了,cur2后面的所有元素都比cur1小,cur1++

我们需要找到(nums[cur1]),给原始下标的位置ret+=right-cur2+1

所以,最重要的就是我们要找到当前元素**(nums[cur1])**的原始下标,合并的时候其实是在左边拿一个右边拿(说到底其实都是左边拿一个右边拿一个,因为会分到只有一个元素,再从下往上合并),所有,当左拿一右拿一的时候,元素的位置可能就不是元素的时的位置了,因为进行排序过了。

我们可以创建一个index数组,数组的元素就是原始数组的下标,将原始数组元素和数组下标进行绑定,这样元素进行排序移动的时候,就可以很快找到元素的原始x

cpp 复制代码
class Solution 
{
    //这些放在全局就不用给mergeSort函数中传参了
    vector<int> ret;//返回的数组
    vector<int> index;//记录nums中当前元素的原始下标
    int tmpNums[500010];//合并两个数组的时候的辅助数组
    int tmpIndex[500010];//给数组下标的辅助数组
public:
    vector<int> countSmaller(vector<int>& nums) 
    {
        int n = nums.size();
        ret.resize(n);
        index.resize(n);
        //初始化一下index数组--存nums数组的原始下标
        for(int i = 0; i < n; i++)
            index[i] = i;
        mergeSort(nums, 0, n - 1);
        return ret;
    }
    void mergeSort(vector<int>& nums, int left, int right)
    {
        //归并排序的主逻辑
        if(left >= right) return ;
        //1、算出中间,将数组分为两块
        int mid = (left + right) >> 1;
        //2、数组被划分为 [left, mid] [mid+1, right]
        mergeSort(nums,left, mid);//先处理左边
        mergeSort(nums, mid + 1, right);//再处理右边
        //3、我们要找该值后面所有比他小的数 降序
        //左边一个右边一个
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right)
        {
            if(nums[cur1] <= nums[cur2])
            {
                tmpNums[i] = nums[cur2];//tmpNums数组进行操作的时候,tmpIndex也要同步操作
                tmpIndex[i++] = index[cur2++];
            }
            else//nums[cur1] > nums[cur2]
            {
                ret[index[cur1]] += right - cur2 + 1;//因为原始下标和元素是绑定的,所以index[cur2]就是cur2位置的值的下标---
                tmpNums[i] = nums[cur1];
                tmpIndex[i++] = index[cur1++];
            }
        }
        //4、在while循环之后处理剩下的排序过程
        while(cur1 <= mid) //让cur1的值放到两个辅助数组中
        {
            tmpNums[i] = nums[cur1];
            tmpIndex[i++] = index[cur1++];
        }
        while(cur2 <= right) //让cur1的值放到两个辅助数组中
        {
            tmpNums[i] = nums[cur2];
            tmpIndex[i++] = index[cur2++];
        }
        //5、还原两个数组
        for(int j = left; j <= right; j++)
        {
            nums[j] = tmpNums[j - left];
            index[j] = tmpIndex[j - left];
        }
    }
};
cpp 复制代码
else//nums[cur1] > nums[cur2]
{
    ret[index[cur1]] += right - cur2 + 1;//因为原始下标和元素是绑定的,所以index[cur2]就是cur2位置的值的下标---
    tmpNums[i] = nums[cur1];
    tmpIndex[i++] = index[cur1++];
}

【解释代码】为什么是index[cur1]? 因为这个时候已经找到nums[cur1] > nums[cur2]了,那么我们就知道,此时,cur1值比cur2后面的都大,所以计算的个数就是right - cur2 + 1,而且我们计算的是比cur1值小的所有数的个数,肯定就是index[cur1]喽

8、439.翻转对

前面数大于后面哪个数的两倍 --- 的个数

逆序对的算法是一比一的比较,和归并排序是一样的。

本题是和某一个数的2倍进行比较。和归并排序算法是不一样的,我们此时就不能用归并排序思想来解决了。

利用数组有序,来计算反转对----时间复杂度为O(n)---利用单调性,使用双指针

策略一:计算当前元素后面有多少元素的二倍比我小。---看前面元素(用降序排列)

先让cur1不动(盯着cur1看),cur2向后移动,看后面有多少个两倍比我小,看到什么时候cur2值小于cur1值,如果cur2的值的两倍比我大,cur2就往后移动,是一个降序数组,cur2往后移动的时候值才会变小。当cur2移动到某个位置,发现cur2的两倍比cur1的值小,又应为数组是降序的,那么cur2后面的所有数的二倍都比cur1的值小。此时,ret+=right-cur2+1。 那么我们此时就已经找完了当左边部分为cur1的时候右边所有的比他大的数了,就将cur1++。但是此时我们不能将cur2再移动到开始(回退),因为这样时间复杂度就会变为n^2logn,所以,我们要利用一下数组有序的特性,为什么cur2不用回退?

因为,1的值比2大,右边比他小的都要移动到cur2,现在cur1右移了,移动到2了,那么比他小的数肯定不可能是在cur2的左边,所以不用回退,直接让cur2向右移动,知道出现了比cur1值小的位置,就用公式统计。知道cur1或者cur2移动到最后为止。

策略二:计算当前元素前面有多少元素的一半比我大。-----看后面元素(用升序排列)

先计算反转对,再合并两个有序数组。

cpp 复制代码
class Solution 
{
    int tmp[50010];
public:
    int reversePairs(vector<int>& nums) 
    {
        return mergeSort(nums, 0, nums.size() - 1);
    }
    int mergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return 0;
        int ret = 0;
        //1、先根据中间值将数组分为两部分
        int mid = (left + right) >> 1;
        //[left , mid] [mid+1, right]
        //2、先把左边的反转对 再计算右边的反转对
        ret += mergeSort(nums, left, mid);
        ret += mergeSort(nums, mid + 1, right);
        //3、一左一右 先计算发转对的数量
        int cur1 = left, cur2 = mid + 1, i = left;
        //当前元素,后面的元素有多少个比我小(降序)
        while(cur1 <= mid)//归你管cur1
        {
            while(cur2 <= right && nums[cur2] >= nums[cur1] / 2.0) cur2++;
            if(cur2 > right)
                break;
            ret += right - cur2 + 1;
            cur1++;
        }
        //4、合并两个有序数组
        cur1 =left, cur2 = mid + 1;
        while(cur1 <= mid && cur2 <= right)
            tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur2++] : nums[cur1++];
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        while(cur2 <= right) tmp[i++] = nums[cur2++];
        for(int j = left; j <= right; j++)
        {
            nums[j] = tmp[j];
        }
        return ret;
    }
};
相关推荐
程序员敲代码吗2 小时前
C++与硬件交互编程
开发语言·c++·算法
8K超高清2 小时前
博冠8K广播级讯道摄像机获国际设计大奖
网络·算法·fpga开发·接口隔离原则·智能硬件
你怎么知道我是队长2 小时前
C语言---排序算法1---冒泡排序法
c语言·算法·排序算法
qq_537562672 小时前
C++与Java性能对比
开发语言·c++·算法
忆锦紫2 小时前
图像锐化算法:Robert/Sobel/Laplacian锐化算法及MATLAB实现
图像处理·算法·计算机视觉·matlab
m0_686041612 小时前
C++中的策略模式应用
开发语言·c++·算法
沉默-_-2 小时前
力扣hot100普通数组(1)--C++
java·数据结构·算法·leetcode·数组
wasp5202 小时前
拒绝 OOM:Apache Fesod 高性能 Excel 处理架构全景解析
算法·架构·apache·excel
爱吃生蚝的于勒2 小时前
【Linux】进程信号的保存(二)
linux·运维·服务器·c语言·数据结构·c++·算法