数据结构学习篇之栈_出栈

一.基本介绍

(1)定义:栈是限定仅在表头进行插入和删除操作的线性

(2)特点:LIFO(Last In First Out->后进先出


二.分类

(一)顺序栈

I.基本介绍

数据结构学习篇之栈_栈的基本操作_02

说明:top和base在初始时均指向栈底,base指针一直指向栈底,如果有元素进栈,top就向上移动,如果有元素出栈,top指针下移。

优点:查询速度快

缺点:存储空间需要事先分配,可能会出现溢出问题


存储结构

#define MAXSIZE 100

typedef struct{

SElemType *base;       //栈底指针

SElemType *top;        //栈顶指针

int stacksize;             //栈可用的最大容量

}SqStack;

II.基本操作

初始化

数据结构学习篇之栈_出栈_03

具体代码:

Status InitStack(SqStack &S){

S.base =new SElemType[MAXSIZE];

//为顺序栈动态分配一个容量为MAXSIZE的数组空间

if(!S.base)exit(OVERFLOW);     //存储分配失败

S.top=S.base;             //top初始化为base(空栈)

S.stacksize=MAXSIZE; //stacksize置为栈的最大容量MAXSIZE

return OK;

}

入栈

数据结构学习篇之栈_入栈_04

具体代码:

Status Push(SqStack &S,SElemType e){

//插入元素e为新的元素

if(S.top-S.base=S.stacksize)  return ERROR;//栈满

*S.top++=e;              //元素e压入栈顶,栈顶指针加1

return OK;

}

出栈

数据结构学习篇之栈_栈的基本操作_05

具体代码

Status Pop(SqStack &S,SElemType &e){

if(S.top==S.base)return ERROR;//栈空

e=*--S.top;                //栈指针减1,将栈顶指针赋值给e

return OK;        

}

取栈顶

SElemType GetTop(SqStack &S){

//返回S的栈顶元素,不修改栈顶指针

if(S.top!=S.base)    //栈非空

return *(S.top-1);//返回栈顶元素的值,栈顶指针不变

}

(二) . 链栈

I.基本介绍

数据结构学习篇之栈_栈的基本操作_06

说明:结点结构和线性表的单链表的结点结构是一样的,由于栈是一种后进先出的结构,所以在存储时,新的结点的指针域指向的是上一个结点。

存储结构:

typedef struct StackNode{

ElemType data;   //数据域

struct StackNode *next;  //指针域

}StackNode,*Linktack;

II.基本操作

初始化

数据结构学习篇之栈_入栈_07

Status InitStack(){

//构造一个空栈

S=NULL;

return OK;

}

入栈

数据结构学习篇之栈_栈的基本操作_08

具体代码:

Status Push(LinkStack &S,SelemType){

//在栈顶插入元素e

p=new StackNode;    //生成新结点

p->data=e;         //初始化数据域

p->next =S;   //将新结点插入栈顶

S=p;       //修改栈顶指针为p

}

出栈

Status Pop(LinkStack &S ,SElemType &e){

//删除S的栈顶元素,用e返回其值

if(S==NULL)return ERROR;

e=S->data;

p=S;//用p临时保存栈顶元素空间,已备释放

S=S->next;//修改栈顶指针

delete p;//释放原栈顶的元素

return OK;

}

取栈顶

SElemType GetTop(LinkStack S){

if(S!=NULL)                   //栈非空

return S->data;      //返回栈顶元素的值,栈顶指针不变

}

总结:

      栈的引入简化了程序设计的问题,划分了不同关注层次,式的思考范围缩小,更加聚焦于要解决问题的核心。反之,像数组等,因为要分散精力去考虑数组的下标增减等细节问题,反而掩盖了问题的本质。

三.栈的应用

(一)递归

把一个直接调用自己或通过一系列的调用语句间接调用自己的函数,称为递归函数。

经典实例:(1)斐波那契数列

  (2)汉诺塔问题

      (3)N皇后问题

           (4)全排列

详细内容参考:https://blog.51cto.com/u_15314328/4928741


顺序栈实现代码如下:


package stack;

import java.util.Scanner;

/**
* @author Jin
* @ Date 2022年06月2022/6/30日19:26
*/
public class ArrayStack {
public static void main(String[] args) {
Stack stack=new Stack(4);
String key="";
boolean loop=true;
//控制是否退出菜单
Scanner scanner=new Scanner(System.in);
while(loop){
System.out.println("show:表示显示栈");
System.out.println("exit:退出程序");
System.out.println("push:表示添加数据到栈(入栈)");
System.out.println("pop:表示从栈中取出数据(出栈)");
System.out.println("请输入你的选择:");
key=scanner.next();
switch(key){
case"show":
stack.showStack();
break;
case"push":
System.out.println("请输入一个数:");
int value =scanner.nextInt();
stack.push(value);
break;
case"pop":
try{
int res =stack.pop();
System.out.printf("出栈的数据是%d\n",res);
} catch (Exception e) {
System.out.println(e.getMessage());
//打印出异常信息
}
break;
case"exit":
scanner.close();
loop=false;
//退出循环
break;
default:break;
}
}
}
}
class Stack{
private int maxSize;
private int[]stack;
private int top=-1;
/**构造器*/
public Stack(int maxSize){
this.maxSize=maxSize;
stack=new int[maxSize];
}
/**(1)判断栈是否满*/
public boolean isFull(){
return top==maxSize-1;
}
/**(2)判断栈空*/
public boolean isEmpty(){
return top==-1;
}
/**(3)入栈(将数据压入栈顶)*/
public void push(int value){
if(isFull()){
System.out.println("栈已经满,无法进行添加新元素!!");
}else{
top++;
stack[top]=value;
}
}
/**(4)出栈(将栈顶的元素取出)*/
public int pop(){
if(isEmpty()){
throw new RuntimeException("栈空,没有数据!");
}else{
int middle =stack[top];
top--;
return middle;
}
}
/**(5)遍历栈中元素(从栈顶到栈底开始遍历)*/
public void showStack(){
if(isEmpty()){
System.out.println("栈空,没有数据~~");
return;
}
for (int i = top; i >=0 ; i--) {
System.out.printf("stack[%d]=%d\n",i,stack[i]);
}
}
}

链栈的代码实现如下:

package stack;

import java.sql.SQLOutput;
import java.util.Scanner;

/**
* @author Jin
* @ Date 2022年06月2022/6/30日20:13
* 目标:完成链式存储结构的添加、删除、取栈顶、遍历、退出程序等基本功能
*/
public class LinkedStack {
public static void main(String[] args) {
LinkStack stack1=new LinkStack();
String key="";
boolean loop=true;
//控制是否退出菜单
Scanner scanner=new Scanner(System.in);
while(loop){
System.out.println("show:表示显示栈");
System.out.println("exit:退出程序");
System.out.println("push:表示添加数据到栈(入栈)");
System.out.println("pop:表示从栈中取出数据(出栈)");
System.out.println("head:表示获取栈顶元素的值");
System.out.println("请输入你的选择:");
key=scanner.next();
switch(key){
case"show":
stack1.showStack();
break;
case"push":
System.out.println("请输入一个数:");
int value =scanner.nextInt();
stack1.addNode(value);
break;
case"pop":
try{
int res =stack1.popNode();
System.out.printf("出栈的数据是%d\n",res);
} catch (Exception e) {
System.out.println(e.getMessage());
//打印出异常信息
}
break;
case"exit":
scanner.close();
loop=false;
//退出循环
break;
case"head":
int t=stack1.getHead();
System.out.printf("栈顶元素的值为%d\n",t);
break;
default:break;
}
}
}
}
class LinkStack{
private Stack1 head=new Stack1(0);
public Stack1 S=head;
public LinkStack(){
}
/**(1)入栈*/
public void addNode(int key){
Stack1 t1=new Stack1(key);
t1.data=key;
t1.next=S;
S=t1;
}
/**(2)出栈
* @return*/
public int popNode(){
if(S.next==null){
throw new RuntimeException("栈已空,无法删除");
}else{
int key=S.data;
S=S.next;
//栈顶指针下移
return key;
}

}

/**(3)遍历栈*/
public void showStack(){
Stack1 cur=S;
if(cur.next==null){
System.out.println("栈已经空,无法取出元素");
}
while(cur.next!=null){
System.out.printf("栈中的元素为:%d\n",cur.data);
cur=cur.next;
}
}
/**(4)获取栈顶元素*/
public int getHead(){
if(S.next==null){
System.out.println("栈已空,不存在栈顶元素!!");
}else{
return S.data;
}
return 0;
}
}
class Stack1{
public int data;
/**存储数据*/
public Stack1 next;
/**存储下一个节点的地址*/
/**构造器*/
public Stack1(int data){
this.data=data;
this.next=null;
}
@Override
/**重写toString()方法*/
public String toString() {
return "Stack1{" +
"data=" + data +
", next=" + next +
'}';
}
}