C语言:栈和队列+leetcode232、leetcode225:队实现栈、栈实现队列、leetcode20括号匹配

1. 栈

  • 前言:一般的栈的内核是连续的一片空间,即:数组,那么是如何实现的FILO呢?通过top指针,每次pop只能出top位置的值。此外,栈的实现根据top初始化时处于-1和0位置稍有差异。
  1. 头文件
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>

/*
要改的永远是栈结构体中的成员变量,
改结构体中的成员变量只需要传入结构体地址即可
参数用一级指针即可 
而删除stack时,也只需要把内部数组释放

*/
typedef int STDataType;
typedef struct Stack
{
	STDataType* _a;	// 用指针类型 用数组 这是动态的	且栈一般定义的是动态的
	int _top;		// 栈顶
	int _capacity;  // 容量 :最多放多少
}Stack;
// 初始化栈 
void StackInit(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType data);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素 
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数 
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool StackEmpty(Stack* ps);
// 销毁栈 
void StackDestroy(Stack* ps);

// 数据结构中,一般获取任何值都需要调函数,不要直接输出top之类的值
// 且不同操作不同函数,是一种低耦合 解耦,即不同函数,代码不同

  1. 头文件实现
#include"ds_2_stack.h"


// 栈的初始化:做栈数组大小初始化,以及指针在-1还是0,-1和0的区别是  插入和删除时,先动针还是先动数
void StackInit(Stack* ps)
{
	// 栈不可以为空
	assert(ps);
	ps->_a = NULL;
	ps->_top = ps->_capacity = 0;

}

void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	// 因为初始 top和capacity相等所以插入要扩容
	if (ps->_top == ps->_capacity)
	{
		int newCapacity = ps->_capacity ==0?4:ps->_capacity*2;
		// realloc:(原始数组名,新大小)
		STDataType* tmp = (STDataType*)realloc(ps->_a, newCapacity*sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->_a = tmp;
		// 容量是多少
		ps->_capacity = newCapacity;
	}
	// 直接给数组值:因top初始为0,0可放,所以先放再加
	ps->_a[ps->_top] = data;
	ps->_top++;
}

void StackPop(Stack* ps)
{
	assert(ps);
	// asset中放希望,希望栈不空,所以是 !empty
	assert(!StackEmpty(ps));
	ps->_top--;
}
STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->_a[ps->_top-1];	// 在前一个位置
}

bool StackEmpty(Stack* ps)
{
	assert(ps);
	// top在0为空
	return ps->_top == 0;
}

int StackSize(Stack* ps)
{
	assert(ps);
	return ps->_top;
}

void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->_a);	// 释放
	ps->_a = NULL;	//防止野指针
	ps->_top = ps->_capacity = 0;	// top归0 容量清0
}

// 判断括号匹配
  1. main():
#define _CRT_SECURE_NO_WARNINGS

#include"ds_2_stack.h"

void TestStack()
{
	Stack st;
	StackInit(&st);	// 一级指针,所以传入地址
	/*
	数据结构如果直接访问,可能会出错,因为你不知道内部实现,比如这里的top,初始化不确定是0或1
	if (st._top > 0)
	{
		;
	}*/
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);
	int cur;
	while (!StackEmpty(&st))
	{
		cur = StackTop(&st);
		StackPop(&st);
		printf("出去的是%d \n", cur);
	}
	StackDestroy(&st);

}

int main()
{
	TestStack();
	return 0;
}
  1. 效果
    请添加图片描述

2.1 队列(单项链表节点队列)

注意:
该队列每个元素存在链表节点中,求size时,直接遍历一遍,也可以直接

#include<stdio.h>
#include<malloc.h>
#include<stdbool.h>
#include<stdlib.h>
#include<assert.h>


typedef int QDataType;
typedef struct QListNode
{
	// 队列每个元素是链表节点,而链表节点内应该是 next和data
	struct QListNode* _next;
	QDataType _data;
}QNode;
// 队列的结构 
typedef struct Queue
{
	QNode* _front;	// 队列的每个元素是节点,所以需要用头尾指针存指起来
	QNode* _rear;
}Queue;

// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QDataType data);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QDataType QueueFront(Queue* q);
// 获取队列队尾元素 
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数 
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);

void QueueInit(Queue* q)
{
	assert(q);
	q->_front = q->_rear = NULL;
}



