预备知识
基本操作:
- 栈(stack)LIFO(后进先出):S.top():取出栈顶;S.empty()判断栈是否为空;S.push(x)将x添加至栈;S.pop():弹出栈顶;S.size()栈存储元素的个数
- 队列(queue)FIFO(先进先出):Q.front():返回队列头部元素;Q.back():返回队列尾部元素;Q.empty()判断栈是否为空;Q.push(x)将x添加至栈;Q.pop():弹出栈顶;Q.size()栈存储元素的个数
- 堆(heap):标准库中的priority queue(类型)默认构造最大堆,最小堆构造std::priority queue<int, std::vector,std::greater> small_heap;最大堆构造std::priority queue<int, std::vector,std::less> big_heap;
big_heap.empty():判断堆是否为空;big_heap.pop():弹出对顶元素(最大值);big_heap.push(x)添加元素;big_heap.top()返回堆顶元素(最大值);big_heap.size():返回堆中元素个数
一、使用队列实现栈
使用队列实现栈的下列操作:
push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空
思路:
#include <iostream>
#include <queue>
using namespace std;
class MyStack{
public:
MyStack(){
}
void push(int x){
std::queue<int> temp_queue;//申请一个临时队列temp_queue
temp_queue.push(x); //将x先push进temp_queue
while(!_data.empty()) //然后将_data中的元素放进temp_queue
{
temp_queue.push(_data.front());
_data.pop();
}
while(!temp_queue.empty()) //将temp_queue中的元素放进_data中,此时存在队列中的取出顺序就与栈相同了
{
_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;
};
int main()
{
MyStack S;
S.push(1);
S.push(2);
S.push(4);
S.push(6);
cout<<S.top()<<endl;
S.pop();
cout<<S.top()<<endl;
if(S.empty())
cout<<"This stack is empty!"<<endl;
else
cout<<"This stack is not empty!"<<endl;
return 0;
}
二、使用栈实现队列
使用栈实现队列的下列操作:
push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。
思路:
#include <iostream>
#include <stack>
using namespace std;
class MyQueue{
public:
MyQueue(){
}
void push(int x){
std::stack<int> temp_stack;//临时栈用来调换数据顺序
while(!_data.empty()) //将_data中的数据放入临时栈中
{
temp_stack.push(_data.top());
_data.pop();
}
temp_stack.push(x); //将x push进temp_stack
while(!temp_stack.empty())//将temp_stack放进_data中,数据顺序调换完成
{
_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;
};
int main()
{
MyQueue Q;
Q.push(4);
Q.push(6);
cout<<Q.peek()<<endl;
Q.pop();
cout<<Q.peek()<<endl;
Q.pop();
if(Q.empty())
cout<<"This queue is empty!"<<endl;
else
cout<<"This queue is not empty!"<<endl;
return 0;
}
三、包含min函数的栈
设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) – 将元素 x 推入栈中。
pop() – 删除栈顶的元素。
top() – 获取栈顶元素。
getMin() – 检索栈中的最小元素。
思路:用另一个栈存储各个状态下的最小值
#include <iostream>
#include <stack>
using namespace std;
class MinStack{
public:
MinStack(){
}
void push(int x){
_data.push(x); //将x存进栈_data中
if(_min.empty()){ //如果最小栈_min为空,则直接将x存进最小栈
_min.push(x);
}
else{ //如果最小栈不为空,则将x与_min.top()(上一次存储后最小的元素)进行比较 ,将较小的元素存进_min中
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; //用来存储不同状态下的最小值
};
int main()
{
MinStack S;
S.push(1);
S.push(-2);
S.push(4);
S.push(-6);
cout<<S.top()<<endl;
S.pop();
cout<<"The min number is";
cout<<S.getMin()<<endl;
return 0;
}
代码精炼:
class MinStack{
public:
MinStack(){
}
void push(int x){
_data.push(x);
if(_min.empty() || x<=_min.top()){ //如果x小于等于最小栈的栈顶或者最小栈为空则将x存进最小栈
_min.push(x);
}
}
void pop(){
if(_data.top()==getMin()){ //弹出元素时,如果 _min中的元素等于将要弹出的元素则一同弹出
_min.pop();
}
_data.pop();
}
int top(){
return _data.top();
}
int getMin(){
return _min.top();
}
private:
std::stack<int> _data; //用来存储数据
std::stack<int> _min; //用来存储不同状态下的最小值
};
四、合法的出栈序列
思路:
#include <iostream>
#include <stack>
#include <queue>
using namespace std;
bool is_valid_order(std::queue<int> &Q){
std::stack<int> S;
int n = Q.size();
for(int i=1;i<=n;i++){ //按1-n将元素push进S中
S.push(i);
while(!S.empty()&&Q.front()==S.top()){ //如果栈与队列元素相同则将栈与队列中的元素弹出
//还要判断S是否为空;用while,if只会判断以此,如果有多个相等的元素则会出现错误
Q.pop();
S.pop();
}
}
if(S.empty()){
return true;
}
return false;
}
int main()
{
std::queue<int> QQ;
QQ.push(3);
QQ.push(1);
QQ.push(2);
QQ.push(4);
bool a=is_valid_order(QQ);
if(a){
cout<<"This order is valid"<<endl;
}
else
cout<<"This order is not valid"<<endl;
return 0;
}
五、简单计算器
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -,非负整数和空格 。
思路
代码:
#include <iostream>
#include <stack>
#include <string>
using namespace std;
class solution
{
public:
int calculator(string s)
{
static const int STATE_BEGIN=0; //利用向量机的思想,设置三种状态,分别是开始,数值,和符号
static const int NUMBER_STATE=1;
static const int OPERATION_STATE=2;
stack<int> number_stack; //申请一个栈用来存储数值
stack<char> operator_stack; //用来存储符号
int number=0; //用来处理数字,将字符型的数字转化成整型
int STATE = STATE_BEGIN; //将状态设置成初始状态
int compute_flag = 0; //将计算符号设置为零,不进行计算
for(int i=0; i < s.size();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--; //i回一位,因为对当前位进行判断的后i会进行一次自加移动到下一位,因而要对当位置的内容进行操作则i往前回一位
break; //推出case,继续switch
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,operator_stack);
}
number = 0; //数字入栈后number置为0
i--; //i回一位
STATE = OPERATION_STATE; //状态置为符号
}
break;
case OPERATION_STATE:
if(s[i] == '+'|| s[i] == '-'){
operator_stack.push(s[i]);//如果是符号则将符号push进符号栈中 ,并且计算标志置为1
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退回一位
i--;
}
else if(s[i] ==')'){ //遇到右括号则直接进行计算
compute(number_stack,operator_stack);
}
break;
}
}
if(number!=0) {
number_stack.push(number); //最后数字不为零则将其放进数字栈进行相关计算
compute(number_stack, operator_stack);
}
if(number==0 && number_stack.empty()) //数字为零,且数字栈为空,返回零
//不能判断符号栈,符号比数字少,因此总返回0.要细心啊啊啊!!!!
return 0;
return number_stack.top(); //返回数字栈顶部的数字
}
void compute(stack<int> &number_stack, stack<char> &operator_stack){ //计算函数
if(number_stack.size()<2){ //如果数字栈小于两个数则不用进行计算
return;
}
int num2 = number_stack.top(); //令num2等于数字站顶部的数
number_stack.pop(); //弹出顶部数字
int num1 = number_stack.top();
number_stack.pop();
if(operator_stack.top()=='+'){ //对num1、num2 进行计算
number_stack.push(num1+num2);
}
else if(operator_stack.top()=='-'){
number_stack.push(num1-num2);
}
operator_stack.pop();
}
};
int main()
{
solution solve;
string s = "1-(2+1)";
int a=solve.calculator(s);
cout<<a<<endl;
return 0;
}
六、求数组中的K大数
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
思路
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
class Solution{
public:
int findKthlargest(vector<int>& nums,int K){
priority_queue<int, vector<int>, greater<int> > Q;//最小堆
for(int i=0; i < nums.size();i++){//遍历所有数字将前K大的数字入堆
if(Q.size() < K){ //如果堆中数字小于K个则直接入堆
Q.push(nums[i]);
}
else if(Q.top()<nums[i]){ //否则判断堆顶数字是否小于新的数字,如果是则弹出堆顶,新的数字入堆
Q.pop();
Q.push(nums[i]);
}
}
return Q.top();
}
};
int main()
{
vector<int> nums;
nums.push_back(3);
nums.push_back(2);
nums.push_back(1);
nums.push_back(5);
nums.push_back(6);
nums.push_back(4);
Solution solve;
cout<<"The second largest number is:";
cout<<solve.findKthlargest(nums, 2)<<endl;
return 0;
}
七、寻找中位数
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,[2,3,4] 的中位数是 3 [2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
思路:
添加元素时:
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
class MedianFinder{
public:
priority_queue<double, vector<double>, greater<double> > small_queue;//最小堆,用来存储较大的一半元素
priority_queue<double, vector<double>, less<double> > big_queue;//最大堆,用来存储较小的一半元素
MedianFinder(){//初始化
}
void addNum(int num){//添加数字
if(big_queue.empty()){
big_queue.push(num);
return;
}
if(big_queue.size()==small_queue.size()){//情况1:如果最大堆与最小堆元素个数一样
if(num < big_queue.top()){ //如果num小于最大堆则直接存进最大堆
big_queue.push(num);
}
else{ //否则存进最小堆
small_queue.push(num);
}
}
else if(big_queue.size() > small_queue.size()){//情况2:如果最大堆的元素多一个
if(num < big_queue.top()){ //num小于最大堆堆顶,则要讲最大堆堆顶放进最小堆再弹出堆顶存入num
small_queue.push(big_queue.top());
big_queue.pop();
big_queue.push(num);
}
else{ //否则直接将num存进最小堆
small_queue.push(num);
}
}
else if(big_queue.size() < small_queue.size()){//情况3:最小堆元素多一个
if(num < small_queue.top()){ //num小于最小堆堆顶,则直接存进最大堆
big_queue.push(num);
}
else{ //否则将最小堆堆顶存进最大堆后弹出,再将num存进
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();
}
};
int main()
{
MedianFinder M;
int test[] = {6,11,1,7,99,4,33};
for(int i=0;i<7;i++){
M.addNum(test[i]);
cout<<M.findMedian()<<endl;
}
return 0;
}