栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

栈和队列(C语言实现)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

栈和队列(C语言实现)

目录

1.栈

1.1栈的概念及结构

1.2栈的实现:

2.队列

2.1队列的概念及结构

2.2队列的实现

3.经典OJ题

3.1有效括号

3.2 用队列实现栈   

3.3 用栈实现队列

3.4设计循环队列


1.栈

1.1栈的概念及结构

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

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

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

栈实现的两种方式: 两种都可以,非要选一种数组栈结构稍微好一点。

数组栈:尾插尾删效率高,缓存率还高。

链式栈:如果使用尾做栈顶,尾插尾删,要设计成双向链表,否则删除数据效率低。如果用头做栈顶,头插头删,就可以设计成单链表。

1.2栈的实现:

1.声明栈的所有函数Stack.h:

#include 
#include 
#include 

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);//获取栈的有效元素个数
int StackEmpty(Stack* ps);//检测栈是否为空。空为1,不空为0
void StackDestory(Stack* ps);//栈销毁

2.栈的初始化:

void StackInit(Stack* ps)
{
    assert(ps);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

初始化理解图: 

ps是一个结构体指针变量,存的是ST变量的地址,ST是一个结构体变量,含有一个指针和两个整数。

3.栈入数据:

void StackPush(Stack* ps, STDataType data)
{
    assert(ps);

    //当capacity==top时,就会扩容
	if (ps->capacity == ps->top)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		int* temp = (int* )realloc(ps->a,sizeof(Stack)* newcapacity);
		if (temp == NULL)
		{
			//开辟失败
			printf("动态申请失败n");
			exit(-1);
		}
		ps->a = temp;
		ps->capacity = newcapacity;
	}

    //数据入栈
	ps->a[ps->top] = data;
	ps->top++;

}

注意:1.realloc 在pa->a(第一个参数)为空时,可以当作malloc来使用。

           2.当capacity==0时,会先开辟4个空间的大小,capacity不为0时,扩大原来空间的2倍,即8个空间

           3.入数据就是在数组的top位置上插入数据。

4.取栈顶数据与出栈是一起使用的,才符合栈的性质。也就是出栈。

1.取栈顶数据

STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->a[ps->top - 1];
}

2.出栈

出栈不需要将数据复位为0,下次再插入数据时,就直接覆盖了原来的数据。

void StackPop(Stack* ps)
{
    assert(ps);
    assert(!StackEmpty(ps));
	ps->top--;
}

5.获取栈的有效元素个数 

top保存是最后一个有效数据的后面的数字。

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

6.检测栈是否为空

为空返回1,不为空返回0

int StackEmpty(Stack* ps)
{
    assert(ps);
	return ps->top == 0;
}

7.栈的销毁

void StackDestory(Stack* ps)
{
    assert(ps);
	free(ps->a);
	ps->capacity = ps->top = 0;
}

8.测试

#include "Stack.h"

int main()
{

	Stack ST;
	StackInit(&ST);
	StackPush(&ST,1);
	StackPush(&ST,2);
	StackPush(&ST,3);
	StackPush(&ST,4);
	StackPush(&ST, 5);
	while (!StackEmpty(&ST))
	{
		printf("%d ", StackTop(&ST));
		StackPop(&ST);
	}
	
	StackEmpty(&ST);

	return 0;
}

结果:符合栈的先进后出原则。

入栈从栈顶入,过程: 

 出栈从栈顶出, 过程:

2.队列

2.1队列的概念及结构

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

2.2队列的实现

队列使用链表更加适合

1.队列的头文件Queue.h

#include
#include
#include 

typedef int QDataType;
typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QNode;


typedef struct Queue
{
	QNode* front;
	QNode* tail;
}Queue;