/* 注意两个错误:1.节点的data给值
*  2. 给node->next =NULL
*  3. 另外注意做push要区分是第一次push还是后push
*/
void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	// 先申请队列的每个元素:链表节点
	struct QListNode* node = (struct QListNode*)malloc(sizeof(struct QListNode));
	// 每次创建新节点 应该让next为NULL
	node->_next = NULL;
	if (node == NULL)
	{
		perror("push_malloc fail");
		exit(-1);
	}
	// 没有给它值 犯了错了 没给data
	// 如果是第一次
	if (q->_front == NULL)
	{
		q->_front = q->_rear = node;	// next不用置空
		q->_front->_data = data;
	}
	else
	{
		q->_rear->_next = node;
		q->_rear = node;
		q->_rear->_data = data;
	}
}



void QueuePop(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	 出队,或删除,一定要单独拿出这个节点,free更直接,不然front先做next 
	struct QListNode* head = q->_front;
	// 头变节点的下一个
	q->_front = q->_front->_next;
	free(head);
	// 如果出队完,应该防止尾变野指针,所以置NULL
	if (!q->_front)
	{
		q->_front = q->_rear = NULL;
	}
	
	// 只有一个节点
	/*if (q->_front->_next == NULL)
	{
		free(q->_front);
		q->_front = q->_rear = NULL;
	}
	else
	{
		QNode* del = q->_front;
		q->_front = q->_front->_next;
		free(del);
	}*/
}

QDataType QueueFront(Queue* q)
{
	assert(q);
	// 头指针为空,也要报错
	assert(!QueueEmpty(q));
	return q->_front->_data;
}

// 把Empty换bool吧
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->_rear->_data;
}

bool QueueEmpty(Queue* q)
{
	assert(q);
	// 头尾等且其一为NULL
	return q->_front == q->_rear && q->_rear == NULL;
}


int QueueSize(Queue* q)
{
	assert(q);
	int size = 0;
	QNode* cur = q->_front;
	// 从来没有给尾巴后面练过NULL,这里会不会错
	while (cur)
	{
		++size;
		cur = cur->_next;
	}
	return size;

}

void QueueDestroy(Queue* q)
{
	assert(q);
	// 一个个节点来出来删
	struct QListNode* cur = q->_front;
	while (cur)
	{
		struct QListNode* next = cur->_next;
		free(cur);
		cur = next;
	}
	// 最后都置空
	q->_front = q->_rear = NULL;
}

注意点:(本人犯过的错误):

  1. pop():在做pop时,我使用了如下画了X的代码:陷入的思维错误是使用了Empty判断当前删除掉的是不是最后一个元素,而empty的实现方法是:
    return q->_front == q->_rear && q->_rear == NULL;
    它判断当前的front和rear是不是相等且其中之一不存在,而我利用free删除最后一个元素之后,还没有将rear和front置NULL,就不能调用这个函数,修改写法可以按照下面的写法或者把判断条件变成:if(!q->front):即队头不存在,再做置NULL,如下标X的方法不会进去if内部。所以,在做数据结构的函数实现时候,调用某些已经实现了的方法,需要考虑是否当前函数的操作会影响你调用的函数,不可贸然使用。
    在这里插入图片描述
  2. empty():我原始的代码是如下这个鸟样:明显错了,缺少了判断rear或front是否有一方为NULL。
    在这里插入图片描述
    而且优雅的写法是:直接返回bool值
    return q->_front == q->_rear && q->_rear == NULL;

2.2队列(双向循环队列)

    1. 特点:与单向队列不同,为了区分队列的空和满判断情况,往往比实际存储数量多开辟一个空间,即队尾永远指向空。
    1. 常用判断式:其中N是队列元素个数,而N代表队列总空间(比元素个数大1) 。默认下标从0开始
      队空:(rear == front)
      队满:(rear+1)%(n+1) == front
      队元素个数:(rear - front + N) % N 【先判断队列是不是满,不满才用此公式】
    1. 实现:
      核心:使用数组和数组下标。
1.队列结构体定义:

循环数组是怎样循环起来的:我们定义结构体,内部使用动态数组a开辟固定长度。循环队列大小常常固定不变。此外,连接起来是因为我们在做入队操作和出队操作时,通过+N再加上对N取模,与实际空间位置无关,它之所以循环,是因为队列等数据结构具逻辑性。即:人为赋予逻辑关系。

typedef struct {
	int front;
	int rear;
	int* a;// 用数组来开辟空间

} MyCircularQueue;

