栈和队列的模拟实现——图文详解(C语言)

1.栈的概念及结构
:一种特殊的线性表,只允许在固定的一段进行插入和删除元素操作。进行数据插入和删除操作的一段称为栈顶,另一端称为栈底。栈中的数据元素遵守先进后出的原则。
压栈:栈的插入操作叫做进栈\压栈,入数据在栈顶
出栈: 栈的删除操作叫做出栈,出数据也在栈顶
在这里插入图片描述
2.栈的应用: 1.改变元素的次序 2.括号的匹配 3.用栈将递归转换为循环

3.栈的基本操作:

typedef int SData;
//栈中没有任意位置的插入和删除,若要使用,使用顺序表 栈不需要遍历
//动态栈 
typedef struct Stack
{
    
     
	SData* array;
	int capacity;
	int size;    //栈中元素的个数  栈顶
}Stack;

void StackInit(Stack* ps)
{
    
    
	assert(ps);
	ps->array = (SData*)malloc(sizeof(SData) * 10);
	if (ps->array == NULL)
	{
    
    
		assert(0);  
		return;
	}
	ps->capacity = 10;
	ps->size = 0;
}

void CheakCapacity(Stack* ps)
{
    
    
	assert(ps);
	if (ps->capacity == ps->size)
	{
    
    
		//1 开辟新空间
		SData* temp = (SData*)malloc(sizeof(SData) * ps->size * 2);
		if (temp == NULL)
		{
    
    
			assert(0);
			return;
		}
		//2 拷贝元素
		memcpy(temp, ps->array, sizeof(SData) * ps->size * 2);
		//3 释放旧空间
		free(ps->array);
		//4 使用新空间
		ps->array = temp;
		ps->capacity *= 2;
	}
}

//入栈: 尾插
void StackPush(Stack* ps, SData data)
{
    
    
	//assert(ps);
	CheakCapacity(ps);//插入之前一定要检测是否扩容
	ps->array[ps->size++] = data;
}

//出栈; 尾插
void StackPop(Stack* ps)
{
    
    
	if (StackEmpty(ps))
		return;
	ps->size--;
}

//获取栈顶元素
SData StackTop(Stack* ps)
{
    
    
	assert(!StackEmpty(ps));//不为空才能获取栈顶元素
	return ps->array[ps->size - 1];
}

//获取栈中有效元素的个数
int StackSize(Stack* ps)
{
    
    
	assert(ps);
	return ps->size;
}

//检测栈是否为空
int StackEmpty(Stack* ps)
{
    
    
	assert(ps);
	return ps->size == 0;
}

//销毁栈
void StackDestory(Stack* ps)
{
    
    
	assert(ps);
	free(ps->array);
	ps->array = NULL;
	ps->capacity = 0;
	ps->size = 0;
}

队列

1.队列的概念及结构
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头
在这里插入图片描述
2.队列的实现
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

连续空间进行存储:
在这里插入图片描述
入队列:相当于在连续空间中进行尾插操作,时间复杂度O(1)
出队列:相当于在连续空间中进行头删
在这里插入图片描述
出对列操作:
1.保持队头不动,直接将后序的元素整体往前搬移一个位置,时间复杂度O(N)
2.让队头指针往后移动,优点是不需要搬移元素,时间复杂度O(1),缺点是当rear移动到空间末尾时,不能直接插入元素,否则就会越界
队列的假溢出:空间中还有3个空余位置可以插入元素 -->解决方案:循环队列
队列的真溢出:空间中有效元素个数已经达到空间的最大值

循环队列
循环队列创建好之后,没有向队列中存储任何元素,即空队列,front == rear在同一位置,
注意:内存没有环状,下图是为了方便理解给出的一种图解
在这里插入图片描述
队列的插入:
在这里插入图片描述
插入元素,每次插入到队尾的位置,即rear的位置,插入完成后让rear往后移动,在插入元素时,当front==rear时,队列已满
注意:当rear移动到空间末尾时,必须将rear置为0回到空间的起始位置。当rear追上front时,说明循环队列中元素已经存满

新的问题:如何判断队列是空还是满?
1.少用一个存储空间,假如满的话用(rear+1)%容量 == front即为空间已满,空的话front == rear
2.用flag标记,将flag的初始值设置为0,每次插入元素时将flag设置为1,每次删除元素时将flag设置为0
队列空:front == rear && flag == 0
队列满:front == rear && flag == 1
3.设置一个计数count,记录队列中有效元素的个数,
对列空:count == 0
队列满:count == 空间容量
在这里插入图片描述
队列的模拟实现
在这里插入图片描述
用两个栈模拟实现队列

typedef int DataType;
typedef struct stack
{
    
    
    DataType* array;
    int capacity;
    int size;
}Stack;

//栈的初始化
void StackInit(Stack* ps)
{
    
    
    assert(ps);
    ps->array = (DataType*)malloc(sizeof(DataType)*3);
    ps->capacity = 3;
    ps->size = 0;
}

void CheckCapacity(Stack* ps)
{
    
    
    if(ps->size >= ps->capacity)
    {
    
    
        //申请新空间
        int newCapacity = ps->capacity * 2;
        DataType* temp = (DataType*)malloc(sizeof(DataType)*newCapacity);

        if(temp)
        {
    
    
            for(int i = 0; i < ps->size; ++i)
            temp[i] = ps->array[i];

            //释放旧空间
            free(ps->array);
            ps->array = temp;
            ps->capacity = newCapacity;
        } 
    }
}

//检测栈是否为空
int StackEmpty(Stack* ps)
{
    
    
    assert(ps);
    return 0 == ps->size;
}

//入栈
void StackPush(Stack* ps, DataType data)
{
    
    
    assert(ps);

    CheckCapacity(ps);
    //插入元素
    ps->array[ps->size] = data;
    ps->size++;
}

//出栈
void StackPop(Stack* ps)
{
    
    
    if(StackEmpty(ps))
    return;

    ps->size--;
}

//获取栈顶元素
DataType StackTop(Stack* ps)
{
    
    
    assert(ps);
    return ps->array[ps->size - 1];
}

//获取栈中有效元素的个数
int StackSize(Stack* ps)
{
    
    
    assert(ps);
    return ps->size;
}

//销毁
void StackDestroy(Stack* ps)
{
    
    
    assert(ps);
    free(ps->array);
    ps->capacity = 0;
    ps->size = 0;
}

typedef struct {
    
    
    Stack s1; //模拟入队列
    Stack s2; //模拟出队列
} MyQueue;

/** Initialize your data structure here. */

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

/** 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) {
    
    
    if(StackEmpty(&obj->s2))
    {
    
    
        //将s1中的元素搬移到s2中
        while(!StackEmpty(&obj->s1))
        {
    
    
            StackPush(&obj->s2, StackTop(&obj->s1));
            StackPop(&obj->s1);
        }
    }

    int ret = StackTop(&obj->s2);//将栈顶元素放到ret中
    StackPop(&obj->s2);//删除栈顶元素
    return ret;
} 

/** Get the front element. */
int myQueuePeek(MyQueue* obj) {
    
    
    if(StackEmpty(&obj->s2))
    {
    
      
        //将s1中的元素搬移到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) {
    
    
    //free之前先销毁栈上的数据
    StackDestroy(&obj->s1);
    StackDestroy(&obj->s2);

    free(obj);
}

猜你喜欢

转载自blog.csdn.net/aaaaauaan/article/details/106597994