栈
栈是一种特殊的线性表,其中只允许在一端进行插入及删除元素操作称为栈顶端,另一端称为栈底。
栈的基本操作
进栈操作:栈的插入元素操作称为进栈/入栈,入栈的数据保存在栈顶
出栈:栈的删除操作称为出栈,出栈数据也在栈顶
一、栈的基本知识
入栈过程与出栈过程:
进栈:修改栈顶指针,将需要入栈元素放入栈顶指针指向空间
出栈:保存栈顶指针所指空间元素,修改栈顶指针
二、栈的操作
栈只能在栈顶端进行插入/删除操作,具有“先进后出(FILO)”特性
利用顺序表来实现栈操作:
数据类型定义:
typedef int STdatatype;
1、定义栈结构:
typedef struct stack {
STdatatype *_data; //栈的数据域
int _size; //记录栈中有效元素的个数
int _capacity; //栈的容量
}stack;
首先需要队栈进行初始化操作,预防野指针的出现:
void stack_init(stack* st)
{
if (st == NULL)
return;
st->_data = NULL;
st->_size = st->_capacity = 0;
}
2、入栈操作:
首先要进行栈空间的判断,若空间足够则直接入栈,空间不足需要为新元素开辟新空间。
修改栈顶指针,即栈顶指针加一
void stack_push(stack* st, STdatatype val) //进栈
{
if (st == NULL) // 判空栈
return;
checkcapacity(st); //容量的检查-----扩容
st->_data[st->_size++] = val; //修改栈顶指针并在其指向空间放入新元素val
}
//扩容操作
void checkcapacity(stack* st)
{
if (st->_capacity == st->_size) {
int newcapacity = st->_capacity == 0 ? 1 : 2 * st->_capacity;
st->_data = (STdatatype*)realloc(st->_data, sizeof(STdatatype)*newcapacity);
st->_capacity = newcapacity;
}
}
3、出栈:
出栈在栈顶端操作,栈顶指针减一
void stack_pop(stack* st) //删除栈顶元素----出栈
{
if (st == NULL || st->_size == 0)
return;
--st->_size; //修改有效元素个数,修改栈顶指针指向
}
4、获取栈顶元素
直接返回栈顶指针指向空间的内容即可
STdatatype stack_top(stack*st) //获取栈顶元素
{
return st->_data[st->_size - 1];
}
5、判栈空
如果栈中有效元素个数为0 ,说明栈为空,否则栈不空
int stack_empty(stack *st)
{
if (st == NULL || st->_size == 0)
return 1;
else
return 0;
}
6、栈的销毁
void stack_destory(stack* st) //销毁
{
free(st->_data);
st->_capacity = st->_size = 0;
}
队列
队列只允许在一端进行插入数据操作,而在另一端进行删除数据操作的特殊线性表,队列具有先进先出(FIFO) 特点,允许进行插入的一端称为队尾,允许删除的一端称为队头。
队列的基本操作
入队: 允许进行插入数据的一端-----------队尾
出队: 允许进行删除数据的一端-----------队头
一、队列的基本知识
定义队列时需要定义两个指针:队头指针、队尾指针
二、队列的操作
队列在队头进行删除元素操作,队尾进行插入元素操作,拥有“先进先出(FIFO)”特性,使用带尾指针的单链表来实现。
定义数据类型:
typedef int Qdatatype;
1、创建一个队列
typedef struct QNode {
Qdatatype _data; //队列元素
struct QNode* _next; //指向下一个元素
}QNode;
typedef struct Queue {
QNode* _head; //队头指针
QNode* _tail; //队尾指针
int _size; //记录队列中元素的个数
}Queue;
首先对创建好的队列进行初始化操作:
void Queue_init(Queue * q)
{
if (q == NULL)
return;
q->_head = q->_tail = NULL;
q->_size = 0;
}
2、入队操作:队尾操作
入队之前首先要开辟一个新节点的空间来保存新的元素:
QNode* creatNode(Qdatatype val) //创建新节点
{
QNode* node = (QNode*)malloc(sizeof(QNode));
node->_data = val;
node->_next = NULL;
return node;
}
在进行新元素的插入,以及队尾指针的更新操作:
void Queue_push(Queue* q, Qdatatype val) //插入------队尾
{
QNode* node = creatNode(val);
if (q->_head == NULL) //若当前队列为空即插入元素为当前队列的第一个元素时
q->_head = q->_tail = node;
else {
q->_tail->_next = node; //当前队列插入前非空,直接修改队尾指针
q->_tail = node;
}
++q->_size;
}
3、出队操作:对头操作
void Queue_pop(Queue* q) //删除------队头
{
if (q == NULL || q->_head == NULL)
return;
QNode* next = q->_head->_next; //保存队头指针的下一个节点内容
free(q->_head); //删除队头元素
q->_head = next; //修改队头指针指向
//如果队列中只有一个数据
if (q->_head == NULL)
q->_tail = NULL;
--q->_size;
}
4、获取队头队尾元素
Qdatatype Queue_front(Queue* q) //取队头
{
return q->_head->_data;
}
Qdatatype Queue_back(Queue* q) //取队尾
{
return q->_tail->_data;
}
5、队列的判空
int Queue_empty(Queue* q) //判空
{
/*if (q == NULL || q->_head == NULL)
return 1;
return 0;*/
return q->_head == NULL;
}
6、队列的销毁
void Queue_destory(Queue* q)
{
QNode* cur = q->_head;
while (cur) {
QNode* next = cur->_next;
free(cur);
cur = next;
}
q->_head = q->_tail = NULL;
}
三、循环队列
分析:
循环队列的引入:
为了防止产生假溢出问题,提升空间利用率,我们可以采用循环队列的方式来进行存储:
但是直接来使用又会存在一个新的问题:
怎样判断循环队列中所有空间都被有效使用了?
按照普通想法,如果一个队列的队尾指针达到了队列最大容量的位置,我们将传统的队列的队尾指针可以折回来指向队头
指针以上疑问,我们采取了三种方法来进行解决:
方法一:牺牲一个空间的位置
方法二:采用一个标记 flag
起初设置 flag=0;
空间满的情况:入队导致队满,因此每入队一个元素将 flag 置为 1
空间空的情况:出队导致队空,因此每出队一个元素将 flag 置为 0
方法三:采用一个计数器 count
起始将 count=0;
每入队一个元素 count++
每出队一个元素 count–
针对上述情况,下面来实现一下循环队列的一些基本操作吧:
typedef struct {
int* array;
int capacity;
int size; //有效元素个数
int front; //队头
int back; //队尾
}MyCircularQueue;
//创建一个队列
MyCircularQueue* myCircularQueueCreat(int k) //循环队列最终存储 k 个元素
{
MyCircularQueue* mq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
if (NULL == mq) //申请空间失败
return NULL;
mq->array = (int*)malloc(sizeof(int)*k);
mq->capacity = k;
mq->size = 0;
mq->back = mq->front = 0;
return mq;
}
//判满
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
if (obj->size == obj->capacity) //如果此时有效元素个数=最大容量则满
return true;
else
return false;
}
//判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
if (obj->size == 0)
return true;
else
return false;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int val)
{
//入队
if (myCircularQueueIsFull(obj))
return false; //循环链表满不可再入
//存在空间可以入,则在队尾入 并将队尾指针后移
obj->array[obj->back++] = val;
//当移动至末尾时,需要将队尾指针循环到起始位置
if (obj->back == obj->capacity)
obj->back = 0;
obj->size++; //有效元素个数加一
return true;
}
//出队
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
if (myCircularQueueIsEmpty(obj)) //队空不能出
return false;
//可以出队,队头出队
obj->front++;
//当移动到空间末尾时,需要将队头指针循环到起始位置
if (obj->front == obj->capacity)
obj->front = 0;
obj->size--; //出队有效元素个数减一
return true;
}
//获取队头元素
int myCircularQueueFront(MyCircularQueue* obj)
{
if (!myCircularQueueIsEmpty(obj)) //非空队列取队头
return obj->array[obj->front];
else
return -1; //空队列返回 -1
}
//获取队尾元素
int myCircularQueueRear(MyCircularQueue* obj)
{
//非空队列取队尾,注意队尾指针 back 指向的是有效队尾元素的下一个空间
if (!myCircularQueueIsEmpty(obj))
return obj->array[(obj->back - 1 + obj->capacity) % obj->capacity];
return -1; //空队列返回 -1
}
//销毁
void myCircularQueueFree(MyCircularQueue* obj)
{
free(obj->array); //首先销毁有效元素的空间
free(obj); //其次销毁整个队列空间
}
用栈来实现队列
栈:先进后出,栈顶操作
队列:先进先出,队头删,队尾入
思路:使用两个栈来实现:入队栈、出队栈
入队栈:只进行入队操作
出队栈:只进行出队操作
实现过程
入队:
出队:
再入栈:
代码:
1、调用栈的实现过程:
typedef int STdatatype; //调用栈的实现过程
typedef struct stack {
STdatatype *_data;
int _size;
int _capacity;
}stack;
void stack_init(stack* st)
{
if (st == NULL)
return;
st->_data = NULL;
st->_size = st->_capacity = 0;
}
void checkcapacity(stack* st)
{
if (st->_capacity == st->_size) {
int newcapacity = st->_capacity == 0 ? 1 : 2 * st->_capacity;
st->_data = (STdatatype*)realloc(st->_data, sizeof(STdatatype)*newcapacity);
st->_capacity = newcapacity;
}
}
void stack_push(stack* st, STdatatype val) //进栈
{
if (st == NULL) //空栈
return;
checkcapacity(st);
st->_data[st->_size++] = val;
}
void stack_pop(stack* st) //删除栈顶元素----出栈
{
if (st == NULL || st->_size == 0)
return;
--st->_size;
}
int stack_size(stack * st)
{
if (st == NULL)
return 0;
return st->_size;
}
int stack_empty(stack *st)
{
if (st == NULL || st->_size == 0)
return 1;
else
return 0;
}
STdatatype stack_top(stack*st)
{
return st->_data[st->_size - 1];
}
void stack_destory(stack* st)
{
if (st) {
if (st->_data) {
free(st->_data);
st->_data = NULL;
st->_capacity = st->_size = 0;
}
}
}
2、定义两个栈结构来实现队列操作:
typedef struct {
stack pushST; //入队栈
stack popST; //出队栈
} MyQueue;
MyQueue* myQueueCreate() {
//动态创建
MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));
stack_init(&pq->pushST);
stack_init(&pq->popST);
return pq;
}
void myQueuePush(MyQueue* obj, int x) {
//入队栈
stack_push(&obj->pushST,x);
}
int myQueuePop(MyQueue* obj) {
//出对栈------判断是否为空
//空,需要把入队栈的元素导入
//不空,之间出栈
int front;
if (!stack_empty(&obj->popST)) {
front = stack_top(&obj->popST);
stack_pop(&obj->popST); //出栈
}
else {
while (!stack_empty(&obj->pushST)) {
stack_push(&obj->popST, stack_top(&obj->pushST));
stack_pop(&obj->pushST);
}
front = stack_top(&obj->popST);
stack_pop(&obj->popST);
}
return front;
}
int myQueuePeek(MyQueue* obj) { //获取队头元素
if (stack_empty(&obj->popST))
{
while (!stack_empty(&obj->pushST)) {
stack_push(&obj->popST, stack_top(&obj->pushST));
stack_pop(&obj->pushST);
}
}
return stack_top(&obj->popST);
}
bool myQueueEmpty(MyQueue* obj) { //判空
return stack_empty(&obj->popST) && stack_empty(&obj->pushST);
}
void myQueueFree(MyQueue* obj) {
stack_destory(&obj->pushST); //销毁栈结构
stack_destory(&obj->popST);
free(obj);
}
用队列来实现栈
实现过程
获取栈顶元素:要在队列中获取正确的栈顶元素,需要先找到最后一个入队的元素,即队尾元素就是栈结构中的栈顶元素
代码:
1、调用队列操作:
typedef int qdatatype;
typedef struct qnode {
qdatatype _data;
struct qnode* _next;
}qnode;
typedef struct queue {
qnode* _head;
qnode* _tail;
int _size;
}queue;
void queue_init(queue * q)
{
if (q == NULL)
return;
q->_head = q->_tail = NULL;
q->_size = 0;
}
qnode* creatnode(qdatatype val) //创建新节点
{
qnode* node = (qnode*)malloc(sizeof(qnode));
node->_data = val;
node->_next = NULL;
return node;
}
void queue_push(queue* q, qdatatype val) //插入------队尾
{
qnode* node = creatnode(val);
if (q->_head == NULL)
q->_head = q->_tail = node;
else {
q->_tail->_next = node;
q->_tail = node;
}
++q->_size;
}
void queue_pop(queue* q) //删除------队头
{
if (q == NULL || q->_head == NULL)
return;
qnode* next = q->_head->_next;
free(q->_head);
q->_head = next;
//如果队列中只有一个数据
if (q->_head == NULL)
q->_tail = NULL;
--q->_size;
}
int queue_empty(queue* q) //判空
{
/*if (q == null || q->_head == null)
return 1;
return 0;*/
return q->_head == NULL;
}
qdatatype queue_front(queue* q) //取队头
{
/*if (q == null || q->_head == null)
return;*/
return q->_head->_data;
}
qdatatype queue_back(queue* q) //取队尾
{
return q->_tail->_data;
}
int queue_size(queue* q)
{
if (q == NULL)
return 0;
return q->_size;
}
void queue_destory(queue* q)
{
qnode* cur = q->_head;
while (cur) {
qnode* next = cur->_next;
free(cur);
cur = next;
}
q->_head = q->_tail = NULL;
}
2、定义队列来实现栈:
typedef struct {
queue q; //定义一个队列
}mystack;
mystack* mystackcreat()
{
//动态创建一个队列栈
mystack* pst = (mystack*)malloc(sizeof(mystack));
queue_init(&pst->q); //进行初始化
return pst;
}
void mystack_push(mystack* obj, int x)
{
queue_push(&obj->q, x); //进栈 -- 进队
}
int mystack_pop(mystack* obj) //出栈:要找到入队的最后一个元素--相当于是栈顶元素
{
int n = queue_size(&obj->q);
while (n > 1) {
int front = queue_front(&obj->q);
queue_pop(&obj->q);
queue_push(&obj->q,front);
--n;
}
int top = queue_front(&obj->q); //找到栈顶元素
queue_pop(&obj->q); //出栈
return top;
}
int mystack_top(mystack* obj) //栈顶元素---入队的队尾元素
{
return queue_back(&obj->q);
}
bool mystack_empty(mystack* obj)
{
return queue_empty(&obj->q);
}
bool mystack_size(mystack* obj)
{
return queue_size(&obj->q);
}
void mystack_free(mystack* obj)
{
queue_destory(&obj->q);
free(obj);
}
本篇主要要理解并熟悉栈与队列的特点,同时熟练掌握基本的入栈出栈(入队出队)、判空,获取某一端元素等基本过程,读者可以通过画图来增加理解并练习!
(博客内容全为原创,有任何问题都可以评论私聊哦!欢迎发现不足^ .^)