文章目录
一、栈
1、栈的概念及结构
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶
2、栈的实现
栈的实现一般使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的 代价比较小。
// 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈 #define N 10 typedef int STDataType; typedef struct Stack { STDataType _a[N]; int _top; // 栈顶 }Stack; // 支持动态增长的栈 typedef struct Stack { STDataType* _a; int _top; // 栈顶 int _capacity; // 容量 }Stack; void StackInit(Stack* ps);// 初始化栈 void StackPush(Stack* ps, STDataType data);// 入栈 void StackPop(Stack* ps);// 出栈 STDataType StackTop(Stack* ps);// 获取栈顶元素 int StackSize(Stack* ps);// 获取栈中有效元素个数 int StackEmpty(Stack* ps);// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 void StackDestroy(Stack* ps);// 销毁栈
(1)初始化栈
void StackInit(Stack* ps) { assert(ps); ps->_a = NULL; ps->_capacity = 0; ps->_top = 0; }
(2)入栈
思路:
①根据数据个数(top)和最大容量(capacity)判断是否需要扩容。
②存储数据。
③更新数据个数(top)。
void StackPush(Stack* ps, STDataType data) { assert(ps); if (ps->_top == ps->_capacity)//判断是否需要扩容 { int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2; STDataType* p = realloc(ps->_a, sizeof(STDataType) * newcapacity); assert(p); ps->_a = p; ps->_capacity = newcapacity;//更新新的容量 } ps->_a[ps->_top] = data;//存储数据 ps->_top++;//更新数据个数(top) }
(3)出栈
思路:
①栈顶就是数组的尾,直接将有效数据个数(top)减一即可,不需要删除。
②如果栈为空,就报错,否则会造成越界。
void StackPop(Stack* ps) { assert(ps); assert(!StackEmpty(ps));//判空函数,后面实现 ps->_top--; }
(4)获取栈顶元素
思路:
①直接top-1使用下标,访问数据
②判断栈是否为空
STDataType StackTop(Stack* ps) { assert(ps); assert(!StackEmpty(ps)); return ps->_a[ps->_top - 1]; }
(5)获取栈元素个数
思路:
返回top即可
int StackSize(Stack* ps) { assert(ps); return ps->_top; }
(6)判断栈是否为空
思路:
根据top判断,为0就是空,返回true,否则返回false。
int StackEmpty(Stack* ps) { assert(ps); return ps->_top == 0; }
(7)销毁
思路:
将栈顶(top)和最大容量(capacity)置0,free掉指针a所指向的空间,将a指针置空即可。
void StackDestroy(Stack* ps) { assert(ps); free(ps->_a); ps->_a = NULL; ps->_capacity = ps->_top = 0; }
(8)栈的全部代码
stack.h(头文件)
#pragma once #define _CRT_SECURE_NO_WARNINGS #pragma warning(disable:6031) #include<stdio.h> #include<stdlib.h> #include<assert.h> // 支持动态增长的栈 typedef int STDataType; typedef struct Stack { STDataType* _a; int _top; // 栈顶 int _capacity; // 容量 }Stack; void StackInit(Stack* ps);// 初始化栈 void StackPush(Stack* ps, STDataType data);// 入栈 void StackPop(Stack* ps);// 出栈 STDataType StackTop(Stack* ps);// 获取栈顶元素 int StackSize(Stack* ps);// 获取栈中有效元素个数 int StackEmpty(Stack* ps);// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 void StackDestroy(Stack* ps);// 销毁栈
stack.c(源文件)
#include"stack.h" void StackInit(Stack* ps) { assert(ps); ps->_a = NULL; ps->_capacity = 0; ps->_top = 0; } void StackPush(Stack* ps, STDataType data) { assert(ps); if (ps->_top == ps->_capacity) { int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2; STDataType* p = realloc(ps->_a, sizeof(STDataType) * newcapacity); assert(p); ps->_a = p; ps->_capacity = newcapacity; } ps->_a[ps->_top] = data; 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; } int StackEmpty(Stack* ps) { assert(ps); return ps->_top == 0; } void StackDestroy(Stack* ps) { assert(ps); free(ps->_a); ps->_a = NULL; ps->_capacity = ps->_top = 0; }
test.c(测试文件)
#include"stack.h" void test1() { Stack p; StackInit(&p); StackPush(&p, 1); StackPush(&p, 2); StackPush(&p, 3); StackPop(&p); while (!StackEmpty(&p)) { printf("%d ", StackTop(&p)); StackPop(&p); } StackDestroy(&p); } int main() { test1(); return 0; }
二、队列
1、什么是队列
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
2、队列的实现
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数 组头上出数据,效率会比较低。
结构体这里,我们使用两个结构体进行声明一个用来存放数据,一个用来记录头尾指针。(节省头尾指针空间);
#pragma once #define _CRT_SECURE_NO_WARNINGS #pragma warning(disable:6031) #include<stdio.h> #include<stdlib.h> #include<assert.h> // 链式结构:表示队列 typedef int QDataType; typedef struct QListNode { struct QListNode* _next; 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);
### (1)初始化队列
void QueueInit(Queue* q) { assert(q); q->_front = NULL; q->_rear = NULL; }
(2)队尾入列
思路:
①malloc新结点存储数据。
②判断队列是否为空,采取不同的链接方式。
void QueuePush(Queue* q, QDataType data) { assert(q); QNode* new = (QNode*)malloc(sizeof(QNode)); assert(new); new->_data = data; new->_next = NULL; if (q->_front == NULL) { q->_front = q->_rear = new; } else { q->_rear->_next = new; q->_rear = new; } }
(3)队头出列
思路:
①记录下当前头结点的下一个结点,释放头结点,然后更新指针。
②注意为空报错,不进行删除
③如果删除空队列,将尾指针置空
void QueuePop(Queue* q) { assert(q); assert(!QueueEmpty(q));//判断是否为空 QNode* front_next = q->_front->_next; free(q->_front); q->_front = front_next; if (q->_front == NULL) { q->_rear == NULL; } }
(4)获取队列头尾元素
思路:
注意队列不能为空,直接返回
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; }
(5)队列中有效元素个数
定义一个计数,遍历链表。
int QueueSize(Queue* q) { assert(q); int n = 0; QNode* cur = q->_front; while (cur) { n++; cur = cur->_next; } return n; }
(6)判断队列是否为空
根据头指针是否为空判断
int QueueEmpty(Queue* q) { assert(q); return q->_front == NULL; }
(7)销毁队列
遍历释放,最后将头尾指针置空
void QueueDestroy(Queue* q) { assert(q); QNode* cur = q->_front; while (cur != NULL) { QNode* next = cur->_next; free(cur); cur = next; } q->_front = q->_rear = NULL; }
(8)队列的全部代码
Queue.h(头文件)
#pragma once #define _CRT_SECURE_NO_WARNINGS #pragma warning(disable:6031) #include<stdio.h> #include<stdlib.h> #include<assert.h> // 链式结构:表示队列 typedef int QDataType; typedef struct QListNode { struct QListNode* _next; 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);
Queue.c(源文件)
#include"queue.h" void QueueInit(Queue* q) { assert(q); q->_front = NULL; q->_rear = NULL; } void QueuePush(Queue* q, QDataType data) { assert(q); QNode* new = (QNode*)malloc(sizeof(QNode)); assert(new); new->_data = data; new->_next = NULL; if (q->_front == NULL) { q->_front = q->_rear = new; } else { q->_rear->_next = new; q->_rear = new; } } void QueuePop(Queue* q) { assert(q); assert(!QueueEmpty(q)); QNode* front_next = q->_front->_next; free(q->_front); q->_front = 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) { n++; cur = cur->_next; } return n; } int QueueEmpty(Queue* q) { assert(q); return q->_front == NULL; } void QueueDestroy(Queue* q) { assert(q); QNode* cur = q->_front; while (cur != NULL) { QNode* next = cur->_next; free(cur); cur = next; } q->_front = q->_rear = NULL; }
test.c(测试文件)
#include"queue.h" void test1() { Queue p; QueueInit(&p); QueuePush(&p, 1); QueuePush(&p, 2); QueuePush(&p, 3); QueuePush(&p, 4); //QueuePop(&p); //QueuePop(&p); while (!QueueEmpty(&p)) { QDataType front = QueueFront(&p); printf("%d ", front); QueuePop(&p); } QueueDestroy(&p); } int main() { test1(); return 0; }
相较于链表,栈和队列的实现更加简单.
QNode* cur = q->_front;
while (cur != NULL)
{
QNode* next = cur->_next;
free(cur);
cur = next;
}
q->_front = q->_rear = NULL;
}<font color="pink">test.c(测试文件)</font> ```c #include"queue.h" void test1() { Queue p; QueueInit(&p); QueuePush(&p, 1); QueuePush(&p, 2); QueuePush(&p, 3); QueuePush(&p, 4); //QueuePop(&p); //QueuePop(&p); while (!QueueEmpty(&p)) { QDataType front = QueueFront(&p); printf("%d ", front); QueuePop(&p); } QueueDestroy(&p); } int main() { test1(); return 0; }
相较于链表,栈和队列的实现更加简单.