什么?入门链表后你还在栈堆里徘徊?


highlight: vs2015

大家好,我是melo,一名大二上软件工程在读生,经历了一年的摸滚,现在已经在工作室里边准备开发后台项目啦。
不过这篇文章呢,还是想跟大家聊一聊数据结构与算法,学校也是大二上才开设了数据结构这门课,希望可以一边学习数据结构一边积累后台项目开发经验。
上次的文章,我们已经初步入门了数据结构:链表 想入门数据结构,却总是拜倒在链表的石榴裙下?,入门后你是否坚持下来了呢,是不是在栈堆里边徘徊不前了,这篇带你来攻破ta!

普通栈

知识点

后进先出(只能在一端操作)

image.png

只能对栈顶(表尾)进行插入和删除

image.png

保证栈顶先出即可

image.png

**空栈最好是top=-1

image.png

应用场景

1)子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。
2)处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数、区域变量等数据存入堆栈中。
3)表达式的转换 [中缀表达式转后缀表达式] 与求值(实际解决)。
4)二叉树的遍历。
5)图形的深度优先(depth一first)搜索法。

数组实现栈

Java版

注意构造器,是根据maxSize,然后记得this.maxSize=maxSize

class ArrayStack{
    //栈的大小
    private int maxSize;
    //维护的数组
    private int[] stack;
    //栈顶
    private int top;

    public ArrayStack() {
    }

    public ArrayStack(int maxSize) {
        this.stack = new int[maxSize];
        this.top = -1;
        this.maxSize=maxSize;
    }

    public int pop(){
        if(isEmpty()){
            throw new RuntimeException("栈已空");
        }
        return this.stack[top--];
    }

    public void push(int val){
        if(isFull()){
            throw new RuntimeException("栈已满");
        }
        stack[++top]=val;
    }

    public boolean isFull(){
        return this.top==this.maxSize-1;
    }

    public boolean isEmpty(){
        return this.top==-1;
    }
}

C语言版

头文件SqStack.h

#include"Common.h"

typedef struct{
    ElemType* elem;//存储空间基址
    int top;//栈顶的下一位
    int size;//当前分配的内存容量
    int increment;//扩容时的增量
}SqStack; 

Status initStack_Sq(SqStack& S, int size, int inc);//初始化顺序栈
Status DestroyStack_Sq(SqStack& S);//销毁顺序栈
Status StackEmpty_Sq(SqStack& S);//判断是否为空栈
void ClearStack_Sq(SqStack& s);//清空栈
Status Push_Sq(SqStack& S, ElemType e);//元素入栈
Status Pop_Sq(SqStack& S, ElemType& e);//元素出栈,用e返回
Status GetTop_Sq(SqStack S, ElemType& e);//取栈顶元素,用e返回

头文件Common.h

Status起别名(可观性更好一点,类似Java中的封装返回结果)
定义一些相关的Status返回值
定义ElemType,方便扩展,更换元素数据类型

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

//定义一些返回值
#define OK 1
#define ERROR 0
#define OVERFLOW -1

//默认使用int类型返回值
typedef int Status;
//需要更改元素类型就在此更换int
typedef int ElemType;

源文件SqStack.cpp

#include"Common.h"
#include"SqStack.h"

//初始化顺序栈
Status initStack_Sq(SqStack &S, int size, int inc) {
    S.elem = (ElemType*)malloc(size * sizeof(ElemType));
    if (S.elem == NULL || size <= 0 || inc <= 0) return ERROR;
    S.size = size;
    S.increment = inc;
    S.top = 0;
    return OK;
}

//销毁顺序栈
Status DestroyStack_Sq(SqStack &S) {
    S.top = 0;
    S.size = 0;
    S.increment = 0;
    free(S.elem);
    /*此处后来发现,free操作并不会改变指针的值
        (所以并不会变成NULL),只是让他所指向的内存空间失效了,
        不能正常访问了而已
        */
    /*
        //若销毁失败
    if (S.elem!=NULL) return ERROR;
        */
    return OK;
}

