9.用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
示例 1:
输入:
[“CQueue”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:
输入:
[“CQueue”,“deleteHead”,“appendTail”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
分析
这道题的意图是要求我们操作两个“先进后出”的栈实现一个“先进先出”的队列CQueue.
class CQueue {
public:
CQueue() {
}
void appendTail(int value) {
stack1.push(value);
}
int deleteHead() {
// 先检查stack2是否为空,如果stack2为空,需要stack1传过来
if(stack2.empty()){
if(stack1.empty()) return -1;
while(!stack1.empty()){
// int& data = stack1.top();
// stack2.push(data);
// stack1.pop();
stack2.push(stack1.top());
// 将stack1栈顶元素压栈到stack2,以实现队列功能
stack1.pop();
}
}
// 因为stack2中的元素比stack1中的元素先进去,所以
//当stack2中有元素的时候,可以直接出栈
// 反证法:如果stack2中的元素不比stack1中的元素靠前,那么当
//stack2中有元素时,stack1中又进来一个元素,这时如果先把stack1
//中的栈顶元素移动到stack2中时,就实现了后进先出了,矛盾
// if(!stack2.empty()),栈2不为空,直接返回栈2的栈顶元素
int& head = stack2.top();
stack2.pop();
return head;
}
private:
stack<int> stack1;
stack<int> stack2;
};
/**
* Your CQueue object will be instantiated and called as such:
* CQueue* obj = new CQueue();
* obj->appendTail(value);
* int param_2 = obj->deleteHead();
*/
面试题31. 栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。
示例 1:
输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
示例 2:
输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。
分析
该题的思路:
借助辅助栈,按照压栈序列pushed依次输入辅助栈,再将栈顶元素与popped序列比较,若元素对应相等,则弹出该栈顶元素,否则继续压栈
时间复杂度:O(N) 将所以元素一遍入栈,一遍出栈,需要 O(2N)。
空间复杂度:O(N)。使用了辅助栈 res。
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
stack<int> res;
if(pushed.size() != popped.size())//首先判断两栈大小是否相同
return false;
int i=0;//用popped[i]记录popped中被弹出的元素
for(auto m:pushed)
{
res.push(m);//按照压栈序列入辅助栈
while(!res.empty() && res.top()==popped[i])
{//若栈顶元素与弹出序列元素相同则弹出,i指向下一个要弹出的元素
res.pop();
i++;
}
}
return res.empty();//最后操作完成,栈为空即合法
}
};
面试题39. 数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2
限制:
1 <= 数组长度 <= 50000
分析:
方法一:摩尔投票法
摩尔投票算法是基于这个事实:每次从序列里选择两个不相同的数字删除掉(或称为“抵消”),最后剩下一个数字或几个相同的数字,就是出现次数大于总数一半的那个。
class Solution {
public:
int majorityElement(vector<int>& nums) {
int ans,cnt=0;
for(auto num:nums)
{
if(cnt == 0)
{
ans = num;
++cnt;
}else{
if(ans == num) ++cnt;
else
--cnt;
}
}
return ans;
}
};
方法二:采用map
class Solution {
public:
int majorityElement(vector<int>& nums) {
map<int,int> res;//定义map
int len = nums.size();
for(auto i:nums)
{
res[i]++;//遍历数组,统计每个元素出现的次数
}
for(auto i:nums)
{
//再次遍历数组,判断每个元素出现次数与len的大小
if(res[i]*2 > len)
return i;
}
return -1;
}
};
class Solution {
public:
int majorityElement(vector<int>& nums) {
//方法1:排序后中间的元素一定是出现超过一半的数字
sort(nums.begin(),nums.end());
return nums[nums.size()/2];
//方法2:哈希表
unordered_map<int,int>mp;
for(auto it : nums){
mp[it]++;
if(mp[it]>nums.size()/2) return it;
}
return 0;
//方法3:超过一半的数字比其他所有数字的总和次数多
int n=1;
int result=nums[0];
for(int i=1;i<nums.size();i++){
if(n==0){
result=nums[i];
n=1;
}
else if(result==nums[i])n++;
else n--;
}
return result;
}
};