记一下今天做的LeetCode每日一题 -502

博主分享了在LeetCode问题502中,从尝试贪心算法优化时间复杂度,到使用有序二叉树,最终通过优先级队列解决问题的曲折过程。过程中涉及到了排序、数据结构优化和代码重构,揭示了对C++库不熟悉的教训。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

每日一题–502

记一下我今天曲折地做这条题目的路

前言:题目介绍

原题是这样的502. IPO - 力扣(LeetCode) (leetcode-cn.com)
在这里插入图片描述
在这里插入图片描述

1. 想都不想 直接贪心算法

是的,对于一个题目当然要来点贪心算法来理解一下流程了(bushi),流程如下图所示:

筛选当前可用项目
选定收益最大的
项目已达上限?
加入新的可用项目
结束

所以就手撸了下面的贪心算法,然后果然不出所料的,超时了。在这里插入图片描述

int findMaximizedCapital(int k, int w, vector<int>& profits, vector<int>& capital) {
        //每次都在可选列表中找到边际收益最大的
        vector<int> projects,remains;

        //循环时间复杂度为n
        for(int i = 0; i < profits.size() ; i++)
        {
            if(capital[i] <= w) projects.push_back(i);
            else remains.push_back(i);
        }
        int max_arg,max_pro;

        // 循环时间复杂度为 k * n
        for(int times = 0; times < k ; times++ ){
            //找到最高边际收益
            max_pro = max_arg = -1;
            cout << "备选projects:" ;
            show_voctor(projects);
            //循环时间复杂度为 n  
            for(int i = 0 ; i < projects.size(); i++){
                if(profits[projects[i]] > max_pro){
                    max_arg = i;
                    max_pro = profits[projects[i]];
                }
            }
            cout << "选中的 project:";
            cout << projects[max_arg] <<endl;

            if(max_pro >= 0)
            {
                w += max_pro;
                projects.erase(projects.begin() + max_arg);
                //添加剩下的
                int remains_size = remains.size();
                // 复杂度为n
                for(int i = 0; i < remains_size ; i++)
                {
                    if(capital[remains[i]] <= w) projects.push_back(remains[i]);
                    else remains.push_back(remains[i]);
                }
                for(int i = 0; i < remains_size ; i++)
                {
                    remains.erase(remains.begin());
                }
                cout << "第" << times << "次投资结束\n" <<endl;              
            }
            else break;
        }
        cout << w << endl;
        return w;
    }

2. 果断开始优化时间复杂度

是的既然超时了就要想办法来简化时间复杂度,于是我天王盖地虎宝塔镇蛇妖一顿优化呀。

最好优化的一部分就是能够被使用的数据部分,排序后,备选项目中加入新的内容就不需要再进行一次遍历了,而直接进行选择就可以了。于是我就进行了归并排序,但这里其实还有一点问题,就是在选择了备选的内容后仍然进行了一次基于利润的归并排序,这里可能存在一定的问题。虽然但是,问题却没出在时间复杂度上,而是很奇怪的问题。

在这里插入图片描述

这个bug真的很让人难受,主要是,这里面几万条数据我也没办法找到问题出在哪里。

3. 换了有序二叉树

虽然不知道为什么会出现上面奇奇怪怪的问题,我猜测可能是第二次归并排序的时候出现了问题,这时候就凭借我还有的一点点数据结构与算法的知识,想起来了有一个东西叫有序二叉树,这东西如果我每次添加一个节点的话,时间复杂度一般是log2n,而弹出最大的数也一般是log2n,于是我就手撸出来了一个二叉树(惊叹于这竟然能跑)。

然后!!刚刚的问题解决了,我大惊失色,果断提交!

结果,又双叒叕超时了。在这里插入图片描述

于是我点开了数据,我nm!
在这里插入图片描述

是的,如图所示,数据有一半都是10000,二叉树结构直接被逼到最坏情况。(我采用的是左子树小于,右子树大于等于的策略)

这怎么办呢?简直太可怕了吧!

于是我,直接给每一个节点加了个计数功能。

        treeNode(int val,treeNode* left = 0, treeNode* right = 0,int num = 1):\
            val(val),left(left),right(right),num(num){}
        int val,num; //num表示有几个这个数字
        treeNode *left,*right;
    };

加了计数功能之后,终于顺利解决了问题。在这里插入图片描述

具体代码如下

