栈和队列(数据结构)

今天再来看一种数据结构,叫做栈

1、概念及其结构

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶

出栈:栈的删除操作叫做出栈。出数据也在栈顶。

2、栈的实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构更优一点。因为数组在尾上插入数据的代价比较小

 书归正传,我们先写一下栈的简单结构和它的功能

先写一个栈的初始化

当然少不了删除了

这里我们将top初始化为0,所以就是先插入数据然后top++

如果我们将top定义成了-1,那么在插入数据的时候就是top++,然后再插入数据

我们再写一个判断栈是否为空的功能

我们写了栈的插入那么就要写栈的删除

在写一个找出top位置的功能

 好了,这下我们来看队列

队列
1、概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的特性

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

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

由于在队头出数据需要挪动,效率比较低,所以队列用链表来实现比较好一点,用最简单的单链表就行

先写好结构

2、队列的实现

由于队列是队尾入数据,队头出数据故我们需要两个指针分别指向队尾和队头

我们就用一个结构体来存储这两个指针

接下来就是老生常谈的初始化

还有删除整个队列

当然少不了插入数据

还有删除单个队列数据

接着是取队头数据和队尾数据

最后是判断是否为空和计算大小

栈和队列OJ
括号匹配问题

 有效的括号 - 力扣(LeetCode)

这道题就是让你查看给的括号到底能不能都匹配在一起,这道题我们用栈做最靠谱了。

思路是左括号入栈,右括号出栈,栈顶元素进行匹配

这里就是左括号入栈

然后是右括号出栈

也会有特殊例子,就像只有一个左括号,那么只入栈就结束了,这时我们就要加一个条件判断栈是否为空,如果栈为空,那么说明栈出完了,那就算正常结束,如果栈不为空那就是flase

还有一种情况,就是只给了一个右括号,那么就没有入栈,但是程序还会找栈的top,这时也会报错,那我们就要在写一个判断语句

最后这才算大功告成 

typedef char STDataType;

typedef struct Stack

{

   STDataType* a;

    int capacity;

    int top;//初始值为0,表示栈顶元素写一个位置的下标

}ST;

void StackInit(ST* ps);//初始化

void StackDestroy(ST* ps);//删除整个栈的数据

void StackPush(ST* ps,STDataType x);//插入数据

void StackPop(ST* ps);//删除栈的一个数据

STDataType StackTop(ST* ps);//找出top位置

bool StackEmpty(ST* ps);//判断栈是否为空

int StackSize(ST* ps);//栈的大小

//初始化

void StackInit(ST* ps)

{

    assert(ps);

    ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);

    if (ps->a == NULL)

   {

        perror("malloc fail");

        exit(-1);

   }

    ps->capacity = 4;

    ps->top = 0;

}

//删除

void StackDestroy(ST* ps)

{

    assert(ps);

    free(ps->a);

    ps->a = NULL;

    ps->top = ps->capacity = 0;

}

//插入数据

void StackPush(ST* ps, STDataType x)

{

    assert(ps);

    if (ps->capacity == ps->top)

   {

        STDataType* tmp = (STDataType*)realloc(ps->a, ps->capacity * sizeof(STDataType) * 2);

        if (tmp == NULL)

       {

            perror("realloc fail");

            exit(-1);

       }

        ps->a = tmp;

        ps->capacity *= 2;

   }

    ps->a[ps->top] = x;

    ps->top++;

}

//判断栈是否为空

bool StackEmpty(ST* ps)

{

    assert(ps);

    return ps->top == NULL;

}



//删除栈的一个数据

void StackPop(ST* ps)

{

    assert(ps);

    assert(!StackEmpty(ps));

    ps->top--;

}

//找出top位置

STDataType StackTop(ST* ps)

{

    assert(ps);

    assert(!StackEmpty(ps));

    return ps->a[ps->top - 1];

}

 //栈的大小

int StackSize(ST* ps)

{

    assert(ps);

    return ps->top;

}



