数据结构面试之三——栈的常见操作

简介: 1.基于数组的栈的三要素:1)栈的最大容量maxSize; 2)栈的当前容量=当前栈中元素的个数=栈顶top-1;3)动态数组存储栈的元素 Type* list; 2.对于出栈、入栈的操作要对应先判断栈空、栈满;如果空、满要有异常处理或错误提示。

数据结构面试之三——栈的常见操作

题注:《面试宝典》有相关习题,但思路相对不清晰,排版有错误,作者对此参考相关书籍和自己观点进行了重写,供大家参考。


三、栈的基本操作


3.1用数组构造栈


【注意以下几点】:


1.基于数组的栈的三要素:1)栈的最大容量maxSize; 2)栈的当前容量=当前栈中元素的个数=栈顶top-1;3)动态数组存储栈的元素 Type* list;


      2.对于出栈、入栈的操作要对应先判断栈空、栈满;如果空、满要有异常处理或错误提示。


      3.注意入栈push操作,先入栈后top+1;出栈pop则相反,先top-1,再出栈。【注意因为,top指向数组中最后一个元素的下一个位置】。


      4.拷贝构造函数和赋值函数要注意,尤其赋值的时候,避免自身赋值、同时要注意大小不一致的不能完成赋值操作,要有相关提示。


   


template<typenameType>

class arrStack

{

public:

      arrStack(intnSize=100);

      ~arrStack();

      arrStack(constarrStack& copyStack);

      arrStack&operator=(const arrStack& otherStack);

      voidinitializeStack();

      void destroyStack();

      bool isStackEmpty();

      bool isStackFull();

      void push(constType& item);

      void pop(Type&poppedItem);

private:

      int maxSize;

      int top;

      Type* list;

};

template<typename Type>

arrStack<Type>::arrStack(int nSize=100)

{

      if(nSize < 0)

      {

             nSize = 100;

             list = newType[nSize];

             top = 0;

             maxSize = 100;

      }

      else

      {

             list = newType[nSize];

             top = 0;

             maxSize =nSize;

      }

}

template<typename Type>

arrStack<Type>::~arrStack()

{

      if(!list)

      {

             delete[]list;                //注意数组的删除,为delete []list;

             list = NULL;

      }

}

template<typename Type>

arrStack<Type>::arrStack(const arrStack& copyStack)

{

      maxSize =copyStack.maxSize;

      top = copyStack.top;

      list = newType[maxSize];           //注意需要自定义大小,容易出错.

      for( int i = 0; i <top; i++)

      {

             list[i] =copyStack.list[i];

      }

}

template<typename Type>

arrStack<Type>& arrStack<Type>::operator=(constarrStack& otherStack)

{

      if(this ==&otherStack)

      {

             cout <<"can't copy oneSelf!" << endl;

             return *this;

      }

      else

      {

             if(maxSize !=otherStack.maxSize)

             {

                    cout<< "The Size of two stack are not equal!" << endl;

                    return*this;

             }

             else

             {

                    maxSize= otherStack.maxSize;

                    top =otherStack.top;

                    for( inti = 0; i < top; i++)

                    {

                           list[i]= otherStack.list[i];

                    }//endfor

                    return*this;

             }

      }//end else

}

template<typename Type>

void arrStack<Type>::initializeStack()

{

      destroyStack();

}

template<typename Type>

void arrStack<Type>::destroyStack()

{

      top = 0;

}

template<typename Type>

bool arrStack<Type>::isStackEmpty()

{

      return (top == 0);

}

template<typename Type>

bool arrStack<Type>::isStackFull()

{

      return (top ==maxSize);

}

template<typename Type>

void arrStack<Type>::push(const Type& item)

{

      if(!isStackFull())

      {

             list[top] =item;

             top++;

      }

      else

      {

             cout <<"The Stack was already Full!" << endl;

      }

}

template<typename Type>

void arrStack<Type>::pop(Type& poppedItem)

{

      if(!isStackEmpty())

      {

             top--;

             poppedItem =list[top];

      }

      else

      {

             cout <<"The Stack was already Empty!" << endl;

      }

}

3.2用链表构造栈


链表构造栈注意:


1) 判断栈空的标记是top==NULL;由于栈的空间可以动态分配,因此可以认为链栈不会满。


2) 栈的入栈Push操作要先判定栈是否已经满,非满才可以入栈。先入栈,再调整top指针;


3) 栈的出栈Pop操作要先判定栈是否已空,非空才可以出栈。先调整top指针,再出栈,并删除原结点。


4) 可以加count判定当前结点的个数。



template<typename Type>

struct nodeType

{

      Type info;

      nodeType* link;

};

template<typename Type>

class linkedStack

{

public:

      linkedStack();

      ~linkedStack();

      linkedStack(constlinkedStack<Type>&);

      linkedStack&operator=(const linkedStack<Type>&);

