线性结构-栈

简介: 线性结构-栈
栈是 Stack一个后进先出 Last In First Out,LIFO的线性表,他要求只在表尾对数据执行删除和插入等操作。
栈就是一个线性表, 可以是数组、也可以是链表。但它的操作有别于一般的线性表。栈的元素必须先进后出,也就是 先进入栈的元素必须后出栈。而不能像一般的链表或数组那样从任意位置读取元素。
栈的操作只能在线性表的表尾进行,这个标为被称为栈的栈顶 top,相应的表头被称为栈的栈底 bottom


栈的数据必须从栈顶进入,也必须从栈顶取出,先入栈的数据在后入栈的数据下面。
栈中不含有任何数据时的状态叫作空栈,此时栈顶top等于栈底bottom。

栈的定义

前面说过,作为一个线性结构,栈既可以用数组实现,也可以用链表实现。
在大多数情况下,我们用数组来实现栈。

public class MyStack {
    int[] stack; // 用数组实现一个栈
    int top; // 栈顶索引,实际上就是栈顶位置的数组下标
    int capacity; // 栈的容量
    public MyStack(int capacity) {
        stack = new int[capacity]; // 动态初始化栈,长度为capacity
        top = 0; // 栈顶索引为0,说明此时是空栈
        this.capacity = capacity; // 初始化栈的容量
    }
    //更多操作
}
MyStack类中并没有定义栈底位置bottom的值。这是因为我们是使用数组来实现栈的,所以bottom的值恒为0。
如果采用链表的形式实现栈,则需要定义bottom的值,它应当是指向栈底节点的指针变量(引用变量)。

MyStack是我们定义的栈类,包含3个成员变量:

  • stackint[]类型的数组,说明栈将存储整型。
  • top:栈顶在数组stack中的下标,一般规定top始终指向栈顶元素的上一个空间。真正的栈顶元素是stack[top-1]
  • capacity:记录栈当前的容量,随着栈中元素不断增加,可对栈进行扩容,变量capacity的值也应随之调整。

在调用构造函数public MyStack(int capacity)初始化一个栈后。top等于bottom等于0,即栈顶等于栈底,此时栈中没有数据(空栈)

栈的基本操作

入栈

入栈就是向栈中存入数据。入栈要在栈顶进行,每当向栈顶压入一个数据,栈顶指针top都要+1,直到栈满。

public void push(int elem) {
    // 入栈操作
    if (top == capacity) {
        // 达到栈容量上限,需要扩容
        increaseCapacity();
    }
    stack[top] = elem; // 将元素elem存放在stack[top]
    top++; // top指向栈顶元素的上一个空间,此时栈顶元素为stack[top-1]
}

在数据入栈前判断栈是否已满if (top == capacity)
因为规定了top始终指向栈顶元素的上一个空间,下标top是从0开始计算的,所以**stack[top-1]**就是栈顶元素,并且**top**值等于该栈中元素的个数。当top等于capacity时,栈中元素的个数等于capacity,top已经指向stack数组的外面,所以不能再向栈中压入元素,需要调用increaseCapacity()函数对栈进行扩容。

public void increaseCapacity() {
    // 增加栈的容量
    // 初始化一个新栈,容量是原stack容量的2倍
    int[] stackTmp = new int[stack.length * 2];
    System.arraycopy(stack, 0, stackTmp, 0, stack.length);
    stack = stackTmp;
}

将参数存入栈顶,即执行stack[top] = elem;操作。
然后移动top,指向栈顶元素的上一个位置top++;

出栈

函数pop()的作用是将栈顶元素取出并返回,同时调整栈顶指针top的值-1

public int pop() {
    // 出栈操作
    if (top == 0) {
        // 栈顶等于栈底,说明栈中没有数据
        System.out.println("There are no elements in stack");
        return -111;
    }
    return stack[--top];
}

与所有数据结构一样,在执行增删操作之前,要先判断是否符合条件。
从栈顶取出元素前,需要判断栈内是否有元素。当top==0时,栈内没有元素,pop的操作将是非法的,所以需要返回一个无效值ERROR_ELEM_VALUE,在介绍“线性结构-数组”中,有一道“删除重复元素”的题目,当时将重复元素赋值为-111,也是同样的道理。一旦将某个整数赋值给常量**ERROR_ELEM_VALUE**,则该整数就不能作为栈中的有效元素了

来两道题

二/十进制转换


利用栈结构将二进制数转换为十进制数


利用栈的FILO特点,方便位权运算

首先将二进制数从高位到低位顺序入栈。然后从栈顶依次取出每一个元素。当取出第i个元素时,结果增加$2^{i-1}$。最终的和即为该二进制数对应的十进制数。

public class BiToDecTest {
    public static String BiToDec(String binary) {
        MyStack stack = new MyStack(10);
        int i;
        for (i = 0; i < binary.length(); i++) {
            if (binary.charAt(i) == '0') {
                stack.push(0); // 将二进制0入栈
            } else {
                stack.push(1);// 将二进制1入栈
            }
        }
        i = 0;
        int e;
        long sum = 0;
        while ((e = stack.pop()) != stack.ERROR_ELEM_VALUE) {
            sum = sum + (int) (e * Math.pow(2, i));
            i++;
        }
        return String.valueOf(sum);
    }

    public static void main(String args[]) {
        System.out.println("The result of converting 11001 to decimal number is");
        System.out.println(BiToDecTest.BiToDec("11001"));
    }
}

public static String BiToDec(String binary)String.valueOf(sum)的作用是将binary指定的二进制串转换成对应的十进制串并返回。参数**binary****String**类型,为了与之对应,函数的返回值也是**String**类型,并通过**String.valueOf()**函数将十进制转换成对应的字符串

括号匹配问题


已知表达式中只允许有两种括号:圆括号()和方括号[]。它可以任意地嵌套使用,例如[()][()()][()([])]都是合法的表达式。括号必须成对出现,[(])([)][())等不成对出现的情况是非法的。编写一个程序,判断一个括号表达式是否合法。


一道栈的经典问题
  1. 合法的情况下,第一个入栈的字符应该是左括号。
  2. 我们可以在检测到下一个字符为右括号时,弹出栈内的左括号,查看是否匹配。
  3. 如果不匹配或已经空栈,则表明括号表达式不合法。
我们介绍一段没上面那么好理解的代码:

循环遍历字符串上的字符,每个字符进行如下判断:
首先是判断是否栈空,如果栈不为空,则将栈顶c与临时字符expression.charAt(i)匹配,成功则继续遍历;不成功则再将刚刚出栈的元素塞回,并将临时字符入栈。
如果栈为空,则将临时字符expression.charAt(i)直接入栈。
如果表达式合法,所有元素都被弹出,最后结果是空栈。
因此最后一步即为判断是否为空栈,栈空则表示合法。不为空则非法。

public static boolean MatchBracket(String expression) {
    // 判断括号表达式expression是否合法
    MyStack stack = new MyStack(10); // 初始化栈
    char c;
    for (int i = 0; i < expression.length(); i++) {
        if ((c = stack.pop()) != stack.ERROR_ELEM_VALUE) {
            // 从栈中弹出了有效元素,说明栈不为空
            if (!match(c, expression.charAt(i))) {
                // 如果栈顶元素c跟expression的第i个括号
                // 不相匹配,则将c重新入栈,同时将
                // expression的第i个括号入栈
                stack.push(c);
                stack.push(expression.charAt(i));
            }
        } else {
            // 如果是空栈,说明输入的是第1个括号,因此保存在栈中
            stack.push(expression.charAt(i));
        }
    }
    if (stack.pop() != stack.ERROR_ELEM_VALUE) {
        // 栈不为空,说明表达式expression不合法,返回false
        return false;
    } else {
        return true; // 栈为空,说明表达式expression合法,返回true
    }
}

在检索完括号表达式之后,如果栈已空,说明左右括号完全匹配,即括号表达式合法。
如果栈未空,则表明输入的括号不完全匹配,也就是括号表达式非法。
对于匹配括号的函数,我们不能简单地作==的判断,因为'('注定不等于')'
如果是C++,我们可以使用map容器,右括号为索引,所括号为实值。因为我们是检测到右括号之后才去匹配左括号,所以要将右括号作为索引。
如果是Java,也有类似的容器。但在这里,我们使用更粗暴的方法。

private static boolean match(char a, char b) {
    if ((a == '(' && b == ')') ||
            (a == '[' && b == ']')) {
        return true;
    }
    return false;
}
目录
相关文章
|
14天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
90 9
|
5天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
14 1
|
8天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
13天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
56 16
|
11天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
13天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
40 4
|
17天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
30天前
数据结构(栈与列队)
数据结构(栈与列队)
17 1
|
1月前
|
存储 JavaScript 前端开发
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
为什么基础数据类型存放在栈中,而引用数据类型存放在堆中?
67 1
|
1月前
【数据结构】-- 栈和队列
【数据结构】-- 栈和队列
16 0