【数据结构与算法】栈的深入学习(题目练习与源码)!

                                           

大家好,本篇的内容:  栈的认识,栈的使用,栈的实现!栈的实现源码附在最后!

首先,先让我们来认识一下栈:

一.(Stack):

一、栈的概念
栈,一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
出栈:栈的删除操作叫做出栈。出数据在栈顶。

                                 

那么,如何实现栈呢?

通过我们c语言的基础和前篇链表的知识,我们知道可以使用数组或者链表来实现栈。

数组栈

                       

链表栈

                        

需要注意,链表存在单链表和双向链表的区别,单链表实现时,栈顶只能是头;而双向链表实现时,栈顶可以是头,也可以是尾

所以实现栈的方法是多样可选的,看大家喜欢用哪种方法来实现,这里我使用数组栈。

但当我们定义一个栈的时候,就会有一些需要注意的地方:

                        

                              

大家看这俩种思路,当top==0时表示栈为空或者top==0时表示存进去一个数据,那么就存在了一个歧义,top==0时到底表示的时空栈还是表示一个数据?

如果你定义top==0表示一个数据,那么你在定义空栈的时候就要定义成top==-1;但是这种写法会很不方便,给后面带来许多的麻烦,当你要忘压栈的时候,就需要先++top,再进行存数据。

                         

另一种写法:

                       

 top==0时表示栈为空,当压栈进入一个数据时,top++,top==1,这时top指向元素的下一个位置,我认为这种写法是比较好的。

还有一个需要注意的地方:

                                      

我们知道栈是后进先出的,那么如果有一个栈,进栈序列是1,2,3,4,进栈过程中可以出栈,那么它的出栈序列就不是单一的4,3,2,1了,它可以在3进栈之前让2出栈,4进栈结束后再出栈,那么出栈序列就变成了2,4,3,1;

二、栈的使用:

这里模拟几个功能:
入栈:

public class MyStack {
    public static void main(String[] args) {
        Stack<Integer> s1 = new Stack<>();
        s1.push(1);
        s1.push(2);
        s1.push(3);
        s1.push(4);
        s1.push(5);
        System.out.println(s1);
    }
}

              

出栈:

public class MyStack {
    public static void main(String[] args) {
        Stack<Integer> s1 = new Stack<>();
        s1.push(1);
        s1.push(2);
        s1.push(3);
        s1.push(4);
        s1.push(5);
        System.out.println(s1);
        s1.pop();
        s1.pop();
        System.out.println(s1);
    }
}

获取栈顶元素:

public class MyStack {
    public static void main(String[] args) {
        Stack<Integer> s1 = new Stack<>();
        s1.push(1);
        s1.push(2);
        s1.push(3);
        s1.push(4);
        s1.push(5);
        System.out.println(s1.peek());
    }
}

判断栈是否为空:
 

public class MyStack {
    public static void main(String[] args) {
        Stack<Integer> s1 = new Stack<>();
        s1.push(1);
        s1.push(2);
        s1.push(3);
        s1.push(4);
        s1.push(5);
        System.out.println(s1.empty());
    }
}

  

 三、栈的应用场景


1. 若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是(C)
 A: 1,4,3,2  B: 2,3,4,1  C: 3,1,4,2  D: 3,4,2,1

问题分析:类似于这种栈的选择题,如果元素较少,我们直接心算就可以,元素较多的话我们可以画图来解决,本题c选项,先出的是3,那么就是1,2,3进栈,然后3出栈,第二个出栈选项给的是1,我们知道1是第一个进栈的,那么想出1,2必须先出,所以C选项错误!
 
2.一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出栈的顺序是( B)。
A: 12345ABCDE  B: EDCBA54321  C: ABCDE12345  D: 54321EDCBA

问题分析:简单明了,栈的结构先进后出,直接选B。

栈我们认识的差不多了,那让我们来做几个题感受一下吧

有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。

示例 1:

输入:s = "()"
输出:true
示例 2:

输入:s = "()[]{}"
输出:true
示例 3:

输入:s = "(]"
输出:false

解题思路:本题我们可以利用栈来解决,首先我们遍历字符串,把我们遇到的左括号先进栈,遇到右括号不进栈,这个时候先判断栈是否为空,若为空,那么就是右括号多了,直接返回false;否则这时栈顶元素弹出,看这两个括号是否匹配,匹配循环继续,直到最后栈为空,不匹配,直接返回false。

