一、基礎知識
棧:後進先出的線性表;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;
}