//判断是否为空栈
Status StackEmpty_Sq(SqStack& S) {
    return S.top == 0;
}

//清空栈
void ClearStack_Sq(SqStack& S) {
    S.top = 0;
}

//元素入栈
Status Push_Sq(SqStack& S, ElemType e) {
    if (S.top >= S.size) {
        S.elem = (ElemType*)realloc(S.elem, sizeof(ElemType) * (S.size + S.increment));
    }
    if (S.elem == NULL) return ERROR;
    S.elem[S.top] = e;
    S.top++;
    return OK;
}

//元素出栈,用e返回
Status Pop_Sq(SqStack& S, ElemType& e) {
    if (StackEmpty_Sq(S)) return ERROR;
    e = S.elem[--S.top];
    return OK;
}

//取栈顶元素,用e返回
Status GetTop_Sq(SqStack S, ElemType& e) {
    if (StackEmpty_Sq(S)) return ERROR;
    e = S.elem[S.top-1];
    return OK;
}

注意

此处有点小问题喔,就是栈的销毁操作,起初我是

free(S.elem);
//若销毁失败 
if (S.elem!=NULL) {
    return ERROR; 
}

后来再回过头来看这篇博客的时候,发现free后其实S.elem并不会等于NULL,为此我又整理了一篇关于"迷途"指针的问题,具体可参照 迷途指针

链栈

**判空

当top指针刚好指向基址的时候,说明此时链栈为空

image.png

入栈(满了就扩容)

直接S.top==NULL,就是栈满了的情况

image.png
#include "allinclude.h"  //DO NOT edit this line
Status Push_Sq2(SqStack2 &S, ElemType e) { 
    // Add your code here
    //其实可以直接S.top==NULL即可
    if(*(S.top)>=S.size){
        S.elem=(ElemType*)realloc(S.elem,sizeof(ElemType)*(S.size+S.increment));
        if(S.elem==NULL) return ERROR;
    }  
     *(S.top)=e;
     S.top++;
     return OK;
}

进制转换

image.png
#include "allinclude.h"
void Conversion(int N, int rd)
{  // Add your code here
  SqStack stack;
  //初始化栈
  InitStack_Sq(stack,10,1);
  //N>0  
  while(N>0){
    Push_Sq(stack,N%rd);
    N/=rd;
  }
  //再出栈便可实现逆序打印出来(因为栈的特性)
  while(!StackEmpty_Sq(stack)){
    ElemType temp;
    Pop_Sq(stack,temp);
    printf("%d",temp);
  }

}

括号匹配

(力扣)20有效的括号

https://leetcode-cn.com/problems/valid-parentheses/

最初版本

思路

  • 特判右括号,判断是否跟栈顶元素匹配
    • 若栈已经为空了,说明左括号不够用来匹配,无效
    • 若不匹配,也无效
    • 若匹配,则将匹配到的左括号出栈,暂时有效
  • 其他情况(左括号)直接压入栈
  • 最后出循环的时候,若栈中还有元素说明左括号没有被匹配完,也无效

细节

  • 有不符合的直接return false

优化

  • 奇数个直接无效
bool isValid(char * s){
    int length = strlen(s);
    //初始化为-1
    int top=-1;
    char stack[length] ;
    //若本身就是奇数个,则直接无效
    if(length%2!=0){
        return false;
    }
    //遍历字符串
    for(int i=0;i<length;i++){
        //特判右括号
        if(s[i]==')'){
            //若栈已空或者栈顶元素不匹配,直接false
            if(top<0||stack[top]!='('){
                return false;
            }
            //否则将匹配到的左括号出栈
            top--;
        }
        //同理
        else if(s[i]=='}'){
            if(top<0||stack[top]!='{'){
                return false;
            }
            top--;
        }
        else if(s[i]==']'){
            if(top<0||stack[top]!='['){
                return false;
            }
            top--;
        }
        //若是左括号.直接加入栈
        else stack[++top]=s[i];
    }
    //出来时若栈不为空,说明没有完全匹配掉
    if(top!=-1){
        return false;
    }
    return true;
}

