2020.4月学完的,回来记笔记,温故而知新…
一、栈
1、什么是栈
栈(Stack)是一种线性存储结构,它具有如下特点:
(1)栈中的数据元素遵守”先进后出”(First In Last Out)的原则,简称FILO结构。
(2)限定只能在栈顶进行插入和删除操作。
2、栈的特点
栈:后进先出(
LIFO-last in first out
) : 最后插入的元素最先出来。
二、顺序栈
顺序栈,即栈的顺序存储结构是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附指针top指示栈顶元素在顺序栈的位置。通常top=0表示空栈。
用C语言描述是,因为不能确定整个过程中所需最大空间的大小,所以我们可以设一个初始容量,在栈的空间不够时在逐段扩大。
优点:连续存储,空间利用率高
缺点:不方便数据的增删
2.1、顺序栈的基本操作:
顺序栈主要有以下几种基本操作:
(1)InitStack()栈初始化
(2)EmptyStack() 判断栈是否为空
(3)push(): 向栈内压入一个成员
(4)StackLength()顺序栈的元素个数
(5)GetTop()顺序栈的栈顶元素
(6)pop(): 从栈顶弹出一个成员
(7)StackTraverse()从栈顶到栈底对每个元素进行遍历
(8)ClearStack() 清空顺序栈
(9)DestroyStack()顺序栈的销毁
2.2、 顺序栈的具体代码实现:
栈作为一个最简单的数据结构,实现起来也非常容易。
// ============= 顺序栈的各项操作 ============
#include <stdio.h>
#include <stdlib.h>
#define STACK_INIT_SIZE 100//储存空间初始分配量
#define STACK_INCREMENT 10//存储空间分配增量
#define OK 1
#define ERROR 0
typedef int StackType; //栈元素类型
typedef int Status;
typedef struct {
StackType *base; //在构造之前和销毁之后,base的值为NULL
StackType *top; //栈顶指针
int stackSize; //当前已分配的存储空间,以元素为单位
}SqStack; //顺序栈
//栈的初始化
Status InitStack(SqStack *p) {
p->base = (StackType*)malloc(STACK_INIT_SIZE * sizeof(StackType));
if (p->base == NULL) return ERROR; //内存分配失败
p->top = p->base; //栈顶与栈底相同表示一个空栈
p->stackSize = STACK_INIT_SIZE;
return OK;
}
//判断栈是否为空
Status EmptyStack(SqStack *p) {
//若为空栈 则返回OK,否则返回ERROR
if (p->top == p->base) return OK;
else return ERROR;
}
//顺序栈的压入
Status Push(SqStack *p,StackType e) {
//插入元素e为新的栈顶元素
if ((p->top - p->base)>= p->stackSize) //栈满,追加储存空间
{
p->base = (StackType*)realloc(p->base, (p->stackSize + STACK_INCREMENT) * sizeof(StackType));
if (p->base == NULL) return ERROR;// 储存空间分配失败
p->top = p->base + p->stackSize; //可能有人觉得这句有点多余(我当时也是这么想的 后面有解释)
p->stackSize += STACK_INCREMENT;
}
*(p->top) = e;
(p->top)++;
return OK;
}
// 顺序栈弹出栈顶元素
Status Pop(SqStack *p,StackType *e) {
//若栈不空,则删除p的栈顶元素,用e返回其值
if (p->top == p->base) return ERROR;
--(p->top);
*e = *(p->top);
return OK;
}
//顺序栈的销毁
Status DestroyStack(SqStack *p) {
//释放栈底空间并置空
free(p->base);
p->base = NULL;
p->top = NULL;
p->stackSize = 0;
return OK;
}
//将顺序栈置空 栈还是存在的,栈中的元素也存在,如果有栈中元素的地址任然能调用
Status ClearStack(SqStack *p) {
p->top= p->base;
return OK;
}
//返回顺序栈的元素个数
Status StackLength(SqStack p) {
//栈顶指针减去栈底指针等于长度,因为栈顶指针指向当前栈顶元素的下一个位置
return p.top - p.base;
}
//返回顺序栈的栈顶元素
Status GetTop(SqStack *p, StackType *e) {
//若栈不为空,则用e返回p的栈顶元素
if (p->top > p->base) {
*e = *(p->top - 1); return OK;
}
else return ERROR;
}
//从栈顶到栈底对每个元素调用某个函数
Status StackTraverse(SqStack p){
//从栈底到栈顶依次对栈中的每个元素调用函数printFun()
while (p.top > p.base)
printf("%d ",*(--p.top));
printf("\n");
return OK;
}
//测试栈的各种操作
int main() {
int n;
StackType *e,num;
SqStack *pStack,stack; //stack 秒啊!秒啊!
pStack = &stack;
e=(StackType*)malloc(sizeof(StackType)); //为指针e分配内存地址
InitStack(pStack); //初始化栈
if (EmptyStack(pStack) == 1) printf("-------栈为空-------\n");
printf("请输入栈的元素个数:");
scanf("%d", &n);
printf("请输入%d个数字:", n);
for (int i = 0; i < n; i++)
{
scanf("%d", &num);
Push(pStack, num);
}
print("----------------\n");
if (EmptyStack(pStack) != 1) printf("-----栈不为空-----\n");
printf("栈的长度为:%d\n", StackLength(stack));
GetTop(pStack, e);
printf("栈顶元素为:%d\n", *e);
printf("----------------\n");
printf("打印栈中的元素:\n");
StackTraverse(stack);
// 知道为什么用 stack 而不用 pStack? 因为遍历元素如果使用 pStack 这会导致指针的移动,
// 导致后面的StackTraverse 函数运行不出结果,所以创建一个普通类型的 stack 仅是遍历元素不一定指针 恰到好处
printf("----------------\n");
printf("---弹出栈顶元素---\n");
Pop(pStack, e);
printf("%d\n",*e);
printf("-----------------\n");
printf("打印栈中的元素:\n");
StackTraverse(stack);
printf("-----------------\n");
printf("-------清空栈-----\n");
if (ClearStack(pStack) == 1) printf("已清空栈\n");
printf("-------销毁栈------\n");
if (DestroyStack(pStack) == 1) printf("已销毁栈\n");
return 0;
}
三、链栈
3.1. 链栈的实现思路同顺序栈类似,顺序栈是将数顺序表(数组)的一端作为栈底,另一端为栈顶;链栈也如此,通常我们将链表的头部作为栈顶,尾部作为栈底, 链栈含头结点模型示意图如下:
将链表头部作为栈顶的一端,可以避免在实现数据 “入栈” 和 “出栈” 操作时做大量遍历链表的耗时操作。
链表的头部作为栈顶,意味着:
- 在实现数据"入栈"操作时,需要将数据从链表的头部插入;
- 在实现数据"出栈"操作时,需要删除链表头部的首元节点;
因此,链栈实际上就是一个只能采用头插法插入或删除数据的链表。
3.1、链栈基本操作:
链栈的基本操作如下:
(1)createStack () 创建链栈;
(2)stackEmpty() 判断是否为空;
(3) Push() 入栈;
(4) Pop ()出栈;
(5)getTopEle()取栈顶元素;
(6) getStackLength() 获取栈元素个数;
3.2、链栈的具体代码实现:
// ============== 链栈的基本操作 ===============
#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define ERROR 0
typedef int StackType; //栈元素类型
typedef int Status;
typedef struct node{
StackType data; // 节点数据
struct node *next; // 节点指针
} stackNode;
// 创建链栈
stackNode* createStack() {
stackNode* head = (stackNode *)malloc(sizeof(stackNode)); // 创建头结点
if (head == NULL) {
printf("创建链栈失败\n");
return ERROR;
}
printf("创建链栈成功\n");
head->data = 0;
head->next = NULL;
return head;
}
// 判断栈是否为空
Status stackEmpty(stackNode *listStack){
return listStack->next == NULL ? 1: 0;
}
// 入栈
Status Push(stackNode *listStack, StackType e){
if (listStack == NULL) return ERROR;
stackNode *node = (stackNode*)malloc(sizeof(stackNode)); // 创建新结点
if (node == NULL) {
printf("新节点创建失败\n");
}
node->data = e;
node->next = listStack->next;
listStack->next = node;
return OK;
}
// 出栈
Status Pop(stackNode *listStack){
if (listStack == NULL && listStack->next == NULL) return ERROR;
stackNode *temp = listStack->next;
listStack->next = temp->next;
int val = temp->data;
free(temp);
return val;
}
// 取栈顶元素
Status getTopEle(stackNode *listStack, StackType *e){
if (listStack->next == NULL) return ERROR;
*e = listStack->next->data;
return OK;
}
// 获取栈元素个数
Status getStackLength(stackNode *listStack){
if (listStack == NULL || listStack->next == NULL) return ERROR;
stackNode *p = listStack->next;
int len = 0;
while (p!= NULL) {
len++;
p = p->next;
}
return len;
}
void clearStack(stackNode *listStack)
{
while(!stackEmpty(listStack))
Pop(listStack);
}
void destroyStack(stackNode *listStack)
{
while(!stackEmpty(listStack))
Pop(listStack);
free(listStack);
listStack = NULL;
}
int main() {
int n,num;
stackNode *listStack = createStack();
StackType *e = (StackType * )malloc(sizeof(StackType));
int bool = stackEmpty(listStack);
if (bool == 1) printf("栈为空\n");
else printf("栈不为空\n");
printf("---------------------\n");
printf("请输入要入栈的个数:");
scanf("%d",&n);
printf("请输入 %d 个数字:", n);
for (int i = 0; i < n; ++i) {
scanf("%d",&num);
Push(listStack,num);
};
printf("---------------------\n");
int value = Pop(listStack);
printf("出栈栈顶元素:%d\n", value);
printf("---------------------\n");
getTopEle(listStack,e);
printf("取栈顶元素: %d\n",*e);
printf("---------------------\n");
int length = getStackLength(listStack);
printf("栈元素个数为: %d\n", length);
printf("---------------------\n");
printf("遍历出栈中所有元素:");
for (int j = 0; j < n-1; ++j) {
// 因为出栈了一个元素 ,所以 n 要减 1
printf("%d ",Pop(listStack));
}
printf("\n--------------------\n");
clearStack(listStack);
printf("栈已清空\n");
printf("---------------------\n");
destroyStack(listStack);
printf("栈已销毁\n");
printf("---------------------\n");
return 0;
}