《栈》

来源:互联网 发布:linux运维培训 编辑:程序博客网 时间:2024/06/06 09:54

==============================================================================

==============================================================================

含min函数的栈

struct MinStackElement

{

 int data;

 int min;

};

 

struct MinStack

{

 MinStackElement* data;

 int size;

 int top;

};

 

MinStack MinStackInit(int maxSize)

{

 MinStack stack;

 stack.size = maxSize;

 stack.data = (MinStackElement*)intmalloc(sizeof(MinStackElement) * maxSize);

 stack.top = 0;

 return stack;

}

 

void MinStackFree(MinStack stack)

{

 free(stack.data);

}

 

void MinStackPush(MinStack stack, int d)

{

 if(stack.top == stack.size)

 error("Out of stack space!");

 MinStackElement* p =stack.data[stack.top];

 p->data = d;

 p->min = (stack.top == 0 ?d:stack.data[top-1]);

 if(p->min > d )

  p->min = d;

 top++;

}

 

int MinStackPop(MinStack stack)

{

 if(stack.top == 0)

 error("Stack is empty");

 return stack.data[--stack.top].data;

}

 

int MinStackMin(MinStack stack)

{

 if(stack.top == 0)

 error("Stack is empty");

 return stack.data[stack.top - 1].data;

}

 

==============================================================================

==============================================================================

两个栈实现一个队列

 

 

==============================================================================

==============================================================================

堆和栈解析

堆与栈解析 C++

C++中堆和栈的完全解析

内存分配方面:

堆: 操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样代码 中的delete语句才能正确的释放本内存空间。我们常说的内存泄露,最常见的就是堆泄露(还有资源泄露),它是指程序在运行中出现泄露,如果程序被关闭掉的话,操作系统会帮助释放泄露的内存。

栈:在函数调用时第一个进栈的主函数中的下一条指令(函数调用语句的下一条可执行语句)的地址然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈,然后是函数中的局部变量。

一、预备知识—程序的内存分配

一个由c/C++编译的程序占用的内存分为以下几个部分

1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。

3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放

4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放

5、程序代码区—存放函数体的二进制代码。

 

有些说法,把3,4合在一起,也有的把3分成自由存储区(malloc/free)和全局/静态存储区。

这与编译器和操作系统有关。

 

——————————————————————————————————

在进行C/C++编程时需要程序员对内存的了解比较精确,经常使用到的内存有以下几种:

      :由编译器自动分配和释放,存放函数的参数值、局部变量的值,操作方式类似于数据结构中的栈

      :一般由程序员分配和释放,与数据结构中的堆是两码事,操作方式类似于链表

      全局区(静态区):全局变量和静态变量的存储时放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和静态变量在一块区域,程序结束后由系统释放

      文字常量区:常量字符串

      程序代码区:程序的二进制代码

      用代码解析:

[cpp] view plaincopy

  1. <span style="font-size:24px;">int a=0;//全局初始化区 
  2. char *p1;//全局未初始化区 
  3. main() 
  4.     int b;// 
  5.     char s[]="aaa";// 
  6.     char *p2;// 
  7.     char *p3="bbb";//p3在栈,bbb在常量区 
  8.   
  9.     static int c=0;//全局初始化区 
  10.     p1=(char*)malloc(10);// 
  11.     strcpy(p1,"123");//123 在常量区 
  12. </span> 

      区别:

     1,由系统自动分配和释放,由程序员申请并释放

     2,只要栈的剩余空间大于所申请的空间,系统就分配,否则,报异常

对于堆,操作系统有一个记录空闲内存地址的链表,当系统收到申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆节点,然后将该节点从空闲区链表中删除,将该节点的空间分配给程序,若是找到的节点地址空间大于申请的大小,系统会把剩余的节点空间重新添加到内存空闲区链表中

     3,对于栈,在window下,栈是向低地址扩展的数据结构,是一块连续的区域,栈的大小是2MB,如果申请的空间超过栈的剩余空间,将提示栈溢出

对与堆,是向高地址扩展的数据结构,且不连续,堆的大小受限于计算机系统的虚拟内存,堆获得的空间比较大,也比较灵活

     4,申请效率,栈由系统分配,速度快,程序员无法控制。堆由程序员分配,速度慢,容易产生碎片,用起来方便

     5,内容,栈在函数调用时,参数由右往左入栈,然后是局部变量,静态变量不入栈 出栈时,局部变量先出栈,然后是参数。

堆一般用堆的头部用一个字节存放堆的大小,便于delete或者free

     对于如下函数:

    

