栈与队列实现与练习

栈Stack与队列Queue详解

一.栈的基本概念

1.栈的定义

栈(stack):是允许在一端进行插入或者删除的线性表。栈是一种线性表。只是限定了条件为在一端进行操作。

image-20231223172055210

栈顶(TOP):是栈在操作那一段的第一个元素,为图中的a5
栈底(Bottom):栈的最低部,是最后一个弹出的元素,为a1,图中情况中,a1一定是最后一个弹出的。
空栈(Empty):没有任何元素存储的栈就是空栈,上图弹出a1后不压入任何元素,就成了空栈。

栈是后进先出的顺序表 因为栈顶是一直是后进的,优先弹出。

二.栈的接口设计以及实现

1.结构体的设计

采用顺序结构存储的栈称为顺序栈。栈的主要操作时压栈和出栈,需要频繁对尾部进行增删。那么就不方便采用链式结构了,因为链式结构删除队尾的效率是很低的。所以数组来实现栈。数组在队尾增删的效率是比较可观的。栈结构体设置了三个属性

typedef int STDataType;
typedef struct Stack
{
	STDataType* _a;
	int _top;		// 栈顶
	int _capacity;  // 容量 
}Stack;

a用来存储元素的一位数组,_top指向栈顶元素的下一位,也是元素的个数。_capacity用来控制一维数组容量

image-20231224002517021

空栈

image-20231224002703461

栈不为空

2.接口以及实现
// 初始化栈 
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 StackD
(1)初始化
void StackInit(Stack* ps) {
	assert(ps);
	ps->_a = NULL;
	ps->_top = 0;
	ps->_capacity = 0;
}

将结构体的属性设置为默认值(NULL或0)

(2)入栈
void StackPush(Stack* ps, STDataType data) {
	assert(ps);
	if (ps->_top == ps->_capacity)
	{
		int newCapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
		STDataType* st = realloc(ps->_a, sizeof(STDataType) * newCapacity);
		if (st == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->_a = st;
		ps->_capacity = newCapacity;
	}
	ps->_a[ps->_top++] = data;
}

在添加栈元素前检查存储栈元素的顺序表的空间是否满足,如果不满足则进行扩容,在返回一个新的顺序表。

(3)出栈
void StackPop(Stack* ps) {
	assert(ps&&ps->_a&&ps->_top);
	ps->_top--;
}

如果栈不为空直接返回栈顶位置-1,下次添加元素的时候会覆盖这个位置。查询元素访问不到这个元素。

(4)获得栈顶元素
STDataType StackTop(Stack* ps) {
	assert(ps&&ps->_top);
	return ps->_a[ps->_top - 1];
}

因为top指针指向的是栈顶元素的下一个,所以要返回top-1位置的元素。

(5)判断是否为空栈
int StackEmpty(Stack* ps) {
	assert(ps);
	return ps->_top == 0;
}

返回元素是否为0

(6)返回栈元素个数
int StackSize(Stack* ps) {
	assert(ps);
	return ps->_top;
}

直接返回size

队列

一.队列的基本概念

1.栈的定义

队列(queue)是只允许在一端进入,另一端删除的线性表。
队列的特点是先进先出,就像日常排队,先来的在前,先排到。

image-20231224013612262

二.队列的设计以及实现

1.结构体的设计
typedef int QDataType;
// 链式结构:表示队列 
typedef struct QListNode
{
	struct QListNode* _next;
	QDataType _data;
}QNode;
typedef struct Queue
{
	QNode* _front;
	QNode* _rear;
	int size;
}Queue;

队列的操作是入队和出队。入队是对尾进行操作,出队是对头进行操作,所以就不采用顺序结构进行存储了,因为顺序表对删除的效率太低。入队是尾插,出队是头删。可以使用链表进行存储,时间复杂度都是O(1)。队列元素的属性有存储的值和下一个节点。队列的属性有队列的头还有尾,还有队列的元素。要出队时将队列的头的指针进行改变,要入队时将队列的尾的指针的next改变,再将尾指针改变。

2接口以及实现
// 初始化队列 
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 
int QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);
(1)初始化
void QueueInit(Queue* q) {
	q->_front = q->_rear = NULL;
	q->size = 0;
}

