什麼?入門連結串列後你還在棧堆裡徘徊?

MeloJun發表於2021-10-13

大家好,我是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);
	//若銷燬失敗
	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;
}

鏈棧

**判空

當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;
}

最後

  • 最近偷懶了,一直有在認真記錄筆記,但是卻疏於整理,沒有發出來,希望近幾天可以抽出更多時間來繼續整理佇列,雜湊表,排序等方面的知識

相關文章