void QueueInit(Queue* q);//队列初始化
void QueuePush(Queue* q, QDataType x);//入队列
void QueuePrint(Queue* q);//打印队列
void QueuePop(Queue* q);//出队列
QDataType QueueFront(Queue* q);//获取队列头数据
QDataType QueueBack(Queue* q);//获取队尾元素
void QueueDestory(Queue* q);//销毁队列
int QueueEmpty(Queue* q);//队列是否为空
int QueueSize(Queue* q);//队列元素个数

2.队列的初始化

void QueueInit(Queue* q)
{
	assert(q);
	q->front =q->tail = NULL;
}

3.入队列

void QueuePush(Queue* q, QDataType x)
{
	assert(q);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("动态申请失败n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;

	if (q->front == NULL)
	{
		q->tail=q->front = newnode;
	}
	else
	{
		q->tail->next = newnode;
		q->tail = newnode;
	}

}

4.出队列

void QueuePop(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	QNode* next = q->front->next;
	free(q->front);
	q->front = next;
	if (q->front == NULL)
	{
		q->tail = NULL;//防止野指针。
	}
}

5.获取队列头数据

QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->front->data;
}

6.获取队尾数据

QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->tail->data;
}

7.获取有效数据个数

int QueueSize(Queue* q)
{
	assert(q);
	QNode* temp = q->front;
	int count = 0;
	while (temp!=NULL)
	{
		count++;
		temp = temp->next;
	}
	return count;
}

8.检测队列是否为空

int QueueEmpty(Queue* q)
{
	assert(q);
	return q->front == NULL;
}

9. 队列销毁

void QueueDestory(Queue* q)
{
	assert(q);
	QNode* temp = q->front;
	while (temp != NULL)
	{
		QNode* next = temp->next;
		free(temp);
		temp = next;
	}
	q->front = q->tail = NULL;
}

代码测试:

#include "Queue.h"


int main()
{
	Queue queue;
	QueueInit(&queue);
	QueuePush(&queue,1);
	QueuePush(&queue,2);
	QueuePush(&queue,3);
	QueuePush(&queue,4);
	QueuePush(&queue,5);
	//获取队头数据
	printf("队头数据为:%dn", QueueFront(&queue));
	QueuePop(&queue);//队列出一次数据

	QueuePush(&queue, 6);

	QueuePrint(&queue);
	return 0;
}

结果:

过程:

1.入数据就是在队尾插入数据:

2.获取队头数据并出队列

 3.插入6数据入队列

3.经典OJ题

3.1有效括号

力扣https://leetcode-cn.com/problems/valid-parentheses/本题思路:把栈的程序复制到上边,当字符是左括号,就入栈,当是右括号时,就出栈。当匹配时,就返回true,不匹配时,就返回false。

OJ代码实现: 

bool isValid(char * s){
    Stack st;
    StackInit(&st);

    while(*s!='')
    {
        if(*s=='('||*s=='['||*s=='{')
        {
           StackPush(&st,*s);
           
        }
        else 
        {
            //防止只有一个右括号
          if(StackEmpty(&st))
            {
                StackDestory(&st);
                return false;
            }
          STDataType temp=StackTop(&st);
          StackPop(&st);
          if((temp=='(' && *s!=')' )|| (temp=='['&& *s!=']')|| (temp=='{' && *s!='}'))
          {
              StackDestory(&st);
              return false;
          }

        }
        s++;
    }
//若栈不为空,则就不匹配
    if(!StackEmpty(&st))
    {
        StackDestory(&st);
        return false;
    }


    StackDestory(&st);
    return true;

3.2 用队列实现栈   

力扣https://leetcode-cn.com/problems/implement-stack-using-queues/submissions/主要思想是:利用两个队列来实现栈的先进后出,比如我在队列1里入了五个数据1,2,3,4,5此时我想实现栈,也就是需要先取数据5,此时需要将1,2,3,4入到队列2中去,使得队列1只剩下5,然后取队列1的头数据,并出队列,此时便完成了栈的后进先出,下一次就是反过来也就是队列2的数据入队列1,只剩下最后一个数据,直到两个队列都为空。若是入数据就向非空的队列中入数据,也就是入队列。

初始化后的结构:

 入数据后的结构:

OJ代码实现: 

typedef struct {
    Queue q1;
    Queue q2;
} MyStack;


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

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(nonempty))
    {
        Queue* temp=empty;
        empty=nonempty;
        nonempty=temp;
    }
    while(QueueSize(nonempty)>1)
    {
     QueuePush(empty,QueueFront(nonempty));
     QueuePop(nonempty);
    }
    int temp=QueueFront(nonempty);
    QueuePop(nonempty);
    return temp;
}

