leetcode-链表+动规

21. 合并两个有序链表

双指针往后遍历即可

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode* result = new ListNode(0);
        ListNode* cur =  result;
        while(list1 != NULL && list2 !=NULL){
            if(list1->val < list2->val){
                cur->next = list1;
                list1 = list1->next;
            }else{
                cur->next = list2;
                list2 = list2->next;
            }
            cur = cur->next;
        }
        if(list1 != NULL){
            cur->next = list1;
        }
        if(list2 != NULL){
            cur->next = list2;
        }
        return result->next;
    }
};

2. 两数相加

思路也是双链表,我采取最暴力的解题办法,对于进一位得情况,用单独标志位表示是否进1,

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* result = new ListNode;
        ListNode* cur = result;
        bool flag = false;

        while(l1 != nullptr && l2 != nullptr){
            ListNode* temp = new ListNode;
            int va = l1->val + l2->val;
            if(flag)
                va++;
            if(va<10){
                temp->val = va;
                cur->next = temp;
                flag = false;
            }else{
                flag = true;
                temp->val = va%10;
                cur->next = temp;
            }
            cur = cur->next;
            l1 = l1->next;
            l2 = l2->next;
        }
        if(!flag)
            cur->next = l1 ? l1 : l2;
        else{
            l1 = l1 ? l1:l2;
            while(l1){
                if(flag){
                    int temp = l1->val + 1;
                    flag = temp>9 ? true:false;
                    l1->val = temp%10;
                    cur->next = l1;
                }else{
                    cur->next = l1;
                }
                l1 = l1->next;
                cur = cur->next;
            }
            if(flag){
                ListNode* last = new ListNode(1);
                cur->next = last;
            }
        }
        return result->next;
    }
};

25.K个一组反转链表

之前做个类似题,好像是K个反转数组,这里普通思路很简单,遍历,定义新的链表,不断加节点就好。两个主要点,首先就是满足k,单独定义一个flag就好。其次就是k的反转,利用栈特性就好,每次反转,遍历数据压入栈,然后遍历栈,依次弹出加入结果中就好

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    bool flag(ListNode* node, int k){
        while(k-->0){
            if(node == nullptr)
                return false;
            node = node->next;
        }
        return true;
    }
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* result = new ListNode;
        ListNode* i = result;
        stack<int> s;
        ListNode* cur = head;
        while(flag(cur, k)){
            int temp = k;
            while(temp-->0){
                s.push(cur->val);
                cur = cur->next;
            }
            while(!s.empty()){
                ListNode* node = new ListNode(s.top());
                s.pop();
                i->next = node;
                i = i->next;
            }
        }
        i->next = cur;
        return result->next;;
    }
};

138.随即链表的复制

这题主要是random的确定。暴力思路很简单,就是遍历判断random是否为null,如果不是,那就在遍历链表,找到指向得节点位置。这题主要是镜像关系,需要四个Node标志位置。

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/

class Solution {
public:
    Node* copyRandomList(Node* head) {
        Node* result = new Node(1);
        Node* i =result;
        Node* cur = head;
        while(cur){ //
            Node* temp = new Node(cur->val);
            i->next = temp;
            i = i->next;
            cur = cur->next;
        }
        cur = head;
        i = result->next;
        Node* temp = head;
        Node* temp_result = i;
        while(cur){
            if(cur->random == NULL){
                cur = cur->next;
                i = i->next;
                continue;
            }
            while(temp && temp != cur->random){
                temp = temp->next;
                temp_result = temp_result->next;
            }
            i->random = temp_result;
            temp = head;
            temp_result = result->next;
            cur = cur->next;
            i = i->next;
        }
        return result->next;
    }
};

23. 合并k个升序链表

暴力很简单,遍历所有链表,val放到vector中,然后排序,利用vec构建链表就好

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode* result = new ListNode;
        ListNode* cur = result;
        vector<int> temp;
        for(int i=0; i<lists.size(); i++){
            ListNode* n = lists[i];
            while(n){
                temp.emplace_back(n->val);
                n = n->next;
            }
                
        }
        sort(temp.begin(), temp.end());
        reverse(temp.begin(), temp.end());
        while(!temp.empty()){
            ListNode* t = new ListNode(temp.back());
            temp.pop_back();
            cur->next = t;
            cur = cur->next;
        }
        return result->next;
    }
};

