数据结构:栈和队列

目录

一、栈

1、概念

 2、栈的实现

3、栈的区分

二、队列

1、概念

2、队列的实现

 3、队列拓展

下列是我为大家准备的较为综合理解的题,大家可以参考一下代码:

1、用队列实现栈的功能:

2、用栈实现队列的功能: 

3、循环队列: 


一、栈

1、概念

        栈是一种特殊的线性表,该结构只允许从线性表固定的一端进行操作。其中,被操作的一端被称为栈顶,另一端称为栈底。栈中的数据元素遵循先进后出原则。 

        数据进入栈顶被称为压栈或入栈;        数据取出栈顶称为出栈。入栈的元素向栈底靠拢

 

 2、栈的实现

        由于栈是从一端进行操作的,与我们学习过的顺序表相似,其中只对尾部进行操作,算数复杂度相比链表较低,则用顺序表实现。

在栈结构中,没有数据元素遍历的操作!!!

        栈的基本操作:入栈、出栈、返回栈中元素个数、返回栈顶元素、栈是否为空、栈销毁

栈的实现接口:                gitee:Stack.h / Stack.c

数据结构 DS/2022-03-09 栈和队列 · vipover/学习基本代码 - 码云 - 开源中国 (gitee.com)

3、栈的区分

        递归:在应用递归函数中,递归函数调用返回过程类似于栈先进后出的特性。递归函数先被调用的后返回函数值,则可以将递归过程改为栈结构循环实现。

        栈区:程序运行时操作系统将内存划分出来一块特殊的内存区域,主要存放与函数相关联的一些数据

        栈帧:随函数调用时被创建的一块内存空间,函数返回时回收,主要将函数运行时所需空间按照特定的结构组织起来

二、队列

        与栈一样,队列结构中没有数据元素遍历的操作

1、概念

        队列也是一种特殊的线性表,该结构只允许从线性表的一端进行插入数据元素(队尾),只允许从线性表的另一端进行删除数据元素(队头)。队列中数据元素遵循先进先出原则。

        入队:从队尾进行一系列插入数据元素;        出队:从队头进行一系列删除数据元素             

2、队列的实现

        根据队列结构的特性是先入先出原则,则根据头删的算法复杂度,我们用单向链表进行实现队列结构。(其中用单链表实现时,尾插数据不需要一直进行遍历,队列结构的队尾指针会指向最后一个人入队数据元素)

        队列的基本操作:入队、出队、返回队列元素个数、返回队列头、尾元素、队列是否为空、队列销毁

队列实现接口:        gieee:Queue.h / Queue.c

数据结构 DS/2022-03-09 栈和队列 · vipover/学习基本代码 - 码云 - 开源中国 (gitee.com)

 3、队列拓展

        在实现队列时,我们用单链表来实现,使得算术复杂度较低。顺序表也可做为底层代码,但是运用头删的功能实现队列的出队列时,会使时间复杂度变为O(N),为防止算术复杂度变多,我们可以用队头指针指向后续元素移动,而不是队头指针保持不动,后续元素向前移动,这样时间复杂度变为了O(1),同时产生一种假溢出现象:随着入、出队列的进行,back指向顺序表空间末尾,无法继续入队列,但在前面空间内,有剩余位置--->front队头指针不是顺序表的起始位置。这种现象称为假溢出。若前面空间为空,则成为你真溢出。

        为解决顺序表结构实现队列所造成的假溢出问题,我们可以了解一种特殊的结构:循环队列(一种假想的循环结构,实际还是顺序表结构,通过更改rear队尾指针实现循环功能)

判断循环队列容量是否满的方法:

1、通过少存放一个空间的方法,实现t指针rear+1 == fron相等的方式,判断容量是否未满的问题

2、通过设立入、出队列指标,标记最终哪种操作导致指针front == rear相等;假设标记为vet,入队列vet = 1,出队列vet = 0,在front == ret && vet = 1该条件下为满容量,相反为空

3、通过设立计数单位count ,当count == N时,容量为满

在上述方法中,若判断满容量则需要扩容。(根据之前的顺序表知识实现)

        以下是循环链表的实现:用第三种方式判断容量是否为满

