栈和队列的实现以及相关OJ题目

一、栈

1.栈的概念及结构

栈:是一种特殊的·线性表,其只允许在固定的一端进行插入和删除元素操作
进行数据插入和删除操作的一端成为栈顶,另一端成为栈底。

栈遵循的原则:是先进后出。

在这里插入图片描述

2.栈的动态实现

栈的实现一般可以使用数组或者链表来实现,相对而言数组的结构实现会更优, 因为在数组在尾上插入数据的代价比较小。
注意:栈不能遍历

常用操作的模拟实现:
初始化、入栈、出栈、获取元素的个数、判空、获取栈定元素。

我们实现的是支持动态增长的栈

.h文件

# pragma once

typedef int DataType;

typedef struct stack
{
	DataType* arr;
	int capacity;
	//表示栈中有多少元素——栈顶
	int size;

}stack; 
//初始化栈
void StackInit(stack* ps);
//入栈
void StackPush(stack* ps, DataType data);
//出栈
void StackPop(stack* ps);
//获取栈顶元素
DataType StackTop(stack* ps);
//获取栈中有效个数
int StackSize(stack* ps);
//检测栈是否为空
int StackEmpty(stack* ps);
//销毁栈
void StackDestroy(stack* ps);
int  Capacity(stack* ps);

.c文件

# include <stdio.h>
# include <assert.h>
# include <malloc.h>
# include "ZList.h"

//栈的初始化
void StackInit(stack* ps)
{
	assert(ps);
	ps->arr = (DataType*)malloc(sizeof(DataType)* 3);
	if (NULL == ps->arr)
	{
		assert(0);
		return;
	}
	ps->capacity = 3;
	ps->size = 0;
}
//自动给栈扩容
void  Checkcapacity(stack* ps)
{
	assert(ps);
	if (ps->size == ps->capacity)
	{
		ps->arr = (DataType*)realloc(ps->arr, sizeof(DataType)*ps->capacity * 2);
		if (NULL == ps->arr)
		{
			assert(0);
			return;
		}
		ps->capacity *= 2;
	}
}
int Capacity(stack* ps)
{
	assert(ps);
	return ps->capacity;
}

//入栈
void StackPush(stack* ps, DataType data)
{
	assert(ps);
	Checkcapacity(ps);
	ps->arr[ps->size++] = data;
}

//出栈
void StackPop(stack* ps)
{
	assert(ps);
	if (StackEmpty(ps))
		return;
	ps->size--;
}

//获取栈顶元素
DataType StackTop(stack* ps)
{
	assert(ps&&!StackEmpty(ps));
	return ps->arr[ps->size - 1];

}
//获取栈中有效元素
int StackSize(stack* ps)
{
	assert(ps);
	return ps->size;
}
//检测链表是否为空
int StackEmpty(stack* ps)
{
	assert(ps);
	return 0 == ps->size;
}
//销毁栈
void StackDestroy(stack* ps)
{
	assert(ps);
	if (ps->arr)
	{
		free(ps->arr);
		ps->arr = NULL;
		ps->capacity = 0;
		ps->size = 0;
	}
}
void TestStack()
{
	stack s;
	StackInit(&s);

	StackPush(&s, 1);//入栈
	StackPush(&s, 2);
	StackPush(&s, 3);
	StackPush(&s, 4);
	StackPush(&s, 5);
	StackPush(&s, 6);
	StackPush(&s, 7);
	printf("capacity = %d\n", Capacity(&s));
	printf("size = %d\n", StackSize(&s));
	printf("top = %d\n", StackTop(&s));

	StackPop(&s);//出栈
	StackPop(&s);
	StackPop(&s);
	StackPop(&s);
	StackPop(&s);
	printf("capacity = %d\n", Capacity(&s));
	printf("size = %d\n", StackSize(&s));
	printf("top = %d\n", StackTop(&s));

	StackDestroy(&s);
}

int main()
{
	TestStack();
}

3.有关于栈的OJ题目

3.1括号匹配问题

题目描述:
在这里插入图片描述

解题思路:

 1.依次获取每个括号
2.检测该括号是左括号还是右括号
			是左括号则入栈
			是右括号----如果是有效匹配则之前一定有和之匹配的左括号
