队列和栈的基本性质和应用

栈和队列在实现结构上可以有数组和链表两种形式:

数组实现比较容易;

用链表结构比较复杂,因为有指针。



栈结构的基本操作:

pop  从栈顶弹出一个元素;

top或peek 只访问栈顶元素,但是不弹出删除;

push 向栈顶压入一个元素;

size 返回当前栈中的元素个数;


队列的基本操作:

与站操作不同的是,push操作为在队头加入元素;

而pop操作是从队列尾部弹出一个元素。


栈和队列的基本操作都是时间复杂度为o(1)的操作;

栈可以看做一条射线,底部是固定的;队列是一条直线,操作在两端进行,一端用来进入队列,一端用来出队列;


特殊结构:双端队列结构为首尾都可以压入和弹出元素!!!

优先级队列为根据元素的优先级值,决定元素的弹出顺序;优先级队列的结构堆结构,并不是线性结构。


树的遍历方式有先序、中序、后序以及按层遍历!

图的遍历方式:深度优先遍历(DFS)和宽度优先遍历(BFS)!


深度优先遍历可以用栈实现:元素入栈的顺序就是深度优先遍历的顺序也就是访问的顺序;

宽度优先遍历用队列实现:元素进队列的顺序就是宽度优先遍历的顺序,也就是访问的顺序。


平时使用的递归函数实际上用到了提供的函数系统栈,递归的过程可以看做递归函数依次进入函数站的处理过程!

所有用递归函数可以做的过程一定更可以用非递归的方式实现;


案例一:

实现一个特殊的栈,在实现栈的基本功能的 基础上,再实现返回栈中最小元素的操作getmin。

要求:

1. pop push getMin操作的时间复杂度丢失o(1)/

2. 设计的栈类型可以使用现成的栈结构。

一共有两种方式,两种方式都是利用两个栈,其中一个栈用来保存正常的数据元素,记为stackdata,另一个栈用来保存每一步的最小值,记为stackmin.。

第一种方式中,只有当前数小于等于stackmin的栈顶时,该数放入stackmin。在弹出的时候,只有stackdata当前值等于stackmin栈顶的时候才会弹出。

由压入规则可知,stackmin始终记录的都是压入的stackdata当前的最小值。再弹出的时候,stackmin在stackdata弹出的时候,依旧维护着stackdata中的最小值。

另一种方式中,当前数和stackmin栈顶元素比较,较小的压入stackmin。如果当前数不比stackmin中的数小,那么就依旧向stackmin中压入上一次压入的值。

stackmin中始终记录着stackdata中每一步的最小值,不管是压入还是弹出。并且stackdata和stackmin是同步弹出的,不需要比较。

由上面的两种方式可以知道。它们都是利用第二个栈stackmin来保存stackdata每一步的最小值。

并且这两种方式的时间复杂度都是o(1)额外空间复杂度都是o(n),

区别在于方案一的stackmin在压入时稍微节省空间,弹出时稍微浪费时间,方案二stackmin在压入时稍微浪费空间,弹出时节省时间,因为弹出的时候不需要比较,同步弹出就可以。

class Solution {
public:
    stack datastack;
    stack minstack;
    void push(int value) {
        datastack.push(value);
        if(!minstack.empty()&&value<=minstack.top())
            minstack.push(value);
        else if(minstack.empty())
            minstack.push(value);
    }
    void pop() {
        if(!datastack.empty()&&(datastack.top()==minstack.top())){
            minstack.pop();
            datastack.pop();
        }else if(!datastack.empty()&&(datastack.top()!=minstack.top())){
            datastack.pop();
        }
        
    }
    int top() {
       return datastack.top();
    }
    int min() {
       
           return minstack.top();
    }
};

上面的代码是方案一的实现!!


案例二:编写一个类,只能用两个栈结构实现队列,支持队列的基本操作(add,poll,peek)。对应c++队列中的(push pop front empty)

需要注意的是栈是先进后出,但是队列是先进先出,用两个栈正好可以实现一个队列。

实现方式为:

一个栈为压入栈,stackpush,压入数据时只向这个栈压入数据。

另一个栈是弹出栈,stackpop,在弹出数据时只从这个栈弹出数据。

因此将stackpush中的数据倒入stackpop栈中数据的顺序就倒过来了。

再从stackpop中弹出数据时,顺序就和队列一样了!!!

有两个需要注意的点:

1. 如果stackpush要往stackpop中倒入数据,那么必须要把stackpush中的所有数据一次性到完。

2. 如果stackpop中有数据,则不能发生倒数据的行为。

不能违反上面两点规则,否则会发生错误。

class TwoStack {
public:
    vector twoStack(vector ope, int n) {
        // write code here
        vector result;
        int temp;
        for(int i=0;i             if(ope[i]!=0){
                if(push_stack.empty()&&!pop_stack.empty()){
                    while(!pop_stack.empty()){
                        push_stack.push(pop_stack.top());
                        pop_stack.pop();
                }
                }
            push_stack.push(ope[i]);
            }else{
                if(pop_stack.empty()){
                while(!push_stack.empty()){
                   temp= push_stack.top();
                   push_stack.pop();
                   pop_stack.push(temp);       
            }
            }
                result.push_back(pop_stack.top());
                pop_stack.pop();
            }
    }
        return result;
        
    }
private:
    stack push_stack;
    stack pop_stack;
    
};

