C语言实现栈与队列

一.栈的定义

  1.栈:是只允许在一边进行插入或删除的顺序表。■特别之处在于只能在一端进行删除、插入。

  2.栈顶:进行删除或插入操作的一端。

  3.栈底:不能进行删除或插入的一端。

二.栈的算法

 
栈的实现可以用数组(顺序表),也可以使用链表,这里使用顺序表来实现。栈的常见操作有栈的初始化、销毁、增删等等,以下是栈创建过程中使用的函数。

  1.宏定义(初始准备)

  不直接定义一个数组,而采用realloc、malloc来动态管理顺序表大小,使栈的大小不再受限。再定义一个top栈顶指针来方便后续的增删操作。

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

typedef int MyDataType;//将int类型重定义叫做MyDataType
typedef struct Stack
{
	MyDataType* a;//一个MyDataType类型的指针,可以进行动态内存开辟
	int capacity;//栈的最大容量
	int top;//用于栈顶指针
}ST;

2.栈的初始化

  在初始化中,将top栈顶指针指向0,但此时栈中没有元素(此时栈为空)。当然也可以将top初始化为-1,此时当top为0时栈中含有一个元素。

ST st;//在测试代码中预先定义一个ST类型的变量
void  InitStack(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;//将栈顶指针指向0
}

3.向栈顶插入元素

   在插入数据前,需要判断ps指针是否为空,还要判断栈是否已经达到最大容量,如果size=capacity(栈已达到最大容量)就需要使用realloc扩容(注意:使用realloc函数时,若第一个参数为空,则函数返回一个开辟了指定大小的地址)。当top初始化为0时,先赋值,在++top;而当top初始化为-1时,先++top,再赋值。

void  PushStack(ST* ps, MyDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		int NewSize = ps->top == 0 ? 4 : ps->capacity * 2;//用三目运算符可以避免对top是否为零的两种情况进行讨论
		MyDataType* temp = (MyDataType*)realloc(ps->a, sizeof(MyDataType) * NewSize);
		if (temp == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		ps->a = temp;
		ps->capacity = NewSize;
	}
	ps->a[ps->top] = x;
	ps->top++;//先赋值再将top加一
}

4.判断栈是否为空

bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;//ps->top为0,表达式为真返回true
}

 5.删除栈顶元素

  删除栈顶元素之前首先需要判断栈是否为空,如果为空则不能删除元素。顺序表删除尾部元素十分简单,只需要将栈顶指针指向的位置减一即可。

void  PopStack(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
		--ps->top;
}

6.计算栈中元素个数

  由于对top初始化时的值不同,计数的条件不同,所以读取个数的操作也由我们来书写,而不由使用者自己来判断。

int StackSize(ST* ps)
{
	assert(ps);
	assert(ps->top >= 0);
	return ps->top ;
}

7.读取栈顶元素

MyDataType StackTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);//需要判断栈是否为空,为空则不能读取栈顶元素
	return ps->a[ps->top-1];
}

8.销毁栈

  销毁需要把动态开辟的空间全部释放,并将指针置为空。

void DestroyStack(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

三.栈的例题

题目链接:

有效的括号

题目解答:

栈的特点是后入先出,与括号之间匹配判断一致

typedef char MyDataType;
typedef struct Stack
{
	MyDataType* a;
	int capacity;
	int top;
}ST;

void  InitStack(ST*ps);
void  PushStack(ST* ps,MyDataType x);
void  PopStack(ST* ps);
void DestroyStack(ST* ps);
bool StackEmpty(ST* ps);
MyDataType StackTop(ST* ps);
int StackSize(ST* ps);
void  InitStack(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}
void  PushStack(ST* ps, MyDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		int NewSize = ps->top == 0 ? 4 : ps->capacity * 2;
		MyDataType* temp = (MyDataType*)realloc(ps->a, sizeof(MyDataType) * NewSize);
		if (temp == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		ps->a = temp;
		ps->capacity = NewSize;
	}
	ps->a[ps->top] = x;
	ps->top++;
}
void  PopStack(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
		--ps->top;
}
void DestroyStack(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}
bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}
MyDataType StackTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	return ps->a[ps->top-1];
}
int StackSize(ST* ps)
{
	assert(ps);
	assert(ps->top >= 0);
	return ps->top ;
}//手动搭建一个栈
bool isValid(char* s) 
{
    ST st;
    InitStack(&st);  
    while(*s)
    {
        if(*s=='('||*s=='['||*s=='{')//当为左括号时入栈
        {
            PushStack(&st,*s);
        }
        else//当为右括号的时候
        {
            if(StackEmpty(&st))
            {
                DestroyStack(&st);
                return false;
            }//如果栈为空时就读取到右括号,
             //即说明还未有左括号就有右括号,显然不符合规律
            int stacktop=StackTop(&st);
            PopStack(&st);
                if(*s==')'&&stacktop!='('||
               *s==']'&&stacktop!='['||
               *s=='}'&&stacktop!='{' )//出栈并一一判断
               {
                   DestroyStack(&st);
                   return false;
               }
        }
        s++;
    }  
    bool ret=StackEmpty(&st);
    return ret;
}