改进版本

  • 先统一处理右括号变成左括号,以便后续可以直接统一判断相不相等即可

细节

  • 注意要先

//若是左括号.直接加入栈
else{
stack[++top]=s[i];
continue;
}

bool isValid(char * s){
    int length = strlen(s);
    //初始化为-1
    int top=-1;
    char stack[length] ;
    //若本身就是奇数个,则直接无效
    if(length%2!=0){
        return false;
    }
    //遍历字符串
    for(int i=0;i<length;i++){
        //将右括号统一变成左括号,后续可以直接统一判断相不相等即可
        if(s[i]==')') s[i]='(';
        else if(s[i]=='}') s[i]='{';
        else if(s[i]==']') s[i]='[';

        //若是左括号.直接加入栈
        else{ 
            stack[++top]=s[i];
            continue;
        }

        //若栈已空或者栈顶元素不匹配,直接false
        if(top<0||stack[top]!=s[i]){
            return false;
        }else{
        //否则将匹配到的左括号出栈
            top--;
        }
    }
    //出来时若栈不为空,说明没有完全匹配掉
    if(top!=-1){
        return false;
    }
    return true;
}

题解(最优)

用函数将右括号预处理包装起来了,而且如果不是右括号,会返回0,直接解决了上边的三个都不符合得再else和continue的情况

  • 要用个ch来保存!!!!!!!!!!!!!!!!!!!!!!!!,因为只是传值,没有传引用
char pairs(char a) {
    if (a == '}') return '{';
    if (a == ']') return '[';
    if (a == ')') return '(';
    return 0;
}

bool isValid(char* s) {
    int n = strlen(s);
    if (n % 2 == 1) {
        return false;
    }
    int stk[n + 1], top = 0;
    for (int i = 0; i < n; i++) {
        //注意要用个ch来保存
        char ch = pairs(s[i]);
        if (ch) {
            if (top == 0 || stk[top - 1] != ch) {
                return false;
            }
            top--;
        } else {
            stk[top++] = s[i];
        }
    }
    return top == 0;
}

细节容易出错版本

我这里是直接compare(exp[i]),但是没有修改到exp[i],他依然是右括号,下边仍然拿这个来测就错了

#include "allinclude.h"
int compare(char str){
  if(str==')') return '(';
  if(str=='}') return '{';
  if(str==']') return '[';
  return 0;
}

Status matchBracketSequence(char* exp, int n)
{  // Add your code here
    //若是空字符串或者奇数长度,则直接不匹配
  if(n==0||n%2!=0) return false;
  SqStack stack;
  InitStack_Sq(stack,n,1);
  for(int i=0;i<n;i++){
    //若是右括号
    if(compare(exp[i])){
      ElemType temp;
      GetTop_Sq(stack,temp);
      //若非空且匹配,则出栈  
      if(stack.top==0||exp[i]!=temp){
        return false;
      }else{
        Pop_Sq(stack,temp);
      }
    }
    //若是左括号则直接入栈
    else{
      Push_Sq(stack,exp[i]);
    }
  }
  //若非空,说明左括号没有匹配完
  if(!StackEmpty_Sq){
    return false;
  }
  return true;
}

最后

  • 最近偷懒了,一直有在认真记录笔记,但是却疏于整理,没有发出来,希望近几天可以抽出更多时间来继续整理队列,哈希表,排序等方面的知识
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,367评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,959评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,750评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,226评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,252评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,975评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,592评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,497评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,027评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,147评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,274评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,953评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,623评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,143评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,260评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,607评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,271评论 2 358

推荐阅读更多精彩内容