void merge_sort(vector<int>& reference, int* ordered_args, int len){
        //reference 是数值的参考对象,ordered_args是用来存储排序后的顺序
        //这部分算法借鉴了菜鸟教程 https://www.runoob.com/w3cnote/merge-sort.html c++ 迭代版
        int* a = ordered_args; 
        int* b = new int[reference.size()]; 

        //初始化索引表
        for(int i = 0; i < len; i++) a[i] = b[i] = i;

        for(int seg = 1; seg < len ; seg *= 2){
            for (int start = 0; start < len; start += seg + seg) {
                int low = start, mid = min(start + seg, len), high = min(start + seg + seg, len);
                int k = low;
                int start1 = low, end1 = mid;
                int start2 = mid, end2 = high;
                while (start1 < end1 && start2 < end2)
                    b[k++] = reference[a[start1]] < reference[a[start2]] ? a[start1++] : a[start2++];
                while (start1 < end1)
                    b[k++] = a[start1++];
                while (start2 < end2)
                    b[k++] = a[start2++];
            }
            int *temp = a;
            a = b;
            b = temp;
        }
        if (a != ordered_args) {
            for (int i = 0; i < len; i++)
                b[i] = a[i];
            b = a;
        }
        delete[] b;
    }

    class OBT//order binary tree,右侧大于等于根
    {
    public:
        class treeNode{
        public:
            treeNode(int val,treeNode* left = 0, treeNode* right = 0,int num = 1):\
                val(val),left(left),right(right),num(num){}
            int val,num; //num表示有几个这个数字
            treeNode *left,*right;
        };

    private:
        treeNode* root = 0;

    public:
        void insert(int num){
            if(!root) {
                root = new treeNode(num);
                assert(root);
                return;
            }
            if(num < root->val)         insert(num, root->left);
            else if(num > root->val)    insert(num, root->right);
            else root->num++;
        }

        void insert(int num, treeNode* &root){
            if(!root) {
                root = new treeNode(num);
                assert(root);
                return;
            }
            if(num < root->val)         insert(num, root->left);
            else if(num > root->val)    insert(num, root->right);
            else root->num++;
        }

        int pop_max(){
            int value;
            treeNode *temp = root;
            if(root->right == NULL){
                root->num--;
                value = root->val;
                if(!root->num){
                    root = root->left;
                    delete temp;
                }
                return value;
            }
            treeNode* node = root;
            while(node->right) {
                temp = node;
                node = node->right;
            }

            value = node->val; //备份需要获取的数值
            node->num--;
            //删除节点
            if(!node->num){
                temp->right = node->left;//有就赋值过去了
                delete node;
            }
            return value;
        };
        bool is_empty(){
            return root == NULL;
        }
        void show()
        {
            if(root->right) show(root->right);
            cout << root->val << ' ';
            if(root->left)  show(root->left);
        }
        void show(treeNode* root){
            if(root->right) show(root->right);
            cout << root->val << ' ';
            if(root->left)  show(root->left);
        }
    };

    int findMaximizedCapital(int k, int w, vector<int>& profits, vector<int>& capital) {
        //先根据capital排序
        int* ordered_args = new int[capital.size()];
        merge_sort(capital,ordered_args, capital.size());
        //for(int i = 0 ; i < capital.size() ; i++) cout << capital[ordered_args[i]] <<endl;
        if(capital[ordered_args[0]] > w) return w;
        int now_arg = 0; //表示第几个位置之前的项目都有足够资金做了
        int times = 0; //表示选择了几个项目了
        OBT available_proj;
        //最外层循环,选中足够数量的项目即可结束
        while(times < k){
            //指针右移到能够买的位置
            for(; now_arg < capital.size()?capital[ordered_args[now_arg]] <= w:false ;\
                now_arg++ ) available_proj.insert(profits[ordered_args[now_arg]]);
            //根据利益进行排序
            if(available_proj.is_empty()) return w;
            w += available_proj.pop_max();
            times++;
        }
        return w;
    }

5. 看了题解,有被伤害到

看了下题解,大家用的都是优先级队列,把我手敲了n长的代码给用库代替了,o(╥﹏╥)o认栽了,确实是我对cpp不够熟悉,这些内置的库都不太清楚。但我很奇怪,内置的优先级队列在我看到的blog里(未深究)是使用的最大堆,理论上跟普通二叉树的表现应该差不多,但是实际上就不会超时呢?不过手敲一敲数据结构还是挺有意思的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值