队列

一.队列的定义

  队列是只允许在一端插入元素,另一端删除元素的线性表,特点是先进先出。

二.队列的算法

      队列是从队尾进,队头出,所以采用链表的结构来实现,如果采用数组结构,则会使队头操作繁琐。与栈类似,我们仍然是实现以下函数代码。

 1.宏定义(初始准备)

  Queue这个结构体中还包含两个QNode结点结构体。包含队列头尾结点,能够帮助从队尾插入和从队头删除。

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

typedef int MyDataType;//将int类型重命名为MyDataType
typedef struct QueueNode
{
	MyDataType val;
	struct QueueNode* next;
}QNode;//单链表的结点结构

typedef struct Queue//队列中包含队头、队尾、队列大小
{
	QNode* front;
	QNode* back;
	int size;
}QL;

2.队列的初始化

void QueueInit(QL* pq)
{
	assert(pq);//判断pq是否为空指针
	pq->size = 0;
	pq->front = pq->back = NULL;
}

3.向队尾插入元素

void QueuePush(QL* pq, MyDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->val = x;
	newnode->next = NULL;
	if (pq->back == NULL)
		pq->front = pq->back = newnode;//链表为空,则让front和back都指向newnode
	else
	{
		pq->back->next = newnode;
		pq->back = newnode;
	}
	pq->size++;
}//与单链表搭建类似,由于不含头结点,
//所以需要分链表是否为空的两种情况讨论。

4.删除队头元素

void QueuePop(QL* pq)//与单链表不同,这里不需要传二级指针,
                     //因为这里只是修改结构体中的内容,而不是改变结构体地址
{
	assert(pq);
	assert(!QueueEmpty(pq));//断言链表不为空,为空则不能删除数据
	if (pq->front->next == NULL)//链表只含有一个数据
	{
		free(pq->front);
		pq->back = pq->front = NULL;
	}
	else
	{
		QNode* cur = pq->front->next;
		free(pq->front);
		pq->front = cur;
	}
	pq->size--;
}

5.读取队尾和队头元素

MyDataType QueueFront(QL* pq)//队头元素
{
	assert(pq);
	assert(!QueueEmpty(pq));//不能为空链表
	return pq->front->val;
}
MyDataType QueueBack(QL* pq)//队尾元素
{
	assert(pq);
	assert(!QueueEmpty(pq));//不能为空链表
	return pq->back->val;
}

6.判断队列是否为空,读取队列大小

    即使是读取队列元素个数这样的操作也靠函数实现,能有效避免使用者不明变量含义而造成读取元素个数错误。

bool QueueEmpty(QL* pq)//判断队列是否为空
{
	assert (pq);
	return  pq->front==NULL;//若头结点为空,则队列不含任何元素,队列为空
}
int QueueSize(QL* pq)//读取队列大小
{
	return pq->size;
}

7.队列的销毁

void QueueDestroy(QL* pq)
{
	assert(pq);
	QNode* cur = pq->front;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}//每个结点一一释放空间
	pq->back = pq->front = NULL;
	pq->size = 0;
}

三.队列的例题

题目链接:

设计循环队列

题目解答:

  可以使用单链表和数组,但显然使用数组更方便。问题的关键在于数组尾元素的处理,这时要灵活使用取模操作。


typedef struct {
    int *a;
    int front;
    int rear;
    int k;
} MyCircularQueue;


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

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

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


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

bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    return false;
    obj->front=(obj->front+1)%(obj->k+1);
    return true;    
}

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

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    return -1;
    else
    return obj->a[(obj->rear+obj->k)%(obj->k+1)];

}

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

栈与队列的互相实现

题目链接:

题目一:用栈实现队列

题目二:用队列实现栈

题目解答:

 题目一:由于C语言没有库函数来搭建栈,所以我们必须手动搭建一个栈。思路很简单:用一个栈专门来进数据;另一个栈专门来出数据。

typedef int MyDataType;
typedef struct Stack
{
	MyDataType* a;
	int capacity;
	int top;
}ST;

void  InitStack(ST*ps);
void  PushStack(ST* ps,MyDataType x);
void  PopStack(ST* ps);
void DestroyStack(ST* ps);
bool StackEmpty(ST* ps);
MyDataType StackTop(ST* ps);
int StackSize(ST* ps);
void  InitStack(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}
void  PushStack(ST* ps, MyDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		int NewSize = ps->top == 0 ? 4 : ps->capacity * 2;
		MyDataType* temp = (MyDataType*)realloc(ps->a, sizeof(MyDataType) * NewSize);
		if (temp == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		ps->a = temp;
		ps->capacity = NewSize;
	}
	ps->a[ps->top] = x;
	ps->top++;
}
void  PopStack(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
		--ps->top;
}
void DestroyStack(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}
bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}
MyDataType StackTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	return ps->a[ps->top-1];
}
int StackSize(ST* ps)
{
	assert(ps);
	assert(ps->top >= 0);
	return ps->top ;
}


