栈和队列(1)

现在是个激动人心的时刻,因为我们来到了栈和队列的章节。

栈是一种特殊的线性表,只允许在一端进行插入和删除操作。进入数据插入和删除的一端叫作栈顶,另一端称为栈底。具有后进先出的特点。

压栈:数据的插入操作叫作进栈/入栈/压栈。

出栈:栈的删除操作叫作出栈,出栈也在栈顶。

栈的实现可以用数组或者线性表实现,相对而言,数组实现更优,因为数组在尾上插入的代价较小。

队列的表示和实现:

队列只允许在一端进行插入操作,在另一端进行删除操作。具有先进先出的特点。

入队列:进入插入操作的一端为队尾。

出队列:进行删除操作的一端为队头。

关于原地扩容和异地扩容:

异地扩容就是在新的地方开辟一个新的空间。

以上代码测试出如果是一样的,那么就是原地扩容,如果不一样,就是异地扩容。 

此时地址是一样的。但是当我们把扩容的空间增大,地址不一样。

数据都在内存里,在堆上。

不会直接访问数据。在缓存,就叫做缓存命中。如果不在缓存,就叫作不命中,要把数据从内存加载到缓存,再访问。

顺序表和链表都可以用来实现栈,不过一般都使用顺序表,因为栈相当于是阉割版的顺序表,只用到了顺序表的尾插和尾删操作,顺序表的尾插和尾删不需要搬移元素效率非常高,故一般都是使用顺序表实现。

链式结构实现栈时,每次入栈相当于链表中头插一个节点,没有扩容一说。

实现静态的栈:

#define N 10
struct Stack
{
   int a[N];
};

 动态的栈:

Stack.h

#pragma once
#include<stdio.h>
#innclude<stdlib.h>
#include<stdbool.h>
typedef int STDataType;
struct Stack
{
   STDataType* a;
   int top;
   int capacity;
}ST;

void STInit(ST* pst);//pst指针都是在函数参数接收的,调用函数的时候传递&st就可以给pst指针赋值
void STDestory(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->top=0;// ps->top表示要插入位置的下标, 然后ps->top也表示当前栈里面有多少个元素
//0就表示当前插入的下标位置为0,栈里面的元素个数为0
// top指向栈顶数据//pst->top =-1;
    pst->capcity=0;
}
 

void STDestory(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->capcity)
    {
       int newCapcity=pst->capcity==0?4:pst->capcity*2;
       STDataType* tmp=realloc(pst->a,newCapcity*sizeof(STDataType));
//ptr是要调整的内存地址,size为调整后的大小。返回值为调整后内存的起始位置。
       if(tmp==NULL)
       {
           perror("realloc fail");
           return;
       }
       pst->a=tmp;
       pst->capcity=newCapcity; 
       pst->a[pst->top]=x;
       pst->top++;
}
void STPop(ST* pst)
{
  assert(pst);
  pst->top--;
}
STDataType STTop(ST* pst)//获取栈顶元素
{
  assert(pst);
  assert(pst->top>0);
  return pst->a[pst->top-1];
}
bool STEmpty(ST* pst)
{
  assert(pst);
  return pst->top==0;
}
int STSize(ST* pst)
{
   assert(pst);
  return pst->top;
}
int main()
{
   ST s;
   STInit(&s);
   STPush(&s,1);
   STPush(&s,2);
   STPush(&s,3);
   while(!STEmpty(&s))
   {
      printf("%d ",STTop(&s);
      STPop(&s);
   }
  STDestory(&s);
}

 

 我们应该知道,top应该指向栈顶,还是栈顶的下一个元素呢?top等于0是有数据还是没数据呢。所以当栈里面没有元素的时候,top应该指向-1。

那么我们的初始化可以改成这样:

void STInit(ST* pst)
{
    assert(pst);
    pst->a=NULL;
    pst->top=0;//top指向栈顶数据的下一个位置
//top指向栈顶数据 pst->top=-1;
    pst->capcity=0;
}
 

20. 有效的括号 - 力扣(LeetCode)

第一步:左括号入栈,第二步:出栈顶的左括号判断和右括号是否匹配,如果匹配,继续,如果不匹配,终止。

bool isValid(char* s) {
   ST st;
   STInit(&st);
   while(*s)
   {
    if(*s=='('||*s=='['||*s=='{')//左括号入栈
    {
        STPush(st,*s);
    }
    else//右括号取栈顶的左括号尝试匹配
    {
        if(STEmpty(&st))
        {
           return false;
        }
        char top=STTop(&st);
        STPop(&st);
        if((top=='('&&*s!=')')
        ||(top=='{'&&*s!='}')
        (top=='('&&*s!=')'))//不匹配
        {
            return false;
        }
    }
    ++s;
   }
//如果栈不为空,说明左括号比右括号多,数量不匹配
   bool ret=STEmpty(&st);
   STDestory(&st);
   return ret;
}

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值