循环队列实现接口:        gieee:CircularQueue.h / CircularQueue.c

数据结构 DS/2022-03-09 栈和队列 · vipover/学习基本代码 - 码云 - 开源中国 (gitee.com) 


下列是我为大家准备的较为综合理解的题,大家可以参考一下代码:

1、用队列实现栈的功能:

        225. 用队列实现栈 - 力扣(LeetCode)

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

//链表结点
typedef struct QNode{
    struct QNode* next;
    int data;
}QNode;

//队列结构
typedef struct Queue{
    QNode* front;//队头
    QNode* back;//队尾
}Queue;

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

//创建新结点
QNode* BuyQNode(int data)
{
	QNode* node = (QNode*)malloc(sizeof(QNode));
	if (node == NULL) {
		assert(0);
		return NULL;
	}
	node->data = data;
	node->next = NULL;
	return node;
}

// 初始化队列(若无头结点,则直接进行指针操作,不需要初始化)
void QueueInit(Queue* q) {
	assert(q);
	//初始化指向同一个头结点
	q->front = BuyQNode(0);
	q->back = q->front;
}

// 队尾入队列
void QueuePush(Queue* q, int data)
{
	assert(q);
	QNode* newNode = BuyQNode(data);//队列新元素入队
	q->back->next = newNode;//队列线性链接起来
	q->back = newNode;//队尾指向队列最后一个元素
}

// 队头出队列
void QueuePop(Queue* q)
{
	if (QueueEmpty(q))
		return;
	QNode* delNode = q->front->next;
	if (q->front->next == q->back) //队列只剩下最后一个元素,以防队尾是野指针
		q->back = q->front;
	q->front->next = delNode->next;
	free(delNode);
}

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

// 获取队列队尾元素
int QueueBack(Queue* q)
{
	assert(q);
	if (QueueEmpty(q))
		assert(0);//这里指队尾 无元素
	return q->back->data;
}

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

// 销毁队列
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->front->next; 
	while (cur) {
		q->front->next = cur->next;
		free(cur);
		cur = q->front->next;
	}
	free(q->front);//释放头结点
	q->front = NULL;
	q->back = NULL;
}




//双队列实现栈结构

//栈结构
typedef struct {
    Queue q1;
    Queue q2;
} MyStack;

//队列栈的初始化
MyStack* myStackCreate() {
    MyStack* ms = (MyStack*)malloc(sizeof(MyStack));
    if(ms == NULL)
        return NULL;
    //初始化队列,由于在栈结构中创建的俩个队列,则此时传递的是队列的空间
    QueueInit(&ms->q1);
    QueueInit(&ms->q2);
    return ms;
}

void myStackPush(MyStack* obj, int x) {
    assert(obj);
    if(!QueueEmpty(&obj->q1))//q1队列不为空
        QueuePush(&obj->q1,x);
    else//q1为空
        QueuePush(&obj->q2,x);
}

int myStackPop(MyStack* obj) {
    assert(obj);
    int val = 0;
    if(!QueueEmpty(&obj->q1)){
        while(QueueSize(&obj->q1) > 1){
            QueuePush(&obj->q2,QueueFront(&obj->q1));
            QueuePop(&obj->q1);
        }
        val = QueueFront(&obj->q1);
        QueuePop(&obj->q1);
    }
    else{
        while(QueueSize(&obj->q2) > 1){
            QueuePush(&obj->q1,QueueFront(&obj->q2));
            QueuePop(&obj->q2);
        }
        val = QueueFront(&obj->q2);
        QueuePop(&obj->q2);
    }
    return val;
}

int myStackTop(MyStack* obj) {
    assert(obj);
    if(!QueueEmpty(&obj->q1))
        return QueueBack(&obj->q1);
    else
        return QueueBack(&obj->q2);
}

bool myStackEmpty(MyStack* obj) {
    assert(obj);
    if(QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2))
        return true;
    else
        return false;
}

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


