数据结构笔记-栈

201 阅读5分钟

特点:只能在表尾(栈顶)a1插入和删除,后进先出。表头an是栈底
存储方式:顺序栈,链栈

一、顺序栈

定义:地址连续,依次存储,数组实现
用栈顶指针保存栈顶位置,有两种方法:①指向栈顶元素的实际位置(对应的数组下标,从-1开始)②指向下一次入栈的位置,从0开始。

基本操作:

typedef struct {
	int data[MAXSIZE];
	int top;//栈顶指针
}SeqStack;

SeqStack s;

//栈顶指针指向实际栈顶的位置
//初始化
s.top = -1;
//入栈
if (s.top < MAXSIZE - 1)
	s.data[++s.top] = x;
else printf("overflow");
//出栈
if (s.top >= 0)
	s.top--;
//判栈空
s.top == -1;
//判栈满
s.top == MAXSIZE - 1;
//栈顶元素
s.data[s.top];

二、链栈

不带表头的单链表,头指针对应栈顶位置
结构体维护栈:

// 栈顶指针为top,无头结点
typedef struct Snode {
    int data;
    struct Snode* next;
} *LinkStack;

初始化栈

void InitStack(LinkStack& top) {
    top = NULL;
}

出栈

void pop(LinkStack& top) {
    if (top != NULL) // 栈非空
    {
        LinkStack p = top;
        top = top->next;
        free(p);
    }
}

入栈

void push(LinkStack& top, int e) {
    LinkStack s = (LinkStack)malloc(sizeof(struct Snode));
    s->data = e;
    if (StackEmpty(top) == 1) top = s;
    else {
        s->next = top;
        top = s; // 更新栈顶指针
    }
}

获取栈顶元素

int GetTop(LinkStack top) {
    if (top == NULL) {
        return -1;
    }
    else {
        int e = top->data;
        return e;
    }
}

判断栈是否为空

bool StackEmpty(LinkStack top) {
    return (top == NULL); // 若栈为空,返回1
}

主函数

int main() {
    LinkStack top;
    InitStack(top);
    int n, x;
    printf("请输入创建栈的元素个数:");
    scanf_s("%d", &n);
    printf("请输入入栈元素:");
    int i;
    for (i = 0; i < n; i++) {
        int y;
        scanf_s("%d", &y);
        push(top, y);
    }
    printf("\n");
    printf("请输入入栈元素:");
    scanf_s("%d", &x);
    push(top, x);
    while (StackEmpty(top) != 1) {
        printf("获取新的栈顶元素:");
        int e = GetTop(top);
        printf("%d", e);
        printf("-----栈顶元素出栈-----\n");
        pop(top);
    }
    printf("empty!");
    return 0;
}

三、双栈共享存储空间

两个栈共用一个一维数组v[M],栈底分别设在数组的两端,两个栈可互补空缺,使得某个栈实际可用空间大于M/2

94c81016aec0016af21ae601a004817.jpg

四、栈的应用

1.括号匹配

这是一道栈的经典题目,也可以见LeetCode20。
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。
  • 左括号必须以正确的顺序闭合。
  • 注意空字符串可被认为是有效字符串。

我们先来分析一下不匹配的场景,有三个。

  • 多了左括号
  • 多了右括号
  • 括号数量匹配,类型不匹配

算法:遇到左括号,就把对应的右括号放进栈里。遇到右括号,与栈顶元素进行比较,如果相同,就匹配成功,弹出栈顶元素。

  • 若字符串遍历完了,但栈不为空,则多了左括号。这是情况一。
  • 若比较时发现不匹配的括号,是第三种情况。
  • 若栈空了,而字符串没有遍历玩完,则多了右括号,是第二种情况。

如果一开始遇到左括号,把左括号放进栈里,那么之后遇到右括号的时候,还要再判断一次栈中的左括号和字符串里的右括号是否匹配,太麻烦了,所以我们采取第一种方法。

2.逆波兰式

首先,我们来认识一下什么是逆波兰式。
逆波兰式是一种后缀表达式,操作符位于操作数的后面,不需要括号来改变运算的优先级。可以想象成二叉树的后序遍历(左右中)。正常的表达式是二叉树的中序遍历,符合书写习惯,但计算机无法加括号,而利用栈和后缀表达式可以免去加括号,直接计算。
那么如何用栈来实现呢?
首先,遍历表达式,遇到数字则入栈,遇到运算符则弹出两个数字,进行运算后,将结果入栈。如此反复。栈里的最后一个数字就是最终结果。
代码实现:

#include<stdlib.h>
#include<string.h>
#include <stdio.h>
// 栈顶指针为top,无头结点
typedef struct Snode {
    char data;
    struct Snode* next;
} *LinkStack;

void InitStack(LinkStack& top) {
    top = NULL;
}

void pop(LinkStack& top) {
    if (top != NULL) // 栈非空
    {
        LinkStack p = top;
        top = top->next;
        free(p);
    }
}

bool StackEmpty(LinkStack top) {
    return (top == NULL); // 若栈为空,返回1
}
void push(LinkStack& top, char e) {
    LinkStack s = (LinkStack)malloc(sizeof(struct Snode));
    s->data = e;
    if (StackEmpty(top) == 1) top = s;
    else {
        s->next = top;
        top = s;
    }
}

int evalRPN(char* tokens, int tokensSize) {
    LinkStack top = NULL;
    for (int i = 0; i < tokensSize; i++) {
        if (tokens[i] == '+' || tokens[i] == '-' || tokens[i] == '*' || tokens[i] == '/') {
            int num1 = top->data - '0';
            pop(top);
            int num2 = top->data - '0';
            pop(top);
            int add;
            if (tokens[i] == '+') { add = num2 + num1; push(top, add + '0'); }
            else if (tokens[i] == '-') { add = num2 - num1; push(top, add + '0'); }
            else if (tokens[i] == '*') { add = num2 * num1; push(top, add + '0'); }
            else if (tokens[i] == '/') { add = num2 / num1; push(top, add + '0'); }
			//比较不能用scrmp我也不知道 为啥 
        } else {
            push(top, tokens[i]);
        }
    } 
    int result = top->data - '0';//char转换成int类型 
    free(top);
    return result;
}

int main() {
    int tokensSize;
    char tokens[100];
    printf("请输入数组元素数量:");
    scanf("%d", &tokensSize);
    printf("请输入后缀表达式:");
    for (int i = 0; i < tokensSize; i++) {
        scanf(" %c", &tokens[i]);
    }
    int result = evalRPN(tokens, tokensSize);
    printf("计算结果:%d\n", result);
    return 0;
}

3.十进制转八进制数

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

typedef struct Snode {
    int data;
    struct Snode* next;
} *LinkStack;

void InitStack(LinkStack& top) {
    top = NULL;
}

int pop(LinkStack& top) {
    int e;
    if (top != NULL) {
        LinkStack p = top;
        e = p->data;
        top = top->next;
        free(p);
    }
    return e;
}

bool StackEmpty(LinkStack top) {
    return (top == NULL);
}

void push(LinkStack& top, int e) {
    LinkStack s = (LinkStack)malloc(sizeof(struct Snode));
    s->data = e;
    if (StackEmpty(top)) {
        s->next = NULL;
        top = s;
    } else {
        s->next = top;
        top = s;
    }
}

void conversion(LinkStack& top, int N) {
    int octalNum[100];
    int i = 0;
    while (N) {
        octalNum[i++] = N % 8;
        N = N / 8;
    }
    for (int j = i - 1; j >= 0; j--) {
        printf("%d", octalNum[j]);
    }
}

int main() {
    int N;
    LinkStack top;
    InitStack(top);

    printf("请输入十进制数:");
    scanf("%d", &N);
    printf("转换后的八进制数为:");
    conversion(top, N);

    return 0;
}

另一种写法

#include <stdio.h>
#include<math.h>
typedef struct Snode {
    int data;
    struct Snode* next;
} *LinkStack;

void InitStack(LinkStack& top) {
    top = NULL;
}

bool StackEmpty(LinkStack top) {
    return (top == NULL);
}

void push(LinkStack& top, int e) {
    LinkStack s = new Snode;
    s->data = e;
    if (StackEmpty(top)) {
        s->next = NULL;
        top = s;
    } else {
        s->next = top;
        top = s;
    }
}

int pop(LinkStack& top) {
    int e;
    if (top != NULL) {
        LinkStack p = top;
        e = p->data;
        top = top->next;
        delete p;
    }
    return e;
}

int DecimalToOctal(int dec) {
    LinkStack top;
    InitStack(top);

    int octalNum = 0, i = 0;

    while (dec != 0) {
        push(top, dec % 8);
        dec /= 8;
        i++;
    }
    
    while (!StackEmpty(top)&&i>=1) {
        octalNum += pop(top) * pow(10 ,i-1);
        i--;
    }

    return octalNum;
}

int main() {
    int N;
    printf("请输入十进制数:");
    scanf("%d", &N);
    printf("转换后的八进制数为:%d\n", DecimalToOctal(N));

    return 0;
}