将队列指针中的所有属性都设为默认值

(2)元素入队操作
void QueuePush(Queue* q, QDataType data) {
	assert(q);
	QNode* p = (QNode*)malloc(sizeof(QNode));
	if (p == NULL)
	{
		perror("malloc failed");
		return;
	}
	p->_data = data;
	p->_next = NULL;
	if (q->size == 0)
	{
		q->_front = q->_rear = p;
	}
	else {
		q->_rear->_next = p;
		q->_rear = p;
	}
	q->size++;
}

先申请一个新节点,将新节点的next置空。如果队列没有元素,那么加入元素后队列的头和尾就会指向同一元素。所以将头和尾都指向新节点。如果队列已经有了元素,那只需要将新节点设置rear的next,并将新节点更新尾rear。

(3)元素出队操作
void QueuePop(Queue* q) {
	assert(q && q->size);
	QNode* p = q->_front;
	q->_front = q->_front->_next;
	q->size--;
	free(p);
}

直接将队列指针的front更新为front的next。出队必须保证队列不为空。

(4)获得队列头部元素
QDataType QueueFront(Queue* q) {
	assert(q && q->size);
	return q->_front->_data;
}

直接返回队列头部指针指向的值。

(5)获得队尾元素
QDataType QueueBack(Queue* q) {
	assert(q && q->size);
	return q->_rear->_data;
}

直接返回队列尾部指针指向的值。

(6)获得队列长度
int QueueSize(Queue* q) {
	assert(q );
	return q->size;
}

队列结构有定义,直接返回。

(7)判断队列是否为空
int QueueEmpty(Queue* q) {
	assert(q);
	return q->size == 0;
}

返回队列长度是否等于0。

栈与队列练习

