力扣中等题记录C++

文章目录

  • 链表用法
    • 2.两数相加
    • 143.重排链表
    • 92.反转链表 II
    • 24.两两交换链表中的节点
  • 优先队列用法
    • 2208. 将数组和减半的最少操作次数
  • 逻辑思维题
    • 822.反转卡片游戏
  • 字符串处理
    • 722. 删除注释
  • 动态规划
    • 1749. 任意子数组和的绝对值的最大值

链表用法

在写链表的过程中,顺便写一下emplace_back和push_back的区别,刚开始用的时候就发现题解里经常用的是emplace_back,但是我实测用push_back好像并没有什么区别,写题的时候遇到不知道是哪报错,顺便查了一下两者的区别。
比较通俗一点的解释就是emplace_back 只会调用一次构造函数,不会在原地构造完之后再次调用复制构造函数,也就是emplace_back 的优势在于可以避免创建临时对象,造成性能损失。

2.两数相加

题意:两个非空链表分别对应一个整数,每个节点存储整数上的某一位,要求将这两个数相加,并需要将和以链表形式返回。
思路:
本来想的是再创建一个新的链表,然后把链表l1和l2的相加的结果建造一个新链表,但是写着写着突然发现要用好多链表,感觉这样很浪费内存,突发奇想能不能放在l1里,然后再避开l1为空指针的情况,最后再进位。

    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* bianli = l1;
        ListNode* ans = l1;
        while(l1 || l2)
        {
           if(l1 && l2)
            {
                l1 -> val = l1 -> val + l2 -> val;
                l2 = l2 -> next;
            }
            if(l1 -> next == NULL)
            {
                l1 -> next = l2;
                break;
            }
            l1 = l1 -> next;
        }
        int temp = 0;
        while(bianli)
        {
            bianli -> val += temp;
            temp = 0;
            while(bianli -> val >= 10)
            {
                temp ++;
                bianli -> val -= 10;
            }
            if(temp && bianli -> next == NULL)
            {
                ListNode* tem = new ListNode(temp);
                temp = 0;
                bianli -> next = tem;
            }
            bianli = bianli -> next;
        }
        return ans;
    }

143.重排链表

题目大意:给定一个单链表的头节点,原始排列顺序为L1 -> L2 -> L3 -> L4 -> … -> Ln
需要更改排列顺序为L1 -> Ln -> L2 -> Ln-1 -> L3 -> Ln-2 -> …
比较简单的一个做法就是:将链表转换成ListNode* 形式的线性表,用Vector进行存储,此时我们能够很轻松地根据vector数组的下表进行查找,因为是线性表,所以可以通过下标方便快捷地对链表节点进行查询。
里面有个小坑就是当用左右指针来进行更改的时候,左指针可能会大于右指针,如果不及时进行判断,链表末尾直接会错乱,因此一定需要进行判断。

    void reorderList(ListNode* head) {
        vector<ListNode* >a;
        ListNode* cur = head;
        if(head == NULL)
            return ;
        while(head)
        {
            a.emplace_back(head);
            head = head -> next;
        }
        int right = a.size() - 1, left = 0, count = 1;
        while(left < right)
        {
            a[left] -> next = a[right];
            left ++;
            //此时,如果不进行判断,则会出现left  == right 的情况,即到达最终节点
            if(left == right)
                break;
            a[right] -> next = a[left];
            right --;
        }
        //需要手动给最后一个节点的next赋值为空,否则链表带环
        a[left] -> next = NULL;
        return ;
    }