[cpp] view plaincopy

 

  1. void fun(int param1,int param2,int param3) 
  2.     int var1=param1; 
  3.     int var2=param2; 
  4.     int var3=param3; 
  5.     { 
  6.   
  7.         other code; 
  8.   
  9.     } 

 

     当调用函数时,栈是从高地址向低地址分布,EBP是栈底指针,ESP是栈顶指针,参数从右往左入栈,先压param3,然后是param2,最后是param3,然后是函数的返回地址入栈,

进入函数后函数地址入栈,EBP入栈,然后把ESP值给EBP,再接着是局部变量入栈,即var1入栈,var2入栈,var3入栈,按照声明的顺序存放在EBP-4,EBP-8,EBP-12的位置

     



     函数调用调用一个函数时,先将堆栈原先的基址(EBP)入栈,以保存之前任务的信息。然后将栈顶指针的值赋给EBP,将之前的栈顶作为新的基址(栈底)

然后在这个基址上,开辟相应的空间用作被调用函数的堆栈。函数返回后,从EBP中可取出之前的ESP值,使栈顶恢复函数调用前的位置;再从恢复后的栈顶可弹出之前的EBP值(已入栈),因为这个值在函数调用前一步被压入堆栈。这样,EBP和ESP就都恢复了调用前的位置,堆栈恢复函数调用前的状态

 

 

 

==============================================================================

==============================================================================

32 - 两个队列实现一个栈

两个队列实现一个栈

 

题目:
说明如何用两个队列来实现一个栈,并分析有关栈操作的运行时间。

解法:
1.
有两个队列q1q2,先往q1内插入abc,这做的都是栈的push操作。
2.
现在要做pop操作,即要得到c,这时可以将q1中的a,b两个元素全部dequeue并存入q2中,这时q2中元素为ab,对q1再做一次dequeue操作即可得到c
3.
如果继续做push操作,比如插入df,则把df插入到q2中,
4.
此时若要做pop操作,则做步骤2
5.
以此类推,就实现了用两个队列来实现一个栈的目的。

注意在此过程中,新push进来的元素总是插入到非空队列中,空队列则用来保存pop操作之后的那些元素,那么此时空队列不为空了,原来的非空队列变为空了,总是这样循环。

对于pushpop操作,其时间为O(n).

 

#include<iostream>
#include <stack>
#include <assert.h>
using namespace std;

// 两个队列实现一个栈
template<typename T> class CStack
{
public:
CStack() {}
~CStack() {}

void mypush(constT& element);

void mypop();


private:
deque <T> m_queue1;
deque <T> m_queue2;
};

template<typenameT> void CStack<T>::mypop()
{
if (m_queue1.size() == 0)
{
while (m_queue2.size() > 1)
{
T& data = m_queue2.front();
m_queue2.pop_front();
m_queue1.push_back(data);
}
assert(m_queue2.size() == 1); //
确保队列2内有一个元素
T& result = m_queue2.front();
m_queue2.pop_front();
cout << result << endl;
}

else if(m_queue2.size() == 0)
{
while (m_queue1.size() > 1)
{
T& data = m_queue1.front();
m_queue1.pop_front();
m_queue2.push_back(data);
}
assert(m_queue1.size() == 1); //
确保队列1内有一个元素
T& result = m_queue1.front();
m_queue1.pop_front();
cout << result << endl;
}
}


template<typename T> void CStack<T>::mypush(const T& element)
{
if (m_queue1.size() > 0)
{
m_queue1.push_back(element);
}
else if (m_queue2.size() > 0)
{
m_queue2.push_back(element);
}
else
{
m_queue1.push_back(element);
}
}
int main()
{
CStack<int> myStack;
myStack.mypush(1);
myStack.mypush(2);
myStack.mypush(3);
myStack.mypop();
myStack.mypush(4);
myStack.mypop();

cout <<"Hello world!" << endl;
return 0;
}

 

==============================================================================

==============================================================================

32 - 两个栈实现队列

 

两个栈实现队列

———————————————————————————————————————

 

出这道题,主要考察3点:

1.       在短时间内,能不能找到解决这道题的足够清晰的思路(思维是否敏捷、清晰)。

2.       能不能在单向表述中,清楚地描述自己的思路和想法(表述能力是否达到要求)。

3.       对于某些具体细节,能不能考虑到(是否足够细致)。

  • 有一个细节是可以优化一下的。即:在出队时,将s1的元素逐个倒入s2时,原在s1栈底的元素,不用倒入s2(即只s1.Count()-1个),可直接弹出作为出队元素返回。这样可以减少一次压栈的操作。
  • 真正性能较高的,其实是另一个变种。即:

入队时,将元素压入s1

出队时,判断s2是否为空,如不为空,则直接弹出顶元素;如为空,则将s1的元素逐个倒入s2,把最后一个元素弹出并出队。

这个思路,避免了反复栈,仅在需要时才一次。但在实际面试中很少有人说出,可能是时间较少的缘故吧。

 

以上几个思路乍看没什么问题了,但其实还是有个细节要考虑的。其实无论什么方法和情况,都要考虑没有元素可供出队时的处理2个栈都为空的时候,出队操作一定会引起异常)。在实际写代码时,忽略这些判断或异常处理,程序会出现问题。所以,能不能考虑到这些细节,也体现了个人的素养。

 

个人感觉,这道题确实有助于我鉴别应聘的人。但对于面试,毕竟还是要看面试者的综合素质,一道(或几道)题定生死不可取。

 

———————————————————————————————————————

 

解法

准备两个栈。一个是input, 一个是output

1、入栈:直接用input.push()

2、出栈:如果output非空。则利用output出栈。

                 如果output为空。则把input中的元素全部popoutput栈中。再利用output出栈。

 

说得简单一点就是从input栈进去,从ouput栈出去。明白为什么叫inputoutput没。

实际上就是利用output栈把出去的顺序颠倒一下。

略微可以优化的,就是在pop的时候,如果要把input全部倒到output中,可以把最后一个元素,留在input中。最后从input那里pop

 

代码 stas.h

[cpp]view plaincopyprint?

  1. #ifndef _STACK_AS_QUEUE_H  
  2. #define _STACK_AS_QUEUE_H  
  3.   
  4. #ifndef _NAME_SPACE_BEGIN_  
  5. #define _NAME_SPACE_BEGIN_ namespace stas {  
  6. #define _NAME_SPACE_END_ }  
  7. #endif  
  8. #include <iostream>  
  9. #include <stack>  
  10.   
  11. _NAME_SPACE_BEGIN_ 
  12.   
  13.   
  14. template<class T> 
  15. class queue { 
  16. private
  17.     std::stack<T> input; 
  18.     std::stack<T> output; 
  19. private
  20.     void _pop_input_into_output(const int leave = 0); 
  21. public
  22.     queue() {} 
  23.     ~queue(){} 
  24.     const bool empty() const
  25.     void push(const T &v); 
  26.     void pop(); 
  27.     T &top(); 
  28. }; 
  29.   
  30. template<class T> 
  31. void queue<T>::_pop_input_into_output(const int leave) { 
  32.     while (input.size() > leave) { 
  33.         output.push(input.top()); 
  34.         input.pop(); 
  35.     } 
  36.   
  37. template<class T> 
  38. const bool queue<T>::empty() const { 
  39.     return input.empty() && output.empty(); 
  40.   
  41. template<class T> 
  42. void queue<T>::push(const T &v) { 
  43.     input.push(v); 
  44.   
  45. template<class T> 
  46. void queue<T>::pop() { 
  47.     if (!output.empty()) output.pop(); 
  48.     else { 
  49.         this->_pop_input_into_output(1); 
  50.         output.pop(); 
  51.     } 
  52.   
  53. template<class T> 
  54. T &queue<T>::top() { 
  55.     if (!output.empty()) return output.top(); 
  56.     else { 
  57.         this->_pop_input_into_output(); 
  58.         return output.top(); 
  59.     } 
  60.   
  61.   
  62. _NAME_SPACE_END_ 
  63. #endif  /*end of _STACK_AS_QUEUE_H */ 



再写个测试的主程序 main.cpp

输入1 x表示把x入队列

输出0表示把队首元素出队。

[cpp]view plaincopyprint?

  1. #include <iostream>  
  2. #include <string>  
  3. #include <fstream>  
  4. #include "stas.h"  
  5. using namespace std; 
  6. using namespace stas; 
  7.   
  8. int main(int argc, char *argv[]) { 
  9.     stas::queue<int> q; 
  10.     while (1) { 
  11.         int a, b; 
  12.         cin >> a; 
  13.   
  14.         if (a%2) {  
  15.             cin >> b; 
  16.             q.push(b); 
  17.         } else {  
  18.             cout << q.top() << endl;  
  19.             q.pop();  
  20.         } 
  21.     } 
  22.     return 0; 

 

 

==============================================================================

==============================================================================

 

 

==============================================================================

==============================================================================

 

 

==============================================================================

==============================================================================

 

 

==============================================================================

==============================================================================

 

0 0
原创粉丝点击