一.栈:一种先进后出,后进先出的数据结构
类比现实中的:箱子存放东西
栈分为顺序栈和链栈,本文件实现的是不定长的顺序栈
顺序栈的插入和删除都在尾部,因为入栈和出栈的时间复杂度为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;
}