23. Merge k Sorted Lists等题 第二周解题报告

原题

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */

题意简洁明了,K个有序链表,归并成一个有序链表。马上想到的是,每次选出K个链表头中数值最小的那个,加到result链表上。
因此问题就变成了,如何在K个数字里,快速找到最小数,并重复这个操作n次(n为总节点数)。如果像合并两个链表一样纯粹地用比较的话,找最小数的复杂度是O(K*n),链表的更新操作为O(n),因此最后复杂度是O(K*n)。事实证明无法在给定时间内通过这题。
因此使用堆结构来维护K个链表头的大小关系结构,构造的最初最小堆的时间复杂度是O(log(n)),每次取出后补一个数,加上维护堆结构的复杂度后,总的复杂度为O(n*log(n)),而且每一次取空一个链表,这个n都会变小(因为K减小)。
有一个要注意的点是,空链表是当成无穷大的数,必须排到末端,然后把K减小1。但如果遇到[NULL,4,6]这样的情况,就会排成[4,NULL,6]这样的错误结果,导致数字6永远消失,并且会在NULL里取数据导致RE。因此在构建堆的时候,要把空的链表直接剔除。在维护堆的时候,发现新添加进来的链表头为NULL,直接与堆最后一位交换,就可以避免出现上述情况了。

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int k = buildHeap(lists);
        ListNode* root = new ListNode(-1);
        ListNode* ret = root;

        while(k) {
            // for(auto x: lists) {
            //     if(x) {
            //         cout << x->val << " ";
            //     } else {
            //         cout << -1 << " ";
            //     }
            // } cout << endl;
            // cout << k << ", " << ((lists[0])?lists[0]->val:-1) << endl;
            root->next = lists[0];
            lists[0] = (lists[0])?lists[0]->next:NULL;
            root = root->next;
            if(lists[0] == NULL) {
               swap(lists[0], lists[k-1]);
               k--;
            }
            k = minHeap(lists, 0, k);
        }

        return ret->next;
    }

    int minHeap(vector<ListNode*>& lists, int index, int k) {
        int iMin, iLeft, iRight;
        set<ListNode*> emptyList;
        while(1) {
            iMin = index;
            iLeft = iMin*2 + 1;
            iRight = iLeft + 1;

            if(iLeft < k && lists[iLeft] && (lists[index]==NULL || lists[index]->val > lists[iLeft]->val)) {
                iMin = iLeft;
            }

            if(iRight < k && lists[iRight] && (lists[iMin]==NULL || lists[iMin]->val > lists[iRight]->val)) {
                iMin = iRight;
            }


            if(iMin != index) {
                if(lists[index] == NULL) {
                    emptyList.insert(lists[index]);
                }
                swap(lists[iMin], lists[index]);
                index = iMin;
            } else {
                break;
            }
        }
        return k-emptyList.size();
    }

    int buildHeap(vector<ListNode*>& lists) {
        vector<ListNode*> tmp;
        for(auto l : lists) {
            if(l) {
                tmp.push_back(l);
            }
        }
        lists = tmp;
        int k = lists.size();
        int par = floor((k-1)/2);
        for(int i=par; i>=0; i--) {
            k = minHeap(lists, i, k);
        }
        return k;
    }
};

282. Expression Add Operators

原题

Given a string that contains only digits 0-9 and a target value, return all possibilities to add binary operators (not unary) +, -, or * between the digits so they evaluate to the target value.

Examples: 
"123", 6 -> ["1+2+3", "1*2*3"] 
"232", 8 -> ["2*3+2", "2+3*2"]
"105", 5 -> ["1*0+5","10-5"]
"00", 0 -> ["0+0", "0-0", "0*0"]
"3456237490", 9191 -> []

题意为给个字符串num和一个数字target,在num的各个字符中间插入+-*,使得算式的结果是target
明显的DFS,只是乘法在算式中有优先级,要如何在下一层深搜时抵消掉上一次的运算优先级错误。一次乘法只会对它上一次运算符产生影响,因此只需要记录上一层的运算结果和叠加值。如果这次使用乘法进行搜索,那么把上次的运算结果减去叠加值就行了。
还有就是记得剔除0开头的非法数字,比如00, 04。

代码