int myStackTop(MyStack* obj) {

    Queue* empty=&obj->q1;
    Queue* nonempty=&obj->q2;
    if(QueueEmpty(nonempty))
    {
        Queue* temp=empty;
        empty=nonempty;
        nonempty=temp;
    }
   return  QueueBack(nonempty);

}

bool myStackEmpty(MyStack* obj) {
 
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

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

3.3 用栈实现队列

力扣https://leetcode-cn.com/problems/implement-queue-using-stacks/

 主要思想:利用两个栈,完成队列的先进先出的性质。

代码实现: 

typedef struct {
    Stack st1;//存数据的栈
    Stack st2;//出数据的栈

} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue* mq=(MyQueue*)malloc(sizeof(MyQueue));
    StackInit(&mq->st1);
    StackInit(&mq->st2);
    return mq;
}

void myQueuePush(MyQueue* obj, int x) {
    StackPush(&obj->st1,x);//入数据就向栈1入数据
}

int myQueuePop(MyQueue* obj) {
     if(StackEmpty(&obj->st2))//只有栈2 为空时才能将栈1的数据入栈2
    {
        while(!StackEmpty(&obj->st1))
        {
            StackPush(&obj->st2,StackTop(&obj->st1)); //栈2为空将栈1的所有数据依次出栈
            StackPop(&obj->st1);
        }
    }
        int top=StackTop(&obj->st2);
        StackPop(&obj->st2);
        return top;
}

int myQueuePeek(MyQueue* obj) {
      if(StackEmpty(&obj->st2))//只有栈2 为空时才能将栈1的数据入栈2
    {
        while(!StackEmpty(&obj->st1))
        {
            StackPush(&obj->st2,StackTop(&obj->st1)); //栈2为空将栈1的所有数据依次出栈
            StackPop(&obj->st1);
        }
    }
     int top=StackTop(&obj->st2);
     return top;

}

bool myQueueEmpty(MyQueue* obj) {
    return StackEmpty(&obj->st1) && StackEmpty(&obj->st2);
}

void myQueueFree(MyQueue* obj) {
    StackDestory(&obj->st1);
    StackDestory(&obj->st2);
    free(obj);
}

3.4设计循环队列

力扣https://leetcode-cn.com/problems/design-circular-queue/

重点:循环队列,无论使用数组实现还是链表实现,都要多开一个空间,也就意味着,要是一个存k个数据的循环队列,要开k+1个空间否则无法实现判空和判满。链表和数组都可以实现。

多开一个空间的原因:

1.无法判满还是判空:

2.解决判空和判满 

本代码使用数组实现循环队列:

typedef struct {
    int *a;
    int front;
    int tail;
    int k;

} MyCircularQueue;

bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);

MyCircularQueue* myCircularQueueCreate(int k) {
     MyCircularQueue*cq=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
     cq->a=(int* )malloc(sizeof(int)*(k+1));
     cq->front=0;
     cq->tail=0;
     cq->k=k;
     return cq;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    obj->a[obj->tail++]=value;

    (obj->tail)%=(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->tail+obj->k)%(obj->k+1)];
          
}

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

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return  (obj->tail+1)%(obj->k+1) ==obj->front;
}

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/690086.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号