代码实现:

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack();
        for(int i=0;i<s.length;i++){
            char ch = s.charAt(i);
            //判断是不是左括号
            if(ch=='('|| ch=='['|| ch=='{'){
                stack.push(ch);
            }else{
                if(stack.empty()){
                    //遇到了右括号,此时不匹配!
                    return false;
                }
            }
            char ch2 = stack.peek();
            if(ch2 == '[' &&ch == ']'||ch2 == '(' &&ch == ')'||ch2 == '{' &&ch == '}'){
                stack.pop();
            }else{
                return false;
            }
        }
    }
    //当字符串遍历完成了,但是栈不为空,说明左括号还在栈当中没有匹配完成
    if(!stack.empty()){
        return false;
    }
    return true;
}

再来做一个吧:

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。

1. 0<=pushV.length == popV.length <=1000

2. -1000<=pushV[i]<=1000

3. pushV 的所有数字均不相同

示例1

输入:

[1,2,3,4,5],[4,5,3,2,1]

返回值:

true

说明:

可以通过push(1)=>push(2)=>push(3)=>push(4)=>pop()=>push(5)=>pop()=>pop()=>pop()=>pop()

这样的顺序得到[4,5,3,2,1]这个序列,返回true 。

示例2

输入:

[1,2,3,4,5],[4,3,5,1,2]

返回值:

false

说明:

由于是[1,2,3,4,5]的压入顺序,[4,3,5,1,2]的弹出顺序,要求4,3,5必须在1,2前压入,且1,2不能弹出,但是这样压入的顺序,1又不能在2之前弹出,所以无法形成的,返回false 。

解题思路:

因为题目给了两个序列,我们首先遍历第一个序列,让其元素进栈,在这个循环中如果栈顶元素等于给定序列,那么栈顶元素出栈,j++,函数返回值直接判断栈是否为空,为空说明我们的次序是匹配的,不为空,说明这个序列不合法!

代码实现:
 

import java.util.*;
 
public class Solution {
    public boolean IsPopOrder(int [] pushA, int [] popA) {
        Stack<Integer> stack = new Stack<>();
        int j = 0;
        for (int i = 0; i < pushA.length; i++) {
            stack.push(pushA[i]);
            while (j < popA.length && !stack.empty()
                    && stack.peek().equals(popA[j])) {
//用equals是因为Integer的范围是-128~127 如果超过这个范围肯定会报错 系统给的测试用例应该没有超过这个范围 所以测试可以通过
                stack.pop();
                j++;
            }
        }
        return stack.empty();
    }
}

栈的知识就到此结束了,希望对大家有所帮助!

栈的源码:

Stack.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>


typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;		// 标识栈顶位置的
	int capacity;
}ST;

void STInit(ST* pst);
void STDestroy(ST* pst);

// 栈顶插入删除
void STPush(ST* pst, STDataType x);
void STPop(ST* pst);
STDataType STTop(ST* pst);

bool STEmpty(ST* pst);
int STSize(ST* pst);



Stack.c

#include"Stack.h"

void STInit(ST* pst)
{
	assert(pst);

	pst->a = NULL;
	pst->capacity = 0;

	// 表示top指向栈顶元素的下一个位置
	pst->top = 0;

	// 表示top指向栈顶元素
	//pst->top = -1;
}

void STDestroy(ST* pst)
{
	assert(pst);

	free(pst->a);
	pst->a = NULL;
	pst->top = pst->capacity = 0;
}

// 栈顶插入删除
void STPush(ST* pst, STDataType x)
{
	assert(pst);

	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		pst->a = tmp;
		pst->capacity = newcapacity;
	}

	pst->a[pst->top] = x;
	pst->top++;
}

void STPop(ST* pst)
{
	assert(pst);
	// 不为空
	assert(pst->top > 0);

	pst->top--;
}

STDataType STTop(ST* pst)
{
	assert(pst);
	// 不为空
	assert(pst->top > 0);

	return pst->a[pst->top - 1];
}

bool STEmpty(ST* pst)
{
	assert(pst);

	/*if (pst->top == 0)
	{
		return true;
	}
	else
	{
		return false;
	}*/

	return pst->top == 0;
}

int STSize(ST* pst)
{
	assert(pst);

	return pst->top;
}

Tast.c

#include"Stack.h"

int main()
{
	ST s;
	STInit(&s);
	STPush(&s, 1);
	STPush(&s, 2);
	STPush(&s, 3);
	printf("%d ", STTop(&s));
	STPop(&s);
	printf("%d ", STTop(&s));
	STPop(&s);

	STPush(&s, 4);
	STPush(&s, 5);

	//    一     对     多
	// 入栈顺序  --  出栈顺序
	while (!STEmpty(&s))
	{
		printf("%d ", STTop(&s));
		STPop(&s);
	}
	printf("\n");

	return 0;
}

 

 

  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值