3.检测该括号和栈顶的括号是否匹配
)和(     }和{         ]和[  
匹配的话:栈顶的左括号出栈
否则:括号不匹配

程序部分:

       核心代码
bool isValid(char * s)
{   bool flag=false;
    stack st;
    StackInit(&st);
//"(" ")"
    for(int i=0;i<strlen(s);++i)
    {   //如果是左括号,
        if('('==s[i]||'['==s[i]||'{'==s[i])
        {	//入栈
            StackPush(&st,s[i]);
        }
        else
        {   //如果拿到右括号
        //但是栈刚好是空的
            if(StackEmpty(&st))
            {
                StackDestroy(&st);
                    return false;
            }
        	//检测该括号和栈顶的括号是否匹配
            char top=StackTop(&st);
            if(s[i]==')'&&top=='('||
             s[i]==']'&&top=='['||
             s[i]=='}'&&top=='{')
             {
                 StackPop(&st);
             }
             else
             {
                break;
             }
        }
    }

    if(StackEmpty(&st))
    {
        flag=true;
    }
    StackDestroy(&st);
    return flag;

}

二、队列

1.队列的概念以及结构

队列:只允许在一端进行插入数据的操作,在另一端进行数据删除的特殊线性表。进行插入一端称为队尾;进行删除的一段成为队头。
遵循原则:先进先出
在这里插入图片描述

2.队列的实现

队列也可以用数组和链表结构实现,不过在使用链表的结构实现更好,因为,使用数组的结构,出队列的效率会比较低。

在这里插入图片描述

入队列的操作时间复杂度尾 O(N),因为需要将元素整体搬移。
所以这里采用带头单向链表实现队列:

核心代码

.h文件

#pragma once


// 此处采用带头单链表来实现队列

typedef int DataType;
//节点的结构
typedef struct QNode
{
	struct QNode* next;
	DataType data;
}QNode;
//队列的结构
typedef struct Queue
{
	QNode* front;
	QNode* rear;
}Queue;

//队列的初始化
void QueueInit(Queue* q);
// 入队列
void QueuePush(Queue* q, DataType data);
// 出队列
void QueuePop(Queue* q);
// 获取队头元素
DataType QueueFront(Queue* q);
// 获取队尾元素
DataType QueueBack(Queue* q);
// 获取队列中有效元素的个数
int QueueSize(Queue* q);
// 检测队列是否为空
int QueueEmpty(Queue* q);
// 将队列销毁掉
void QueueDestroy(Queue* q);


void TestQueue();

.c文件

#include "DLList.h"
#include <malloc.h>
#include <assert.h>
#include <stdio.h>


QNode* BuyQueueNode(DataType data)
{
	QNode* node = (QNode*)malloc(sizeof(QNode));
	if (NULL == node)
	{
		assert(0);
		return NULL;
	}

	node->data = data;
	node->next = NULL;

	return node;
}

void QueueInit(Queue* q)
{
	assert(q);

	q->front = q->rear = BuyQueueNode(0);
}

// 入队列
void QueuePush(Queue* q, DataType data)
{	//从尾部插入
	assert(q);
	q->rear->next = BuyQueueNode(data);
	q->rear = q->rear->next;
}

// 出队列
void QueuePop(Queue* q)
{
	QNode* delNode = NULL;
	if (QueueEmpty(q))
		return;

	delNode = q->front->next;
	q->front->next = delNode->next;

	// 队列中只有1个元素,将该元素删除之后,需要让rear指向头节点的位置
	if (NULL == delNode->next)
		q->rear = q->front;

	free(delNode);
}

// 获取队头元素
DataType QueueFront(Queue* q)
{
	assert(!QueueEmpty(q));
	//q->front为头节点->next为第一个节点->data 值域的值
	return q->front->next->data;
}

// 获取队尾元素
DataType QueueBack(Queue* q)
{
	assert(!QueueEmpty(q));
	return q->rear->data;
}

// 获取队列中有效元素的个数
int QueueSize(Queue* q)
{
	assert(q);
	int count = 0;
	QNode* cur = q->front->next;
	while (cur)
	{
		++count;
		cur = cur->next;
	}

	return count;
}

// 检测队列是否为空
int QueueEmpty(Queue* q)
{
	assert(q);
	return NULL == q->front->next;
}

// 将队列销毁掉
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->front;
	while (cur)
	{
		q->front = cur->next;
		free(cur);
		cur = q->front;
	}

	q->front = q->rear = NULL;
}