第二种做法-栈
这种做法也是偶然看到的, 就是建立一个类型为ListNode*的Stack容器,但是做法大同小异,主要的思路是:Stack容器的特性就是先进后出,因此只需要在遍历链表的过程中,将遇到的节点按照顺序全部压入堆栈当中,最后再从堆栈中一个个弹出即可,但是也需要进行判断,当两个节点相等时也要及时停止。
第三种做法-找链表中点 -> 链表反转 -> 链表合并
在找链表中点的时候,用的是快慢指针法,要注意的是,在判断fast 的next 以及next -> next 指针时,next指针应该放前面。

    ListNode* middleList(ListNode* head)
    {
        if(head == NULL)
            return NULL;
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast->next!= nullptr && fast->next->next!= nullptr)
        {
            fast = fast -> next -> next;
            slow = slow -> next;
        }
        return slow;
    }
    ListNode* reverseList(ListNode* head)
    {
        ListNode* cur = head;// cur -> 4
        ListNode* pre = nullptr;// pre -> 3
        while(cur != NULL)
        {
            ListNode* temp = cur -> next;
            cur -> next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
    void MergeList(ListNode* l1, ListNode* l2)
    {
        ListNode* cur;
        while(l2 != NULL)
        {
            ListNode* temp1 = l1 -> next;
            ListNode* temp2 = l2 -> next;
            l1 -> next = l2;
            l2 -> next = temp1;
            l1 = temp1;
            l2 = temp2;
        }
        return ;
    }
    void reorderList(ListNode* head) {
        if(head == NULL)
            return ;
        ListNode* middle = middleList(head);
        ListNode* rightlist = reverseList(middle -> next);
        middle -> next = NULL;
        MergeList(head, rightlist);
        return ;
    }

92.反转链表 II

因为刚做完反转链表的题,感觉之前都是动不动就抄题解,有点怕这么下去会一直持续抄题解,有点怕了,这个题目认认真真debug了一下,发现没有那么难。
思路:找到要反转的那一截链表的前节点和反转后的链表的最末尾的节点,让反转完的节点能够连接成功。

    ListNode* reverseBetween(ListNode* head, int left, int right) {
        if(head == NULL)
            return NULL;
        int count = 1;
        ListNode* pre = nullptr;
        //pre代表的是反转之前的 最后一个节点
        ListNode* headd = head;
        while(count < left && headd)
        {
            pre = headd;
            headd = headd -> next;
            count ++;
        }
        ListNode* prev = nullptr;
        ListNode* temp = nullptr;
        ListNode* last = headd;
        //last 代表的是反转后的最末尾的节点
        for(int i = left; i <= right ;i++)
        {
            
            temp = headd -> next;
            headd -> next = prev;
            prev = headd;
            headd = temp;
        }
        if(pre)
            pre -> next = prev;
        else
            head = prev;
        if(last)
            last -> next = temp;
        return head;
    }

24.两两交换链表中的节点

想到的第一种做法:新建一个虚拟指针,将这个虚拟指针用于新建一个链表,然后将新建链表返回。

    ListNode* swapPairs(ListNode* head) {
        ListNode* newhead = new ListNode(-1);
        ListNode* bianli = newhead;
        while(head != NULL && head -> next != NULL)
        {
            ListNode* now = head;
            ListNode* n_next = head -> next;
            head = head -> next -> next;
            newhead -> next = n_next;
            newhead = newhead -> next;
            newhead -> next = now;
            newhead = newhead -> next;
        }
        if(head && head -> next == NULL)
        {
            newhead -> next = head;
            newhead = newhead -> next;
        }
        newhead -> next = NULL;
        return bianli -> next;
    }

第二种办法:建立线性表

    ListNode* swapPairs(ListNode* head) {
        vector<ListNode*>a;
        ListNode* pre;
        if(head == NULL)
            return NULL;
        while(head)
        {
            a.emplace_back(head);
            head = head -> next;
        }
        int length = a.size() - 1;
        for (int i = 0; i < length; i += 2)
        {
            ListNode* temp = a[i];
            a[i] = a[i + 1];
            a[i + 1] = temp;
        }
        for (int i = 0; i < length ; i ++)
            a[i] -> next = a[i + 1];
        a[length] -> next = nullptr;
        return a[0];
    }

题解新方法:递归(还没学会,先挖个坑)

    ListNode* swapPairs(ListNode* head) {
        if (head == nullptr || head->next == nullptr) {
            return head;
        }
        ListNode* newHead = head->next;
        head->next = swapPairs(newHead->next);
        newHead->next = head;
        return newHead;
    }

优先队列用法

2208. 将数组和减半的最少操作次数

大概题意就是给个正整数组,可以选择数组中任意一个数,减小到恰好一半,要求将数组减小到一半以下的最少操作次数。
很容易就能想到,如果要大幅度减小,那么肯定每次都要减小当前数组中最大的数,因此问题就转换成了如何找到当前数组中的最大数,此时就要用到优先队列。
普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。
在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (first in, largest out)的行为特征。
普通的优先队列默认输出最大值,也就是大根堆。
用法与普通队列差不多(用到了再写,主要需要注意的是:priority_queue在弹出队首元素时,使用的函数为top();

    int halveArray(vector<int>& nums) {
        priority_queue<double>Q(nums.begin(), nums.end());
        double sum = 0, count = 0;
        int ans = 0;
        for (int i = 0; i < nums.size() ; i++)
            sum += nums[i];
        count = sum;
        while(count > sum / 2.0)
        {
            double now = Q.top();
            Q.pop();
            count = count - (now / 2.0);
            now /= 2.0;
            Q.push(now);
            ans ++;
        }
        return ans;
    }

逻辑思维题

822.反转卡片游戏

题意:桌子上n 张卡, 卡有正面,背面两个数字。可以翻转任意卡,选择一张,如果这张卡背面的数字与任何一张卡正面的数字都不一样,就是想要的数字,要找 想要的数字里的最小值
如果没有一个符合 输出0
最多1000 张卡
题解的思路很震撼:如果有一张卡,正面反面都是同一个数字,那么这个数字必定重复,绝对不是答案,只需要把所有的卡中,正面反面是同一个数字的这个数字找出来,除了这个数字以外的都是答案。
(没想过这个题这么简单)

    int flipgame(vector<int>& fronts, vector<int>& backs) {
        int length = fronts.size();
        int num = 2001;
        set<int>a;
        bool Jud_Same[length + 1];
        for (int i = 0; i < length ; i++)
            if(fronts[i] == backs[i])
                a.insert(fronts[i]);
        for (int i = 0; i < length ; i++)
        {
            if(a.find(fronts[i]) == a.end())
                num = min(num, fronts[i]);
            if(a.find(backs[i]) == a.end())
                num = min(num, backs[i]);
        }
        return num == 2001 ? 0 : num;
    }

字符串处理

722. 删除注释

C++ 语言中,注释分为两种,一种注释是块注释,即以 /* 开头 */ 结尾的注释, 其中所有的内容都会被注释,
另一种注释是:// 开头的注释,注释单行

    vector<string> removeComments(vector<string>& source) {
        string s = "";
        vector<string>ans;
        string temp = "";

        for (int i = 0; i < source.size() ;i ++)
            s += source[i] + "\n";
        for (int i = 0; i < s.size() ; i++)
        {
            if(s[i] == '/' && s[i + 1] == '*')
            {
                s[i] = 0;
                s[i + 1] = 0;
                i += 2;
                while(i < s.size())
                    if(s[i] == '*' && i + 1 < s.size() && s[i + 1] == '/')
                    {
                        s[i] = s[i + 1] = 0;
                        i++;
                        break;
                    }
                    else
                        s[i++] = 0;
            }
            if(s[i] == '/' && i + 1 < s.size() && s[i + 1] == '/')
                while(i < s.size())
                    if(s[i] == '\n')
                        break;
                    else
                        s[i++] = 0;

        }
        for (int i = 0; i < s.size(); i++)
        {
            if(s[i] && s[i] != '\n')
                temp += s[i];
            else if(s[i] == '\n' && temp != "")
            {
                ans.emplace_back(temp);
                temp = "";
            }
        }
        return ans;    
    }

动态规划

1749. 任意子数组和的绝对值的最大值

这个题目看错题了…没看到一定要连续,附上不需要连续的代码:

    int maxAbsoluteSum(vector<int>& nums) {
        int n = nums.size();
        int dp_zheng[n][2];
        int dp_fu[n][2];
        dp_zheng[0][0] = 0;
        dp_zheng[0][1] = nums[0];

        dp_fu[0][0] = 0;
        dp_fu[0][1] = nums[0];
        for (int i = 1; i < n; i++)
        {
            dp_zheng[i][0] = max(0, max(dp_zheng[i - 1][1], dp_zheng[i - 1][0]));
            cout << "dp_zheng[i][0]" << dp_zheng[i][0] << endl;
            dp_zheng[i][1] = max(0, max(dp_zheng[i - 1][0] + nums[i] , dp_zheng[i - 1][1] + nums[i]));
            cout << "dp_zheng[i][1]" << dp_zheng[i][1] << endl;

            dp_fu[i][0] = min(0, min(dp_fu[i - 1][1], dp_fu[i - 1][0]));
            cout << "dp_fu[i][0]" << dp_fu[i][0] << endl;
            dp_fu[i][1] = min(0, min(dp_fu[i - 1][1] + nums[i], dp_fu[i - 1][0] + nums[i]));
            cout << "dp_fu[i][1]" << dp_fu[i][1] << endl << endl;

        }
        int ans_zheng = max(abs(dp_zheng[n - 1][0]), abs(dp_zheng[n - 1][1]));
        int ans_fu = max(abs(dp_fu[n - 1][0]), abs(dp_fu[n - 1][1]));
        return max(ans_fu, ans_zheng);
    }

不连续的比较好写,实际上就是加上这个数结果 > 0 就加进来

    int maxAbsoluteSum(vector<int>& nums) {
        int positive_max = 0, positive_sum = 0;
        int negative_max = 0, negative_sum = 0;
        for (int i = 0; i < nums.size(); i++)
        {
            positive_sum = max(0, positive_sum + nums[i]);
            positive_max = max(positive_max, positive_sum);
            negative_sum = min(0, negative_sum + nums[i]);
            negative_max = min(negative_max, negative_sum);
        }
        return max(positive_max, -negative_max);
    }

你可能感兴趣的:(Leetcode刷题,leetcode,c++)