Chapter 3 | Stacks and Queues

Whether you are asked to implement a simple stack / queue, or you are asked to implement a modified version of one, you will have a big leg up on other candidates if you can flawlessly work with stacks and queues. Practice makes perfect! Here is some skeleton code for a Stack and Queue class.

当面试官需要你实现一个简单的堆栈或者列队的时候,你能顺利的写完,那你就比一般的应聘者先人一步了。熟能生巧,下面的这些基本的堆栈和队列的框架代码一定要掌握。

3.1

 Describe how you could use a single array to implement three stacks.

译文:

你如何只用一个数组实现三个栈?

解答

Approach 1:
Divide the array in three equal parts and allow the individual stack to grow in that limited space (note: “[“ means inclusive, while “(“ means exclusive of the end point).
»»for stack 1, we will use [0, n/3)
»»for stack 2, we will use [n/3, 2n/3)
»»for stack 3, we will use [2n/3, n)
This solution is based on the assumption that we do not have any extra information about the usage of space by individual stacks and that we can’t either modify or use any extra space. With these constraints, we are left with no other choice but to divide equally.

解法一:
将数组划分成3等份,每一份独立的用来实现堆栈。
*第一个堆栈:从 0     至 n/3
*第二个堆栈:从 n/3  至 2n/3
*第三个堆栈:从2n/3 至 n
这种解法是基于对每个堆栈的使用没有额外的使用说明,所以我们直接为每个堆栈划分固定的大小。


#include <iostream>

using namespace std;


 int stackSize = 300;
 int* buffer = new int [stackSize * 3];
 int stackPointer[] = {0, 0, 0}; // stack pointers to track top elem

 void push(int stackNum, int value) {
	 /* Find the index of the top element in the array + 1, and
	  * increment the stack pointer */
	   int index = stackNum * stackSize + stackPointer[stackNum] + 1;
	 stackPointer[stackNum]++;
	 buffer[index] = value;
 }

 int pop(int stackNum) {
	 int index = stackNum * stackSize + stackPointer[stackNum];
	 stackPointer[stackNum]--;
	 int value = buffer[index];
	 buffer[index]=0;
	 return value;
}

 int peek(int stackNum) {
	 int index = stackNum * stackSize + stackPointer[stackNum];
	 return buffer[index];
	 
 }

 bool isEmpty(int stackNum) {
	 return stackPointer[stackNum] == stackNum*stackSize;
}

int main()
{
	push(0,1);
	push(1,987);
	push(2,67);
	cout<<pop(2)<<endl;
	push(2,4);
	cout<<pop(2)<<endl;
	return 0;
}


Approach 2:
In this approach, any stack can grow as long as there is any free space in the array.
We sequentially allocate space to the stacks and we link new blocks to the previous block. This means any new element in a stack keeps a pointer to the previous top element of that particular stack.
In this implementation, we face a problem of unused space. For example, if a stack deletes some of its elements, the deleted elements may not necessarily appear at the end of the array.
So, in that case, we would not be able to use those newly freed spaces.
To overcome this deficiency, we can maintain a free list and the whole array space would be given initially to the free list. For every insertion, we would delete an entry from the free list. In case of deletion, we would simply add the index of the free cell to the free list.
In this implementation we would be able to have flexibility in terms of variable space utilization
but we would need to increase the space complexity.

解法二:
解法二中的,主要数组中还有空余的空间,堆栈就还能增长。
每次为堆栈分配一个空间的时候,在这个新空间中记录上一个空间地址。这样堆栈中的每个元素都有一个指针指向之前的元素。
这样的实现方法有一个问题就是如果一个堆栈弹出一个空间(释放空间),这个空间并不会作为空闲空间现在数组后面。这样话我们就不能使用新产生的空闲空间。
为了解决这个问题,我们用一个列表来记录空闲的空间。当有新空闲空间出现,我们就把它加入到这个表中。如果需要新分配一个空间,就从这个表中删除一个元素。
这样的实现方法使得3个堆栈能够动态的使用数组的空间,但是这是以增大空间复杂度换来的。


3.2 

How would you design a stack which, in addition to push and pop, also has a function min which returns the minimum element? Push, pop and min should all operate in O(1) time.

译文:

实现一个栈,除了push和pop操作,还要实现min函数以返回栈中的最小值。 push,pop和min函数的时间复杂度都为O(1)。

解答

看到这个题目最直接的反应是用一个变量来保存当前栈的最小值,让我们来看看这样可行否? 如果栈一直push那是没有问题,入栈元素如果比当前最小值还小,那就更新当前最小值。 可是如果pop掉的栈顶元素就是最小值,那么我们如何更新最小值呢?显然不太好办。 既然只用一个变量没法解决这个问题,那我们就增加变量。如果说每个结点除了保存当前的 值,另外再保存一个从该结点到栈底的结点中的最小值。那么,不论哪个结点成为了栈顶结 点,我们都有办法取得剩下的这些元素的最小值。代价是付出的空间多了一倍。

代码如下:

const int MAX_INT = ~(1<<31);//2147483647

typedef struct node{
    int val, min;
}node;

class StackWithMin{
public:
    StackWithMin(int size=1000){
        buf = new node[size];
        buf[0].min = MAX_INT;
        cur = 0;
    }
    ~StackWithMin(){
        delete[] buf;
    }
    void push(int val){
        buf[++cur].val = val;
        if(val<buf[cur-1].min) buf[cur].min = val;
        else buf[cur].min = buf[cur-1].min;
    }
    void pop(){
        --cur;
    }
    int top(){
        return buf[cur].val;
    }
    bool empty(){
        return cur==0;
    }
    int min(){
        return buf[cur].min;
    }

private:
    node *buf;
    int cur;
};

这种实现方式有一个明显的问题:数据冗余。比如说,栈里的数据从栈底到栈顶是1到10000, 那么,每个结点保存的最小值都是1,也就是保存了1的10000份拷贝,有这个必要吗? 直觉告诉我们应该是没必要的,我们应该可以想办法只保留一份1的拷贝, 然后如果结点是这些1到10000,就正确地返回这个最小值。这个要怎么做呢? 我们假设除了用一个栈s1来保存数据,还用另一个栈s2来保存这些非冗余最小值。那么, 当我们将数据压到要s1时,同时将它和s2的栈顶元素比较,如果不大于s2的栈顶元素, 那么将当前值也压入s2中。这样一来,s2中保存的就是一个阶段性最小值。 即s2中的每个值都是s1中栈底到达某个位置的最小值。那么,如果执行pop操作呢? 执行pop操作除了将s1中的栈顶元素出栈,还要将它和s2中的栈顶元素比较,如果相等, 说明这个值是栈底到栈顶的最小值,而它出栈后,最小值就不再是它了。所以, s2也要将栈顶元素出栈,新的栈顶元素将对应s1剩下元素中新的最小值。

代码如下:

class stack{
public:
    stack(int size=1000){
        buf = new int[size];
        cur = -1;
    }
    ~stack(){
        delete[] buf;
    }
    void push(int val){
        buf[++cur] = val;
    }
    void pop(){
        --cur;
    }
    int top(){
        return buf[cur];
    }
    bool empty(){
        return cur==-1;
    }

private:
    int *buf;
    int cur;
};

class StackWithMin1{
public:
    StackWithMin1(){

    }
    ~StackWithMin1(){

    }
    void push(int val){
        s1.push(val);
        if(val<=min())
            s2.push(val);
    }
    void pop(){
        if(s1.top()==min())
            s2.pop();
        s1.pop();
    }
    int top(){
        return s1.top();
    }
    bool empty(){
        return s1.empty();
    }
    int min(){
        if(s2.empty()) return MAX_INT;
        else return s2.top();
    }

private:
    stack s1, s2;
};



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值