2. 实现
// 内核是数组
typedef struct {
	int front;
	int rear;
	int* a;	// 用数组来开辟空间
	int N;	// 空间(比size大1)

} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
	MyCircularQueue* obj = (struct MyCircularQueue*)malloc(sizeof(MyCircularQueue));
	// k是队列元素个数 真实空间要大1
	obj->a = (int*)malloc(sizeof(int)*(k+1));
	obj->front = 0;
	obj->rear = 0;	
	obj->N = k+1;	// 空间 
    return obj;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
	return obj->front == obj->rear;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
	return (obj->rear + 1)%(obj->N) == obj->front;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
	// 没有满,才能插
	if (myCircularQueueIsFull(obj))
	{
		return false;
	}
	// 没满就放了++
	else
	{
		obj->a[obj->rear] = value;
		obj->rear++;
		// 防止obj在最后超过一圈
		obj->rear %= obj->N;
		return true;
	}
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
	if (myCircularQueueIsEmpty(obj))
		return false;
	else
	{
		// 数组删数据不需要清
		obj->front++;
		obj->front %= obj->N;
		return true;
	}
}

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

// ** 4 第2点: 注意巧妙的写法 ***
int myCircularQueueRear(MyCircularQueue* obj) {
	if (myCircularQueueIsEmpty(obj))
	{
		return -1;
	}
	else
	{
		// 取队尾 应是减1位,但是可能rear在0位置
		// 只能判断back是不是0
		return obj->a[(obj->rear - 1 + obj->N) % obj->N];
	}
}

// 注意这里有两个malloc,所以就malloc两次
void myCircularQueueFree(MyCircularQueue* obj) {
	free(obj->a);
	free(obj);
	return ;
}
4. 上面实现中的注意点
  1. 不论是入队出队,涉及指针的++,但是由于循环需要回到0,所以一定记得做%N【N是队列空间大小,是n+1】
  2. 求尾rear元素时,返回的是数组a的rear-1+n%n。因为队列预留一个空间的原因,最后一个数据存在rear前一个位置,而当rear==0时,-1是负数,所以我们应该用+n模n,使得结果回到末位。
  3. 队列的free:循环队列内核是动态数组,使用了malloc,所以最后需要free内部的数组空间,但是本身结构体也使用了malloc,所以还需要队本身使用free。

3. leetcode232 栈实现队列

leetcode232

typedef struct Stack
{
	int* stk;	// 栈内核也是数组
	int top;	// 栈大小也就是指针
	int stkCapacity;	//栈的容量
	// 奇怪它不用指针吗
}Stack;
// 栈的大小

// param:输入栈大小
Stack* stackCreate(int capacity)
{
	// 用malloc 注意:栈和队列,均要用两次malloc,结构体本身和内部的stk
	Stack* ret = (struct Stack*)malloc(sizeof(Stack));
	ret->stk = (int*)malloc(sizeof(int)*capacity);


	ret->stkCapacity = capacity;
	ret->top = 0;	// 让起始位置为0 所以先存再++
	return ret;
}

// 这里先不做满的判断
void stackPush(Stack* st, int val) {
	// 容量:top从0开始
	printf("给位置%d存值\n", st->top);
	st->stk[st->top] = val;
	printf("push栈时接收到的val:%d\n", val);
	st->top++;
}

void stackPop(Stack* st)
{
	// 因为是数组,直接减
	st->top--;
}

int stackTop(Stack* st)
{
	return st->stk[st->top-1];
}

bool stackEmpty(Stack* st)
{
	return st->top==0;
}

void stackFree(Stack* st)
{
	// 这里我比leetcode答案多写了一行
	free(st->stk);
	free(st);
}

// 队列使用栈实现:需要的是出的栈的指针和入的栈的指针
typedef struct {
	Stack* ins;	// 接收放
	Stack* outs;	// 发送放
} MyQueue;


MyQueue* myQueueCreate() {
	MyQueue* ret = (MyQueue*)malloc(sizeof(MyQueue));
	ret->ins = stackCreate(100);
	ret->outs = stackCreate(100);
	return ret;
}

// 1. 只要栈的发送方有,就都给栈接收方
void out2to(MyQueue* que)
{
	// 只要发送栈不空
	printf("这里是out2in\n");
	while (!stackEmpty(que->outs))
	{
		int val = stackTop(que->outs);
		stackPop(que->outs);
		printf("out2in调用了pop\n");
		printf("out2in时,对ins做push\n");
		stackPush(que->ins, val);
	}
}


