3.栈和队列(基础数据结构)

一.栈:一种先进后出,后进先出的数据结构

在这里插入图片描述
类比现实中的:箱子存放东西
栈分为顺序栈和链栈,本文件实现的是不定长的顺序栈
顺序栈的插入和删除都在尾部,因为入栈和出栈的时间复杂度为O(1)

顺序栈怎么设计

typedef struct Stack
{
	int* base; //用来malloc申请动态内存
	int stack_Size; //栈总体个数
	int top; //栈顶指针,当前栈存放的有效元素个数,指向下一个存放数据的位置
}Stack,*PStack;

在这里插入图片描述
顺序栈的具体代码实现: (一共有10中操作方法)

#include <stdio.h> 
#include <assert.h> 
#include <stdlib.h> 
#include "stack.h" 
void InitStack(PStack ps) //初始化
{ 
	assert(ps != NULL); 
	if(ps == NULL) return; 
	ps->base = (int *)malloc(sizeof(int) * INIT_SIZE); 
	assert(ps->base != NULL); 
	ps->top = 0; 
	ps->stack_Size = INIT_SIZE; 
}

bool Push(PStack ps, int val) //入栈操作(尾插)
{  
	assert(ps != NULL); 
	if(ps == NULL) return false; 
	if(IsFull(ps)) //判满 
	{ 
		//如果满了,扩容 Inc(ps); 
	} 
	ps->base[ps->top++] = val;//插入 
	return true; //返回值 
}

bool Get_top(PStack ps, int *rtval) //获取栈顶元素的值,但不删除 
{ 
	assert(ps != NULL); 
	if(ps == NULL) return false; 
	//-1 \0 //\0只用字符串认识他,别人根本不理它 '#'也不行 ‘#’== 35 
	if(IsEmpty(ps)) 
	{ 
		return false; 
	}
	*rtval = ps->base[ps->top-1]; 
	return true;
}

bool Pop(PStack ps, int *rtval) //获取栈顶元素的值,但删除,出栈操作(尾删) 
								//int Pop(PStack ps)    
{ 
	assert(ps != NULL && !IsEmpty(ps)); 
	if(ps == NULL || IsEmpty(ps)) return false; 
	获取值 //*rtval = ps->base[ps->top-1]; 有效个数-1 //ps->top--; //
	//返回值 
	*rtval = ps->base[--ps->top]; 
	return true; 
}

bool IsEmpty(PStack ps) //判空
{ 
	return ps->top == 0; 
}
 
bool IsFull(PStack ps) //判满
{ 
	return ps->top == ps->stack_Size; 
}

int Get_length(PStack ps) //获取有效元素个数 
{ 
	return ps->top; 
}
 
void Clear(PStack ps)   //清空
{
	ps->top = 0;
}
 
void Destroy(PStack ps) //销毁
{  
	assert(ps != NULL); 
	if(ps == NULL) return; 
	//释放malloc申请的动态内存 
	free(ps->base); //将指针赋值为NULL,防止出现野指针 
	ps->base = NULL; 
	ps->top = 0; 
	ps->stack_Size = 0; 
}
//扩容 
void Inc(PStack ps)//如果按2倍去扩容 1.5 
{  
	assert(ps != NULL); 
	if(ps == NULL) return;  
	ps->stack_Size *= 2; //给栈空间最大容量重新赋值 
	//从堆内申请更大的空间给我们 
	ps->base = (int *)realloc(ps->base, sizeof(int) * ps->stack_Size); 
	//realloc指在原有空间的基础上再多申请一些空间
}

链栈

我们实现的是带头结点的链栈
栈顶:入栈和出栈,让指向链表头部,时间复杂度则为O(1)(类比头插和头删)
链栈的示意图:
在这里插入图片描述
链栈怎么设计:

typedef struct LStack
{
	int data;//数据域
	LStack* next;//指针域
}LStack,*PLStack;

链栈的具体代码实现:

#include <stdio.h>   //(链栈)
#include <assert.h> 
#include <stdlib.h> 
//realloc 第一个:用的太少,容易出错  第二个点:有的编译器还需要
#include <alloc.h> 
#include "lstack.h" 

//带头节点的链栈
typedef int ElemType;
typedef struct LStack 
{
    ElemType data;
    LStack* next;
}LStack, * PLStack;

static PLStack Buy_Node(PLStack p, ElemType value)  //将新节点插入到p结点的后面
{
    PLStack new_node = (PLStack)malloc(sizeof(LStack));
    if (nullptr == new_node) return nullptr;
    new_node->next = p->next;
    new_node->data = value;
    return new_node;
}

