3.1栈
在线性表表尾进行插入和删除操作,表尾指栈顶,而不是栈底。特殊之处在于限制了这个线性表的插入和删除的位置,它始终只在栈顶进行,也使得:栈底是固定的,最先进栈的只能在栈底。
1 栈的顺序存储 ---- 利用数组
设计思路
1.1 栈 属于 先进后出的数据结构
//栈的结构体
struct SStack
{
void * data[MAX]; //数组
//栈的元素个数
int m_Size;
};
1.2 初始化栈 init
//初始化栈
seqStack init_SeqStack()
{
struct SStack * stack = malloc(sizeof(struct SStack));
if (stack == NULL)
{
return NULL;
}
//清空数组中的每个元素
memset(stack->data, 0, sizeof(void*)*MAX);
stack->m_Size = 0;
return stack;
}
1.3 入栈 push
//入栈
void push_SeqStack( seqStack stack, void * data)
{
if (stack == NULL)
{
return;
}
if (data == NULL)
{
return;
}
//判断是否已经栈满 ,如果满了 不可以再入栈了
struct SStack * myStack = stack;
if (myStack->m_Size == MAX)
{
return;
}
myStack->data[myStack->m_Size] = data; //入栈 尾插
myStack->m_Size++; //更新栈大小
}
1.4 出栈 pop
//出栈
void pop_SeqStack(seqStack stack)
{
if (stack == NULL)
{
return;
}
//如果是空栈 不执行出栈
struct SStack * myStack = stack;
if (myStack->m_Size <= 0)
{
return;
}
//执行出栈
myStack->data[myStack->m_Size - 1] = NULL;
//更新栈的大小
myStack->m_Size--;
}
1.5 访问栈顶元素 top
//获取栈顶元素
void * top_SeqStack(seqStack stack)
{
if (stack == NULL)
{
return NULL;
}
struct SStack * myStack = stack;
//如果是空栈 返回 NULL
if (myStack->m_Size == 0)
{
return NULL;
}
return myStack->data[myStack->m_Size - 1];
}
1.6 返回栈大小 size
//栈的大小
int size_SeqStack(seqStack stack)
{
if (stack == NULL)
{
return -1;
}
struct SStack * myStack = stack;
return myStack->m_Size;
}
1.7 判断栈是否为空 isEmpty
//判断栈是否为空
int isEmpty_SeqStack(seqStack stack)
{
if (stack == NULL)
{
return -1; //真
}
struct SStack * myStack = stack;
if (myStack->m_Size <= 0)
{
return 1; //真
}
return 0; //返回假 代表不是空栈
}
1.8 销毁栈 destroy
//销毁栈
void destroy_SeqStack(seqStack stack)
{
if (stack == NULL)
{
return;
}
free(stack);
stack = NULL;
}
1.9测试
//测试
struct Person
{
char name[64];
int age;
};
void test01()
{
//准备出数据
struct Person p1 = { "aaa", 10 };
struct Person p2 = { "bbb", 20 };
struct Person p3 = { "ccc", 30 };
struct Person p4 = { "ddd", 40 };
struct Person p5 = { "eee", 50 };
//初始化栈
seqStack stack = init_SeqStack();
//入栈
push_SeqStack(stack, &p1);
push_SeqStack(stack, &p2);
push_SeqStack(stack, &p3);
push_SeqStack(stack, &p4);
push_SeqStack(stack, &p5);
while ( isEmpty_SeqStack(stack) == 0 ) //如果栈不为空 进行访问栈顶元素,并且出栈
{
struct Person * pTop = top_SeqStack(stack); //栈顶元素
printf("栈顶元素 姓名: %s 年龄: %d\n", pTop->name, pTop->age);
//出栈
pop_SeqStack(stack);
}
//栈的大小
int size = size_SeqStack(stack);
printf("栈的大小为:%d\n", size);
//销毁栈
destroy_SeqStack(stack);
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
2 栈的链式存储 ---- 利用链表
设计思路
和顺序存储的对外接口完全一
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct StackNode
{
struct StackNode * next; //只维护指针域
};
//链式的栈结构体
struct LStack
{
struct StackNode pHeader; //头节点
int m_Size; //栈的大小
};
typedef void * LinkStack;
//初始化栈
LinkStack init_LinkStack()
{
struct LStack * myStack = malloc(sizeof(struct LStack));
if ( myStack == NULL)
{
return NULL;
}
myStack->pHeader.next = NULL;
myStack->m_Size = 0;
return myStack;
}
//入栈
void push_LinkStack( LinkStack stack , void * data)
{
if ( stack == NULL)
{
return;
}
if ( data == NULL)
{
return;
}
//入栈 就是 头插
struct LStack * myStack = stack;
//拿到用户数据的前四个字节
struct StackNode * myNode = data;
//插入节点
myNode->next = myStack->pHeader.next;
myStack->pHeader.next = myNode;
//更新栈的大小
myStack->m_Size++;
}
//出栈
void pop_LinkStack( LinkStack stack )
{
if (stack == NULL)
{
return;
}
struct LStack * myStack = stack;
//如果是空栈 不出栈了
if ( myStack->m_Size <= 0 )
{
return;
}
//保存第一个有数据的节点 栈顶元素
struct StackNode * pFirst = myStack->pHeader.next;
myStack->pHeader.next = pFirst->next;
//更新链表长度 栈大小
myStack->m_Size--;
}
//返回栈顶元素
void * top_LinkStack(LinkStack stack)
{
if ( stack == NULL)
{
return NULL;
}
struct LStack * myStack = stack;
if (myStack->m_Size <= 0 )
{
return NULL;
}
return myStack->pHeader.next; //将第一个有数据的节点返回就可以了
}
//返回栈大小
int size_LinkStack(LinkStack stack)
{
if (stack == NULL)
{
return -1;
}
struct LStack * myStack = stack;
return myStack->m_Size;
}
//判断是否为空
int isEmpty_LinkStack(LinkStack stack)
{
if (stack == NULL)
{
return -1;
}
struct LStack * myStack = stack;
if (myStack->m_Size <= 0 )
{
return 1;
}
return 0;
}
//销毁
void destroy_LinkStack(LinkStack stack)
{
if (stack == NULL)
{
return ;
}
free(stack);
stack = NULL;
}
//测试
struct Person
{
struct StackNode node;
char name[64];
int age;
};
void test01()
{
//准备出数据
struct Person p1 = { NULL,"aaa", 10 };
struct Person p2 = { NULL,"bbb", 20 };
struct Person p3 = { NULL,"ccc", 30 };
struct Person p4 = { NULL,"ddd", 40 };
struct Person p5 = { NULL,"eee", 50 };
//初始化栈
LinkStack stack = init_LinkStack();
//入栈
push_LinkStack(stack, &p1);
push_LinkStack(stack, &p2);
push_LinkStack(stack, &p3);
push_LinkStack(stack, &p4);
push_LinkStack(stack, &p5);
while (isEmpty_LinkStack(stack) == 0) //如果栈不为空 进行访问栈顶元素,并且出栈
{
struct Person * pTop = top_LinkStack(stack); //栈顶元素
printf("栈顶元素 姓名: %s 年龄: %d\n", pTop->name, pTop->age);
//出栈
pop_LinkStack(stack);
}
//栈的大小
int size = size_LinkStack(stack);
printf("栈的大小为:%d\n", size);
//销毁栈
destroy_LinkStack(stack);
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
3 栈的应用- 就近匹配案例
设计思路
3.1 扫描字符串
3.2 准备栈
3.3 如果是左括号 ---- 入栈
3.4 如果是右括号
3.4.1 栈是否为空
3.4.2 如果不为空 出栈
3.4.3 如果为空 立即停止 并且报错
3.5 遍历所有字符后 ,判断栈是否为空
3.5.1 如为空 没问题
3.5.2 如果不为空 报错 左括号没有匹配到对应的右括号
3.6 销毁栈
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include "seqStack.h"
/*
从第一个字符开始扫描
当遇见普通字符时忽略,
当遇见左括号时压入栈中
当遇见右括号时从栈中弹出栈顶符号,并进行匹配
匹配成功:继续读入下一个字符
匹配失败:立即停止,并报错
结束:
成功: 所有字符扫描完毕,且栈为空
失败:匹配失败或所有字符扫描完毕但栈非空
*/
//判断字符是否是左括号
int IsLeft(char ch)
{
return ch == '(';
}
//判断字符是否是右括号
int IsRight(char ch)
{
return ch == ')';
}
void printError(char * str, char * errMsg,char * pos)
{
printf("错误的信息:%s\n", errMsg);
printf("%s\n", str);
//计算打印空格数量
int num = pos - str;
for (int i = 0; i < num; i++)
{
printf(" ");
}
printf("A\n");
}
void test01()
{
char * str = "5+5*(6)+9/3*1-(1+3(";
char * p = str;
//初始化栈
seqStack stack = init_SeqStack();
while ( *p != '\0')
{
//如果是左括号 入栈
if (IsLeft(*p))
{
push_SeqStack(stack, p);
}
//如果是右括号 弹出栈中的栈顶元素
if (IsRight(*p))
{
//如果栈的元素个数>0 说明 可以匹配
if (size_SeqStack(stack)>0)
{
//弹出栈顶元素
pop_SeqStack(stack);
}
else //空栈 匹配失败
{
printError(str, "右括号没有匹配到左括号",p);
break;
}
}
p++;
}
//判断栈是否为空栈
while ( size_SeqStack(stack) > 0)
{
printError(str, "左括号没有匹配到右括号", top_SeqStack(stack));
//弹出栈顶元素
pop_SeqStack(stack);
}
//销毁栈
destroy_SeqStack(stack);
stack = NULL;
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
4 中缀表达式和后缀表达式
4.1 中缀是符合人类习惯
4.2 后缀符合计算机使用
4.3 中缀如何转为后缀表达式
4.4 计算机如果利用后缀表达式进行计算
3.2队列
基本概念
- 队列是一种特殊的受限制的线性表
- 队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表
- 队列是一种先进先出的线性表,简称FIFO。允许插入的一端为队尾,允许删除的一端为队头,队列不允许在中间部位进行操作。
1 队列---顺序存储
1.1 利用写好的动态数组实现 顺序存储的队列数据结构
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//动态数组结构体
struct dynamicArray
{
void ** pAddr; //维护在堆区真实数组指针
int m_capacity; //数组容量
int m_size; //数组大小
};
//初始化数组
struct dynamicArray * init_DynamicArray(int capacity)
{
if (capacity <= 0)
{
return NULL;
}
struct dynamicArray * array = malloc(sizeof(struct dynamicArray));
//判断内存是否申请成功
if (array == NULL)
{
return NULL;
}
//设置容量
array->m_capacity = capacity;
//设置大小
array->m_size = 0;
//维护在堆区数组的指针
array->pAddr = malloc(sizeof(void*)*array->m_capacity);
return array;
}
//插入功能
void insert_dynamicArray(struct dynamicArray * array, int pos, void * data)
{
if (array == NULL)
{
return;
}
if (data == NULL)
{
return;
}
if (pos < 0 || pos > array->m_size)
{
//无效的位置 进行尾插
pos = array->m_size;
}
//先判断是否已经 满载 ,如果满载了 动态开辟
if (array->m_size >= array->m_capacity)
{
//1、申请一个更大的内存空间
int newCapacity = array->m_capacity * 2;
//2、创建新空间
void ** newSpace = malloc(sizeof(void *)* newCapacity);
//3、将原有数据 拷贝到新空间下
memcpy(newSpace, array->pAddr, sizeof(void*)*array->m_capacity);
//4、释放原有空间
free(array->pAddr);
//5、更改指针指向
array->pAddr = newSpace;
//6、更新新容量大小
array->m_capacity = newCapacity;
}
//插入新数据元素
//从最后一个位置开始 依次移动数据 后移
for (int i = array->m_size - 1; i >= pos; i--)
{
array->pAddr[i + 1] = array->pAddr[i];
}
//将新元素插入到指定位置
array->pAddr[pos] = data;
//更新大小
array->m_size++;
}
//遍历数组
void foreach_DynamicArray(struct dynamicArray * array, void(*myForeach)(void *))
{
if (array == NULL)
{
return;
}
if (myForeach == NULL)
{
return;
}
for (int i = 0; i < array->m_size; i++)
{
myForeach(array->pAddr[i]);
}
}
//删除数组中元素 -- 按照指定位置进行删除
void removeByPos_DynamicArray(struct dynamicArray * arr, int pos)
{
if (arr == NULL)
{
return;
}
if (pos < 0 || pos > arr->m_size - 1)
{
//无效的位置 直接return
return;
}
//从pos位置开始 到数组尾 数据进行前移
for (int i = pos; i < arr->m_size - 1; i++)
{
arr->pAddr[i] = arr->pAddr[i + 1];
}
arr->m_size--;
}
//删除数组中的元素 --- 按照值来进行删除
void removeByValue_DynamicArray(struct dynamicArray * arr, void * data, int(*myCompare)(void *, void *))
{
if (arr == NULL)
{
return;
}
if (data == NULL)
{
return;
}
for (int i = 0; i < arr->m_size; i++)
{
if (myCompare(arr->pAddr[i], data))
{
removeByPos_DynamicArray(arr, i);
break;
}
}
}
//销毁数组
void destroy_DynamicArray(struct dynamicArray * arr)
{
if (arr == NULL)
{
return;
}
if (arr->pAddr != NULL)
{
free(arr->pAddr);
arr->pAddr = NULL;
}
free(arr);
arr = NULL;
}
1.2 对外提供的接口
1.3 初始化队列
//初始化队列
seqQueue init_SeqQueue()
{
struct dynamicArray * arr = init_DynamicArray(MAX);
return arr;
}
1.4 入队
//入队
void push_SeqQueue(seqQueue queue, void * data)
{
if (queue == NULL)
{
return;
}
if (data == NULL)
{
return;
}
struct dynamicArray * myQueue = queue;
if (myQueue->m_size >= MAX)
{
return;
}
//入队 === 尾插
insert_dynamicArray(myQueue, myQueue->m_size, data);
}
1.5 出队
//出队
void pop_SeqQueue(seqQueue queue)
{
if (queue == NULL)
{
return;
}
struct dynamicArray * myQueue = queue;
if (myQueue->m_size <= 0)
{
return;
}
removeByPos_DynamicArray(myQueue, 0);
}
1.6 返回队头
//返回队头元素
void * front_SeqQueue(seqQueue queue)
{
if (queue == NULL)
{
return NULL;
}
struct dynamicArray * myQueue = queue;
return myQueue->pAddr[0];
}
1.7 返回队尾
//返回队尾元素
void * back_SeqQueue(seqQueue queue)
{
if (queue == NULL)
{
return NULL;
}
struct dynamicArray * myQueue = queue;
return myQueue->pAddr[myQueue->m_size - 1];
}
1.8 返回队列大小
//返回队列大小
int size_SeqQueue(seqQueue queue)
{
if (queue == NULL)
{
return -1;
}
struct dynamicArray * myQueue = queue;
return myQueue->m_size;
}
1.9 销毁队列
//销毁
void destroy_SeqQueue(seqQueue queue)
{
if (queue == NULL)
{
return;
}
destroy_DynamicArray(queue);
}
1.10 循环队列判断队列满的条件
(q->rear+1)%MaxSize==q->front
2 队列---链式存储
2.1 和顺序存储对外接口一致
2.2 利用链表方式实现
2.3 实现出一种 符合 先进先出的数据结构
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//链表节点
struct QueueNode
{
struct QueueNode * next; //只维护指针域
};
//队列结构体
struct LQueue
{
//头节点
struct QueueNode pHeader;
//队列大小
int m_Size;
//维护尾节点的指针
struct QueueNode * pTail;
};
typedef void * LinkQueue;
//初始化队列
LinkQueue init_LinkQueue()
{
struct LQueue * myQueue = malloc(sizeof(struct LQueue));
if (myQueue == NULL)
{
return NULL ;
}
myQueue->pHeader.next = NULL;
myQueue->m_Size = 0;
myQueue->pTail = &myQueue->pHeader; //尾节点初始化 就是在头节点
return myQueue;
}
//入队
void push_LinkQueue(LinkQueue queue, void * data)
{
if (queue == NULL)
{
return;
}
if (data == NULL)
{
return;
}
//还原真实队列结构体
struct LQueue * myQueue = queue;
//取出用户数据的前四个字节
struct QueueNode * myNode = data;
//插入新的节点
myQueue->pTail->next = myNode;
myNode->next = NULL;
myQueue->pTail = myNode;
//更新队列长度
myQueue->m_Size++;
}
//出队
void pop_LinkQueue(LinkQueue queue)
{
if (queue == NULL)
{
return;
}
struct LQueue * myQueue = queue;
if (myQueue->m_Size <= 0)
{
return;
}
if (myQueue->m_Size == 1)
{
myQueue->pHeader.next = NULL;
myQueue->pTail = &myQueue->pHeader;
myQueue->m_Size = 0;
return;
}
//第一个有数据的节点
struct QueueNode * pFirst = myQueue->pHeader.next;
//更新指针的指向
myQueue->pHeader.next = pFirst->next;
//更新队列长度
myQueue->m_Size--;
}
//返回队头
void * front_LinkQueue(LinkQueue queue)
{
if (queue == NULL)
{
return NULL;
}
struct LQueue * myQueue = queue;
return myQueue->pHeader.next;
}
//返回队尾
void * back_LinkQueue(LinkQueue queue)
{
if (queue == NULL)
{
return NULL;
}
struct LQueue * myQueue = queue;
return myQueue->pTail;
}
//返回队列大小
int size_LinkQueue(LinkQueue queue)
{
if (queue == NULL)
{
return -1;
}
struct LQueue * myQueue = queue;
return myQueue->m_Size;
}
//销毁
void destroy_LinkQueue(LinkQueue queue)
{
if (queue == NULL)
{
return;
}
free(queue);
queue = NULL;
}
//测试队列
struct Person
{
void * node;
char name[64];
int age;
};
void test01()
{
//初始化队列
LinkQueue queue = init_LinkQueue();
//准备数据
struct Person p1 = { NULL, "aaa", 10 };
struct Person p2 = { NULL, "bbb", 20 };
struct Person p3 = { NULL, "ccc", 30 };
struct Person p4 = { NULL, "ddd", 40 };
struct Person p5 = { NULL, "eee", 50 };
//入队
push_LinkQueue(queue, &p1);
push_LinkQueue(queue, &p2);
push_LinkQueue(queue, &p3);
push_LinkQueue(queue, &p4);
push_LinkQueue(queue, &p5);
while (size_LinkQueue(queue) > 0)
{
//获取队头元素
struct Person * pFront = front_LinkQueue(queue);
printf("队头元素--- 姓名: %s 年龄: %d\n", pFront->name, pFront->age);
//获取队尾
struct Person * pBack = back_LinkQueue(queue);
printf("队尾元素--- 姓名: %s 年龄: %d\n", pBack->name, pBack->age);
//出队
pop_LinkQueue(queue);
}
printf("::::队伍大小为: %d\n", size_LinkQueue(queue));
//销毁
destroy_LinkQueue(queue);
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}