具体实现如上面的程序所示,但是需要时刻注意的是那两个不能违反的规则,否则会发生错误!!

从stackpush倒向stackpop这个行为可以选择在很多方法中(add poll peek)包含该逻辑,但要保证倒入数据的两个注意点不被违反。


案例三:实现一个栈的逆序,但是只能用递归函数和这个栈本身的操作来实现,而不能自己申请另外的数据结构。

首先介绍第一个函数:这个函数的功能是移除栈底元素并返回。利用了递归。

public int get(Stack stack){

int result=stack.top();

stack.pop();

if(stack.empty()){

return result;

}else{
int last=get(Stack);

stack.push(result);

return last;

}
}

对于下面的例子来说,栈中原来的样子为:

1

2

3

队列和栈的基本性质和应用_第1张图片

上面的程序执行完毕之后,1和2又被重新压入栈中,3被返回。

上面的方法被记为get方法。


下面介绍一个函数:把栈中的元素逆序的主方法。也是利用了递归。

public void reverse(Stackstack){

if(stack.isEmpty()){

reurn;

}

int i=get(stack);

reverse(stack);

stack.push();

}

这个函数利用了上面的方法,也就是删除栈底元素并返回。

也是利用了递归。

队列和栈的基本性质和应用_第2张图片

上面的例子中,栈中原来的样子为:

1

2

3

需要注意的是vector也可以当做stack来用,这样的话vector中的push_back相当于stack中的push,然后vector中的back相当于stack中的top,vector中的pop_back相当于stack中的pop。需要注意的是下面的程序中是将vector数组中的最后一个元素作为栈顶元素。之所以会这样是因为push_back是在数组的最后添加一个数据;pop_back是去掉数组的最后一个数据;back是得到数组的最后一个单元的引用;而front是得到数组头的引用。因此这里使用的是back而不是front。

class StackReverse {
public:
    vector reverseStack(vector A, int n) {
        // write code here
        //首先需要实现的删除栈底元素,并且返回
        reverse(A,n);
        return A;
       
    }
       //首先需要实现的删除栈底元素,并且返回,这个函数也是利用递归函数实现,
    int getStack(vector& A,int n){
        int temp=A.back();
        A.pop_back();
        if(A.empty()){
          return temp;
        }
        else{
             int last=getStack(A,n);
             A.push_back(temp);
            return last;//需要注意的是这里的last是非常重要的!!!
    }       
    }
   void reverse(vector& B,int n ){
        int temp;
        if(B.empty()){
        return;//注意在递归函数中,如果中间要返回那么如果这个递归函数的返回值不是空,那么中间的返回也要有返回值。
        }else{
        temp=getStack(B,n);
        reverse(B,n);
        B.push_back(temp);
    }
}
};

具体的实现如上所示,上面的程序中将vector当做了stack来用!!

案例四:一个栈中元素类型为整形,现在想将该栈从顶到底按照从大到小的顺序排序,只允许申请一个栈,除此之外,可以申请新的变量,但不能申请额外的数据结构,如何完成排序?

方法为将想要排序的栈记为stack,申请的辅助栈记为help,在stack上执行pop操作,弹出的元素记为current,这个current要和help中的栈顶元素进行比较,如果current小于或者等于help栈顶元素的话,就直接push进入help栈;如果current大于help的栈顶元素,那么将help中的元素逐渐弹出并且压回到stack中,直到current小于等于help中的栈顶元素,然后将current压入栈。重复执行上面的操作,直到stack中没有元素,然后将help中的元素弹出压入到stack中。

class TwoStacks {
public:
    vector twoStacksSort(vector numbers) {
        // write code here
        //这里规定第一个元素为栈顶也就是说,对于vector来说从左到右应该是从大到小排序的
        //但是我们使用pop_back以及push_back 以及front函数的时候是将vector数组的最后的元素作为栈顶的。
        //因此,这里我们将vector从后向前从小到大排序
          stack help;
          int current;
          while(!numbers.empty()){
              if(help.empty()){
                  help.push(numbers.back());
                  numbers.pop_back();
          }else{
                if(numbers.back()>=help.top()){
                    help.push(numbers.back());
                    numbers.pop_back();
                    continue;
                }else{
                    current=numbers.back();
                    numbers.pop_back();
                    while(!help.empty()){
                        if(current                             numbers.push_back(help.top());
                            help.pop();
                        }else{
                            help.push(current);
                            break;
                        }
                    }
                    if(help.empty())
                        help.push(current);
          }
              
    }
    }//最后help从栈顶到栈底是从大到小排列的
         numbers.clear();
        while(!help.empty()){
         numbers.push_back(help.top());
            help.pop();
    }
        return numbers;
    }
        
};

需要时刻注意什么时候pop和什么时刻push!!!


你可能感兴趣的:(队列和栈的基本性质和应用)