      voidinitializeStack();

      void destroyStack();

      bool isStackEmpty()const;

      bool isStackFull()const;

      void push(constType& item);

      void pop(Type&poppedItem);

      void nodeCount();

   

      voidreversePrint();          //逆向打印的非递归实现...

private:

      nodeType<Type>*top;

      int count;         //统计节点个数

};

template<typename Type>

linkedStack<Type>::linkedStack()

{

      count = 0;

      top = NULL;

}

template<typename Type>

linkedStack<Type>::~linkedStack()

{

      while( top != NULL )

      {

             nodeType<Type>*tempNode = new nodeType<Type>;

             tempNode = top;

             top =top->link;

             deletetempNode;

      }//end if

}

template<typename Type>

linkedStack<Type>::linkedStack(constlinkedStack<Type>& copyStack)

{

      if(copyStack.top !=NULL)

      {

             nodeType<Type>*current;

             nodeType<Type>*first;

             nodeType<Type>*newNode;

         

             top = newnodeType<Type>;

             top->info =copyStack.top->info;                //此处的top不能直接用,内存报错!

             top->link =copyStack.top->link;

             first =top;                        //first跟进当前链表...

             current =copyStack.top->link;      //current跟进copy链表...

             while( current!= NULL)

             {

                    newNode= new nodeType<Type>;

                    newNode->link= current->link;

                    newNode->info= current->info;

                    first->link= newNode;

                    first =newNode;

                    current= current->link;

             }//end while

             count =copyStack.count;

      }//end if

      else

      {

             top = NULL;

             count = 0;

      }

}

template<typename Type>

linkedStack<Type>&linkedStack<Type>::operator=(const linkedStack<Type>&otherStack)

{

      //1避免自身赋值

      if(this ==&otherStack)

      {

             cout <<"Can't copy oneself!" << endl;

             return *this;

      }

      //2其他

      else

      {

             if(top != NULL)

             {

                    destroyStack();

             }

             if(otherStack.top!= NULL)

             {

                    nodeType<Type>*current;

                    nodeType<Type>*first;

                    nodeType<Type>*newNode;

                    top =new nodeType<Type>;

                    top->info= otherStack.top->info;

                    top->link= otherStack.top->link;

                    first =top;                        //first跟进当前链表...

                    current= otherStack.top->link;      //current跟进copy链表...

                    while(current != NULL)

                    {

                           newNode= new nodeType<Type>;

                           newNode->link= current->link;

                           newNode->info= current->info;

                           first->link= newNode;

                           first= newNode;

                           current= current->link;

                    }//endwhile

                    count =otherStack.count;

             }//end if

             else

             {

                    top =NULL;

                    count =0;

             }

             return *this;

      }

}

template<typename Type>

void linkedStack<Type>::initializeStack()

{

      destroyStack();

}

template<typename Type>

void linkedStack<Type>::destroyStack()

{

      count = 0;

      top = NULL;

}

template<typename Type>

bool linkedStack<Type>::isStackEmpty() const

{

      return (count == 0);

}

template<typename Type>

bool linkedStack<Type>::isStackFull() const //空间非固定,动态申请!

{

      return false;

}

template<typename Type>

void linkedStack<Type>::push(const Type& item)

{

      if(!isStackFull())

      {

             nodeType<Type>*newNode = new nodeType<Type>;

             newNode->info= item;

             newNode->link= NULL;

             if(top == NULL)

             {

                    top = newNode;

             }

             else

             {

                    newNode->link= top;

                    top =newNode;

             }

             count++;

             cout <<item << " was pushed!" << endl;

      }

}

template<typename Type>

void linkedStack<Type>::pop(Type& poppedItem)

{

      if(!isStackEmpty())

      {

             nodeType<Type>*temp = new nodeType<Type>;

             temp = top;

             poppedItem =top->info;

             top =top->link;

         

             count--;

             cout <<poppedItem << " was popped!" << endl;

             delete temp;

      }

}

template<typename Type>

void linkedStack<Type>::nodeCount()

{

      cout <<"nodeCount = " << count << endl;

}

//上一节的递归实现逆向链表打印,这是非递归的实现。

template<typename Type>

void linkedStack<Type>::reversePrint()          //逆向打印的非递归实现...

{

//  注释部分可作为链表调用栈的非递归实现。

//    nodeType<Type>*current = new nodeType<Type>;

//    current = top;

//    while(current != NULL)

//    {

//           pop(current->info);

//           current =current->link;

//    }

      int poppedItem = 0;

      while(!isStackEmpty())

      {

             pop(poppedItem);

      }


相关文章
|
10月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
849 9
|
10月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
213 59
|
3月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
185 3
|
3月前
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
42 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
8月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
329 77
|
7月前
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
146 11
|
7月前
|
DataX
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
8月前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
236 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
8月前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
130 9

热门文章

最新文章