bool isValid(char * s){

ST st;

StackInit(&st);

while(*s)

{

    if(*s=='['||*s=='{'||*s=='(')

   {

        StackPush(&st,*s);

       ++s;

   }

    else

   {

        if(StackEmpty(&st))

       {

            StackDestroy(&st);

            return false;

       }

        char top=StackTop(&st);

        StackPop(&st);

        if((*s== ']' && top !='[')

        ||(*s=='}' && top !='{')

        ||(*s==')' && top !='('))

       {

            StackDestroy(&st);

            return false;

       }

        else

       {

           ++s;

       }

   }

}

    bool ret = StackEmpty(&st);

    return ret;

}
用队列实现栈

 用队列实现栈 - 力扣(LeetCode)

这道题是用队列实现栈,队列是先进先出,而栈是后进先出

这里push进来1234,如果是栈pop的话就是1 2 3 4依次出来,但队列恰好相反,是4 3 2 1

题目的条件是用两个队列来实现,我们就可以让有数据的那个队列先出数据,出到size剩了一个了就刚好了pop队尾的数据

大思路就是:保持一个队列存数据,一个队列为空,出数据时,倒一下

首先,先定义两个队列,来实现上面所说的功能,也可以定义成指针,不过后面就还需要用malloc

题目第一个要求的功能就是Creat函数,意思就是没有结构体,这就需要我们创建加初始化

这里我们定义一个obj来指向malloc

然后把q1和q2传给初始化函数,最后返回obj

下面就是push函数,按上面的思路,就是保持一个为空,一个不为空,哪个队列不为空就向那个队列插,或者都为空,那就随便插了

接着是pop函数,这里pop函数的要求不仅是删除一个数据,还要返回栈顶的元素,这里我们的思路是找到为空的队列和不为空的,然后将非空的队列里的数据转移到空的队列中,最后再返回栈顶元素

下一个函数是求栈顶元素的,倒也能用队列来回倒,但我们刚好有个求队尾元素的函数 

我们直接调用,这个省了不少事

最后两个一个是判空,另一个是释放队列

值得一提的是,我们如果只释放obj,创造出来的两个q1和q2就并没有被删除,这时就造成了内存泄漏

用栈实现队列

 用栈实现队列 - 力扣(LeetCode)

思路:

我们来看看上一道题的兄弟题目,用栈实现队列

栈是后进先出,而队列是先进先出

删除的思路也是倒一下,把有数据的栈里的数据全部倒到空栈里,剩一个数据的时候就删除,这样就把栈底的数据删了

当我们入数据的时候,因为现在有数据的栈里是234,我们入一个5,顺序就乱了,要想按顺序入数据就还得倒到另一个栈里再入。

但是这样太麻烦了,我们换一种思路,将原先有数据的栈就叫做出栈,为空的栈就叫做入栈,这样也是按顺序存储,出栈时也是哪顺序出,直到右边为空,再想出的时候就再倒。

typedef int STDatatype;
typedef struct Stack
{
  STDatatype* a;
  int capacity;
  int top;   // 初始为0,表示栈顶位置下一个位置下标
}ST;

void StackInit(ST* ps);
void StackDestroy(ST* ps);
void StackPush(ST* ps, STDatatype x);
void StackPop(ST* ps);
STDatatype StackTop(ST* ps);

bool StackEmpty(ST* ps);
int StackSize(ST* ps);


void StackInit(ST* ps)
{
  assert(ps);

  //ps->a = NULL;
  //ps->top = 0;
  //ps->capacity = 0;

  ps->a = (STDatatype*)malloc(sizeof(STDatatype)*4);
  if (ps->a == NULL)
  {
    perror("malloc fail");
    exit(-1);
  }

  ps->top = 0;
  ps->capacity = 4;
}

void StackDestroy(ST* ps)
{
  assert(ps);

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

void StackPush(ST* ps, STDatatype x)
{
  assert(ps);

  // 扩容
  if (ps->top == ps->capacity)
  {
    STDatatype* tmp = (STDatatype*)realloc(ps->a, ps->capacity * 2 * sizeof(STDatatype));
    if (tmp == NULL)
    {
      perror("realloc fail");
      exit(-1);
    }

    ps->a = tmp;
    ps->capacity *= 2;
  }

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

// 20:20继续
void StackPop(ST* ps)
{
  assert(ps);
  assert(!StackEmpty(ps));

  ps->top--;
}

STDatatype StackTop(ST* ps)
{
  assert(ps);
  assert(!StackEmpty(ps));

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

bool StackEmpty(ST* ps)
{
  assert(ps);
  return ps->top == 0;
}

int StackSize(ST* ps)
{
  assert(ps);

  return ps->top;
}


typedef struct {
ST popset;
ST pushset;
} MyQueue;

bool myQueueEmpty(MyQueue* obj);
MyQueue* myQueueCreate() {
MyQueue*pq=(MyQueue*)malloc(sizeof(MyQueue));
StackInit(&pq->popset);
StackInit(&pq->pushset);
return pq;
}

void myQueuePush(MyQueue* obj, int x) {
  assert(obj);
StackPush(&obj->pushset,x);
}


int myQueuePop(MyQueue* obj) {
assert(obj);
assert(!myQueueEmpty(obj));

int k=myQueuePeek(obj);
StackPop(&obj->popset);
return k;
}

int myQueuePeek(MyQueue* obj) {
assert(obj);
assert(!myQueueEmpty(obj));
if(StackEmpty(&obj->popset))
{
    while(!StackEmpty(&obj->pushset))
    {
         StackPush(&obj->popset,StackTop(&obj->pushset));
         StackPop(&obj->pushset);
    }
}
return StackTop(&obj->popset);
}

bool myQueueEmpty(MyQueue* obj) {
  assert(obj);
return StackEmpty(&obj->popset)&&StackEmpty(&obj->pushset);
}

void myQueueFree(MyQueue* obj) {
  assert(obj);
StackDestroy(&obj->popset);
StackDestroy(&obj->pushset);
free(obj);
}

设计循环队列

 设计循环队列 - 力扣(LeetCode)

来看看这个循环队列

这里我们经过权衡利弊,最终选择使用数组实现

至于怎么判空呢,当rear等于front的时候队列就是空的,判满则是用(rear+1)%(k+1)==front判断

这里我们先写一下前面的代码,先malloc出一个结构体来,在malloc出数组,注意这里要多开一个空间,方便判断

然后再顺便把判空和判满写了

下面来看看插入

我们首先进行一个判满,当不满时,rear先插入,再++,但是会有特殊情况

这种情况下rear++就会越界,我们想让他再从0开始,就可以写一个if语句,给他赋值为0。或者是%=一下k+1

然后是出数据,正常情况就是front++,等走到最后一个的时候就%=一下(k+1)

接着是取头,而且题目有规定,如果队列为空要返回-1

然后是取头,也是规定为空返回-1,这个也有特殊情况

最后就可以这样写

当然,还有一种更优思路,不过一般人不太能想到

如果画图,说不定就能想到

最后是释放,也是两层释放

typedef struct {
int*a;//数组
int front;//头
int rear;//尾
int k;//数组的大小
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //多开一个解决判满的问题
    obj->a=(int*)malloc(sizeof(int)*(k+1));
    obj->front=obj->rear=0;
    //这里的k是放满的时候能存储的最大数据个数,空间大小应该是k+1
    obj->k=k;
    return obj;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
assert(obj);
return obj->rear==obj->front;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    assert(obj);
    return ((obj->rear+1)%(obj->k+1))==obj->front;
    }
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
assert(obj);

if(myCircularQueueIsFull(obj))
return false;
obj->a[obj->rear++]=value;
obj->rear%=(obj->k+1);
return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
   if(myCircularQueueIsEmpty(obj))
   return false;
   obj->front++;
   obj->front%=(obj->k+1);
   return true;
}

int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj)){
        return -1;
    }
    return obj->a[obj->front];
} 

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->a[(obj->rear+obj->k)%(obj->k+1)];
}



void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}

/**
 * Your MyCircularQueue struct will be instantiated and called as such:
 * MyCircularQueue* obj = myCircularQueueCreate(k);
 * bool param_1 = myCircularQueueEnQueue(obj, value);
 
 * bool param_2 = myCircularQueueDeQueue(obj);
 
 * int param_3 = myCircularQueueFront(obj);
 
 * int param_4 = myCircularQueueRear(obj);
 
 * bool param_5 = myCircularQueueIsEmpty(obj);
 
 * bool param_6 = myCircularQueueIsFull(obj);
 
 * myCircularQueueFree(obj);
*/

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值