void TestQueue()
{
	Queue q;
	QueueInit(&q);//初始化

	QueuePush(&q, 1);//入队列
	printf("size = %d\n", QueueSize(&q));//有效元素个数
	printf("front = %d\n", QueueFront(&q));//对头元素
	printf("back = %d\n", QueueBack(&q));//队尾元素

	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);
	printf("size = %d\n", QueueSize(&q));
	printf("front = %d\n", QueueFront(&q));
	printf("back = %d\n", QueueBack(&q));

	QueuePop(&q);//出队列
	printf("size = %d\n", QueueSize(&q));
	printf("front = %d\n", QueueFront(&q));
	printf("back = %d\n", QueueBack(&q));


	QueuePop(&q);
	QueuePop(&q);
	QueuePop(&q);
	printf("size = %d\n", QueueSize(&q));
	printf("front = %d\n", QueueFront(&q));
	printf("back = %d\n", QueueBack(&q));

	QueuePop(&q);
	printf("size = %d\n", QueueSize(&q));
	if (QueueEmpty(&q))
	{
		printf("EMPTY!!!\n");
	}
	else
	{
		printf("Error!!!\n");
	}

	QueueDestroy(&q);
}
int main()
{
	TestQueue();
}

3.队列的OJ题目

3.1用队列实现栈

题目描述:
在这里插入图片描述
解题思路:

     该题的思路我用图文加文字的形式:

在这里插入图片描述

程序部分:

 核心代码
typedef struct {
    Queue q1;
    Queue q2;

} MyStack;

/** Initialize your data structure here. */

MyStack* myStackCreate() {
    //这个位置需要用malloc从堆上开辟空间
    MyStack* ms=(MyStack*)malloc(sizeof(MyStack));
    if(NULL==ms)
    {
        return NULL;
    }
    QueueInit(&ms->q1);
    QueueInit(&ms->q2);
    return ms;

}
//入栈
/** Push element x onto stack. */
void myStackPush(MyStack* obj, int x) {
    //插入非空的队列
    if(!QueueEmpty(&obj->q1))
    {
        QueuePush(&obj->q1,x);
    }
    else
    {
        QueuePush(&obj->q2,x);
    }
}
//出栈
/** Removes the element on top of the stack and returns that element. */
int myStackPop(MyStack* obj) {
    int ret=0;
    if(!QueueEmpty(&obj->q1))
    {
        //需要将q1中前n-1个元素导入到q2中
        while(QueueSize(&obj->q1)>1)
        {
           QueuePush(&obj->q2, QueueFront(&obj->q1));
           QueuePop(&obj->q1);
        }
        ret=QueueFront(&obj->q1);
        QueuePop(&obj->q1);
    }
    else
    {
       //需要将q2中前n-1个元素导入到q2中
        while(QueueSize(&obj->q2)>1)
        {
           QueuePush(&obj->q1, QueueFront(&obj->q2));
           QueuePop(&obj->q2);
        }
        //将最后一个元素保存起来
        ret=QueueFront(&obj->q2);
        //将最后一个数据删除掉
        QueuePop(&obj->q2);
    }
    return ret;


}
//取栈顶
/** Get the top element. */
int myStackTop(MyStack* obj) {
    //哪个不空取队尾元素
    if(!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else{
        return QueueBack(&obj->q2);
    }

}

/** Returns whether the stack is empty. */
bool myStackEmpty(MyStack* obj) {
    return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);

}

void myStackFree(MyStack* obj) {
        QueueDestroy(&obj->q1);
        QueueDestroy(&obj->q2);
        free(obj);
}

3.2用栈实现队列

题目描述:
在这里插入图片描述
解题思路:

在这里以图文加解释的方式给出

在这里插入图片描述

程序部分:

  核心代码
typedef struct {
    stack s1;//模拟队尾 ,入队列
    stack s2;//模拟队头,出队列
} MyQueue;