/**
 * Your MyStack struct will be instantiated and called as such:
 * MyStack* obj = myStackCreate();
 * myStackPush(obj, x);
 
 * int param_2 = myStackPop(obj);
 
 * int param_3 = myStackTop(obj);
 
 * bool param_4 = myStackEmpty(obj);
 
 * myStackFree(obj);
*/

2、用栈实现队列的功能: 

         232. 用栈实现队列 - 力扣(LeetCode)

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

//栈结构实现
typedef struct Stack{ 
    int* array; // 申请顺序表
    int top; // 顶部元素
    int capacity; // 个数
}Stack;

//初始化栈
void StackInit(Stack* newStack,int capacity)
{
    assert(newStack);
    if(capacity <= 0)
        assert(0);// 栈结构初始化容量为零
    newStack->array = (int*)malloc(sizeof(int)*capacity);
    newStack->capacity = capacity;
    newStack->top = 0;
}

//栈中顺序表是否扩容
void CheckCapacity(Stack* st)
{
    assert(st);
    if(st->top == st->capacity){
        int newcapacity = st->capacity*2;
        int* temp = (int*)realloc(st->array,sizeof(int)*newcapacity);
        st->array = temp;
        st->capacity = newcapacity;
    }
    return;
}

// 入栈
void StackPush(Stack* st, int data)
{
    assert(st);
    //检查扩容
    CheckCapacity(st);
    st->array[st->top] = data;
    st->top++;
}

// 栈是否为空
int StackEmpty(Stack* st)
{
    assert(st);
    return 0 == st->top;
}

// 出栈
void StackPop(Stack* st)
{
    if(StackEmpty(st))
        return;
    st->top--;
}

// 返回栈顶元素
int StackTop(Stack* st)
{
    if(StackEmpty(st))
        return;
    return st->array[st->top - 1];
}

// 栈元素个数
int StackSize(Stack* st)
{
    assert(st);
    return st->top;
}

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



#if 0 // s2栈为辅助栈
typedef struct {
    Stack s1;
    Stack s2;
} MyQueue;

//初始队列
MyQueue* myQueueCreate() {
    MyQueue* Queue = (MyQueue*)malloc(sizeof(MyQueue));
    StackInit(&Queue->s1,10); 
    StackInit(&Queue->s2,10);
    return Queue; 
}

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

int myQueuePop(MyQueue* obj) {
    assert(obj);
    int pop = 0;
    //将s1中数据出栈到s2中入栈,直至s1中最后一个元素
    while(StackSize(&obj->s1) > 1){
        StackPush(&obj->s2,StackTop(&obj->s1));
        StackPop(&obj->s1);
    }
    pop = StackTop(&obj->s1);
    StackPop(&obj->s1);//s1为空
    //将s2出栈到s1中入栈
    while(StackSize(&obj->s2)){
        StackPush(&obj->s1,StackTop(&obj->s2));
        StackPop(&obj->s2);
    }
    return pop;
}

int myQueuePeek(MyQueue* obj) {
    assert(obj);
    int peek = 0;
    while(StackSize(&obj->s1)){
        StackPush(&obj->s2,StackTop(&obj->s1));
        StackPop(&obj->s1);
    }
    peek = StackTop(&obj->s2);
    while(StackSize(&obj->s2)){
        StackPush(&obj->s1,StackTop(&obj->s2));
        StackPop(&obj->s2);
    }
    return peek;
}

bool myQueueEmpty(MyQueue* obj) {
    assert(obj);
    if(StackEmpty(&obj->s1) && StackEmpty(&obj->s2))
        return true;
    else
        return false;
}

void myQueueFree(MyQueue* obj) {
    assert(obj);
    StackDestory(&obj->s1);
    StackDestory(&obj->s2);
    free(obj);
}
#endif


#if 1 //s2栈为队头栈
typedef struct {
    Stack s1;
    Stack s2;
} MyQueue;

//初始队列
MyQueue* myQueueCreate() {
    MyQueue* Queue = (MyQueue*)malloc(sizeof(MyQueue));
    StackInit(&Queue->s1,10); 
    StackInit(&Queue->s2,10);
    return Queue; 
}

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