typedef struct {
    ST pushstack;
    ST popstack;
    int size;
} MyQueue;


MyQueue* myQueueCreate() 
{
    MyQueue*queue=(MyQueue*)malloc(sizeof(MyQueue));
    InitStack(&queue->pushstack);
    InitStack(&queue->popstack);
    queue->size=0;
    return queue;
}

void myQueuePush(MyQueue* obj, int x) 
{
    assert(obj);    
    PushStack(&obj->pushstack,x);
}

int myQueuePop(MyQueue* obj) 
{
    if(StackEmpty(&obj->popstack))
    {
        while(!StackEmpty(&obj->pushstack))
        {
            int push=StackTop(&obj->pushstack);
            PopStack(&obj->pushstack);
            PushStack(&obj->popstack,push);
        }
    }   
    int pop=StackTop(&obj->popstack);
    PopStack(&obj->popstack);
    return pop; 
}

int myQueuePeek(MyQueue* obj) 
{
    if(StackEmpty(&obj->popstack))
    {
        while(!StackEmpty(&obj->pushstack))
        {
            int push=StackTop(&obj->pushstack);
            PopStack(&obj->pushstack);
            PushStack(&obj->popstack,push);
        }
    }   
    int pop=StackTop(&obj->popstack);
    return pop;     
}

bool myQueueEmpty(MyQueue* obj) 
{
    return StackEmpty(&obj->pushstack)&&StackEmpty(&obj->popstack);    
}

void myQueueFree(MyQueue* obj) 
{
    DestroyStack(&obj->pushstack);
    DestroyStack(&obj->popstack);
    free(obj);    
}

题目二:C语言仍然需要手动搭建队列。思路也不难:出数据时,现将数据导入另一个队列,只剩一个元素时,再弹出数据,如此反复。

typedef int MyDataType;
typedef struct QueueNode
{
	MyDataType val;
	struct QueueNode* next;
}QNode;

typedef struct Queue
{
	QNode* front;
	QNode* back;
	int size;
}QL;

void QueueInit(QL* pq);
void QueueDestroy(QL* pq);
void QueuePush(QL* pq, MyDataType x);
void QueuePop(QL* pq);
MyDataType QueueFront(QL* pq);
MyDataType QueueBack(QL* pq);
bool QueueEmpty(QL* pq);
int QueueSize(QL* pq);
void QueueInit(QL* pq)
{
	assert(pq);
	pq->size = 0;
	pq->front = pq->back = NULL;
}
void QueueDestroy(QL* pq)
{
	assert(pq);
	QNode* cur = pq->front;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->back = pq->front = NULL;
	pq->size = 0;
}
void QueuePush(QL* pq, MyDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->val = x;
	newnode->next = NULL;
	if (pq->back == NULL)
		pq->front = pq->back = newnode;
	else
	{
		pq->back->next = newnode;
		pq->back = newnode;
	}
	pq->size++;
}
void QueuePop(QL* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	if (pq->front->next == NULL)
	{
		free(pq->front);
		pq->back = pq->front = NULL;
	}
	else
	{
		QNode* cur = pq->front->next;
		free(pq->front);
		pq->front = cur;
	}
	pq->size--;
}
MyDataType QueueFront(QL* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->front->val;
}
MyDataType QueueBack(QL* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->back->val;
}
bool QueueEmpty(QL* pq)
{
	assert (pq);
	return  pq->back==NULL;
}
int QueueSize(QL* pq)
{
	return pq->size;
}


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


MyStack* myStackCreate() {
    MyStack*mystack=(MyStack*)malloc(sizeof(MyStack));
    QueueInit(&mystack->queue1);
    QueueInit(&mystack->queue2);
    return mystack;
}

void myStackPush(MyStack* obj, int x) 
{
    if(QueueEmpty(&obj->queue1))
    {
        QueuePush(&obj->queue2,x);
    }    
    else{
        QueuePush(&obj->queue1,x);
    }
}

int myStackPop(MyStack* obj) 
{
    QL* empque=&obj->queue1;
    QL *noempque=&obj->queue2;
    if(QueueEmpty(&obj->queue2))
    {
        empque=&obj->queue2;
        noempque=&obj->queue1;
    }
    while(QueueSize(noempque)!=1)
    {
        int push=QueueFront(noempque);
        QueuePop(noempque);
        QueuePush(empque,push);
    }
    int pop=QueueFront(noempque);
    QueuePop(noempque);
    return pop;
}

int myStackTop(MyStack* obj) {
    if(!QueueEmpty(&obj->queue1))
    return QueueBack(&obj->queue1);
    else
    return QueueBack(&obj->queue2);

}

bool myStackEmpty(MyStack* obj) {

    return QueueEmpty(&obj->queue1)&&QueueEmpty(&obj->queue2);
    
}

void myStackFree(MyStack* obj) {
    free(obj);
    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值