class Solution {
public:
    void dfs(vector<string>& result, string& num, int& target, string exp, int pos, long long now, long long prev) {
        // cout << pos << ", " << exp << " = " << now << endl;
        if(pos == num.size()) {
            if(target == now)
                result.push_back(exp);
            return;
        }

        for(int i=pos; i<num.size(); i++) {
            auto ns = num.substr(pos, i-pos+1);
            auto n = stoll(ns);
            // break zero
            if(num[pos] == '0' && i>pos)
                break;
            dfs(result, num, target, exp+"+"+ns, i+1, now+n, n);
            dfs(result, num, target, exp+"-"+ns, i+1, now-n, -n);
            // cout << now << ", " << prev << ", " << n << endl;
            dfs(result, num, target, exp+"*"+ns, i+1, now-prev+(prev*n), prev*n);
        }
    }

    vector<string> addOperators(string num, int target) {
        if(num.size() == 0) return vector<string>();
        if(num.size() == 1) return (stoi(num) == target? vector<string>(1, num): vector<string>());
        vector<string> ret;
        for(int i=0; i<num.size()-1; i++) {
            auto ns = num.substr(0, i+1);
            auto n = stoll(ns);
            if(num[0] == '0' && i) break;
            dfs(ret, num, target, ns, i+1, n, n);
        }

        if(stoll(num) == static_cast<long long>(target) && !(num[0] == '0' && num.size() > 1)) {
        ret.push_back(num);
    }
        // dfs(ret, num, target, "", 0, 0, 0); // take care of the begining
        return ret;
    }

};

53. Maximum Subarray

原题

Find the contiguous subarray within an array (containing at least one number) which has the largest sum.

For example, given the array [-2,1,-3,4,-1,2,1,-5,4],
the contiguous subarray [4,-1,2,1] has the largest sum = 6.

题意为在一个数组里找到一个子数组,它的元素和是其他子数组元素和的最大者。动态规划可以在O(n)的情况下解出。这样考虑,如果一个子数组是最大和的,那么不管把它的左边边界左移多少,新子数组的和肯定是变小或不变的,也就是说,新加进来的左边的子数组的和,必定是负数或零。例如上面的例子:假如已知最大的子数组为[4,-1,2,1],如果把它左边的两个数加进来,也就是加上[1, -3],新的子数组和肯定是变小的,也就是说新加进来的[1, -3]的元素和肯定是负数。
那么,我们从下标0开始叠加数字,每次更新最大值。如果当前叠加和变成了负数,那么舍去它,从下一个数字重新开始叠加,最后肯定能得到最大的元素和。
例如上面的例子,从-2开始叠加,叠加和如下变化:-2, 1, -3, 4, 3, 5, 6, 1, 5,最大值就为6。

代码

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int maxN = INT_MIN;
        int now = 0;
        for(auto n: nums) {
            if(now < 0) {
                now = 0;
            }
            now += n;
            maxN = max(maxN, now);
        }
        return maxN;
    }
};
To merge k sorted linked lists, one approach is to repeatedly merge two of the linked lists until all k lists have been merged into one. We can use a priority queue to keep track of the minimum element across all k linked lists at any given time. Here's the code to implement this idea: ``` struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(NULL) {} }; // Custom comparator for the priority queue struct CompareNode { bool operator()(const ListNode* node1, const ListNode* node2) const { return node1->val > node2->val; } }; ListNode* mergeKLists(vector<ListNode*>& lists) { priority_queue<ListNode*, vector<ListNode*>, CompareNode> pq; for (ListNode* list : lists) { if (list) { pq.push(list); } } ListNode* dummy = new ListNode(-1); ListNode* curr = dummy; while (!pq.empty()) { ListNode* node = pq.top(); pq.pop(); curr->next = node; curr = curr->next; if (node->next) { pq.push(node->next); } } return dummy->next; } ``` We start by initializing a priority queue with all the head nodes of the k linked lists. We use a custom comparator that compares the values of two nodes and returns true if the first node's value is less than the second node's value. We then create a dummy node to serve as the head of the merged linked list, and a current node to keep track of the last node in the merged linked list. We repeatedly pop the minimum node from the priority queue and append it to the merged linked list. If the popped node has a next node, we push it onto the priority queue. Once the priority queue is empty, we return the head of the merged linked list. Note that this implementation has a time complexity of O(n log k), where n is the total number of nodes across all k linked lists, and a space complexity of O(k).
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值