【数据结构与算法】之栈的基本介绍及其数组、链表实现---第四篇

博主秋招提前批已拿百度、字节跳动、拼多多、顺丰等公司的offer,可加微信:pcwl_Java 一起交流秋招面试经验,可获得博主的秋招简历和复习笔记。 

一、栈的基本介绍

1、栈的基本概念

栈是一种限制在一端进行插入和删除操作的线性表数据结构。栈中有两个比较重要的操作:push(压栈:将元素压入栈顶)和pop(弹栈:从栈顶弹出一个元素)。都满足先进后出、后进先出的特点!

从图中可以看出,我们常把栈的上面称为栈顶,栈顶的第一个元素被称为栈顶元素,相对地,把另外一段称为栈底。向一个栈中插入新元素又称为压栈或者进栈,它是把该元素放到栈顶位置上面,使之成为栈顶元素。那么从一个栈中删除一个元素则被称之为弹栈或者出栈,它会把栈顶元素删除,使其下面的相邻元素成为新的栈顶元素。

虽然栈是一种受限的数据结构,其操作特性均可以用数组和链表实现。但是任何数据结构都是对特定应用场景的抽象,数组和链表虽然灵活,但是却暴露了几乎所有的操作,很容易引发操作失误的风险。所以当某个数据集合只涉及到固定端的插入和删除操作,且满足先进后出,后进先出的特点,那么我们就应该优先考虑使用栈这种数据结构。

其实生活中有很多栈的例子,比如:叠放的盘子:我们要放盘子的时候,只能放在最上面。同时,需要拿盘子的时候,也只能拿最上面的。

2、栈的存储结构

栈是一种线性存储结构,所以线性表的顺序存储(数组)链式存储(链表)都可以实现栈。下面会具体用这两种方式对栈进行实现。

不管是顺序栈还是链式栈,压栈和弹栈都只涉及栈顶个别元素的操作,所以其时间复杂度均为O(1)。

3、栈的应用

栈在实际程序应用中有很多场景,下面列举几个比较常见的:

(1)栈在二进制表达式求值中的应用

在此类场景中,利用两个栈,一个栈用来保存操作数,另外一个用来保存运算符。我们从左到右遍历表达式,当遇到数字,我们就直接压入操作数栈;当遇到运算符,就与运算符栈的栈顶元素进行比较,若比运算符栈的栈顶元素优先级高,就将当前运算符压入运算符栈,若比运算符栈的栈顶元素优先级低或者相等,则从运算符栈取出栈顶运算符,从操作数栈中取出两个操作数,然后进行计算,把计算完的结果再压入操作数栈,然后依次进行比较。

图片来自极客时间的《数据结构与算法之美》专栏

(2)栈在符号号匹配中的应用

比如:括号匹配:{  [()]  })、单引号和双引号的匹配

拿括号匹配举例:从左到右依次扫描字符串,当扫描到左括号时,则将其压入栈中;当扫描到右括号时,则取出栈顶元素中的左括号,如果能够匹配上,则继续扫描;如果不匹配,则说明为非法格式。当所有的括号扫描完成之后,如果栈为空,则说明匹配成功,否则为非法格式。

(3)栈在浏览器中网页的前进后退中的应用

使用A和B两个栈,我们把首次浏览的页面依次压入栈A(比如:网页1-->网页2-->网页3),当点击后退按钮时,再依次从栈A中出栈,并按出栈的顺序将它们压入栈B。所有当我们点击前进按钮时,我们就可以依次从栈B中取出数据放入栈A中了。需要说明的是如果在前进后退期间,点开了新的页面,则栈B清空。当栈A中没有数据时,说明页面不可以后退浏览了,相反当栈B中没有元素的时候,页面不可以前进浏览了。


二、栈的实现(Java实现)

1、数组实现栈

public class ArrayStack {
	
	private Object[] array;     // 数组
	private int count;       // 栈中元素的个数
	private int n;           // 栈的大小
	
	// 初始化数组,申请一个大小为n的数组空间
	public ArrayStack(int n){
		this.array = new Object[n];
		this.count = count;
		this.n = n;
	}
	
	// 压栈
	public boolean push(Object value){
		// 如果数组的空间不够了,直接返回false,入栈失败
		if(count == n){
			return false;
		}
		// 将value压入栈顶,即下标为count的位置
		array[count] = value;
		count++;
		return true;
	}
	
	// 弹栈
	public Object pop(){
		// 栈为空,则直接返回-1
		if(count == 0){
			return null;   
		}
		// 返回栈顶的元素,即下标为count-1的数组元素
		Object obj = array[count - 1];
		count--;
		return obj;
	}
	
	// 返回弹中最近添加的元素,而不删除它
	public Object peek(){
		return array[n-1];
	}
	
	// 显示栈中的数据
	public void display(){
		for(int i = 0; i < count; i++){
			System.out.print(array[i] + ", ");
		}
	}
	
	// 栈中元素的个数
	public int size(){
		return count;
	}
	
	// 判断栈中元素是否为空
	public boolean isEmpty(){
		return count == 0;
	}
	
}

测试代码:

public class ArrayStackTest {

	public static void main(String[] args) {
		
		ArrayStack stack = new ArrayStack(5);
		
		boolean isEmpty = stack.isEmpty();
		System.out.println(isEmpty);
		
		// 压栈
		stack.push("a");
		stack.push("b");
		stack.push("c");
		stack.push("d");
		stack.push("e");
		
		// 显示
		stack.display();
		
		System.out.println();
		// 弹栈
		stack.pop();
		stack.pop();
		stack.pop();
		stack.display();
		
		int size = stack.size();
		System.out.println(size);
		
	}
	
}

2、链表实现栈

public class LinkStack {

	private class Node{
		Object data;
		Node next;
	}
	
	private Node first;   // 定义头结点  
	private int n = 0;        // 链表中元素的个数
	
	public LinkStack(){
		
	}
	
	// 压栈
	public void push(Object data){
		Node oldFirst = first;
		first = new Node();
		first.data = data; 
		first.next = oldFirst;  // 将新的头结点的后继指针指向老的头结点
		n++;
	}
	
	// 弹栈
	public Object pop(){
		Object data = first.data;
		first = first.next;    // 将头结点的后一个结点设为头结点
		n--;
		return data;
	}
	
	// 返回栈中最近添加的元素而不是删除它
	public Object peek(){
		return first.data;
	}

	// 显示栈中的元素
	public void display(){
		for(int i = 0; i < n; i++){
			Object obj = peek();
			System.out.print(obj + ", ");
		}
	}
	
	// 栈中元素的数量
	public int size(){
		return n;
	}
	
	// 判断栈是否为空
	public boolean isEmpty(){
		return n == 0;
	}
	
}

测试代码:

public class LinkStackTest {

	public static void main(String[] args) {

		LinkStack stack = new LinkStack();

		boolean isEmpty1 = stack.isEmpty();
		System.out.println(isEmpty1);
		
		// 压栈
		stack.push("A");
		stack.push("B");
		stack.push("C");
		stack.push("D");
		stack.push("E");

		// 显示栈中的元素
		stack.display();
		int size1 = stack.size();
		System.out.println(size1);

		// 弹栈
		stack.pop();
		stack.pop();
		stack.pop();

		// 显示栈中的元素
		stack.display();
		int size2 = stack.size();
		System.out.println(size2);
		
		boolean isEmpty2 = stack.isEmpty();
		System.out.println(isEmpty2);

	}

}

在极客时间的《数据结构与算法之美》专栏里有个问题,感觉值得mark:

问题:JVM内存管理中的栈(用来存储局部变量和方法调用的)和这里的栈相同吗?

参考:jvm中的堆栈与数据结构中的堆栈

参考及推荐

1、数据结构-栈和队列

2、栈和队列

学习不是单打独斗,如果你也是做Java开发,可以加我微信:pcwl_Java,一起分享经验学习!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值