20. 有效的括号 - 力扣(https://leetcode.cn/problems/valid-parentheses/description/)

使用栈来实现。依次遍历字符串s的每一个字符k。如果k为左括号**“[ ( {“直接入栈,如果k为右括号”] } )”**,不匹配的情况

  1. 栈为空
  2. 括号无法匹配(分类写出三种情况进行逻辑与)

如果匹配的话则需要弹出栈顶元素,继续遍历s。当s遍历完后,返回栈的是否为空。(如果栈不为空,那么就有括号没有匹配,所以为false)。

typedef char STDataType;
typedef struct Stack
{
	STDataType* _a;
	int _top;		// 栈顶
	int _capacity;  // 容量 
}Stack;
void StackInit(Stack* ps) {
	assert(ps);
	ps->_a = NULL;
	ps->_top = 0;
	ps->_capacity = 0;
}
// 入栈 
void StackPush(Stack* ps, STDataType data) {
	assert(ps);
	if (ps->_top == ps->_capacity)
	{
		int newCapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
		Stack* st = realloc(ps->_a, sizeof(STDataType) * newCapacity);
		if (st == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->_a = st;
        ps->_capacity=newCapacity;
	}
	ps->_a[ps->_top++] = data;
}
void StackPop(Stack* ps) {
	assert(ps&&ps->_a&&ps->_top);
	ps->_top--;
}
STDataType StackTop(Stack* ps) {
	assert(ps&&ps->_top);
	return ps->_a[ps->_top - 1];
}
int StackSize(Stack* ps) {
	assert(ps);
	return ps->_top;
}
int StackEmpty(Stack* ps) {
	assert(ps);
	return ps->_top == 0;
}
void StackDestroy(Stack* ps) {
	assert(ps);
	free(ps->_a);
	ps->_capacity = 0;
	ps->_top = 0;
}
bool isValid(char* s) {
    Stack st;
    StackInit(&st);
//    if(sizeof(s)%2)return false; s是指针变量,长度固定为8
while(*s)
{
 if(*s=='('||*s=='['||*s=='{')
 {
     StackPush(&st,*s);
 }   else
 {
     if(StackEmpty(&st)||(*s==']'&&StackTop(&st)!='[')||(*s=='}'&&StackTop(&st)!='{')||(StackTop(&st)!='('&&*s==')')){
         return false;
     }
     StackPop(&st);
 }
 s++;
}
    int ans = StackEmpty(&st);
    StackDestroy(&st);
    return ans;
}

225. 用队列实现栈

队列的特点是先进先出,栈的特点是先进后出。可以使用两个队列。一个负责加入元素,另一个负责弹出元素。这两个队列的身份可以交换。(例如 A负责加入元素时,B就要负责元素的弹出)

  1. 栈的压入 如果队列A为空就向队列B中添加元素,否则就像A中添加元素。
  2. 栈的弹出 找到不为空的队列 将不为空的队列中的所有元素(不包括最后一个)依次出队并入队队列B,弹出队列A中的最后一个元素。
  3. 栈的初始化 将两个队列指针初始化即可。
  4. 返回栈顶 返回不为空队列的队尾元素
  5. 判断是否为空栈 返回两个队列长度逻辑与的结果
typedef int QDataType;
// 链式结构:表示队列 
typedef struct QListNode
{
	struct QListNode* _next;
	QDataType _data;
}QNode;

// 队列的结构 
typedef struct Queue
{
	QNode* _front;
	QNode* _rear;
	int size;
}Queue;
void QueueInit(Queue* q) {
	q->_front = q->_rear = NULL;
	q->size = 0;
}
void QueuePush(Queue* q, QDataType data) {
	assert(q);
	QNode* p = (QNode*)malloc(sizeof(QNode));
	if (p == NULL)
	{
		perror("malloc failed");
		return;
	}
	p->_data = data;
	p->_next = NULL;
	if (q->size == 0)
	{
		q->_front = q->_rear = p;
	}
	else {
		q->_rear->_next = p;
		q->_rear = p;
	}
	q->size++;
}
void QueuePop(Queue* q) {
	assert(q && q->size);
	QNode* p = q->_front;
	q->_front = q->_front->_next;
	q->size--;
	free(p);
}
QDataType QueueFront(Queue* q) {
	assert(q && q->size);
	return q->_front->_data;
}
QDataType QueueBack(Queue* q) {
	assert(q && q->size);
	return q->_rear->_data;
}
int QueueSize(Queue* q) {
	assert(q );
	return q->size;
}
int QueueEmpty(Queue* q) {
	assert(q);
	return q->size == 0;
}
void QueueDestroy(Queue* q) {
	assert(q);
	while (!QueueEmpty(q))
	{
		QueuePop(q);
	}
	q->_rear = q->_front;
}

typedef struct {
        Queue queue1;
         Queue queue2;
} MyStack;


MyStack* myStackCreate() {
    MyStack* m = ( MyStack*)malloc(sizeof(MyStack));
    QueueInit(&m->queue1);
    QueueInit(&m->queue2);
    //   printf("%d\n", &m);
    return m;
}

void myStackPush(MyStack* obj, int x) {
    // printf("%d\n",obj);
    if(QueueEmpty(&obj->queue1))
    {
        QueuePush(&obj->queue2,x);
    }else
    {
         QueuePush(&obj->queue1,x);
    }
}

int myStackPop(MyStack* obj) {
    Queue *queue = &obj->queue1;
    Queue *nonequeue = &obj->queue2;
    if(QueueEmpty(queue))
    {
        queue=&obj->queue2;
        nonequeue=&obj->queue1;
    }
    QNode* rear = queue->_rear;
    while(queue->size>1)
    {
        // printf("%d\n",1);
        QueuePush(nonequeue,QueueFront(queue));
        QueuePop(queue);
    }
    int ans = QueueFront(queue);
QueuePop(queue);
    return ans;
}

int myStackTop(MyStack* obj) {
     Queue *queue = &obj->queue1;
    Queue *nonequeue = &obj->queue2;
    if(QueueEmpty(queue))
    {
        queue=&obj->queue2;
        nonequeue=&obj->queue1;
    }
     return queue->_rear->_data;
}

bool myStackEmpty(MyStack* obj) {
     return QueueEmpty(&obj->queue1) &&  QueueEmpty(&obj->queue2) ;
}

void myStackFree(MyStack* obj) {
    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);
*/

232. 用栈实现队列

队列时按顺序先进先出的。栈是后进先出。用栈实现的时候有两种思路

  1. 两个栈 添加向A添加,出队时将A弹出到B,弹出B的栈顶,在将B弹出到A。单次出队的时间复杂度为O(n)
  2. 依旧两个栈 A负责入队,B负责出队。添加时向A添加,弹出时查看B是否为空,B为空将A的每个元素弹出并压入B,最后弹出B的栈顶,B不为空直接弹出B的栈顶。
typedef int STDataType;
typedef struct Stack
{
	STDataType* _a;
	int _top;		// 栈顶
	int _capacity;  // 容量 
}Stack;
void StackInit(Stack* ps) {
	assert(ps);
	ps->_a = NULL;
	ps->_top = 0;
	ps->_capacity = 0;
}
// 入栈 
void StackPush(Stack* ps, STDataType data) {
	assert(ps);
	if (ps->_top == ps->_capacity)
	{
		int newCapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
		STDataType* st = realloc(ps->_a, sizeof(STDataType) * newCapacity);
		if (st == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->_a = st;
		ps->_capacity = newCapacity;
	}
	ps->_a[ps->_top++] = data;
}
void StackPop(Stack* ps) {
	assert(ps&&ps->_a&&ps->_top);
	ps->_top--;
}
STDataType StackTop(Stack* ps) {
	assert(ps&&ps->_top);
	return ps->_a[ps->_top - 1];
}
int StackSize(Stack* ps) {
	assert(ps);
	return ps->_top;
}
int StackEmpty(Stack* ps) {
	assert(ps);
	return ps->_top == 0;
}
void StackDestroy(Stack* ps) {
	assert(ps);
	free(ps->_a);
	ps->_a = NULL;
	ps->_capacity = 0;
	ps->_top = 0;
}

typedef struct {
    Stack* in;
    Stack* out;
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue *m = (MyQueue*)malloc(sizeof(MyQueue));
    m->in =(Stack*) malloc(sizeof(Stack));
     m->out =(Stack*) malloc(sizeof(Stack));
    StackInit(m->in);
    StackInit(m->out);
    return m;
}
         
void myQueuePush(MyQueue* obj, int x) {
    StackPush(obj->in,x);
}

int myQueuePop(MyQueue* obj) {
    if(StackEmpty(obj->out))
    {
        while(!StackEmpty(obj->in))
        {

            StackPush(obj->out,StackTop(obj->in));
            StackPop(obj->in);
        }
    }
    int ans = StackTop(obj->out);
    StackPop(obj->out);
    return ans;
}

int myQueuePeek(MyQueue* obj) {
     if(StackEmpty(obj->out))
    {
        while(!StackEmpty(obj->in))
        {
            StackPush(obj->out,StackTop(obj->in));
            StackPop(obj->in);
        }
    }
    return StackTop(obj->out);
}

bool myQueueEmpty(MyQueue* obj) {
    return StackEmpty(obj->in) && StackEmpty(obj->out);
}

void myQueueFree(MyQueue* obj) {
StackDestroy(obj->in);
StackDestroy(obj->out);
    free(obj);
}

/**
 * Your MyQueue struct will be instantiated and called as such:
 * MyQueue* obj = myQueueCreate();
 * myQueuePush(obj, x);
 
 * int param_2 = myQueuePop(obj);
 
 * int param_3 = myQueuePeek(obj);
 
 * bool param_4 = myQueueEmpty(obj);
 
 * myQueueFree(obj);
*/
  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值