152. 乘积最大子数组

在数组中找最大乘积得子数组,但问题就是,这里的数组中会包含负数,直接利用那种求和动规思想做不对。因此遇到负数就要考虑一个问题,之前的子数组中是不是还有负数?之前有负数,乘积为负,不会放在dp中,但此时加上i两个负数就会负负得正,因此我们采取两个dp数组,一个取最大值,另一个取最小值。最小值就会保存那个负数乘积,然后每次更新,最大dp取三者最大值,最小dp取三者最小值,三个数就是num[i], mindp[i-1]*num[i];maxdp[i-1]*num[i]。就能写出动规过程了,最后在maxdp中找最大值即可。
然后会发现,每次i只与i-1有关,因此可以利用滚动数组思想,只维护两个数字。代表最大值和最小值,result一直更新。

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        //vector<long> ma(nums.begin(), nums.end()), mi(nums.begin(), nums.end());
        int result = nums[0];
        long ma = nums[0], mi=nums[0];

        for(int i=1; i<nums.size(); i++){
            long tmax = ma, tmin = mi;
            ma = max(tmax*nums[i], max((long)nums[i], tmin*nums[i]));
            mi = min(tmin*nums[i], min((long)nums[i], tmax*nums[i]));
            if(mi < INT_MIN)
                mi = nums[i];
            result = max((int)ma, result);
        }
        //result = max((int)ma, result);
        return result;
    }
};

写的时候犯了个错误,每次递推中,需要把上一次得两个dp记录下来使用,两个局部变量,因为ma递推后值修改,紧接着mi递归需要用原始上一轮的ma,因此需要临时变量保存两个dp。不利用临时变量,mi递推就会用更新后的ma,这是错的。

1081. 不同字符的最小子序列

这题,看着不难,但做的时候很难理解,第一眼以为是求“含不同字符的最大连续序列”,但看一眼子序列概念,麻了。而且还有个最小子序列,所以该题核心点在于

  1. 序列最小,例如aba,要的是ab,而不是ba
  2. 子序列,不一定是连续的,可以删除,相对位置不变就好

思路确实不好想。顺序遍历(左往右),第一个字母肯定加入result,其次,第二个字母,就要看,result的back字母是不是唯一,也就是剩下字符中还有没有该字母,如果没有,那back不能变(因为要保证加入所有字母),加入第二个字母就好。如果还有(说明这个back字母位置可以调整),就要判断back和第二个字母大小了,因为要最小子序列,如果back大,并且后面要有该字母,那我们是不是就可以用后面的那个字母?这样得到的子序列才是小的。所以把back弹出。如果back小,那正常将新字母加入即可。
所以关键点在于,如何保证最小,就是贪心思想,从左往右遍历,每次都要保证,back小于新字母(大于就要更新,这里说的是剩余字符串中还有back的情况,如果没有back,那就没办法了,哪怕back大,也不能动),这样就清晰很多了。
综上所述,需要两个辅助数组,一是表示剩余字母的数量(更新时,判断该字母是否还有?有才能替换,没有得话,即使back大也不能替换)。而是表示result中已经存在的字母,一个去重数组。

class Solution {
public:
    string smallestSubsequence(string s) {
        vector<int> left(26), in_ans(26);
        for(auto c:s)
            left[c-'a']++; //每个字母有几个
        string ans;
        for(char c : s){
            left[c-'a']--; //表示用一个字母
            if(in_ans[c-'a']) //是否在结果中,如果在(用过了),直接跳过。
                continue;
            while(!ans.empty() && c<ans.back() && left[ans.back()-'a']){
                //新字母小于back,并且后面还有该字母,需要将back弹出,最后新字母会加入,保证“最小”
                in_ans[ans.back()-'a'] = 0;
                ans.pop_back();
            }
            ans += c; //加入新字母
            in_ans[c-'a'] = 1;  //表示该字母用过了,满足子序列条件
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值