int myQueuePop(MyQueue* obj) {
    assert(obj);
    if(StackEmpty(&obj->s2)){
        while(StackSize(&obj->s1)){
            StackPush(&obj->s2,StackTop(&obj->s1));
            StackPop(&obj->s1); 
        }
    }
    int pop = 0;
    pop = StackTop(&obj->s2);
    StackPop(&obj->s2);
    return pop;
}

int myQueuePeek(MyQueue* obj) {
    assert(obj);
    if(StackEmpty(&obj->s2)){
        while(StackSize(&obj->s1)){
            StackPush(&obj->s2,StackTop(&obj->s1));
            StackPop(&obj->s1); 
        }
    }
    return StackTop(&obj->s2);
}

bool myQueueEmpty(MyQueue* obj) {
    assert(obj);
    if(StackEmpty(&obj->s1) && StackEmpty(&obj->s2))
        return true;
    else
        return false;
}

void myQueueFree(MyQueue* obj) {
    assert(obj);
    StackDestory(&obj->s1);
    StackDestory(&obj->s2);
    free(obj);
}
#endif

/**
 * Your MyQueue struct will be instantiated and called as such:
 * MyQueue* obj = myQueueCreate();
 * myQueuePush(obj, x);
 
 * int param_2 = myQueuePop(obj);
 
 * int param_3 = myQueuePeek(obj);
 
 * bool param_4 = myQueueEmpty(obj);
 
 * myQueueFree(obj);
*/

3、循环队列: 

        622. 设计循环队列 - 力扣(LeetCode)

typedef struct {
    int* array;
    int N;// 空间总大小
    int front;// 队头
    int rear;// 队尾
    int count;// 有效元素个数--->判断容量
} MyCircularQueue;

//循环队列的初始化
MyCircularQueue* myCircularQueueCreate(int k) {
    if(k <= 0) 
        assert(0);// 传入k值不符合
    MyCircularQueue* queue = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    if(NULL == queue)
        return NULL;
    queue->array = (int*)malloc(sizeof(int)*k);
    queue->N = k;
    queue->count = 0;
    queue->front = queue->rear = 0;
    return queue;
}

// 判断队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    assert(obj);
    return 0 == obj->count;
}

// 判断队列容量是否已满
bool myCircularQueueIsFull(MyCircularQueue* obj) {
    assert(obj);
    return obj->N == obj->count;
}

// 插入一个元素,容量为满插入失败
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
        return false;
 
    obj->array[obj->rear] = value;
    obj->rear++;
    obj->count++;
    // 如果rear走到顺序表末尾,则需要更改指向头部,实现循环
    if(obj->rear == obj->N)
        obj->rear = 0;
    return true;
}

// 删除一个元素,容量为空则删除失败
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return false;
    obj->front++;
    obj->count--;
    // 如果front走到顺序表末尾,则需要更改指向头部,实现循环
    obj->front %= obj->N;
    return true;
}

// 返回队头元素
int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    return obj->array[obj->front];
}

// 返回队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    //若队尾元素在顺序表起始位置,-1会越界
    if(obj->rear == 0)
        return obj->array[obj->N - 1];
    return obj->array[obj->rear - 1];
    //return obj->array[(obj->rear - 1 + obj->N) % obj->N];
}

void myCircularQueueFree(MyCircularQueue* obj) {
    assert(obj);
    free(obj->array);
    obj->N = obj->count = obj->front = obj->rear = 0;
    free(obj);
}

/**
 * Your MyCircularQueue struct will be instantiated and called as such:
 * MyCircularQueue* obj = myCircularQueueCreate(k);
 * bool param_1 = myCircularQueueEnQueue(obj, value);
 
 * bool param_2 = myCircularQueueDeQueue(obj);
 
 * int param_3 = myCircularQueueFront(obj);
 
 * int param_4 = myCircularQueueRear(obj);
 
 * bool param_5 = myCircularQueueIsEmpty(obj);
 
 * bool param_6 = myCircularQueueIsFull(obj);
 
 * myCircularQueueFree(obj);
*/

        到这我们就结束了,希望大家好好学习,理解消化代码的逻辑性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值