• 第四章 栈


    1 基本概念

    • 栈是一种操作受限的线性表,只允许从一端插入和删除数据。

    • 栈有两种存储方式,即线性存储(数组)和链式存储(链表)

    • 栈的一个最重要的特征就是栈的插入和删除只能在栈顶进行,所以每次删除的元素都是最后进栈的元素,故栈也被称为后进先出(LIFO)表

    • 每个栈都有一个栈顶指针,它初始值为-1,且总是指向最后一个入栈的元素。

    • 栈有两种处理方式,即进栈(push)和出栈(pop),因为在进栈只需要移动一个变量存储空间,所以它的时间复杂度为O(1),但是对于出栈分两种情况,栈未满时,时间复杂度也为O(1),但是当栈满时,需要重新分配内存,并移动栈内所有数据,所以此时的时间复杂度为O(n)。

      下图是进栈(push)和出栈(pop)的过程示意图:

      image-20200904134722629

    2 数组模拟栈

    数组模拟栈的基本示意图:

    image-20200904142535450

    数组模拟栈的思路分析:

    • 定义一个top来表示栈顶,初始化为-1;
    • 入栈的操作,当有数据加入到栈时,top++; stack[top] = data;
    • 出栈的操作,int value = stack[top]; top--;return value

    数组模拟栈的Java实现

    package com.victor.stack;
    import java.util.Scanner;
    
    
    public class ArrayStackDemo {
    
    	public static void main(String[] args) {
    		ArrayStack as = new ArrayStack(4);
    		String key = ""; //接收用户输入
    		Scanner scanner = new Scanner(System.in);
    		boolean loop = true;
    		//输出一个菜单栏
    		while(loop){
    			System.out.println("show: 打印栈");
    			System.out.println("push: 入栈");
    			System.out.println("pop: 出栈");
    			System.out.println("peek: 查看栈顶");
    			System.out.println("exit: 退出程序");
    			key = scanner.next();
    			switch (key) {
    			case "show":
    				try {
    					as.show(); 
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage());
    				}
    				break;
    			case "push": //入栈
    				try {
    					System.out.println("请输入一个整数");
    					int value = scanner.nextInt();
    					as.push(value);  //最好判断一下value是不是整数
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage());
    				}
    				break;
    			case "pop": //出栈
    				try {
    					int value = as.pop();
    					System.out.printf("出栈的整数为%d
    ", value);
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage()); //输出pop()方法中定义好的异常信息
    				}
    				break;
    			case "peek": //查看栈顶
    				try {
    					int value = as.peek();
    					System.out.printf("栈顶的整数为%d
    ", value);
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage());
    				}
    				break;
    			case "exit": //退出
    				scanner.close();
    				loop = false;
    				break;
    			default:
    				break;
    			}
    		}
    		System.out.println("程序退出");
    	}
    }
    
    
    //数组栈类
    class ArrayStack {
    	private int maxSize; //栈的最大容量
    	private int[] stack; //数组,用来模拟栈
    	private int top = -1; //栈顶指针
    	
    	//构造方法
    	public ArrayStack(int maxSize) {
    		this.maxSize = maxSize;
    		stack = new int[this.maxSize];
    	}
    	
    	//栈满
    	public boolean isFull() {
    		return top == this.maxSize - 1;
    	}
    	
    	//栈空
    	public boolean isEmpty() {
    		return top == -1;
    	}
    	
    	//入栈
    	public void push(int value) {
    		if (isFull()) {
    			throw new RuntimeException("栈满,不能再入栈了");
    		}
    		stack[++top] = value;
    	}
    	
    	//出栈
    	public int pop() {
    		if (isEmpty()) {
    			throw new RuntimeException("栈空,不能再出栈了");
    		}
    		int value = stack[top--];
    		return value;
    	}
    	
    	//查看栈顶元素
    	public int peek() {
    		if (isEmpty()) {
    			throw new RuntimeException("栈空,你懂的!");
    		}
    		return stack[top];
    	}
    	
    	//打印栈
    	public void show() {
    		if (isEmpty()) {
    			throw new RuntimeException("栈空,你懂的!");
    		}
    		for (int i = top; i >= 0; i--) {
    			System.out.printf("stack[%d]=%d
    ",i,stack[i]);
    		}
    	}
    }
    

    3 链表模拟栈

    • 链栈即用链表实现栈存储结构。

    • 链栈的实现思路同顺序栈类似,顺序栈是将数顺序表(数组)的一端作为栈底,另一端为栈顶。链栈也如此,通常我们将链表的头部作为栈顶,尾部作为栈底,如下图所示:

    image-20200905091924014

    • 将链表头部作为栈顶,可以避免在数据 "入栈" 和 "出栈" 操作时做大量遍历链表的耗时操作
    • 链表的头部作为栈顶,意味着:
      • 在实现数据"入栈"操作时,需要将数据从链表的头部插入;
      • 在实现数据"出栈"操作时,需要删除链表头部的首元节点;

    因此,链栈实际上就是一个只能采用头插法插入或删除数据的链表。

    链表模拟栈的java实现

    package com.victor.stack;
    
    import java.util.Scanner;
    
    public class LinkStackDemo {
    
    	public static void main(String[] args) {
    		LinkStack ls = new LinkStack();
    		String key = ""; //接收用户输入
    		Scanner scanner = new Scanner(System.in);
    		boolean loop = true;
    		//输出一个菜单栏
    		while(loop){
    			System.out.println("show: 打印栈");
    			System.out.println("push: 入栈");
    			System.out.println("pop: 出栈");
    			System.out.println("peek: 查看栈顶");
    			System.out.println("exit: 退出程序");
    			key = scanner.next();
    			switch (key) {
    			case "show": //打印栈
    				try {
    					ls.show(); 
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage());
    				}
    				break;
    			case "push": //入栈
    				try {
    					System.out.println("请输入一个整数");
    					int value = scanner.nextInt();
    					ls.push(value);  //最好判断一下value是不是整数
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage());
    				}
    				break;
    			case "pop": //出栈
    				try {
    					int value = ls.pop();
    					System.out.printf("出栈的整数为%d
    ", value);
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage()); //输出pop()方法中定义好的异常信息
    				}
    				break;
    			case "peek": //查看栈顶
    				try {
    					int value = ls.peek();
    					System.out.printf("栈顶的整数为%d
    ", value);
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage());
    				}
    				break;
    			case "length":
    				System.out.printf("栈长度为%d
    ", ls.getLength());
    				break;
    			case "exit": //退出
    				scanner.close();
    				loop = false;
    				break;
    			default:
    				break;
    			}
    		}
    		System.out.println("程序退出");
    	}
    }
    
    
    //定义结点类
    class ListNode{
    	private int data;
    	private ListNode next = null;
    	
    	//构造方法
    	public ListNode(int data) {
    		this.data = data;
    	}
    	
    	//返回data值
    	public int getData() {
    		return this.data;
    	}
    
    	//设置data值
    	public void setData(int data) {
    		this.data = data;
    	}
    
    	//返回下一个结点地址
    	public ListNode getNext() {
    		return this.next;
    	}
    
    	//设置下一个结点地址
    	public void setNext(ListNode next) {
    		this.next = next;
    	}
    
    	//重写toString方法
    	@Override
    	public String toString() {
    		return "ListNode [data=" + data + "]";
    	}
    }
    
    
    class LinkStack{
    	private ListNode stack; //链表头结点,也是栈顶指针
    
    	//构造方法
    	public LinkStack() {
    		stack = new ListNode(-1);
    	}
    	
    	//栈空
    	public boolean isEmpty() {
    		return stack.getNext() == null;
    	}
    	
    	//入栈,使用头插法插入结点
    	public void push(int value) {
    		ListNode node = new ListNode(value);  //新建结点
    		ListNode curr = stack.getNext();
    		stack.setNext(node);
    		node.setNext(curr);
    	}
    	
    	//出栈,从链表头部删除结点
    	public int pop() {
    		if (isEmpty()) {
    			throw new RuntimeException("栈空,不能再出栈了");
    		}
    		ListNode curr = stack.getNext();
    		int value = curr.getData();
    		stack.setNext(curr.getNext());
    		return value;
    	}
    	
    	//查看栈顶元素
    	public int peek() {
    		if (isEmpty()) {
    			throw new RuntimeException("栈空,你懂的!");
    		}
    		return stack.getNext().getData();
    	}
    	
    	//打印栈
    	public void show() {
    		if (isEmpty()) {
    			throw new RuntimeException("栈空,你懂的!");
    		}
    		ListNode curr = stack.getNext();
    		while(curr != null) {
    			System.out.println(curr.getData());
    			curr = curr.getNext();
    		}
    	}
    	
    	//求链栈长度
    	public int getLength() {
    		int length = 0;
    		ListNode curr = stack.getNext();
    		while(curr != null) {
    			length++;
    			curr = curr.getNext();
    		}
    		return length;
    	}
    }
    

    reference

    数据结构 之 栈【图文详解】https://blog.csdn.net/chiang2018/article/details/82731108

    链栈基本操作(入栈和出栈)C语言详解http://data.biancheng.net/view/171.html

    韩顺平数据结构

  • 相关阅读:
    js中通过Object.prototype.toString方法----精确判断对象的类型
    leecode---dfs,array,局部和全局---152. Maximum Product Subarray
    leecode96---dfs,dp,tree---96. Unique Binary Search Trees
    leecode115---dfs,string---Distinct Subsequences
    JVM:G1垃圾回收器
    java基础---JVM---CMS垃圾回收器
    java基础---JVM---调优,垃圾收集器,内存分配策略
    java基础---JVM---java内存区域与内存溢出问题
    java基础---一致性hash算法
    java基础---多态的实现原理
  • 原文地址:https://www.cnblogs.com/victorxiao/p/13617169.html
Copyright © 2020-2023  润新知