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 & 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 & lists, int index, int k) {
int iMin, iLeft, iRight;
set 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 & lists) {
vector 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;
}
};
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; iauto 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; i1; 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;
}
};
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;
}
};