void myQueuePush(MyQueue* obj, int x) {
	// 入队,给ins->val
	printf("que_push\n");
	printf("que的push应该先给outs,下面是outs在做push\n");
	stackPush(obj->outs, x);
	printf("outs_push完了\n");
}

// 队列做出队,我们要出接收方,而接收方如果没有就不能出队,所以先做发送,如果有,就直接发送
int myQueuePop(MyQueue* obj) {
	// 出队应该出接收方
	if(stackEmpty(obj->ins))
	{
		out2to(obj);
	}
	// 判断完是否空,下面的是一定要做的操作
	int val;
	val = stackTop(obj->ins);
	printf("这里是que的pop,下面调用了ins做pop\n");
	stackPop(obj->ins);
	return val;
}

// 队列的队首:如果ins没有了,就直接做发送操作
// 最后必须做的肯定是返回top值
int myQueuePeek(MyQueue* obj) {
	if (stackEmpty(obj->ins))
	{
		printf("peek调用了out2in\n");
		out2to(obj);
	}
	return stackTop(obj->ins);
}

bool myQueueEmpty(MyQueue* obj) {
	return stackEmpty(obj->ins)&&stackEmpty(obj->outs);
}

void myQueueFree(MyQueue* obj) {
	stackFree(obj->outs);
	stackFree(obj->ins);
}

收获:1. empty判断
2. malloc,总结:
3. 得不到某些值,或者某些值特别大:是因为我压根没给值,或者我st->top = 。应该是st->a[st->top] = val;

4. leetcode 225:队列实现栈

leetcode225
我今天使用单链表实现一次试试。
方法(一) : C++版本:使用STL库,该库中的队列是循环队列。
思路(一):

  1. 因为栈的顺序是FILO,该栈使用一个队列可以巧妙实现的办法是:每次给栈中push,实际是对队列push,但对队列push时,假设内部是321,我们push4到队尾得到:3214,然后做1~size-1的出队和入队,最终得到:4321。用一个队列就可得到栈顺序。
  2. 该题让实现队列的push、pop、empty、top,下面是详细思路:
    push(x):先push(x)到内核的队尾,然后把该元素之前的元素先利用que做pop,再push:范围是1~que.size-1,【这里的que->size是元素个数】
    pop:pop就是直接对队列做pop,但是先取出top,,因为每次push保证了队首元素是出栈想要的第一个元素。
    empty:队列若空,则栈也是空的。
    top:top就是队列的第一个,取front即可。

做法:1. 队栈做push,把x插到队尾。利用循环,pop一下push一下,至队尾之前所有元素。

class MyStack {
public:
// 使用队自带循环队列的库
    queue<int> que;
    MyStack() {

    }
    
    void push(int x) {
        // 放元素,先入队,此时新元素在队尾
        que.push(x);
        // 先push后,做1~size-1次后移
        for(int i = 1; i <que.size();i++)
        {
            que.push(que.front());
            que.pop();
        }
    }
    
    int pop() {
        // pop返回队首元素
        int t = que.front();
        que.pop();
        return t;
    }
    
    int top() {
        // 要栈首,返回队首即可
        return que.front();
    }
    
    bool empty() {
        // 队空即栈空
        return que.empty();
    }
};

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack* obj = new MyStack();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->top();
 * bool param_4 = obj->empty();
 */

方法(二):用两个队列(单链表)去实现栈。

具体过程:

一般情况下,两个队列永远是一个有值,一个无值。
push:

  1. 两个队列都空,任意找一个队列push。
  2. 用一个队列空,一个队列不空,就给不空的做push。
    pop:
  3. 做pop时,先把有值的一方的n-1个值,全部push到空的队列,再pop 删去n-1个,原来有值的队列就剩下了一个值,此时pop得到的值就是栈想要的栈顶。
    top
    做top时,直接返回不空的队列的尾back
    empty:
    empty(q1)&&empty(q2)
#include<stdio.h>
#include<malloc.h>
#include<stdbool.h>
#include<stdlib.h>
#include<assert.h>


typedef int QDataType;
typedef struct QListNode
{
	// 队列每个元素是链表节点,而链表节点内应该是 next和data
	struct QListNode* _next;
	QDataType _data;
}QNode;
// 队列的结构 
typedef struct Queue
{
	QNode* _front;	// 队列的每个元素是节点,所以需要用头尾指针存指起来
	QNode* _rear;
}Queue;

// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QDataType data);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QDataType QueueFront(Queue* q);
// 获取队列队尾元素 
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数 
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);