//初始化
void InitStack(PLStack mylstack)
{
    assert(mylstack != nullptr);
    if(mylstack==nullptr) return;
    mylstack->next = nullptr;
}

//入栈
bool Push(PLStack mylstack, ElemType value)
{
    assert(mylstack != nullptr);
    PLStack new_node = Buy_Node(mylstack, value);
    if (nullptr == new_node) return false;
    mylstack->next = new_node;
    return true;
}

// // 获取栈顶元素但不删除
//ElemType Get_top(PStack mystack); 错误的返回值容易和存储的值冲突,
bool Get_top(PLStack mylstack, int* rtval)  //rtval是获取的值
{
    assert(mylstack != nullptr && rtval != nullptr);
    if (Empty(mylstack))
    {
        return false;
    }
    *rtval = mylstack->next->data;
    return true;
}
// 获取栈顶元素且删除//出栈
bool Pop(PLStack mylstack, int* rtval)
{
    assert(mylstack != nullptr && rtval != nullptr);
    if (Empty(mylstack))
    {
        return false;
    }
    *rtval = mylstack->next->data;

    PLStack p = mylstack->next;
    mylstack->next = p->next;
    free(p);
    p = nullptr;
    return true;
}

//判空
bool Empty(PLStack mylstack)
{
    return mylstack->next == nullptr;
}

//判满 链表栈不需要判满
//bool IsFull(PLStack mystack);

//清空
void Clear(PLStack mylstack)
{
    Destory(mylstack);
}
//销毁
void Destory(PLStack mylstack)
{
    assert(mylstack != nullptr);
    PLStack p = mylstack->next;
    while (p != nullptr)
    {
        mylstack->next = p->next;
        free(p);
        p = mylstack->next;
    }
}

//获取有效长度
int Get_Length(PLStack mylstack)
{
    assert(mylstack != nullptr);
    if (nullptr == mylstack)
    {
        return -1;
    }
    PLStack p = mylstack->next;
    int count = 0;
    while (p != nullptr)
    {
        count++;
        p = p->next;
    }
    return count;
}

二.队列

一种先进先出FIFO的数据结构,也是一种受到限制的顺序表
类比:现实中我们排队买饭,火车站买票
队列:只能一端进行入队,一端进行出队,入队的一端称作队尾,出队的一端称作队头,当队列中没有
数据元素的时候,称作空队
今天我要实现的是顺序队列:
队列存储方式分两种:顺序存储(顺序队列),一种链式存储(链队列)
我们实现的是定长的顺序队列:
在这里插入图片描述
在这里插入图片描述
顺序队列,我们要把它臆想成一个环形,这个做根本原因是:入队和出队的时间复杂度都为O(1)
在这里插入图片描述
做题时我们也遇到过这种处理成环形的题:
约瑟夫环,
在这里插入图片描述
在这里插入图片描述
队列成环后,怎么判断队空和队满?
队列判空条件为rear == front
队列判满条件为rear == front
那么我们怎么设计,让判空和判满不冲突:
第一种解决方案:申请一个变量当做计数器,存放当前队列的有效元素个数
第二种解决方案:浪费一个队尾空间不用,当做标记,当front == rear的时候,
我们认为队列为空,当队尾rear再走一步就遇到队头front的时候,我们认为队列为满
在这里插入图片描述
第三个难点:怎么获取队列有效元素个数?
在这里插入图片描述
顺序循环队列的设计:

#define MAXQSIZE 10 
typedef struct 
{
    int* base;
    //指向动态内存开辟的空间 
    int front;
    //头指针,指向队列第一个元素的下标 
    int rear;
    //为指针,当队列不空的时候,指向待插入元素存放空间的下标(队列尾部节点下一个 空间)
    //int maxsize;
    //当前申请顺序表最大空间个数 
}SqQueue,*PSqQueue;

顺序循环队列可执行的操作:

#pragma once 
//队列:一种先进先出的数据结构 
//为了使插入和删除时间复杂度都为O(1),所以我们将顺序队列想象成环形 
//这里比较难的有三个点: 
//1.为什么要想象成环形? 插入和删除是用的最频繁的操作 
//2.怎么单独实现判空操作,和判满操作? 
// 第一种解决方案:申请一个变量当做计数器,存放当前队列的有效元素个数 
// 第二种解决方案:浪费一个队尾空间不用,当做标记,当front == rear的时候, 
// 我们认为队列为空,当队尾rear再走一步就遇到队头front的时候,我们认为 队列为满 
//3.怎么获取队列有效元素个数? (rear-front+MAXQSIZE)%MAXQSIZE 
#define MAXQSIZE 10 
typedef struct 
{ 
    int *base;
    //指向动态内存开辟的空间 
    int front;
    //头指针,指向队列第一个元素的下标 int rear;
    //为指针,当队列不空的时候,指向待插入元素存放空间的下标(队列尾部节点下一个 空间)
    //int maxsize;
    //当前申请顺序表最大空间个数 
}SqQueue,*PSqQueue; 
//队列可以实现的操作有哪些? 
//增删改查 
//初始化 
void Init_Queue(PSqQueue sq); 
//入队操作 
bool Push(PSqQueue sq, int val); 
//获取队头第一个元素的值,但不删除 
bool Get_top(PSqQueue sq, int *rtval); 
//出队操作(获取队头第一个元素的值,并且删除) 
bool Pop(PSqQueue sq, int *rtval); 
//判空 
bool IsEmpty(PSqQueue sq);
//判满
bool IsFull(PSqQueue sq); 
//获取队列有效元素个数 
int Get_length(PSqQueue sq); 
//清空 
void Clear(PSqQueue sq); 
//销毁 
void Destroy(PSqQueue sq);

顺序循环队列具体代码实现:

#include <stdio.h> #include <assert.h> #include <stdlib.h> #include "queue.h" 
//初始化 
void Init_Queue(PSqQueue sq) 
{ 
    //断言
    assert(sq != NULL); 
    if(sq == NULL) return; 
    //对sq进行初始化 
    sq->base = (int *)malloc(sizeof(int) * MAXQSIZE); 
    assert(sq->base != NULL); 
    sq->front = 0; 
    sq->rear = 0; 
}
//入队操作 
bool Push(PSqQueue sq, int val) 
{ 
    //断言 
    assert(sq != NULL); 
    if(NULL == sq) return false; 
    if(IsFull(sq)) return false; 
    //插入 
    sq->base[sq->rear] = val; 
    //sq->rear++;
    //error 要变成环形处理 
    sq->rear = (sq->rear+1)%MAXQSIZE; 
    //返回值
    return true;
}
//获取队头第一个元素的值,但不删除 
bool Get_top(PSqQueue sq, int *rtval) 
{ 
    //断言 
    assert(sq != NULL &&rtval != NULL); 
    if(sq == NULL || rtval == NULL) return false; 
    if(IsEmpty(sq)) return false; 
    //把值通过输出参数带出去 
    *rtval = sq->base[sq->front]; 
    //返回值 return true; 
}
//出队操作(获取队头第一个元素的值,并且删除) 
bool Pop(PSqQueue sq, int *rtval) 
{ 
    //断言 
    assert(sq != NULL &&rtval != NULL); 
    if(sq == NULL || rtval == NULL) return false; 
    if(IsEmpty(sq)) return false; 
    //把值通过输出参数带出去 
    *rtval = sq->base[sq->front]; 
    //让头指针走一下
    //sq->front++;
    //error 处理成环形 
    sq->front = (sq->front+1)%MAXQSIZE; 
    //返回值 return true; 
}
//判空 
bool IsEmpty(PSqQueue sq) 
{ 
    assert(sq != NULL); 
    if(NULL == sq) return false; 
    return sq->front == sq->rear; 
}
//判满 
bool IsFull(PSqQueue sq) 
{ 
    assert(sq != NULL); 
    if(NULL == sq)
        return false; 
    //队尾指针再走一步,就遇到了队头,这时候我们认为队列满了 
    return (sq->rear+1)%MAXQSIZE == sq->front; 
}
//获取队列有效元素个数 
int Get_length(PSqQueue sq) 
{ 
    assert(sq != NULL);
    if(sq == NULL) return -1;
    //+MAXQSIZE的作用:防止出现负数
    //%MAXQSIZE的作用:防止多加 
    return (sq->rear - sq->front + MAXQSIZE)%MAXQSIZE;
}
//清空 
void Clear(PSqQueue sq) 
{ 
    assert(sq != NULL); 
    if(sq == NULL) return;
    sq->front = 0; 
    sq->rear = 0; 
}
//销毁 
void Destroy(PSqQueue sq)
{ 
    assert(sq != NULL);
    if(NULL == sq) return;
    free(sq->base); 
    sq->base = NULL;
    sq->front = 0;
    sq->rear = 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值