/** Initialize your data structure here. */

MyQueue* myQueueCreate() {
    MyQueue* mq=(MyQueue*)malloc(sizeof(MyQueue));
    if(NULL==mq)
    {
        return NULL;
    }
    StackInit(&mq->s1);
    StackInit(&mq->s2);
    return mq;

}
//入栈q1,模拟入队列
/** Push element x to the back of queue. */
void myQueuePush(MyQueue* obj, int x) {
    StackPush(&obj->s1,x);


}

/** Removes the element from in front of queue and returns that element. */
int myQueuePop(MyQueue* obj) {
    //s2模拟出队列
    //如果s2是空的,将s1中的元素搬移到s2中
    if(StackEmpty(&obj->s2))
    {
        while(!StackEmpty(&obj->s1))
        {
        StackPush(&obj->s2,StackTop(&obj->s1));
        StackPop(&obj->s1);
        }
    }
    int ret=StackTop(&obj->s2);
    StackPop(&obj->s2);
    return ret;
}
//获取队头元素
/** Get the front element. */
int myQueuePeek(MyQueue* obj) {

if(StackEmpty(&obj->s2))
    {
        while(!StackEmpty(&obj->s1))
        {
        StackPush(&obj->s2,StackTop(&obj->s1));
        StackPop(&obj->s1);
        }
    }
    return StackTop(&obj->s2);
}

/** Returns whether the queue is empty. */
bool myQueueEmpty(MyQueue* obj) {
    return StackEmpty(&obj->s1)&&StackEmpty(&obj->s2);
}

void myQueueFree(MyQueue* obj) {
    StackDestroy(&obj->s1);
    StackDestroy(&obj->s2);
    free(obj);
}

3.3循环队列

题目描述:
在这里插入图片描述

解题思路:

  循环队列是为了解决队列假溢出的情况,使开辟的空间不被浪费。

在这里插入图片描述

判断循环队列是空还是满?
1.少存储一个数据

在这里插入图片描述
2.使用计数 count
count= =0,是为空
count= =NULL,则为满。

循环队列主要的实现:入队和出队。
入队中的实现:先判断队列是否满了,如果不满则有两种情况:
如果队尾刚好在空间的末尾则将队尾的元素放在0号下标的位置。
如果正常入队,则是将元素尾插进去,然后将尾的位置rear向后移动。
出队的实现:先判断队列是否为空,如果不为空将头的位置front向后移动,
如果front移到了最后一个位置则将front放在0的下标号。
front每移动一个,count就减少一个。
获取队尾的位置:如果rear在0号标号,则返回最后一个元素,
否则,返回rear-1;
剩余步骤的实现则较为常规。

程序部分:

 核心代码
typedef struct {
    int* arr;
    int capacity;
    int front;
    int back;
    int count;

} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {

MyCircularQueue* mq=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));//在堆上创建了结构体变量
if(NULL==mq)
{
    return NULL;
}
mq->arr=(int*)malloc(sizeof(int)*k);
if(NULL==mq->arr)
{
    return NULL;
}
mq->capacity=k;
mq->front=mq->back=mq->count=0;
return mq;
}
//判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->count==0;

}
//队列满了
bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return obj->count==obj->capacity;

}

//入队
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
 {
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    //rear可能刚好在队列的末尾位置
    if(obj->back == obj->capacity)
    {
        obj->back=0;
    }
    obj->arr[obj->back++]= value;
    obj-> count++;
    return true;
}
//出队
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    obj->front++;
    if(obj->front==obj->capacity)
    {
        obj->front=0;
    }
    obj->count--;
    return true;


}
//获取队列首元素
int myCircularQueueFront(MyCircularQueue* obj){
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->arr[obj->front];

}
//队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {
    //如果rear在下标号为0的位置
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    //if(0==obj->back)
   // {
    //    return obj->arr[obj->capacity-1];
   // }
   // else
    //{
    //return obj->arr[obj->back-1];
    //}
    return obj->arr[(obj->back-1+obj->capacity)%obj->capacity];
}


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


}

总结

将自己所学的知识进行总结,归纳,需要改进的地方还有许多,继续加油吧!!!

  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 20
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值