void QueueInit(Queue* q)
{
	assert(q);
	q->_front = q->_rear = NULL;
}



/* 注意两个错误:1.节点的data给值
*  2. 给node->next =NULL
*  3. 另外注意做push要区分是第一次push还是后push
*/
void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	// 先申请队列的每个元素:链表节点
	struct QListNode* node = (struct QListNode*)malloc(sizeof(struct QListNode));
	// 每次创建新节点 应该让next为NULL
	node->_next = NULL;
	if (node == NULL)
	{
		perror("push_malloc fail");
		exit(-1);
	}
	// 没有给它值 犯了错了 没给data
	// 如果是第一次
	if (q->_front == NULL)
	{
		q->_front = q->_rear = node;	// next不用置空
		q->_front->_data = data;
	}
	else
	{
		q->_rear->_next = node;
		q->_rear = node;
		q->_rear->_data = data;
	}
}



void QueuePop(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	 出队,或删除,一定要单独拿出这个节点,free更直接,不然front先做next 
	struct QListNode* head = q->_front;
	// 头变节点的下一个
	q->_front = q->_front->_next;
	free(head);
	// 如果出队完,应该防止尾变野指针,所以置NULL
	if (!q->_front)
	{
		q->_front = q->_rear = NULL;
	}
	
	// 只有一个节点
	/*if (q->_front->_next == NULL)
	{
		free(q->_front);
		q->_front = q->_rear = NULL;
	}
	else
	{
		QNode* del = q->_front;
		q->_front = q->_front->_next;
		free(del);
	}*/
}

QDataType QueueFront(Queue* q)
{
	assert(q);
	// 头指针为空,也要报错
	assert(!QueueEmpty(q));
	return q->_front->_data;
}

// 把Empty换bool吧
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->_rear->_data;
}

bool QueueEmpty(Queue* q)
{
	assert(q);
	// 头尾等且其一为NULL
	return q->_front == q->_rear && q->_rear == NULL;
}


int QueueSize(Queue* q)
{
	assert(q);
	int size = 0;
	QNode* cur = q->_front;
	// 从来没有给尾巴后面练过NULL,这里会不会错
	while (cur)
	{
		++size;
		cur = cur->_next;
	}
	return size;

}

void QueueDestroy(Queue* q)
{
	assert(q);
	// 一个个节点来出来删
	struct QListNode* cur = q->_front;
	while (cur)
	{
		struct QListNode* next = cur->_next;
		free(cur);
		cur = next;
	}
	// 最后都置空
	q->_front = q->_rear = NULL;
}

// 匿名结构体定义成MyStack
// 结构体S需要两个成员que
typedef struct {
	Queue q1;
	Queue q2;
} MyStack;


MyStack* myStackCreate() {
	MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
	QueueInit(&obj->q1);
	QueueInit(&obj->q2);
	return obj;
}

// 哪个que不空,就给哪个插
void myStackPush(MyStack* obj, int x) {
	if (!QueueEmpty(&obj->q1))
	{
		QueuePush(&obj->q1, x);
	}
	else
	{
		QueuePush(&obj->q2, x);
	}
}

// 哪个队不空 就出队哪个
int myStackPop(MyStack* obj) {
	Queue* empty = &obj->q1;
	Queue* nonempty = &obj->q2;
	if (!QueueEmpty(&obj->q1))
	{
		empty = &obj->q2;
		nonempty = &obj->q1;
	}
	// 只要大于1,就把队列数据前n-1个导入空队列 剩下一个是站定元素
	while (QueueSize(nonempty) > 1)
	{
		QueuePush(empty, QueueFront(nonempty));
		QueuePop(nonempty);
	}
	// 从非空中拿第一个
	int top = QueueFront(nonempty);
	QueuePop(nonempty);
	return top;

}

// 做题默认不会出现空时的调用
// 这里再想想玩什么吧
int myStackTop(MyStack* obj) {
	if (!QueueEmpty(&obj->q1))
	{
		return QueueBack(&obj->q1);
	}
	else
	{
		return QueueBack(&obj->q2);
	}
}

bool myStackEmpty(MyStack* obj) {
	printf("都空了吗%d\n", QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2));
	return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

// 只要是分开的空间,利用了多个malloc就需要挨个去free,
// 直接free(某个值节点不可以)
void myStackFree(MyStack* obj) {
	// 因为stack中的que是结构体,所以需要传&取地址
	QueueDestroy(&obj->q2);
	QueueDestroy(&obj->q1);
	free(obj);
}

