一、基础知识
栈:后进先出的线性表;STL方法包含push(x),pop(),top(),empty()
队列:先进先出的线性表;STL方法包含push(x),pop(),top(),empty()
二、目录
- 使用队列实现栈
三、 解题
1.使用队列实现栈
这题在剑指offer中是面试题9(用两个队列实现栈),leetcode229。使用队列实现栈。
对于1,2,3,4,5这样一个序列,
用栈存储,从上至下为5,4,3,2,1。5为栈顶,使用top返回5,push(x)则x变为栈顶;
用队列存储,从上至下为5,4,3,2,1。1为队头,使用top返回1,push(x)x插入尾部,1还是队头;
思路:
使用两个队列,queue1队列依次push 1 2 3,这时1为队头,使用pop弹出的是1,不符合栈后进先出的要求,于是先将1,2 push进queue2,query1剩余3,此时pop弹出为3,实现了弹出栈顶元素的需求。如需要弹出2,则重复步骤,先将1 push进queue1,直到queue2剩下最后一个元素即2,此时弹出可得到2
待写
2.使用栈实现队列
这题在剑指offer中是面试题9(用两个栈实现队列),leetcode232。
思路:使用两个栈,push值到stack1中,front时将stack1的栈顶push进stack2中,原先stack1中的栈顶就变成stack2的栈底,原先stack1的栈底(队首)变为stack2栈顶,用top可得到stack2的栈顶,也就是队首元素。
#include <stdio.h>
#include <stack>
class MyQueue
{
private:
std::stack<int> stack1;
std::stack<int> stack2;
public:
void push(int x){
stack1.push(x);
}
int front(){ // 实现队列的front方法,获得首元素
if(stack2.empty()){
// 每次将stack1栈top元素push进stack2栈, 最后一次push值为栈的最底端, 也就是队头
while (!stack1.empty())
{
int stacktop = stack1.top();
stack2.push(stacktop);
stack1.pop();
}
}
int res = stack2.top();
return res;
}
void pop(){
stack2.pop();
}
};
int main(){
MyQueue que;
que.push(1);
que.push(2);
que.push(3);
que.push(4);
printf("front: %d ", que.front());
que.pop();
printf("front: %d ", que.front());
que.push(5);
printf("front: %d ", que.front());
}
3.包含Min函数的栈
这题在剑指offer中是面试题9(用两个栈实现队列),leetcode155。
设计一个栈,包含push,pop,top,getMin,要求算法复杂度为O(1)
思路:由于要求复杂度为常数级,故不能采取遍历的方式记录最小值;故使用一个最小值栈,数据栈中每push一个值,直接push就好;对应最小值栈需要将新值和栈顶元素进行进行比较,谁小就push谁。
class MinStack
{
private:
std::stack<int> data_stack;
std::stack<int> min_stack;
public:
void push(int x){
data_stack.push(x);
if(min_stack.empty()){ // 如果最小栈是空的,则直接push
min_stack.push(x);
}else{
if(x > min_stack.top()) // push进x和minstack中较小的那个
x = min_stack.top();
min_stack.push(x);
}
}
void pop(){
data_stack.pop();
min_stack.pop();
}
int top(){
return data_stack.top();
}
int getMin(){
return min_stack.top();
}
};
4.出栈序列是否合法
已知从1至n的数字序列,按顺序入栈,每个数字入栈后即可出栈,也可在栈中停留,等待后面的数字入栈出栈后,该数字再出栈,求该数字序列的出栈序列是否合法?
这题在剑指offer中是面试题31。
思路:
1.出栈结果存储在队列 order中(也可以用辅助栈)
2.按元素顺序,将元素push进入栈,
3.每push1个元素,即检查是否与队列首部元素相同,若相同则弹出队列首元素,弹出栈顶元素,直到两元素不同结束
4.若最终栈为空,说明序列合法,否则不合法
整体复杂度O(n)
#include <stdio.h>
#include <stack>
#include <queue>
class CheckOrderValid
{
public:
bool checkOrderIsValid(std::queue<int> &order){
std::stack<int> s;
int n = order.size();
for(int i = 1; i <= n; i++){
s.push(i);
// 每push 1个元素,即检查是否与队列首部元素相同,若相同则弹出队列首元素,弹出栈顶元素,直到两元素不同结束
while (!s.empty() && s.top()==order.front())
{
s.pop();
order.pop();
}
}
if(!s.empty()) // s不空说明还有元素,即序列不合法
return false;
return true;
}
};
int main(){
CheckOrderValid valid;
std::queue<int> order;
// order.push(6);
order.push(1);
order.push(2);
order.push(3);
order.push(4);
order.push(5);
if(valid.checkOrderIsValid(order)){
printf("yes/n");
}else{
printf("No/n");
}
return 0;
}
5.把字符串转换成整数
把字符串转换成整数
思路:看题目似乎很简单,但是如果考虑清楚输入的可能性,代码量还是挺多的。
#include <cstdio>
long long StrToIntCore(const char* str, bool minus);
enum Status {kValid = 0, kInvalid};
int g_nStatus = kValid;
int StrToInt(const char* str)
{
g_nStatus = kInvalid;
long long num = 0;
if(str != nullptr && *str != '\0')
{
bool minus = false;
if(*str == '+')
str ++;
else if(*str == '-')
{
str ++;
minus = true;
}
if(*str != '\0')
num = StrToIntCore(str, minus);
}
return (int)num;
}
long long StrToIntCore(const char* digit, bool minus)
{
long long num = 0;
while(*digit != '\0')
{
if(*digit >= '0' && *digit <= '9')
{
int flag = minus ? -1 : 1;
num = num * 10 + flag * (*digit - '0');
if((!minus && num > 0x7FFFFFFF)
|| (minus && num < (signed int)0x80000000))
{
num = 0;
break;
}
digit++;
}
else
{
num = 0;
break;
}
}
if(*digit == '\0')
g_nStatus = kValid;
return num;
}
// ====================测试代码====================
void Test(const char* string)
{
int result = StrToInt(string);
if(result == 0 && g_nStatus == kInvalid)
printf("the input %s is invalid.\n", string);
else
printf("number for %s is: %d.\n", string, result);
}
int main(int argc, char* argv[])
{
Test(nullptr);
Test("");
Test("123");
Test("+123");
Test("-123");
Test("1a33");
Test("+0");
Test("-0");
//有效的最大正整数, 0x7FFFFFFF
Test("+2147483647");
Test("-2147483647");
Test("+2147483648");
//有效的最小负整数, 0x80000000
Test("-2147483648");
Test("+2147483649");
Test("-2147483649");
Test("+");
Test("-");
return 0;
}
6.求数组中第K大的数(二叉堆)
一个未排序的数组,求其中第k大的值。 leetcode215题
一个数组中求第k大,只要将前k大的值存入堆中,最小堆会自动调整,始终保证堆顶为堆中最小值,使用top即求得第k大值。
#include <stdio.h>
#include <queue>
主要是std::priority_queue<int, std::vector<int>, std::greater<int> > Q;最小堆
class ArrayKthHeap
{
public:
int findKthLargest(std::vector<int>& nums, int k){
// 数组中第k大元素,则只需要留下数组中前k大元素,最小堆的性质可使得较大的数成为堆顶,直接用pop可求得
std::priority_queue<int, std::vector<int>, std::greater<int> > Q; // 会自动调整堆
for (int i = 0; i < nums.size(); i++)
{
if(Q.size() < k){ // 如果堆中个数小于k,则直接push进堆中
Q.push(nums[i]);
}else if(Q.top() < nums[i]){ // 如果堆顶小于元素,则push进该元素,原堆顶删除
Q.pop();
Q.push(nums[i]);
}
}
return Q.top();
}
};
int main(){
std::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);
ArrayKthHeap smailheap;
int kbig = smailheap.findKthLargest(nums, 2);
printf("%d ", kbig);
return 0;
}
7.求动态数组中位数
剑指offer面试题41,leetcode295
设计一个数据结构,该数据结构动态维护一组数据,且支持如下操作:
1.添加元素:void addNum(int num),将整型num添加至数据结构中。
2.返回数据的中位数:double findMedian返回其维护的数据的中位数
中位数定义
1.若数据个数为奇数,中位数是该组数排序后中间的数。【1,2,3】->2
2.若数据个数为偶数,中位数是该组数排序后中间的两个数字的平均值。【1,2,3,4】->2.5
思路:
最大堆的堆顶是堆中最大的,最小堆的堆顶是堆中最小的,最大堆的堆顶<最小堆的堆顶
动态添加数据到最大最小堆中:需要两个堆大小差距不超过1,新增的数加入到较少的堆中
堆顶相加/2即为中位数
插入时间复杂度nlogn,得到中位数复杂度n
#include <stdio.h>
#include <queue>
class FindMiddleNum
{
private:
std::priority_queue<int, std::vector<int>, std::greater<int> > big_queue;
std::priority_queue<int, std::vector<int>, std::less<int> > small_queue;
public:
/*
最大堆的堆顶是堆中最大的,最小堆的堆顶是堆中最小的,最大堆的堆顶<最小堆的堆顶
动态添加数据到最大最小堆中:需要两个堆大小差距不超过1,新增的数加入到较少的堆中
堆顶相加/2即为中位数
*/
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()){ // big_queue.top()要保持比small_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();
}else{
return small_queue.top();
}
}
};
int main(){
FindMiddleNum finder;
int test[] = {6,10,1,7,99,4,33};
for (int i = 0; i < 7 ; i++)
{
finder.addNum(test[i]);
printf("%lf ", finder.findMedian());
}
return 0;
}