栈
首先来看一下栈的逻辑结构
当栈里没有元素时栈顶和栈底相等。只能从栈顶出入数据。
1.1栈的概念及结构
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
当栈内没有数据时 栈底也就是栈顶
注意:
这里所说的LIFO 后进先出指的是栈内的数据元素的后进先出例如一组数据
1 2 3 4 5
入栈顺序为 1 2 3 4 5
而出栈顺序可以为 2 3 4 5 1 、1 2 4 5 3、1 2 3 5 4、2 3 1 4 5等等
例如 2 3 1 4 5 先将 1 2 压栈 2 出栈 再把 3 压栈 3 出栈 1 再 出栈 然后 4 压栈 4出栈 5 压栈 5 出栈 所以出栈顺序为 2 3 1 4 5 体现了LFIO 2 比 1 后入栈 所以 2 先 出栈 3 比 1后入栈 所以 3 先出栈 栈里只剩下 1 1 出栈 4 5 也是如此
既然栈是只有一个地方可以出入数据那么使用哪种存储类型可以比较方便的实现一个栈呢?
根据我们所学的知识可以知道是可以用链表或者数组实现的,但是因为链表在尾插的时候需要找尾 代价会比较大 因此我们使用数组进行实现
这样在插入数据的时候就比较简单尾删也比较简单
详细代码:
#pragma once
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
typedef int STDataType;// 类型重定义
typedef struct Stack
{
STDataType* _array;//定义一个一维数组
size_t _top;
size_t _capacity;
}Stack;
void StackInit(Stack* ps);//栈的初始化
void StackDestory(Stack* ps);//栈的销毁
void StackPush(Stack* ps, STDataType x);//入栈
void StackPop(Stack* ps);//出栈 即删除栈顶的元素
STDataType StackTop(Stack* ps);//取栈顶的元素 即出栈
size_t StackSize(Stack* ps);//返回栈顶的元素
void StackInit(Stack* ps)
{
assert(ps);
ps->_array = NULL;//数组置为空
ps->_top = 0;//初始时top为零
ps->_capacity = 0;//容量也为零 因此需要动态申请内存
}
void StackDestory(Stack* ps)
{
assert(ps);
if (ps->_array != NULL)//当数组不为空时 进行销毁
{
free(ps->_array);
ps->_array = NULL;
ps->_capacity = ps->_top = 0;
}
}
void StackPush(Stack* ps, STDataType x)
{
assert(ps);
if (ps->_top == ps->_capacity)//当top和容量相等说明栈满了需要增容
{
size_t newcapacity = ps->_capacity == 0 ? 2 :ps->_capacity * 2;//2倍增容
ps->_array = (STDataType*)realloc(ps->_array, newcapacity*sizeof(STDataType));//但内存不足时需要
//重新分配内存 但内存地址有可能发生变化 因此需要更新 arry的地址
ps->_capacity = newcapacity;//再把新的容量给给capacity
}
ps->_array[ps->_top] = x;//放入数据
ps->_top++;//top++
}
void StackPop(Stack* ps)
{
assert(ps && ps->_top > 0);//判断是否有数据
--ps->_top;//删除数据--top即可 相当于 -- 数组下标
}
STDataType StackTop(Stack* ps)
{
assert(ps && ps->_top > 0);
return ps->_array[ps->_top - 1];//注意当数组有数据时top并非栈顶 而是下一个位置 因此需要-1获取栈顶的数据
}
size_t StackSize(Stack* ps)
{
assert(ps);
return ps->_top;
}
void TestStack()
{
Stack s;
StackInit(&s);
StackPush(&s, 1);
StackPush(&s, 2);
StackPush(&s, 3);
StackPush(&s, 4);
while (StackSize(&s))//
{
printf("%d ", StackTop(&s));//取一个栈顶的元素
StackPop(&s);//删除栈顶的元素
}
printf("\n");
StackDestory(&s);
}
int main()
{
TestStack();
system("pause");
return 0;
}
队列
1.队列的概念及结构
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头
下方为队列的逻辑结构:
队列的存储可以使用链表和数组来实现 但是因为数组在插入数据的时候需要不断地移动数据因此时间复杂度比较高
因此使用链表来实现队列 因为队列从队尾进队尾出 因此在队头实现删除 在队尾实现插入 比较简单方便。
具体的看代码的实现:
#pragma once
#include <assert.h>
#include <stdio.h>
#include <malloc.h>
#include<stdlib.h>
typedef int QUDataType;//重定义对列的数据类型
typedef struct QueueNode
{
struct QueueNode* _next;//QueueNode ;里面包含下一个结点 还包括一个值
QUDataType _data;
}QueueNode;//重定义结构体的名称为QueueNode
typedef struct Queue//定义一个队列结构体 里面包含一个队头和一个队尾
{
QueueNode* _front;
QueueNode* _tail;
}Queue;//重命名结构体
void QueueInit(Queue* pq);//初始化
void QueueDestory(Queue* pq);//销毁
QueueNode* BuyQueueNode(QUDataType x);//申请内存 建立新的结点
void QueuePush(Queue* pq, QUDataType x);//插入数据尾插
void QueuePop(Queue* pq);//删除数据 头删
QUDataType QueueFront(Queue* pq);//取队头的数据
QUDataType QueueBack(Queue* pq);//取队尾的数据
int QueueSize(Queue* pq);//获取队列的大小
void QueueInit(Queue* pq)
{
assert(pq);
pq->_front = pq->_tail = NULL;//首先将队头和队尾都置成空
}
void QueueDestory(Queue* pq)
{
QueueNode* cur = pq->_front;//从队头开始删除结点
while (cur)
{
QueueNode* next = cur->_next;//保存下一个结点删除当前结点
free(cur);
cur = next;//循环走下去的方式
}
pq->_front = pq->_tail = NULL;//删除后把最后一个数据也就是队头和队尾都置空
}
QueueNode* BuyQueueNode(QUDataType x)
{
QueueNode* node = (QueueNode*)malloc(sizeof(QueueNode));//malloc为新的结点申请内存
node->_data = x;//赋值
node->_next = NULL;//暂时将下一个结点置为空
return node;
}
void QueuePush(Queue* pq, QUDataType x)
{
QueueNode* newnode = BuyQueueNode(x);//先申请新的结点
assert(pq);
if (pq->_tail == NULL)//当目前没有结点的时候
{
pq->_front = pq->_tail = newnode;//队头和队尾都是第一个插入的数据
}
else
{
pq->_tail->_next = newnode;//当队列已经存在结点的时候就采取尾插的方法 将队尾的下一个
//链到新结点
pq->_tail = newnode;
}
}
void QueuePop(Queue* pq)
{
QueueNode* next = pq->_front->_next;//头删 先存好队头的下一个结点 再删除掉队头的结点
assert(pq && pq->_front != NULL);
free(pq->_front);
pq->_front = next;
if (pq->_front == NULL)//当队头为空的时候 把队尾也置成空
{
pq->_tail = NULL;
}
}
QUDataType QueueFront(Queue* pq)
{
assert(pq && pq->_front != NULL);
return pq->_front->_data;//返回队头的数据
}
QUDataType QueueBack(Queue* pq)
{
assert(pq && pq->_tail != NULL);
return pq->_tail->_data;//返回队尾的数据
}
int QueueSize(Queue* pq)//获取队列的大小长度
{
QueueNode* cur = pq->_front;
size_t size = 0;
assert(pq);
while (cur)
{
++size;
cur = cur->_next;
}
return size;
}
void TestQueue()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
while (QueueSize(&q)>0)
{
printf("%d ", QueueFront(&q));//取队头的数据也就是出队
QueuePop(&q);//删除队头的元素 便于取下一个元素
}
printf("\n");
}
int main()
{
TestQueue();
system("pause");
return 0;
}