数据结构基础笔记(3)栈

栈:后进先出的线性表,如何实现增删查

1栈是什么
栈是一种特殊的线性表
栈与线性表的不同,体现在增和删的操作
栈的数据节点必须后进先出
后进–栈的数据新增操作只能在末端进行,不允许在栈的中间某个节点后新增数据
先出–栈的数据删除操作也只能在末端进行,不允许在栈的中间某个节点后删除数据
为什么要用这种受限的栈呢?
从功能上讲,数组或链表可以替代栈
但问题是:数组或者链表的操作过于灵活
这些没有意义的接口过多,当数据量很大的时候就会出现一些隐藏的风险
虽然栈限定降低了操作的灵活性,但这使得栈在处理只涉及一段新增和删除数据的问题时效率更高
栈包含了表头和表尾
表尾用来输入数据,叫做栈顶
表头是栈底
栈顶和栈底是用来表示这个栈的两个指针
栈也有顺序表示和链式表示,分别称作顺序栈和链栈
2栈的基本操作
栈的新增操作:push压栈
删除操作:pop出栈
2.1顺序栈
把数组的首元素存在栈底,最后一个元素放在栈顶
然后定义一个top指针来指示栈顶元素在数组中的位置
假设栈中只有一个数据元素,则top = 0
一般以top = -1来判定是否为空栈
当定义了栈的最大容量为StackSize时,则栈顶top必须小于StackSize
当需要新增数据元素,即入栈操作时,就需要将新插入元素放在栈顶,并将栈顶指针加1
删除数据元素,即出栈操作,只需要top - 1就可以
查找操作,栈没有额外的改变,需要遍历整个栈来完成基于某些条件的数值查找
2.2链式栈
就是用链表的方式对栈的表示。通常可以把栈顶放在单链表的头部
对于链栈,新增数据的压栈操作,与链表最后的新数据基本相同,需要额外处理的,就是栈的top指针
在链式栈中进行删除操作时,只能在栈顶进行操作
将栈顶的top指针指向栈顶元素的next指针即可完成删除
对于链式栈,新增删除数据元素没有任何循环操作,其时间复杂度均为O(1)
对于查找操作,他需要遍历整个栈来完成基于某些条件的数值查找
2.3数据的新增、删除、查找与线性表区别
相同点:操作原理极为相似,时间复杂度完全一样,都依赖当前位置的指针来进行数据对象的操作
区别:新增和删除的对象,只能是栈顶的数据结点。
3例题
例1:给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。有效字符串需满足:左括号必须与相同类型的右括号匹配,左括号必须以正确的顺序匹配。例如,{ [ ( ) ( ) ] } 是合法的,而 { ( [ ) ] } 是非法的。
这个问题很显然是栈发挥价值的地方。原因是,在匹配括号是否合法时,左括号是从左到右依次出现,而右括号则需要按照“后进先出”的顺序依次与左括号匹配。因此,实现方案就是通过栈的进出来完成。
具体为,从左到右顺序遍历字符串。当出现左括号时,压栈。当出现右括号时,出栈。并且判断当前右括号,和被出栈的左括号是否是互相匹配的一对。如果不是,则字符串非法。当遍历完成之后,如果栈为空。则合法。

在这里插public static void main(String[] args) {

    String s = "{[()()]}";

    System.out.println(isLegal(s));

}

private static int isLeft(char c) {

    if (c == '{' || c == '(' || c == '[') {

        return 1;

    } else {

        return 2;

    }

}

private static int isPair(char p, char curr) {

    if ((p == '{' && curr == '}') || (p == '[' && curr == ']') || (p == '(' && curr == ')')) {

        return 1;

    } else {

        return 0;

    }

}

private static String isLegal(String s) {

    Stack stack = new Stack();

    for (int i = 0; i < s.length(); i++) {

        char curr = s.charAt(i);

        if (isLeft(curr) == 1) {

            stack.push(curr);

        } else {

            if (stack.empty()) {

                return "非法";

            }

            char p = (char) stack.pop();

            if (isPair(p, curr) == 0) {

                return "非法";

            }

        }

    }

    if (stack.empty()) {

        return "合法";

    } else {

        return "非法";

    }

}

例 2,浏览器的页面访问都包含了后退和前进功能,利用栈如何实现?
我们利用浏览器上网时,都会高频使用后退和前进的功能。比如,你按照顺序先后访问了 5 个页面,分别标记为 1、2、3、4、5。现在你不确定网页 5 是不是你要看的网页,需要回退到网页 3,则需要使用到两次后退的功能。假设回退后,你发现网页 4 有你需要的信息,那么就还需要再执行一次前进的操作。
为了支持前进、后退的功能,利用栈来记录用户历史访问网页的顺序信息是一个不错的选择。此时需要维护两个栈,分别用来支持后退和前进。当用户访问了一个新的页面,则对后退栈进行压栈操作。当用户后退了一个页面,则后退栈进行出栈,同时前进栈执行压栈。当用户前进了一个页面,则前进栈出栈,同时后退栈压栈。我们以用户按照 1、2、3、4、5、4、3、4 的浏览顺序为例,两个栈的数据存储过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值