数据结构C语言篇《三》栈和队列概念,模拟函数实现,以及相关OJ面试题

Posted 程序猿是小贺

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构C语言篇《三》栈和队列概念,模拟函数实现,以及相关OJ面试题相关的知识,希望对你有一定的参考价值。

1. 栈

镇楼

栈,存储货物或供旅客住宿的地方,可引申为仓库、中转站,所以引入到计算机领域里,就是指数据暂时存储的地方,所以才有进栈、出栈的说法。可以类比相当于吃饭,吃进去吐出来就是栈(忍着胃部强烈不适码了这句)

1.1栈的概念

栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
栈的特性后进先出

注意:栈不能进行遍历操作

1.2栈的实现方法

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

1.3栈的模拟实现----动态内存

Stack.h

#pragma once

typedef int DataType;

typedef struct Stcak
{
	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);

void TestStack();

Stack.c

#include"Stack.h"
#include<stdio.h>
#include<assert.h>
#include<malloc.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)
{
	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;
	}
}


//入栈
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));
	//此处不能使用if条件判断,因为if条件判断的为合法情况
	//若是栈为空此时栈没有元素,要获取栈顶元素 则为非法情况
	//可以用assert进行判断
	//if (StackEmpty(ps))
	//	return;
	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 con;
	StackInit(&con);

	StackPush(&con, 1);
	StackPush(&con, 2);
	StackPush(&con, 3);
	StackPush(&con, 4);
	StackPush(&con, 5);
	StackPush(&con, 6);
	printf("size = %d\\n", StackSize(&con));
	printf("top = %d\\n", StackTop(&con));

	StackPop(&con);
	StackPop(&con);
	StackPop(&con);
	printf("size = %d\\n", StackSize(&con));
	printf("top = %d\\n", StackTop(&con));

	StackDestroy(&con);
}

test.c

#define _CRT_SECURE_NO_WARNINGS

#include"Stack.h"

int main()
{
	TestStack();
	return 0;
}

1.4关于栈的OJ题

1.括号匹配问题 OJ

typedef char DataType;

typedef struct Stcak
{
	DataType *arr;
	int capacity;	//容量大小
	int size;	//有效元素个数---栈顶
}Stack; 


//栈的初始化
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)
{
	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;
	}
}


//入栈
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);
	assert(ps && !StackEmpty(ps));
	//此处不能使用if条件判断,因为if条件判断的为合法情况
	//若是栈为空此时栈没有元素,要获取栈顶元素 则为非法情况
	//可以用assert进行判断
    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;
	}
}

bool isValid(char * s)
{
    bool flag=false;
    Stack ms;
    StackInit(&ms);
    int n=strlen(s);
    if(n%2==0)
    {
        for(int i=0;i<n;i++)//遍历
        {
            //如果检测该括号为左括号,则入栈
            if(s[i]=='('||s[i]=='['||s[i]=='{')
            {
                StackPush(&ms,s[i]);
            }
            
            else
            {
                //当前取的括号为右括号且此时栈刚好为空
                if(StackEmpty(&ms))
                {
                    StackDestroy(&ms);
                    return false;
                }
                char top=StackTop(&ms);
                if((top=='('&&s[i]==')')||
                   (top=='['&&s[i]==']')||
                   (top=='{'&&s[i]=='}'))
                {
                   StackPop(&ms);
                }
                else
                {
                    break;
                }
            }
        }
        if(StackEmpty(&ms))
        {
            flag=true;
        }
        StackDestroy(&ms);
        return flag;
    }
    else
    {
        StackDestroy(&ms);
        return false;
    }
}

1.5逆波兰表达式

1.5.1概念

逆波兰表达式又叫做后缀表达式,逆波兰表示法是波兰逻辑学家J・卢卡西维兹(J・ Lukasiewicz)于1929年首先提出的一种表达式的表示方法 [1] 。后来,人们就把用这种表示法写出的表达式称作“逆波兰表达式”。逆波兰表达式把运算量写在前面,把算符写在后面。

1.5.2栈实现逆波兰表达式

它的优势在于只用两种简单操作,入栈和出栈就可以搞定任何普通表达式的运算。其运算方式如下:
如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。

2.队列

相当于吃进去拉出来

2.1队列的概念

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,进行插入操作的一端称为队尾 出队列,进行删除操作的一端称为队头。
队列的特性先进先出

2.2队列的实现方法

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。
在这里插入图片描述

2.3队列的模拟实现----链式

Queue.h

#pragma once

typedef int QDataType;
typedef struct QListNode
{
	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 
int QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);

Queue.c

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

QNode* BuyQueueNode(QDataType 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, QDataType 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;

	//如果队列中此时只有一个元素,将该元素删除后还需将rear放在front的位置
	if (delNode->next == NULL)
		q->rear = q->front;
	free(delNode);
}

// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
	assert(!QueueEmpty(q));
	return q->front->next->data;
}

// 获取队列队尾元素
QDataType 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)
	{
		cur = cur->next;
		count++;
	}
	return count;
}

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
	assert(q);
	return q->front->next == NULL;
}

// 销毁队列
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);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);
	QueuePush(&q, 6);
	printf("size = %d\\n", QueueSize(&q));
	printf("front = %d\\n", QueueFront(&q));
	printf("rear = %d\\n", QueueBack(&q));

	QueuePop(&q); 
	QueuePop(&q);
	QueuePop(c 语言数据结构栈和队列的相关操作

全栈模拟入队的实现原理有一定难度详细解析

栈和队列C语言实现附加力扣题

数据结构10:栈和队列

栈和队列 3.1 栈

数据结构(C语言版) 栈和队列 算法设计Demo13