目录
栈的概念与结构
栈的定义
栈的初始化
栈的销毁
入栈
出栈
获取栈顶元素
获取栈中有效数据的个数
判断栈是否为空
栈的完整代码
Stack.h
Stack.c
test.c
队列的概念与结构
队列的定义
队列的初始化
入队
出队
获取队头元素
获取队尾元素
获取队列的有效数据个数
判断队列是否为空
销毁队列
队列的完整代码
Queue.h
Queue.c
test.c
栈是一种特殊的线性存储结构,特点是只允许在一端进行插入与删除数据,也被称为“后进先出”。删除与插入的那一段被称为栈顶,另一端被称为栈底。
出栈:删除数据在栈顶进行。
压栈:插入数据在栈顶进行。
具体如图所示:
栈的实现用数组和链表均能实现,在这里我们使用时数组进行实现。
1、首先定义一个数组。
2、我们需要对栈顶的数据进行删除与插入,所以还要定义一个数来存放栈顶的位置。
3、由于是数组的实现所以我们需要考虑增容的情况,那么还要定义一个容量
综上所述:我们如果用一个结构体将他们封装起来就更好了,因此我们这里的定义运用结构体进行定义。 代码如下:
typedef int STDataType;//将int重命名 便于对数据类型的转换
typedef struct Stack
{
STDataType* a;//栈
STDataType top;//栈顶
STDataType capacity;//容量
}Stack;
分别将定义中的数据进行初始化,代码如下:
// 初始化栈
void StackInit(Stack* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
为了不造成空间的浪费,当我们所开辟的空间不用时,我们应该将他们给释放掉。
代码如下:
// 销毁栈
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
}
注意:如果栈满我们需要动态开辟空间。
图解如下:
代码如下:
// 入栈
void StackPush(Stack* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int newCapacity = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
STDataType* tmp = realloc(ps->a, sizeof(STDataType) * newCapacity);
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
ps->capacity = newCapacity;
ps->a = tmp;
}
ps->a[ps->top] = x;
ps->top++;
}
出栈相对于入栈来说就轻松多了,只需要判断一下是否为空栈后,直接将top--就可以了。
图解如下:
大家可以发现栈中的1并没有删除,但是我们已经访问不到了,并且如果再次入栈,将会覆盖之前的1,所以不必担心。
代码如下:
// 出栈
void StackPop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));//这里用到的是后面会提到的判断栈是否为空的函数
ps->top--;
}
由于top的值刚好是我们需要获取栈顶元素的下标,所以只要先判断栈是否为空,后返回top-1为下标的数就可以了。
代码如下:
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));//这里用到的是后面会提到的判断栈是否为空的函数
return ps->a[ps->top-1];
}
直接返回top就行了,因为top的值就是栈中数据的元素个数。
代码如下:
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
我们这里引用一个bool函数。bool函数的返回值非真既假,也就是他们的返回值就是用来判断真假的,当我们需要返回真假而不需要返回特定的值时来使用它。
代码如下:
// 检测栈是否为空,如果为空返回非零结果,
//如果不为空返回0
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == 0;
}
#pragma once
#include
#include
#include
#include
typedef int STDataType;
typedef struct Stack
{
STDataType* a;//栈
STDataType top;//栈顶
STDataType capacity;//容量
}Stack;
// 初始化栈
void StackInit(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType x);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
bool StackEmpty(Stack* ps);
#include"Stack.h"
// 初始化栈
void StackInit(Stack* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
// 销毁栈
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
}
// 入栈
void StackPush(Stack* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int newCapacity = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
STDataType* tmp = realloc(ps->a, sizeof(STDataType) * newCapacity);
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
ps->capacity = newCapacity;
ps->a = tmp;
}
ps->a[ps->top] = x;
ps->top++;
}
// 出栈
void StackPop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top-1];
}
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
// 检测栈是否为空,如果为空返回非零结果,
//如果不为空返回0
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == 0;
}
#include"Stack.h"
void TestStack1()
{
Stack ST;
StackInit(&ST);
StackPush(&ST, 1);
StackPush(&ST, 2);
StackPush(&ST, 3);
StackPush(&ST, 4);
printf("栈顶元素为:\n");
printf("%d\n", StackTop(&ST));
while(!StackEmpty(&ST))
{
printf("%d ", StackTop(&ST));
StackPop(&ST);
}
StackDestroy(&ST);
}
int main()
{
TestStack1();
return 0;
}
测试代码结果如图所示:
队列类似于排队一样,从一端入数据,另一端出数据。入数据的那一端被称为队尾,出数据的那一段被称为队头。
入队:在队尾进行入数据。
出队:在队头进行出数据。
如图所示:
1、我们使用链表来定义队列。
2、设置一个结构体来定义队头和队尾。
代码如下:
typedef int QDataType;
typedef struct QListNode
{
struct QListNode* next;
QDataType data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* front;
QNode* rear;
}Queue;
代码如下:
// 初始化队列
void QueueInit(Queue* q)
{
q->front = NULL;
q->rear = NULL;
}
由于使用的是链表,其操作类似单链表里面的尾插。如果单链表里面的尾插还不熟悉的请看我之前的博客。 如图所示:
代码如下:
// 队尾入队列
void QueuePush(Queue* q, QDataType x)
{
assert(q);
QNode* cur = BuyQueueNode(x);//这里定义一个函数来创建新结点
if (q->front== NULL)
{
q->front = q->rear = cur;
}
else
{
q->rear->next = cur;
q->rear = cur;
}
}
QNode* BuyQueueNode(QDataType x)
{
QNode* cur = (QNode*)malloc(sizeof(QNode));
cur->next = NULL;
cur->data = x;
return cur;
}
由于是链表,所以操作时将front指向下一个,并且释放front之前指向的那个。
如图所示:
代码如下:
// 队头出队列
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
QNode* next = q->front->next;
free(q->front);
q->front = next;
if (q->front==NULL)
{
q->rear = NULL;//防止野指针
}
}
注意:
如果只剩最后一个元素的时候,我们释放完front后需要将rear也置空,因为此时rear指向的数据已经被释放,如果不置空的话,rear将是野指针。
只需要判断一下队是否为空后,返回front指向的数据即可。
代码如下:
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));//这个是后面即将提到的判空函数
return q->front->data;
}
同上获取队头元素类似。
代码如下:
// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));//此函数是后面要提到的判空函数
return q->rear->data;
}
此操作类似于计算单链表的长度,只要逐个遍历从front到rear就可以了。
代码如下:
// 获取队列中有效元素个数
int QueueSize(Queue* q)
{
assert(q);
int n = 0;
QNode* cur = q->front;
while (cur)
{
cur = cur->next;
n++;
}
return n;
}
在此同样用到了判断栈是否为空的那个bool函数,这里不过多解释。
代码如下:
// 检测队列是否为空,如果为空返回非零结果,
//如果非空返回0
bool QueueEmpty(Queue* q)
{
assert(q);
return q->rear == NULL;
}
由于是链表,所以销毁时应该逐个结点释放。
代码如下:
// 销毁队列
void QueueDestroy(Queue* q)
{
assert(q);
QNode* cur = q->front;
while (cur)
{
/*QNode* next = cur->next;
free(cur);
cur->next;
next = next->next;
cur = next;*/
QueuePop(q);
}
q->front = q->rear = NULL;
}
#pragma once
// 链式结构:表示队列
#include
#include
#include
typedef int QDataType;
typedef struct QListNode
{
struct QListNode* next;
QDataType data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* front;
QNode* rear;
}Queue;
// 初始化队列
void QueueInit(Queue* q);
//创建一个新的结点
QNode* BuyQueueNode(QDataType x);
// 队尾入队列
void QueuePush(Queue* q, QDataType x);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 获取队列队尾元素
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,
//如果非空返回0
bool QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);
#include"Queue.h"
// 初始化队列
void QueueInit(Queue* q)
{
q->front = NULL;
q->rear = NULL;
}
// 队尾入队列
void QueuePush(Queue* q, QDataType x)
{
assert(q);
QNode* cur = BuyQueueNode(x);
if (q->front== NULL)
{
q->front = q->rear = cur;
}
else
{
q->rear->next = cur;
q->rear = cur;
}
}
QNode* BuyQueueNode(QDataType x)
{
QNode* cur = (QNode*)malloc(sizeof(QNode));
cur->next = NULL;
cur->data = x;
return cur;
}
// 队头出队列
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
QNode* next = q->front->next;
free(q->front);
q->front = next;
if (q->front==NULL)
{
q->rear = NULL;//防止野指针
}
}
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->front->data;
}
// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->rear->data;
}
// 获取队列中有效元素个数
int QueueSize(Queue* q)
{
assert(q);
int n = 0;
QNode* cur = q->front;
while (cur)
{
cur = cur->next;
n++;
}
return n;
}
// 检测队列是否为空,如果为空返回非零结果,
//如果非空返回0
bool QueueEmpty(Queue* q)
{
assert(q);
return q->rear == NULL;
}
// 销毁队列
void QueueDestroy(Queue* q)
{
assert(q);
QNode* cur = q->front;
while (cur)
{
/*QNode* next = cur->next;
free(cur);
cur->next;
next = next->next;
cur = next;*/
QueuePop(q);
}
q->front = q->rear = NULL;
}
#include"Queue.h"
void TestQueue()
{
Queue Q;
QueueInit(&Q);
QueuePush(&Q, 1);
QueuePush(&Q, 2);
QueuePush(&Q, 3);
QueuePush(&Q, 4);
printf("队头是:\n");
printf("%d\n", QueueFront(&Q));
printf("队尾是:\n");
printf("%d\n", QueueBack(&Q));
while (!QueueEmpty(&Q))
{
printf("%d ", QueueFront(&Q));
QueuePop(&Q);
}
QueueDestroy(&Q);
}
int main()
{
TestQueue();
return 0;
}
测试结果: