575. 字符串解码
这个题用递归比较好做,需要注意的是如何找到前括号匹配的后括号(并不是找到第一个后括号就是对的),还要注意index移动的问题。
class Solution {
public:
string expressionExpand(string &s) {
if (s=="") return s;
string res="";
int i=0;
int number;
while (i<s.length()) {
if (isch(s[i])) {
res=res+s[i];
i++;
}
else if (isnum(s[i])) {
number=s[i]-'0';
//处理多位数字的情况
while (i+1<s.length() && isnum(s[i+1])) {
number=number*10+(s[i+1]-'0');
i++;
}
i++;
}
else if (s[i]=='[') {
int j=i+1;
int cnt=1;
while (cnt!=0) {
if (s[j]=='[') cnt++;
else if (s[j]==']') cnt--;
j++;
}
string tmp=s.substr(i+1,j-i-2);
string expand=expressionExpand(tmp);
for (int k=1;k<=number;k++) res.append(expand);
i=j;
}
}
return res;
}
bool isch(char c) {
return c>='a'&&c<='z' || c>='A'&&c<='Z';
}
bool isnum(char c) {
return c>='0'&&c<='9';
}
};
如果不使用递归而使用栈,就采用顺序处理的算法。只要找到一个重复字段的结束符号[,就向前找到应该展开的字符,将其展开后重新压栈。这样当字符串处理到末尾,所有压缩字符串都将被展开。
class Solution {
public:
string expressionExpand(string &s) {
//使用两个栈,一个放字符,一个放数字
stack<char> character;
stack<int> copynum;
int i=0;
int number;
while (i<s.length()) {
if (isch(s[i])) {
character.push(s[i]);
}
else if (isnum(s[i])) {
number=s[i]-'0';
while (i+1<s.length() && isnum(s[i+1])) {
number=number*10+(s[i+1]-'0');
i++;
}
//数字的位置用特殊字符‘ ’代替,实际数字放在copynumber里
character.push(' ');
copynum.push(number);
}
else if (s[i]=='[') {
}
else if (s[i]==']') {
//找到应该展开的字段
//为了得到正确顺序的字符串,使用两个栈进行操作。
string newstr=popstack(character);
int n=copynum.top();
//展开字符串
for (int k=0;k<n;k++)
pushstack(character,newstr);
copynum.pop();
}
i++;
}
return popstack(character);
}
string popstack(stack<char> &st) {
stack<char> tmp;
while (!st.empty() && st.top()!=' ') {
tmp.push(st.top());
st.pop();
}
st.pop();
string res="";
while (!tmp.empty()) {
res=res+tmp.top();
tmp.pop();
}
return res;
}
void pushstack(stack<char> &st, string &s) {
for (int i=0;i<s.length();i++)
st.push(s[i]);
}
bool isch(char c) {
return c>='a'&&c<='z' || c>='A'&&c<='Z';
}
bool isnum(char c) {
return c>='0'&&c<='9';
}
};
40. 用栈实现队列
一开始的想法是用两个栈是s1,s2,每次pop()和top()的时候先把s1倒进s2,top()和pop()之后再倒回s1去。但是这样运行速度很低。其实不必每次都倒,将s1看作最新push进来的数字段,s2看作可以出队的栈,只要s2不为空,可以一直用s2进行出队操作,而s1屯着push进来的数字,当s2为空(不够出了)的时候再一起倒进来就行了。
class MyQueue {
public:
stack<int> s1;
stack<int> s2;
MyQueue() {
}
void push(int element) {
s1.push(element);
}
int pop() {
//如果s2没了,就把s1刚存进来的数字全部倒到s2
if (s2.empty()) {
while (!s1.empty()) {
s2.push(s1.top());
s1.pop();
}
}
int a=s2.top();
s2.pop();
return a;
}
int top() {
//如果s2没了,就把s1刚存进来的数字全部倒到s2
if (s2.empty()) {
while (!s1.empty()) {
s2.push(s1.top());
s1.pop();
}
}
return s2.top();
}
};
494. 双队列实现栈
我的思路是弄两个队列,每次要出栈的时候就从一个倒腾到另一个,留下最后那个队位元素就是栈顶元素。方法通过了但也是比较慢,好的思路是从push开始就把队列里的顺序做成栈的顺序。把新来的元素放在空队队首,再把后面的从另一个队列倒过去(倒过去之前已经在上次push时排好栈的顺序了)。
class Stack {
public:
queue<int> q1;
queue<int> q2;
void push(int x) {
if (q1.empty()) {
q1.push(x);
while (!q2.empty()) {
q1.push(q2.front());
q2.pop();
}
}else {
q2.push(x);
while (!q1.empty()) {
q2.push(q1.front());
q1.pop();
}
}
}
void pop() {
if (q1.empty()) q2.pop();
else q1.pop();
}
int top() {
if (q1.empty()) return q2.front();
else return q1.front();
}
bool isEmpty() {
return q1.empty()&&q2.empty();
}
};
528. 摊平嵌套的列表
接口说明
/* class NestedInteger {
* public:
* // Return true if this NestedInteger holds a single integer,
* // rather than a nested list.
* bool isInteger() const;
*
* // Return the single integer that this NestedInteger holds,
* // if it holds a single integer
* // The result is undefined if this NestedInteger holds a nested list
* int getInteger() const;
*
* // Return the nested list that this NestedInteger holds,
* // if it holds a nested list
* // The result is undefined if this NestedInteger holds a single integer
* const vector<NestedInteger> &getList() const;
* };
*/
使用栈解决嵌套问题。由于整个list提前已知,因此直接倒序放进栈里。因为每次只需要找一个值,因此思路是不管后面的嵌套如何,每次只针对栈顶的元素(给出的list的头)进行操作。只要栈顶元素不是整数,就不停的解析栈顶的list直到成为一个整数,弹出。
本来我为了好理解,把这个过程写在next()函数里,而hasnext()里只用了!s.empty()来判断如下代码,但是会报错。因为这样只能确定s里面有东西,但是不知道能不能解析出实际的答案来(面对[[],[]]这样的输入),而且我尝试使用了辅助判断的方法也不行,接口里的代码没给出导致不知道该怎么改。
/错误解法!!!!但是方便理解思路
class NestedIterator {
public:
stack<NestedInteger> s;
NestedIterator(vector<NestedInteger> &nestedList) {
for (int i=nestedList.size()-1;i>=0;i--) {
s.push(nestedList[i]);
}
}
int next() {
int res;
//栈顶是整数就可以直接弹出,否则需要迭代解析,直到把下一个数解析出来
while (!s.top().isInteger()) {
vector<NestedInteger> tmp=s.top().getList();
s.pop();
for (int i=tmp.size()-1;i>=0;i--) {
s.push(tmp[i]);
}
}
res=s.top().getInteger();
s.pop();
return res;
}
bool hasNext() {
//加上这个判断依然不可以
//while (!s.empty() && !s.top().isInteger()
// && s.top().getList().size()==0) {
// s.pop();
//}
return !s.empty();
}
};
正确代码是将解析放到hasnext()里,只有真正解析出来了下一个数了才能算有下一个数。
class NestedIterator {
public:
stack<NestedInteger> s;
NestedIterator(vector<NestedInteger> &nestedList) {
for (int i=nestedList.size()-1;i>=0;i--) {
s.push(nestedList[i]);
}
}
int next() {
int res=s.top().getInteger();
s.pop();
return res;
}
bool hasNext() {
while (!s.empty()) {
//直到栈顶解析出能直接pop的整数,才算有下一个数
//因为如果一个list不为空,一定可以通过多次迭代解析成为整数
if (s.top().isInteger()) return true;
vector<NestedInteger> tmp=s.top().getList();
s.pop();
for (int i=tmp.size()-1;i>=0;i--) {
s.push(tmp[i]);
}
}
return false;
}
};
class BSTIterator {
public:
stack<TreeNode*> s;
BSTIterator(TreeNode * root) {
while (root!=NULL) {
s.push(root);
root=root->left;
}
}
bool hasNext() {
return !(s.empty());
}
TreeNode * next() {
TreeNode* res=s.top();
TreeNode* node=s.top()->right;
s.pop();
while (node!=NULL) {
s.push(node);
node=node->left;
}
return res;
}
};
class ZigzagIterator {
public:
int *p1,*p2;
int *l1,*l2;
bool flag;
ZigzagIterator(vector<int>& v1, vector<int>& v2) {
p1=&v1[0];
p2=&v2[0];
//注意终止的指针设置在end()的位置
l1=&v1[v1.size()];
l2=&v2[v2.size()];
flag=0;
}
int next() {
int res;
if (flag && p2!=l2) {
res=*p2;
p2=p2+1;
}
else if (!flag && p1!=l1){
res=*p1;
p1=p1+1;
}//处理不一样长的情况
else if (p1!=l1) {
res=*p1;
p1=p1+1;
}
else if (p2!=l2) {
res=*p2;
p2=p2+1;
}
flag=!flag;
return res;
}
bool hasNext() {
return p1!=l1 ||p2!=l2;
}
};
541. 左旋右旋迭代器 II
和上一题相同,把指针用vector存贮,k是当前要出数的vector序号。注意k要循环。确认是否还有能输出的数需要循环整个vector,因此把找下一个要出的数的代码写在了hasnext里。
另外,注意vecotor<int*>p 是一个数组p,里面存着很多int指针;而vector<int> *p是一个指针,指向一个int数组。
class ZigzagIterator2 {
public:
vector<int*> p;
vector<int*> end;
int k;
int n;
ZigzagIterator2(vector<vector<int>>& vecs) {
n=vecs.size()-1;
k=0;
for (int i=0;i<=n;i++) {
p.push_back(&vecs[i][0]);
end.push_back(&vecs[i][vecs[i].size()]);
}
}
int next() {
int res=*p[k]++;
k=(k+1)%(n+1);
return res;
}
bool hasNext() {
int count=0;
while (true) {
if (p[k]!=end[k]) return true;
else {count++; k=(k+1)%(n+1);}
if (count==n+1) return false;
}
}
};
601. 摊平二维向量
这个题有很多种不同的做法。1.在初始化的时候直接展开,存入另外一个数据结构中;
2.初始化的时候用this把数组保存下来;
3.用指针;
我分别写了后两个:
class Vector2D {
public:
int i,j;
vector<int> end;
vector<vector<int>> vec2d;
Vector2D(vector<vector<int>>& vec2d) {
this->vec2d=vec2d;
i=0;j=0;
}
int next() {
int res=vec2d[i][j];
j++;
if (j==vec2d[i].size()) {
i++;
j=0;
}
return res;
}
bool hasNext() {
//处理[[],[]]的情况
while (i<vec2d.size() && vec2d[i].size()==0) i++;
//每个vector长短不一,不能按照二维数组的方法判断结束。
if (i<vec2d.size()) return true;
if (i==vec2d.size()-1) return j<vec2d[vec2d.size()-1].size();
return false;
}
};
class Vector2D {
public:
//区别于上面的vector<int*>,这里的*begin,*end都是指向二维数组每行的行首,指向的是一个数组。
vector<int> *begin,*end;
int pos;
Vector2D(vector<vector<int>>& vec2d) {
begin=&vec2d[0];
end=&vec2d[vec2d.size()];
}
int next() {
int res=(*begin)[pos];
pos++;
return res;
}
bool hasNext() {
while (begin != end && pos == (*begin).size())
begin++, pos = 0;
return begin != end;
}
};
?12. 带最小值操作的栈
min只返回当前栈内的最小值,因此利用两个栈,一个是正常的,一个是顶部的值代表当前栈内最小值的栈(这个栈只有top()有意义,其他内容只是作为备用)。
class MinStack {
public:
stack<int> st,minst;
MinStack() {
}
void push(int number) {
st.push(number);
if (minst.empty() || number<=minst.top()) {
minst.push(number);
}
}
int pop() {
int res=st.top();
if (minst.top()==res) minst.pop();
st.pop();
return res;
}
int min() {
return minst.top();
}
};
?122. 直方图最大矩形覆盖
最维护一个最长不下降栈。http://www.cnblogs.com/lichen782/p/leetcode_Largest_Rectangle_in_Histogram.html
class Solution {
public:
int largestRectangleArea(vector<int> &height) {
if(height.size() == 0) return 0;
int res = 0;
height.push_back(0); // 手动设置一个右边界,
//维护一个单调不下降栈
stack<int> s;
for (int i=0;i<height.size();i++) {
if (s.empty() || !s.empty() && height[i]>=height[s.top()] )
s.push(i);
else {//第一个下降点作为右边界(不包含)
//向左找到左边界(第一个比右边界大的数)计算面积
while (!s.empty() && height[s.top()]>height[i]) {
int index=s.top(); s.pop(); //左边界的高度即最矮高度
//如果s为空,说明栈底前面、栈底后面到当前i之前的矩形都比栈底高,最长的长度是i,高度是栈底高
//如果不为空,找到下一个比当前矩形矮的矩形,他们之间的矩形都是比当前高的,高度即短板是当前栈底高
int width=s.empty()?i:i-s.top()-1;//长度
res=max(res,height[index]*width);
}
s.push(i);
}
}
return res;
}
};
510. 最大矩形
把每一行转化为不同高度的直方图,再求每一行的最大面积。
class Solution {
public:
int maximalRectangle(vector<vector<bool>> &matrix) {
if (matrix.size()==0) return 0;
vector<vector<int>> height(matrix.size(),vector<int>(matrix[0].size()+1,0));
//每一行做成一个矩形直方图
//竖着的直方图越长,他末尾一个位置的数字越大,说明此直方图越高
//例子转化成的直方图矩阵是
//[1,1,0,0,1,0]
//[0,2,0,0,2,0]
//[0,0,1,1,3,0]
//[0,0,2,2,4,0]
//[0,0,0,0,5,0]
//和直方图的算法一样,每行最后一个数添加一个0,以便首尾处理实际的最后一个数
for (int i=0;i<matrix.size();i++)
for (int j=0;j<matrix[0].size()+1;j++)
if (matrix[i][j]) {
height[i][j]= i==0?1:height[i-1][j]+1;
}
int res=0;
//计算每一行的最大面积
for (int i=0;i<matrix.size();i++) {
int area=findArea(height[i]);
res=max(area,res);
}
return res;
}
int findArea(vector<int> &height) {
stack<int> s;
int area=0;
for (int i=0;i<height.size();i++) {
if (s.empty() || height[i]>=height[s.top()]) s.push(i);
else {
while (!s.empty() && height[s.top()]>height[i]) {
int index=s.top();s.pop();
int h=height[index];
int w= s.empty()?i:i-s.top()-1;
area=max(area,w*h);
}
s.push(i);
}
}
return area;
}
};
126. 最大树
最开始的思想是用分治做,会超时:
class Solution {
public:
TreeNode * maxTree(vector<int> &A) {
return helper(A,0,A.size()-1);
}
TreeNode* helper(vector<int> &A,int start, int end) {
if (start>end) return NULL;
int mid=findMax(A,start,end);
TreeNode* root=new TreeNode(A[mid]);
if (mid!=start) root->left=helper(A,start,mid-1);
if (mid!=end) root->right=helper(A,mid+1,end);
return root;
}
int findMax(vector<int> &A,int start, int end) {
int index=start; int num=A[start];
for (int i=start+1;i<=end;i++) {
if (A[i]>num) {
num=A[i];
index=i;
}
}
return index;
}
};
使用单调栈的思想,建立一个单调递减的栈,存放每一个右子树。新来的一个数就不断的和栈顶的值比较,判断是应该接在右子树还是变成根。
class Solution {
public:
TreeNode * maxTree(vector<int> &A) {
stack<TreeNode*> s;//维护一个根的直单调递减的栈,栈内的树只有左子树
for (int i=0;i<A.size();i++) {
TreeNode* tmp=new TreeNode(A[i]);
//如果没有元素,往里加
if (s.empty()) {s.push(tmp);continue;}
//如果当前数比栈顶树根大,说明还没找到最大的值,栈顶的树还能再往上添根
//把这个树取出来添上当前的树做根
//如[3,2,1,5] 2进入的时候已经和3连好了,所以5先连2再连3不影响
while (!s.empty() && A[i]>s.top()->val) {
tmp->left=s.top();
s.pop();
}
//栈内如果还有树,此树的根值一定大于当前数,当前数是这个根的右孩子,连上
if (!s.empty()) {
s.top()->right=tmp;
}
s.push(tmp);
}
//由于是根单调递减的栈,栈底的那个是根
while (s.size()>1) s.pop();
return s.top();
}
};
129. 重哈希
一开始我的算法是用set记录下来原有的节点,同时把哈希表内的节点删除,这样就可以完成在原哈希表中的替换。但是和原来的顺序可能不一样,所以还是需要新建一个哈希表。
class Solution {
public:
vector<ListNode*> rehashing(vector<ListNode*> hashTable) {
int n=hashTable.size();
vector<ListNode*> hashTable2(2*n,NULL);
for (int i=0;i<n;i++) {
if (hashTable[i]==NULL) continue;
ListNode* head=hashTable[i];
while (head!=NULL) {
addnode(head->val,2*n,hashTable2);
head=head->next;
}
}
return hashTable2;
}
void addnode(int a,int n, vector<ListNode*> &hashTable) {
int pos=((a%n)+n)%n;
ListNode* tmp=new ListNode(a);
if (hashTable[pos]==NULL) hashTable[pos]=tmp;
else {
ListNode* head=hashTable[pos];
while (head->next!=NULL) head=head->next;
head->next=tmp;
}
}
};
?134. LRU缓存策略
考虑到要不断改变存储数据,这个题用了list结构作为cache,另外建立了一个map,map的值是指向key的迭代器(相当于指针)。题解中使用了list的相关函数emplace_front(),spilce(粘贴的位置,原串,原串要复制的位置)等。还要注意指针的写法。
#include<list>
class LRUCache {
public:
//缓存中的实际内容,key-value对
list<pair<int,int>> cache;
//位置,key-指向实际内容的指针,使用map是为了O(1)时间查找
unordered_map<int,list<pair<int,int>>::iterator> pos;
int capacity;
LRUCache(int capacity) {
this->capacity=capacity;
}
int get(int key) {
if (pos.find(key)==pos.end()) return -1;
cache.splice(cache.begin(), cache, pos[key]);//把cache从pos[key]的位置的内容放到cache.begin()的位置。
pos[key] = cache.begin();//修改指针
return (cache.front().second);
}
void set(int key, int value) {
if (pos.find(key)==pos.end()) {
//list最前面增加新的kv对
cache.emplace_front(make_pair(key,value));
pos[key]=cache.begin();//记录位置
if (pos.size()>capacity){
pos.erase(cache.back().first);//删掉位置对照表中相应的字段
cache.pop_back();//删掉缓存中时间最远的那个
}
}else {
pos[key]->second = value;//pos[key]指向的是键值为key的pair
//pos[key]是一个迭代器/指针,所以用->而不是.
cache.splice(cache.begin(), cache, pos[key]);//把cache从pos[key]的位置插入到cache.begin()的位置。
pos[key] = cache.begin();//修改指针
}
}
};
138. 子数组之和
使用哈希表加快查找subsum的速度。
class Solution {
public:
vector<int> subarraySum(vector<int> &nums) {
if (nums.size()==0) return vector<int>();
vector<int> res;
unordered_map<int,int> subSum;
int sum=0;
subSum[0]=-1;
for (int i=0;i<nums.size();i++) {
sum+=nums[i];
if (subSum.find(sum)==subSum.end()) {
subSum[sum]=i;
}else{
res.push_back(subSum[sum]+1);
res.push_back(i);
return res;
}
}
}
};
105. 复制带随机指针的链表
可以用哈希表记录旧链表节点和新链表节点的关系。但是最快的方法是把1->2->3->NULL扩建成1->1'->2->2'->3->3'->NULL;
171. 乱序字符串
对于每个字符串,都有一个按顺序排列的标准串(注意O(n)时间排列字符串的方法)。把标准串作为key,set里存入其不同的变化,再把size大于1的set打印出来即可。注意由于字符串有可能重复,应使用unordered_multiset。
class Solution {
public:
vector<string> anagrams(vector<string> &strs) {
unordered_map<string,unordered_multiset<string>> hash;
vector<string> res;
for (int i=0;i<strs.size();i++) {
string sortedstr=sort(strs[i]);
if (hash.find(sortedstr)==hash.end()) {
unordered_multiset<string> tmp;
tmp.insert(strs[i]);
hash[sortedstr]=tmp;
}else hash[sortedstr].insert(strs[i]);
}
for (auto it=hash.begin();it!=hash.end();it++) {
if (it->second.size()>1) {
for (auto s:it->second)
res.push_back(s);
}
}
return res;
}
string sort(string s) {
vector<int> tmp(26,0);
for (int i=0;i<s.length();i++) {
tmp[s[i]-'a']++;
}
string news="";
for (int i=0;i<26;i++) {
for (int j=0;j<tmp[i];j++)
news=news+(char)(i+'a');
}
return news;
}
};
124. 最长连续序列
牛逼算法,应该掌握。
class Solution {
public:
int longestConsecutive(vector<int> &num) {
if (num.size()<=1) return num.size();
unordered_set<int> hash(num.begin(),num.end());
int res=1;
for (int i=0;i<num.size();i++) {
if (hash.find(num[i])!=hash.end()) {
hash.erase(num[i]);
int pre=num[i]-1;
int next=num[i]+1;
while (hash.find(pre)!=hash.end()) {
hash.erase(pre);
pre--;
}
while (hash.find(next)!=hash.end()) {
hash.erase(next);
next++;
}
res=max(res,next-pre-1);
}
}
return res;
}
};
130. 堆化
有两种顺序:1.从上往下的顺序处理节点,从下往上安置节点:时间复杂度O(nlogn),因为只有第一个节点不用处理和安置。
2.从下往上处理节点,从上往下安置节点:时间复杂度O(n),因为叶子结点均不用处理和安置。
class Solution {
public:
void heapify(vector<int> &A) {
if (A.size()<=1) return ;
//从上往下处理节点
for (int i=0;i<A.size();i++){
int j=i;
//从当前位置往上安排节点
while ((j-1)/2>=0 && A[j]<A[(j-1)/2]) {
swap(A[j],A[(j-1)/2]);
j=(j-1)/2;
}
}
}
};
class Solution {
public:
void heapify(vector<int> &A) {
if (A.size()<=1) return ;
//从下往上处理节点
for (int i=A.size()/2;i>=0;i--){
int j=i;
//从当前位置往下安排节点
while (j<A.size()) {
//找到三个点中最小的往下换
int smallest=j;
if (j*2+1<A.size() &&A[j*2+1]<A[smallest]) smallest=2*j+1;
if (j*2+2<A.size() &&A[j*2+2]<A[smallest]) smallest=2*j+2;
if (j==smallest) break;//已经是最小的了,不用换了。
swap(A[j],A[smallest]);
j=smallest;
}
}
}
};
4. 丑数 II
思路不是找出丑数,而是构造丑数。从第一个丑数开始,分别和2,3,5相乘形成新的丑数。但是不一定后乘的数顺序一定排在后面,所以这里使用优先队列,保证每次队首出来的数一定是最小的。
c++里的正常定义的优先队列是一个大根堆,如果想定义成小根堆写法是priority_queue<int,vector<int>,greater<int>>。
哈希表用来去重。
class Solution {
public:
int nthUglyNumber(int n) {
priority_queue<long,vector<long>,greater<long>> order;
unordered_set<long> hash;
order.push(1);
hash.insert(1);
int count=0;
long number;
while (count<n) {
number=order.top();
order.pop();
count++;
if (hash.find(number*2)==hash.end()) {
hash.insert(number*2);
order.push(number*2);
}
if (hash.find(number*3)==hash.end()) {
hash.insert(number*3);
order.push(number*3);
}
if (hash.find(number*5)==hash.end()) {
hash.insert(number*5);
order.push(number*5);
}
}
return number;
}
};
545. 前K大数 II
我的方法是用了一个大根堆,记录所有的值。每次出前k个值。
但是由于实现的结构只有添加和取前k个值两个功能,不在前k个值里的数不需要记录,所以可以用小根堆来做。队首是k个数里的最小值,每次push进来的数和最小值比,然后或添加、或删除。
class Solution {
public:
priority_queue<int,vector<int>,greater<int>> p;
int k;
Solution(int k) {
this->k=k;
}
void add(int num) {
if (p.size<k) p.push(num);
else if (num>p.top()) {
p.pop();
p.push(num);
}
}
vector<int> topk() {
/* read p's element and sort */
}
};
但是这样做,在读取队列的时候还是需要先全部出队,再全部放回去。最好的办法是用multiset,有序且支持重复。
class Solution {
public:
multiset<int> s;
int k;
Solution(int k) {
this->k=k;
}
void add(int num) {
s.insert(num);
if (s.size()>k) s.erase(s.begin());
}
vector<int> topk() {
vector<int> res(s.begin(),s.end());
reverse(res.begin(),res.end());
return res;
}
};
class Solution {
public:
map<int, double> highFive(vector<Record>& results) {
map<int,priority_queue<int>> hash;
map<int,double> res;
for (int i=0;i<results.size();i++) {
if (hash.find(results[i].id) == hash.end()) {
hash[results[i].id]=priority_queue<int>();
res[results[i].id]=0.0;
}
hash[results[i].id].push(results[i].score);
}
for (auto it=hash.begin();it!=hash.end();it++) {
priority_queue<int> scores=it->second;
int n=0;
double count=0;
while (!scores.empty() && n<5){
count+=scores.top();
scores.pop();
n++;
}
res[it->first]=(double)(count/n);
}
return res;
}
};
612. K个最近的点
一开始是想直接重定义sort中的比较函数,但是由于sort用的是不稳定排序,因此输出的顺序和原顺序会不一样。如果想一样的话,需要用到优先队列,且要重定义优先队列。
还有一处需要注意的地方,当重写sort中的cmp函数时,若写在类里面,应加上static,相当于一个全局函数,只初始化一次,因此其不可访问类中的成员变量,需要在类外写一个全局变量供其访问。
//都在类的外面
Point global_origin;
bool operator<(Point a, Point b) {//返回true时,说明a的优先级低于b
//若定义的时候使用了greater<T> 则应定义operator>
//此时返回true,说明a的优先级高于b
int d1=(a.x-global_origin.x)*(a.x-global_origin.x)+(a.y-global_origin.y)*(a.y-global_origin.y);
int d2=(b.x-global_origin.x)*(b.x-global_origin.x)+(b.y-global_origin.y)*(b.y-global_origin.y);
int diff=d1-d2;
if (diff==0)
diff=a.x-b.x;
if (diff==0)
diff=a.y-b.y;
return diff<0;
}
class Solution {
public:
vector<Point> kClosest(vector<Point> &points, Point &origin, int k) {
global_origin=origin;
priority_queue<Point> p;
//若定义大根堆priority_queue<Point,vector<Point>,greater<Point>> p;
//则应重定义oparetor>
for (int i=0;i<points.size();i++) {
Point tmp = points[i];
p.push(tmp);
if (p.size()>k) p.pop();
}
vector<Point> res;
while (!p.empty()) {
res.push_back(p.top());
p.pop();
}
reverse(res.begin(),res.end());
return res;
}
};
486. 合并k个排序数组
要求时间复杂度nlogk,说明是每k个数一组进行进行调整。建立一个优先队列,先把k个开头放进去,每出一个数,看看是从哪个数组进来的,就从那个数组再push一个进来。由于出队的时候需要知道来自哪个数组,所以要重新定义一个结构,也要重载运算符。重载运算符有三种方法,给的是struct,改成class也一样。
class point {
public:
int x,y,val;
point(int _x,int _y,int _val):x(_x),y(_y),val(_val) {};
friend bool operator < (class point a,class point b) {
return a.val>b.val;
}
};
class Solution {
public:
vector<int> mergekSortedArrays(vector<vector<int>> &arrays) {
priority_queue<point> pq;
map<int,pair<int,int>> pos;
for (int i=0;i<arrays.size();i++) {
if (arrays[i].size()==0) continue; //处理空列表的情况
point tmp(i,0,arrays[i][0]);
pq.push(tmp);
}
vector<int> res;
while (!pq.empty()) {
point tmp=pq.top();
pq.pop();
res.push_back(tmp.val);
if (tmp.y+1<arrays[tmp.x].size()) {
pq.push(point(tmp.x,tmp.y+1,arrays[tmp.x][tmp.y+1]));
}
}
return res;
}
};
104. 合并k个排序链表
这个题有两种做法, 一个是分治方法两两归并,还有一个是优先队列的方法。
优先队列方法比数组好的地方在于不用用坐标记录后继,但是不好的地方在于也要重载运算符,而且运算数是两个指针,重载运算符的参数不可以有指针,只能使用引用。但是我的引用会报错。找了一个写的对的。
struct cmp {
bool operator() (ListNode *a, ListNode *b) {
return a->val > b->val;
}
};
class Solution {
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
priority_queue<ListNode*> pq;
for (int i=0;i<lists.size();i++) {
if (lists[i]==NULL) continue;
pq.push(lists[i]);
}
ListNode* dummy=new ListNode(0);
ListNode* head=dummy;
while (!pq.empty()) {
ListNode* tmp=pq.top();
pq.pop();
if (tmp->next!=NULL) pq.push(tmp->next);
head->next=tmp;
head=head->next;
}
return dummy->next;
}
};
81. 数据流中位数
巨牛逼的算法,用一个大根堆维护排序数组的左边,小根堆维护排序数组的右边。分别向左堆、右堆添加元素(右堆的元素是从左堆选出的最大的),这样左堆的根节点就是中位数。
class Solution {
public:
/**
* @param nums: A list of integers
* @return: the median of numbers
*/
vector<int> medianII(vector<int> &nums) {
priority_queue<int> maxheap;//数组左半部分
priority_queue<int,vector<int>,greater<int>> minheap;//数组右半部分
vector<int> res;
bool flag=0;
for (int i=0;i<nums.size();i++) {
res.push_back(insert(nums[i],maxheap,minheap,flag));
flag=!flag;
}
return res;
}
int insert(int num, priority_queue<int> &maxheap,
priority_queue<int,vector<int>,greater<int>> &minheap,
bool flag) {
maxheap.push(num);
if (flag) {
int tmp=maxheap.top();
maxheap.pop();
minheap.push(tmp);
}
if (minheap.empty()) return maxheap.top();
while (maxheap.top()>minheap.top()) {
int a=maxheap.top(); maxheap.pop();
int b=minheap.top(); minheap.pop();
maxheap.push(b);
minheap.push(a);
}
return maxheap.top();
}
};
class Solution {
public:
vector<int> topk(vector<int> &nums, int k) {
priority_queue<int,vector<int>,greater<int>> pq;
for (int i=0;i<nums.size();i++) {
pq.push(nums[i]);
if (pq.size()>k) pq.pop();
}
vector<int> res;
while (!pq.empty()) {
res.push_back(pq.top());
pq.pop();
}
reverse(res.begin(),res.end());
return res;
}
};
401. 排序矩阵中的从小到大第k个数
从右上角开始,每次把左边和下面的数放进优先队列里。出栈出到第k个就是所求元素。
和合并k个排序数组一样,这个也需要记录坐标。另外为了避免重复访问,还需要visit数组记录访问情况。
class point {
public:
int x,y,val;
point(int _x,int _y,int _val):x(_x),y(_y),val(_val) {};
friend bool operator > (class point a,class point b) {
return a.val>b.val;
}
};
class Solution {
public:
int kthSmallest(vector<vector<int>> &matrix, int k) {
if (matrix.size()==0) return -1;
priority_queue<point,vector<point>,greater<point>> pq;
pq.push(point(0,0,matrix[0][0]));
vector<vector<bool>> hash(matrix.size(),vector<bool>(matrix[0].size(),true));
hash[0][0]=false;
int count=0;
point num=point(0,0,0);
while (count<k) {
num=pq.top();
pq.pop();
count++;
if (num.x+1<matrix.size() && hash[num.x+1][num.y]) {
hash[num.x+1][num.y]=false;
pq.push(point(num.x+1,num.y,matrix[num.x+1][num.y]));
}
if (num.y+1<matrix[0].size() && hash[num.x][num.y+1]) {
hash[num.x][num.y+1]=false;
pq.push(point(num.x,num.y+1,matrix[num.x][num.y+1]));
}
}
return num.val;
}
};
131. 大楼轮廓
??算法:https://www.jianshu.com/p/36dcb18525a1,把每座大楼看作两堵墙,按位置的升序和高度的降序排列。画的时候有一个cur数组记录当前已经画了但还没结束的左墙,每次碰到更高的左墙或匹配的右墙开始画。题解的精髓在于用一个优先队列存贮cur,保证cur是有序的,匹配的时候也是从高到低进行匹配和绘画。但是由于有时候碰到了右墙,其对应的左墙不是最高的,说明被其他墙覆盖了,需要直接从cur中删除左墙,而pq不能做到这一点。题解使用了multiset完成这个工作,multiset在一些条件下可以代替pq。
class Wall {
public:
int index;
int height;
Wall(int _index,int _height): index(_index),height(_height) {}
};
class Solution {
public:
static bool cmp (Wall a,Wall b) {
if (a.index!=b.index) return a.index<b.index;
else return a.height>b.height;
}
vector<vector<int>> buildingOutline(vector<vector<int>> &buildings) {
if (buildings.size()<=1) return buildings;
vector<Wall> walls;
for (int i=0;i<buildings.size();i++) {
walls.push_back(Wall(buildings[i][0],buildings[i][2]));//左墙
walls.push_back(Wall(buildings[i][1],-buildings[i][2]));//右墙
}
sort(walls.begin(),walls.end(),cmp);
multiset<int> cur;//存放目前还没画完的左墙,
//不存右墙,如果有右墙就直接把对应的左墙删掉,说明画完了
//使用multiset的好处:1.和优先队列一样有顺序,且可以计数(处理重复高度的情况 *1)
//2.可以删除中间的元素
//开始画
int prewall =0;
vector<vector<int>> res;
for (int i=0;i<walls.size();i++) {
if (cur.empty() && walls[i].height>0) { //前面的房子都画完了,重新画左墙
cur.insert(walls[i].height);
prewall=walls[i].index;
continue;
}
//左边:只有在当前左墙比前一座左墙高的时候才画楼
//否则只要把左墙存起来即可
if (walls[i].height>0) {
if (walls[i].height>*cur.rbegin() &walls[i].index>prewall) {
vector<int> tmp;
tmp.push_back(prewall);
tmp.push_back(walls[i].index);
tmp.push_back(*cur.rbegin());
res.push_back(tmp);
prewall=walls[i].index;
}
cur.insert(walls[i].height);//是左墙就进队
}
else {if (walls[i].height==-*cur.rbegin() &&
cur.count(-walls[i].height)==1 &&
//*1 如果后面还有一座一样高的大楼 也不画
walls[i].index>prewall) {
vector<int> tmp;
tmp.push_back(prewall);
tmp.push_back(walls[i].index);
tmp.push_back(*cur.rbegin());
res.push_back(tmp);
prewall=walls[i].index;
}
cur.erase(cur.lower_bound(-walls[i].height));
}
}
return res;
}
};