题目一:用队列实现栈
描述:设计一个栈,支持如下操作,这些操作的算法复杂度需要是常数级,O(1) , 栈的内部存储数据的结构为队列,队列的方法只能包括push、peek(front)、pop、size、empty等标准的队列方法
思路:利用两个队列实现栈
class MyStack{ public: MyStack(){} void push(int x){ std::queue<int>temp_queue; temp_queue.push(x); while (!_data.empty()) { temp_queue.push(_data.front()); _data.pop(); } while (!temp_queue.empty()) { _data.push(temp_queue.front()); temp_queue.pop(); } } int pop(){ int x=_data.front(); _data.pop(); return x; } int top(){ return _data.front(); } bool empty(){ return _data.empty(); } private: std::queue<int> _data; };
题目二:使用栈实现队列
描述:设计一个队列,队列支持如下操作,这些操作的算法复杂度需要是常数级, O(1),队列的内部存储数据的结构为栈,栈的方法只能包括push、top、pop、size、 empty等标准的栈方法
思路:利用2个栈,在 队列调用push的时候,利用临时栈 调换元素次序
class MyQueue{ public: MyQueue(){} void push(int x){ std::stack<int>temp_stack; while (!_data.empty()) { temp_stack.push(_data.top()); _data.pop(); } temp_stack.push(x); while (!temp_stack.empty()) { _data.push(temp_stack.top()); temp_stack.pop(); } } int pop(){ int x=_data.top(); _data.pop(); return x; } int peek(){ return _data.top(); } bool empty(){ return _data.empty(); } private: std::stack<int>_data; };
题目三:包含min函数的栈
描述:设计一个栈,支持如下操作,这些操作的算 法复杂度需要是常数级,O(1)
实现:
1.push(x) : 将元素x压入栈中
2.pop() : 弹出(移除)栈顶元素
3.top() : 返回栈顶元素
4.getMin() : 返回栈内最小元素
思路:
1.1个变量MIN无法完成记录栈中所有状态下的最小值。
2.栈的每个状态,都需要有一个变量记录最小值。可以用一个最小值栈,记录栈每个状态下的最小值
class MinStack{ public: MinStack(){} void push(int x){ _data.push(x); if(_min.empty()){ _min.push(x); } else{ if(x>_min.top()){ x=_min.top(); } _min.push(x); } } void pop(){ _data.pop(); _min.pop(); } int top(){ return _data.top(); } int getMin(){ return _min.top(); } private: std::stack<int>_data; std::stack<int>_min; };
题目四:合法的出栈序列
描述:已知从1至n的数字序列,按顺序入栈,每个数字入栈后即可出栈, 也可在栈中停留,等待后面的数字入栈出栈后,该数字再出栈,求该数字序列的出栈序列是否合法?
比如 3 2 5 4 1 合法 3 1 2 4 5不合法,因为1不可能在2之前
思路:
使用栈和队列模拟入站出站操作
步骤:
1 出站结果存储在队列order中
2 按元素顺序,将元素push进入栈
3 每push一次元素,即检查是否和队列首部元素相同,若相同则弹出队列首元素,弹出战顶元素,直到两元素不同
4 若最终栈为空,说明序列合法,反之不合法
bool check_is_valid_order(std::queue<int>&order)//检查序列存储在队列 { std::stack<int>S;//模拟战 int n=(int)order.size();//n为序列长度,将1-n按顺序入站 for (int i=1; i<=n; i++) { S.push(i); //将i入站 while (!S.empty()&&S.top()==order.front()) { S.pop(); order.pop();//只要S不空且队列头部与战顶相同,即弹出元素 } } if(S.empty()!=false){//如果最终栈不空,则说明序列不合法 return false; } return true; }
题目五 简单计算器
利用状态机的概念,在操作符状态或数字状态下进行相应的处理
测试:
std::string s = "1+(2-3)";
Solution solve;
printf("%d\n", solve.calculate(s));
//字符串转数字 void changeStrToNum(std::string s){ int number=0; for(int i=0;i<s.length();i++){ if (s[i] >= '0' && s[i] <= '9'){ number = number * 10 + s[i] - '0'; } } printf("%d\n",number); }
class Solution { public: int calculate(std::string s) { //三个状态 static const int STATE_BEGIN=0; static const int NUMBER_STATE=1; static const int OPERATION_STATE=2; std::stack<int>number_stack; std::stack<char>operation_stack; int number=0;//当前的数字 int STATE=STATE_BEGIN;//当前状态 int compute_flag=0;//1 为准备计算 0 为 先不能计算 for(int i=0;i<s.length();i++){//遍历字符串 if(s[i]==' ') continue; switch (STATE) { case STATE_BEGIN: if(s[i]>='0'&&s[i]<='9'){//是数字 STATE=NUMBER_STATE;//当前应该转换为数字状态 } else{ STATE=OPERATION_STATE; } i--;//这次只是判断,所以应该退格,因为for循环中i会递增 break; case NUMBER_STATE: if(s[i]>='0'&&s[i]<='9'){//是数字 number=number*10+s[i]-'0';//字符转数字 } else{//不是数字,说明到操作符了 number_stack.push(number); if(compute_flag==1){ //可以计算了 compute(number_stack, operation_stack); } number=0;//本次数字存储完毕,清零 i--;//只是判断下下次应该的状态,所以i退格 STATE=OPERATION_STATE; } break; case OPERATION_STATE: if(s[i]=='+'||s[i]=='-'){ operation_stack.push(s[i]); compute_flag=1;//先标记为可以计算,如果遇到(,在标示为不可计算 } else if(s[i]=='('){//左括号,将要为数字模式,并且先不能计算 STATE=NUMBER_STATE; compute_flag=0; } else if(s[i]>='0'&&s[i]<='9'){ STATE=NUMBER_STATE; i--;//下次为数字模式,退格 } else if(s[i]==')'){//右括号,开始计算 compute(number_stack, operation_stack); } break; } } if(number!=0){ number_stack.push(number); compute(number_stack, operation_stack); } if(number==0&&number_stack.empty()){ int ii=0; return 0; } return number_stack.top(); } private: void compute(std::stack<int> &number_stack, std::stack<char> &operation_stack){ if (number_stack.size() < 2){ return; } int num2 = number_stack.top(); number_stack.pop(); int num1 = number_stack.top(); number_stack.pop(); if (operation_stack.top() == '+'){ number_stack.push(num1 + num2); } else if(operation_stack.top() == '-'){ number_stack.push(num1 - num2); } operation_stack.pop(); } };
题目六 :求数组中第K大的数
描述:
已知一个未排序的数组,求这个数组中第K大的数字。 如,array = [3,2,1,5,6,4] , k = 2,return 5
思路:常规我们可以对数组进行排序,然后找出第K大的数,时间复杂度为O(nlogn),有没有更优化的方法,有的,利用二叉堆,时间负责度位nlogk
维护一个K大小的最小堆,堆中元素个数小于K时,新元素直接进入堆;否则,当 堆顶小于新元素时,弹出堆顶,将新元素加入堆。
解释: 由于堆是最小堆,堆顶是堆中最小元素,新元素都会保证比堆顶小(否则新元素
替换堆顶),故堆中K个元素是已扫描的元素里最大的K个;堆顶即为第K大的数。
int findKthLargest(std::vector<int>&nums,int k) { //构建最小堆 std::priority_queue<int, std::vector<int>, std::greater<int> > Q; for (int i=0; i<nums.size(); i++) { if(Q.size()<k){ Q.push(nums[i]); } else if(nums[i]>Q.top()){ Q.pop(); Q.push(nums[i]); } } return Q.top(); }
题目七 寻找中位数
描述:
设计一个数据结构,该数据结构动态维护一组数据,且支持如下操作: 1.添加元素: void addNum(int num),将整型num添加至数据结构中。 2.返回数据的中位数: double findMedian(),返回其维护的数据的中位数。
中位数定义:
1.若数据个数为奇数,中位数是该组数排序后中间的数。[1,2,3] -> 2 2.若数据个数为偶数,中位数是该组数排序后中间的两个数字的平均值。[1,2,3,4] -> 2.5
分析:
最直观的方法:
存储结构使用数组,每次添加元素或查找中位数时对数组排序,
再计算结果。
时间复杂度:
1.比如 若添加元素时排序,addNum复杂度O(n)[插入排序,最好为O(n),最差为n的平方],findMedian复杂度O(1)
2.若查询中位数时排序,addNum复杂度O(1),findMedian复杂度 O(nlogn)
若添加元素或查询中位数是随机的操作,共n次操作,按上述思想,整体复 杂度最佳为O(n^2)
所以应该寻找最优的方法:
思路:利用堆的性质
动态维护一个最大堆与一个最小堆,最大堆存储一半数据,最小堆存储 一般数据,维持最大堆的堆顶比最小堆的堆顶小,并且2个差一个元素
比如 如果最后 最大堆和最小堆个数一样,那么中位数为两个堆顶的平方,如果其中一个比另一个多一个元素,那么中位数就是多一个元素的堆的堆顶
添加元素的时候对堆进行调整有3种情况:
这里 最大堆简写为 A,最小堆简写为 B
情况一:A和B一样多
a 新元素大于最大堆,新元素插入B
b 新元素小于最大堆,新元素插入A
情况二:A比B多1
a 新元素大于A,新元素插入B
b 新元素小于A,A堆顶插入B,A堆顶移除,然后A插入新元素
情况三:A比B少1
a 新元素小于B,新元素插入A
b 新元素大于B,B堆顶插入A,B堆顶移除,然后B插入新元素
class MedianFinder{ public: MedianFinder(){} void addNum(int num){ if(big_queue.empty()){ big_queue.push(num); return; } if (big_queue.size() == small_queue.size()){ if (num < big_queue.top()){ big_queue.push(num); } else{ small_queue.push(num); } } else if(big_queue.size() > small_queue.size()){ if (num > big_queue.top()){ small_queue.push(num); } else{ small_queue.push(big_queue.top()); big_queue.pop(); big_queue.push(num); } } else if(big_queue.size() < small_queue.size()){ if (num < small_queue.top()){ big_queue.push(num); } else{ big_queue.push(small_queue.top()); small_queue.pop(); small_queue.push(num); } } } double findMedian(){ if (big_queue.size() == small_queue.size()){ return (big_queue.top() + small_queue.top()) / 2; } else if (big_queue.size() > small_queue.size()){ return big_queue.top(); } return small_queue.top(); } private: std::priority_queue<double> big_queue; std::priority_queue<double, std::vector<double>, std::greater<double> > small_queue; };