文章目录
1.栈
1.1栈的概念及结构
定义:一种特殊的线性表。
性质:后进先出;
栈顶:进行数据插入删除的一段称作栈顶;
栈底:另一端称作栈底;
压栈:栈的插入操作叫做进栈/入栈,人数据在栈顶;
出栈:栈的删除操作叫做出栈,出数据也在栈顶;
示例:
1.2栈的实现
1.数组栈
刚好符合栈的单向插入;
2.链式栈
双链表怎么都可以;
但是单链表建议将链表的头设为栈顶,栈尾设尾栈底;
2.栈的实现
头文件
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int STDateType;
typedef struct strack
{
struct strack* pa;
STDateType top;
STDateType capacity;
}ST;
void STInit(ST* pst);
void STDestroy(ST* pst);
void STPush(ST* pst, STDateType x);
void STpop(ST* pst);
STDateType STTop(ST* pst);
bool STEmpty(ST* pst);
int STsize(ST* pst);
2.1栈的初始化
void STInit(ST* pst)
{
pst->pa = NULL;
//pst->top=-1;
pst->top =0;//这里的数值的取值不同,top的指向不同;这里两者都可;
pst->capacity = 0;
}
这里的top的取值不同;top的指向不同;
top为0时:top指针的指向在栈顶的下一位;
top为-1时:top指针的指向 在栈顶位置;
这里top可以;联想到数组的下表;0—正无穷
是合理 的栈顶位置;-1是不存在的;
2.2栈的添加
void STPush(ST* pst, STDateType x)
{
assert(pst);
//扩容
if (pst->top == pst->capacity)
{
int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
STDateType* tmp = (STDateType*)realloc(pst->a, newcapacity * sizeof(STDateType));
//增加内存需要判断内存是否扩容成功
if (tmp == NULL)
{
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newcapacity;
//这里top不需要将其改变原因:top已经在改在的位置了;
}
pst->a[pst->top] = x;
pst->top++;
}
top不需要将其改变原因:top已经在改在的位置了
2.3栈的删除
void STpop(ST* pst)
{
assert(pst);
//删除这里出现问题:当pst的top指针指向0之前的数据原本不应该再次删除;
assert(!STEmpty(pst));
pst->top--;
}
2.4返回栈顶元素
返回栈顶元素//这里主函数不要使用结构体来访问栈顶元素,原因:这里的栈顶元素有两种方式;使用者不知道top是0还是-1;
STDateType STTop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top-1];//这里由于vs2019的版本不同就会导致越界不会报错;
}
2.5检查栈是否为空
检查栈顶是否为空;
bool STEmpty(ST* pst)
{
assert(pst);
//if (pst->top == 0)
//{
// return true;
//}
//else
//return false;
return pst->top == 0;
}
2.6 查看栈内部元素的个数
int STsize(ST* pst)
{
assert(pst);
return pst->top;
}
完整代码
//头文件
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int STDateType;
typedef struct strack
{
STDateType* a;
STDateType top;
STDateType capacity;
}ST;
void STInit(ST* pst);
void STDestroy(ST* pst);
void STPush(ST* pst, STDateType x);
void STpop(ST* pst);
STDateType STTop(ST* pst);
bool STEmpty(ST* pst);
int STsize(ST* pst);
//主文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"strack.h"
void text1()
{
ST st;
STInit(&st);
STPush(&st, 1);
STPush(&st, 2);
STPush(&st, 3);
//栈无法实现打印函数;
//所以使用出栈来元素输出;
while (!STEmpty(&st))
{
printf("%d ", STTop(&st));
STpop(&st);//删除栈顶
}
STDestroy(&st);
}
int main()
{
text1();
return 0;
}
//函数体
#define _CRT_SECURE_NO_WARNINGS 1
#include"strack.h""
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = 0;//这里的数值的取值不同,top的指向不同;
pst->capacity = 0;
}
void STDestroy(ST* pst)
{
assert(pst);
pst->top=0;
pst->capacity = 0;
free(pst->a);
pst->a = NULL;
}
void STPush(ST* pst, STDateType x)
{
assert(pst);
//扩容
if (pst->top == pst->capacity)
{
int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
STDateType* tmp = (STDateType*)realloc(pst->a, newcapacity * sizeof(STDateType));
//增加内存需要判断内存是否扩容成功
if (tmp == NULL)
{
perror("realloc fail");
return;
}
pst->a = tmp;
pst->capacity = newcapacity;
//这里top不需要将其改变原因:top已经在改在的位置了;
}
pst->a[pst->top] = x;
pst->top++;
}
void STpop(ST* pst)
{
assert(pst);
//删除这里出现问题:当pst的top指针指向0之前的数据原本不应该再次删除;
assert(!STEmpty(pst));
pst->top--;
}
//返回栈顶元素//这里主函数不要使用结构体来访问栈顶元素,原因:这里的栈顶元素有两种方式;使用者不知道top是0还是-1;
STDateType STTop(ST* pst)
{
assert(pst);
assert(!STEmpty(pst));
return pst->a[pst->top-1];//这里由于vs2019的版本不同就会导致越界不会报错;
}
bool STEmpty(ST* pst)
{
assert(pst);
//if (pst->top == 0)
//{
// return true;
//}
//else
//return false;
return pst->top == 0;
}
int STsize(ST* pst)
{
assert(pst);
return pst->top;
}
3.队列
3.1.什么是队列:
定义:一种特殊的数据结构。只允许在一端插入数据,在另一端进行删除数据操纵的特殊线性表,队列具有—先进先出;
入队列:进行插入操作的一端称为队尾;
出队列:进行删除操作的一端称作队头;
3.2队列的实现
实现队列的实现;可以使用链表和数组实现。
由于队列的实现需要使用头删数列实现队列时间复杂度较高;所以使用链表来实现;
// 链式结构:表示队列
typedef struct QListNode
{
struct QListNode* _pNext;
QDataType _data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* _front;
QNode* _rear;
}Queue;
// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 获取队列队尾元素
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q)
3.2.1队列结构体的实现
由于使用链表来构造队列;这里链表的每一个元素是由数和指针组成的;整个队列需要指针
和计数的变量来判断;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType date;
}Qnode;
//整个链表的结构
typedef struct Queue
{
Qnode* phead;//头指针
Qnode* ptail;//尾指针
int size;//链表元素的个数
}Queue;
3.2.2队列初始化
将整个队列的结构体的指针和变量置为空和0;
1.添加
2.删除
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
Qnode* cur = pq->phead;
while (cur)
{//这里有两种方法一种是尾删,一种是头删,尾删除需要遍历;所以使用头删
Qnode* next = cur->next;
free(cur);
cur = next;
}
//指针置空加size为0
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
3.2.2队列的添加
使用一级指针原因:这里改变的是结构体的内容;这里需要考虑扩容和添加数据两个部分;
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
Qnode* newnode = (Qnode*)malloc(sizeof(Qnode));
if (newnode == NULL)
{
perror("malloc失败");
}
newnode->date = x;
newnode->next = NULL;
//这里是单链表就决定了这里的初始化需要分情况:什么也没有;已经有一些元素
if (pq->ptail == NULL)
{
assert(pq->phead == NULL);
pq->phead = pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
3.2.3队列的删除
这里的队列需要考虑三种情况;
0和1和多个元素3种情况;
void QueuePop(Queue* pq)
//头部删除
{
assert(pq);
//分三种情况
//0
assert(!QueueEmpty(pq));
//1:
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
//多个
else
{
Qnode* next = pq->phead->next;//这里可能出现空指针的调用;
free(pq->phead);
pq->phead = next;
}
pq->size--;
}
3.2.4找出队头
QDataType QueueFront(Queue* pq)
//选出对头
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->phead->date;
}
3.2.5找出队尾
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq-
3.2.6队列元素的个数
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
3.2.7判断队列是否为空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->phead == 0 && pq->ptail == 0;
}