栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底
栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则
压栈(Push):栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
出栈(Pop):栈的删除操作叫做出栈。出数据也在栈顶
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小
typedef int StackDataType;
typedef struct Stack
{
StackDataType * a;
int top; // 栈顶
int capacity;
}StackType;
void StackTypeInit(StackType* ps)
{
assert(ps);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0; // ps->top = -1
}
栈初始化 ,top可以给-1 ,top可以给 0
压栈需要考虑容量的问题 ,如果容量已满 ,则需要扩容
void StackTypePush(StackType* ps, StackDataType x)
{
// 容量已满 ,考虑增容
if (ps->capacity == ps->top)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
StackType* tmp = (StackType*)realloc(ps->a, sizeof(StackType) * newcapacity);
if (tmp == NULL)
{
printf("realloc fail");
exit(-1);
}
//增容成功
ps->a = tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
void StackTypeDestory(StackType* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
当栈的数据为空时 此时注意不需要删除了
void StackTypePop(StackType* ps)
{
assert(ps);
assert(ps->top > 0);
ps->top--;
}
int StackSize(StackType* ps)
{
assert(ps);
return ps->top;
}
如果栈为空 ,此时就不需要进行出栈操作 ,否则会导致越界,形成非法访问
bool StackEmpty(StackType* ps)
{
assert(ps);
return ps->top == 0 ; // 栈为空 返回true ,不为空 返回 false
}
StackDataType StackTop(StackType* ps)
{
assert(ps);
assert(!StackEmpty(ps)); //StackEmpty 是空 return true 不是空return false
return ps->a[ps->top - 1]; // 初始化是top = 0 是指向栈顶的下一个数据 top-1 即是栈顶数据
}
Stack.c
#include"Stack.h"
void StackTypeInit(StackType* ps)
{
assert(ps);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0; // ps->top = -1
}
void StackTypePush(StackType* ps, StackDataType x)
{
// 容量已满 ,考虑增容
if (ps->capacity == ps->top)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
StackType* tmp = (StackType*)realloc(ps->a, sizeof(StackType) * newcapacity);
if (tmp == NULL)
{
printf("realloc fail");
exit(-1);
}
//增容成功
ps->a = tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
void StackTypeDestory(StackType* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
void StackTypePop(StackType* ps)
{
assert(ps);
assert(ps->top > 0);
ps->top--;
}
int StackSize(StackType* ps)
{
assert(ps);
return ps->top;
}
bool StackEmpty(StackType* ps)
{
assert(ps);
return ps->top;
}
StackDataType StackTop(StackType* ps)
{
assert(ps);
assert(!StackEmpty(ps)); //StackEmpty 是空 return true 不是空return false
return ps->a[ps->top - 1]; // 初始化是top = 0 是指向栈顶的下一个数据 top-1 即是栈顶数据
}
Stack.h
#pragma once
#include
#include
#include
#include
typedef int StackDataType;
typedef struct Stack
{
StackDataType * a;
int top; // 栈顶
int capacity;
}StackType;
void StackTypeInit(StackType* ps);
void StackTypePush(StackType* ps, StackDataType x);
void StackTypeDestory(StackType* ps);
void StackTypePop(StackType* ps);
int StackSize(StackType* ps); // 栈中有效元素个数
bool StackEmpty(StackType* ps); //判断栈是否为空
StackDataType StackTop(StackType* ps); //拿到栈顶数据
test.c
#include"Stack.h"
void TestStack1()
{
StackType ST;
StackTypeInit(&ST);
StackTypePush(&ST, 1);
StackTypePush(&ST, 2);
printf("%d", StackTop(&ST)); //拿到栈顶元素
StackTypePop(&ST);
StackTypePush(&ST, 3);
StackTypePush(&ST, 4);
StackTypeDestory(&ST);
}
void TestStack2()
{
StackType ST;
StackTypeInit(&ST);
StackTypePush(&ST, 1);
StackTypePush(&ST, 2);
StackTypePush(&ST, 3);
StackTypePush(&ST, 4);
StackTypePush(&ST, 5);
//遍历
while (!StackEmpty(&ST))
{
printf("%d ", StackTop(&ST));
StackTypePop(&ST);
}
StackTypeDestory(&ST);
}
void TestStack3()
{
StackType ST;
StackTypeInit(&ST);
StackTypePush(&ST, 1);
StackTypePush(&ST, 2);
printf("%d ", StackTop(&ST));
StackTypePop(&ST);
StackTypePush(&ST, 3);
StackTypePush(&ST, 4);
printf("%d ", StackTop(&ST));
StackTypePop(&ST);
StackTypePush(&ST, 5);
//遍历
while (!StackEmpty(&ST))
{
printf("%d ", StackTop(&ST));
StackTypePop(&ST);
}
StackTypeDestory(&ST);
}
int main()
{
//TestStack1();
//TestStack2();
TestStack3();
return 0;
}
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低
我们采用类似单链表来实现队列 ,但是队列要求队尾入队,队头出队 ,也就意味着需要频繁找尾指针 ,而单链表去找尾指针效率很低,所以定义一个头指针和尾指针,方便找尾节点,这样就会多两个指针 ,如果放进函数里当参数会很麻烦 ,干脆直接封装一个结构体
单链表不定义一个尾指针呢?如果我们给单链表定义了一个尾指针的话,尾插就不再需要去找尾节点了,但是尾删的话,定义的尾指针就显得很鸡肋,此时尾删依然需要找到尾节点的前一个节点,也就说如果单链表定义尾指针,也不能完美解决问题,所以单链表就不定义尾指针
typedef int QueueNodeTypeData;
typedef struct QueueNode
{
struct QueueNode* next;
QueueNodeTypeData data;
}QueueNodeType;
typedef struct Queue
{
QueueNodeType * tail; // 队尾
QueueNodeType * head; //队头
int size;
} Queue; // 链式结构:表示队列
将头和尾初始化为NULL
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
pq->size = 0;
}
和单链表尾插大致相同
void QueuePush(Queue* pq, QueueNodeTypeData x) // 入队
{
assert(pq);
QueueNodeType* newnode =(QueueNodeType*) malloc( sizeof(QueueNodeType) );
if (newnode == NULL)
{
printf(" malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
//扩容成功
//第一次入队
if ( pq->head == NULL)
{
assert(pq->tail == NULL); //pq->head ==NULL , pq->tail != NULL ,就是出问题了
pq->tail = pq->head = newnode;
}
//非第一次入队
else
{
pq->tail->next = newnode; // 类似尾插
pq->tail = newnode; // 更新tail 指针
}
pq->size++;
}
需要注意的是保存下一个节点的地址
void QueueDestory(Queue* pq) //队列的销毁
{
assert(pq);
QueueNodeType * cur = pq->head;
//遍历
while (cur)
{
QueueNodeType* next = cur->next; //保存下一个节点的地址
free(cur); //释放当前节点
cur = next; //不能写成cur=cur->next
}
pq->tail = pq->head = NULL;
pq->size = 0;
}
int QueueSize(Queue* pq)//获取队列中有效元素个数
{
assert(pq);
return pq->size;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
QueueNodeTypeData QueueFront(Queue* pq) //获取队列头部元素
{
assert(pq);
assert(!QueueEmpty(pq)); //防止队列为空
return pq->head->data;
}
QueueNodeTypeData QueueBack(Queue* pq) //获取队列尾部元素
{
assert(pq);
assert(!QueueEmpty(pq)); //防止队列为空
return pq->tail->data;
}
Queue.h
#include
#include
#include
#include
typedef int QueueNodeTypeData;
typedef struct QueueNode
{
struct QueueNode* next;
QueueNodeTypeData data;
}QueueNodeType;
typedef struct Queue
{
QueueNodeType * tail; // 队尾
QueueNodeType * head; //队头
int size;
} Queue; // 链式结构:表示队列
void QueueInit(Queue* pq); // 初始化
void QueueDestory(Queue* pq); //队列的销毁
void QueuePush(Queue* pq , QueueNodeTypeData x ); // 队尾入队列
void QueuePop(Queue* pq); //队头出队列
int QueueSize(Queue* pq);//获取队列中有效元素个数
bool QueueEmpty(Queue* pq); // 判断队列是否为空
QueueNodeTypeData QueueFront(Queue* pq); //获取队列头部元素
QueueNodeTypeData QueueBack(Queue* pq); //获取队列尾部元素
Queue.c
#include"Queue.h"
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
pq->size = 0;
}
void QueueDestory(Queue* pq) //队列的销毁
{
assert(pq);
QueueNodeType * cur = pq->head;
//遍历
while (cur)
{
QueueNodeType* next = cur->next; //保存下一个节点的地址
free(cur); //释放当前节点
cur = next;
}
pq->tail = pq->head = NULL;
pq->size = 0;
}
void QueuePush(Queue* pq, QueueNodeTypeData x) // 入队
{
assert(pq);
QueueNodeType* newnode =(QueueNodeType*) malloc( sizeof(QueueNodeType) );
if (newnode == NULL)
{
printf(" malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
//扩容成功
//第一次入队
if ( pq->head == NULL)
{
assert(pq->tail == NULL); //pq->head ==NULL , pq->tail != NULL ,就是出问题了
pq->tail = pq->head = newnode;
}
else //非第一次入队
{
pq->tail->next = newnode; // 类似尾插
pq->tail = newnode; // 更新tail 指针
}
pq->size++;
}
void QueuePop(Queue* pq) //队头出队列
{
assert(pq);
assert(pq->head != NULL);
//只有一个节点
if (pq->head->next == NULL)
{
free(pq->tail); //出队
pq->tail = pq->head = NULL;
}
// 多个节点
else
{
QueueNodeType* next = pq->head->next; // 保存下一个节点的地址
free(pq->head); // 出队
pq->head = NULL;
pq->head = next; //更新pq->head ,往后走
}
pq->size--;
}
int QueueSize(Queue* pq)//获取队列中有效元素个数
{
assert(pq);
return pq->size;
}
//bool QueueEmpty(Queue* pq) // 判断队列是否为空
//{
// assert(pq);
//
// if (pq->head == 0)
// {
// return true;
// }
// else
// {
// return false;
// }
//}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
QueueNodeTypeData QueueFront(Queue* pq) //获取队列头部元素
{
assert(pq);
assert(!QueueEmpty(pq)); //防止队列为空
return pq->head->data;
}
QueueNodeTypeData QueueBack(Queue* pq) //获取队列尾部元素
{
assert(pq);
assert(!QueueEmpty(pq)); //防止队列为空
return pq->tail->data;
}
Test.c
#include"Queue.h"
void TestQueue1()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
QueueDestory(&q);
}
void TestQueue2()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
QueuePop(&q);
QueuePop(&q);
QueuePop(&q);
QueueDestory(&q);
}
void TestQueue3()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueueEmpty(&q);
QueueDestory(&q);
}
void TestQueue4()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
QueueSize(&q);
QueueDestory(&q);
}
void TestQueue5()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
QueueFront(&q);
QueueBack(&q);
QueueDestory(&q);
}
int main()
{
//TestQueue1();
//TestQueue2();
//TestQueue3();
//TestQueue4();
TestQueue5();
return 0;
}
如果你觉得这篇文章对你有帮助,不妨动动手指给点赞收藏加转发,给鄃鳕一个大大的关注
你们的每一次支持都将转化为我前进的动力!!!