/**
 * Your MyStack struct will be instantiated and called as such:
 * MyStack* obj = myStackCreate();
 * myStackPush(obj, x);

 * int param_2 = myStackPop(obj);

 * int param_3 = myStackTop(obj);

 * bool param_4 = myStackEmpty(obj);

 * myStackFree(obj);
*/

int main()
{
	MyStack* st;
	st = myStackCreate();
	myStackPush(st, 1);
	int val = myStackTop(st);
	printf("top:%d\n", val);
	int p_val = myStackPop(st);
	printf("pop:%d\n", val);
	printf("empty:%d", myStackEmpty(st));

	return 0;
}

“方法(三)”:错误的,不可取, 利用C语言自写单向队列:只使用一个队列时,单项队列的头和尾,一直在变化,这个题会非常复杂。

5. 栈的应用:括号匹配 leetcode20

  1. 思路:
    利用栈后进先出的结构,每次进去右括号,看当前栈顶是否是其对应的左括号
  2. 算法:
    遇到左括号就入栈
    遇到右括号就比较是不是跟当前字符对应的左括号,如果不对应,就返回false
    其中,注意
    a. 右括号多的情况:else中判断栈是否已空。
    b. 左括号多的情况:最后while出去可能剩余左括号,做empty判断
  3. 注意:
    遍历过程中:使用的是*s,且栈的datatype默认是int,虽然s是字符,但是push时因为push的接收类型是int,所以会转为int,取出比较也会转为char,这些不必在意。
    代码:
typedef int STDataType;
typedef struct Stack
{
	STDataType* _a;	// 用指针类型 用数组 这是动态的	且栈一般定义的是动态的
	int _top;		// 栈顶
	int _capacity;  // 容量 :最多放多少
}Stack;
// 初始化栈 
void StackInit(Stack* ps);
// 入栈 
void StackPush(Stack* ps, STDataType data);
// 出栈 
void StackPop(Stack* ps);
// 获取栈顶元素 
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数 
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps);
// 销毁栈 
void StackDestroy(Stack* ps);


// 栈的初始化:做栈数组大小初始化,以及指针在-1还是0,-1和0的区别是  插入和删除时,先动针还是先动数
void StackInit(Stack* ps)
{
	// 栈不可以为空
	assert(ps);
	ps->_a = NULL;
	ps->_top = ps->_capacity = 0;

}

void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	// 因为初始 top和capacity相等所以插入要扩容
	if (ps->_top == ps->_capacity)
	{
		int newCapacity = ps->_capacity ==0?4:ps->_capacity*2;
		// realloc:(原始数组名,新大小)
		STDataType* tmp = (STDataType*)realloc(ps->_a, newCapacity*sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->_a = tmp;
		// 容量是多少
		ps->_capacity = newCapacity;
	}
	// 直接给数组值:因top初始为0,0可放,所以先放再加
	ps->_a[ps->_top] = data;
	ps->_top++;
}

void StackPop(Stack* ps)
{
	assert(ps);
	// asset中放希望,希望栈不空,所以是 !empty
	assert(!StackEmpty(ps));
	ps->_top--;
}
STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->_a[ps->_top-1];	// 在前一个位置
}

int StackEmpty(Stack* ps)
{
	assert(ps);
	// top在0为空
	return ps->_top == 0;
}

int StackSize(Stack* ps)
{
	assert(ps);
	return ps->_top;
}

void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->_a);	// 释放
	ps->_a = NULL;	//防止野指针
	ps->_top = ps->_capacity = 0;	// top归0 容量清0
}

bool isValid(char* s) {
	struct Stack* st;
	st = (Stack*)malloc(sizeof(Stack));;
	StackInit(st);
	while (*s)
	{
		if (*s == '(' || *s == '[' || *s == '{')
		{
			StackPush(st, *s);
		}
		else
		{
			// 右括号多的情况:即栈内空了,当前是右括号
			if(StackEmpty(st))
			{
				return false;
			}
			char t = StackTop(st);
			if (*s == ')')
			{
				if (t != '(')
				{
					return false;
				}
			}
			else if (*s == ']')
			{
				if (t != '[')
				{
					return false;
				}
			}
			else if (*s == '}')
			{
				if (t != '{')
				{
					return false;
				}
			}
			StackPop(st);
		}
		s++;
	}
	// 最后栈不空,肯定里面是因为有左括号,所以就直接返回错
	if (!StackEmpty(st))
	{
		return false;
	}

	return true;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值