【学习笔记】数据结构(三)

news2024/11/24 2:18:20

栈和队列

文章目录

  • 栈和队列
    • 3.1 栈 - Stack
      • 3.1.1 抽象数据类型栈的定义
      • 3.1.2 栈的表示和实现
    • 3.2 栈的应用举例
      • 3.2.1 数制转换
      • 3.2.2 括号匹配的检验
      • 3.2.3 迷宫求解
      • 3.2.4 表达式求值 - 波兰、逆波兰
      • 3.2.5 反转一个字符串或者反转一个链表
    • 3.3 栈与递归的实现
    • 3.4 队列 - Queue
      • 3.4.1 抽象数据类型队列的定义
      • 3.4.2 链队列--队列的链式表示和实现
      • 3.4.3 循环队列--队列的顺序表示和实现

3.1 栈 - Stack

3.1.1 抽象数据类型栈的定义

栈(stack)是限定仅在表尾进行插人或删除操作的线性表。栈又称为后进先出(last in first out)的线性表(简称 LIFO 结构)。

表尾端称为栈顶(top),表头端称为栈底(bottom)。

不含元素的空表称为空栈。

在这里插入图片描述

栈的抽象数据类型的定义:

在这里插入图片描述

3.1.2 栈的表示和实现

顺序栈 , 即栈的顺序存储结构是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针top指示栈顶元素在顺序栈中的位置。通常的top=0表示空栈。

由于栈在使用过程中所需最大空间的大小很难估计,因此,一般来说,在初始化设空栈时不应限定栈的最大容量。一个较合理的做法是:先为栈分配一个基本容量,然后在应用过程中,当栈的空间不够使用时再逐段扩大。为此,可设定两个常量:STACK_INIT_SIZE(存储空间初始分配量)和STACKINCREMENT(存储空间分配增量)。

typedef struct {
	SElemType * base;
	SElemType * top;
	int stacksize; //指示栈的当前可使用的最大容量
}SqStack;

栈的初始化操作为:

  • 按设定的初始分配量进行第一次存储分配;

  • 称base为栈底指针, 在顺序栈中,它始终指向栈底的位置, 若base的值为 NULL, 则表明栈结构不存在。

  • 称top为栈顶指针,其初值指向栈底,即top=base可作为栈空的标记,每当插人新的栈顶元素时,指针top增1;删除栈顶元素时,指针top减1,因此,非空栈中的栈顶指针始终在栈顶元素的下一个位置上。

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>
#include <stdlib.h>

#define STACK_INIT_SIZE 100 //存储空间初始分配量
#define STACKINCREMENT 10 //存储空间分配增量

#define OK 1 //完成
#define OVERFLOW -1 //失败
#define ERROR -2 //错误

typedef int Status;
typedef struct {
	int* base; // 在栈构造之前和销毁之后,base的值为NULL
	int* top; // 栈顶指针
	int stacksize; //指示栈的当前可使用的最大容量
}SqStack;

Status InitStack(SqStack* S) {
	// 构造一个空栈S
	S->base = (int*) malloc(STACK_INIT_SIZE * sizeof(int));
	if (!S->base) exit(OVERFLOW);
	S->top = S->base;
	S->stacksize = STACK_INIT_SIZE;
	return OK;
}

Status Push(SqStack* S, int e) {
	// 插入元素 e为新的栈顶元素
	if (S->top - S->base >= S->stacksize) { //栈满,追加存储空间
		S->base = (int*)realloc(S->base, (S->stacksize + STACKINCREMENT) * sizeof(int));
		if (!S->base) exit(OVERFLOW);
		S->top = S->base + S->stacksize;
		S->stacksize += STACKINCREMENT;
	}
	*S->top++ = e;
	return OK;
}

Status Pop(SqStack* S, int* e) {
	// 若栈不空,则删除s的栈顶元素,用e返回其值,并返回OK;否则返回 ERROR
	if (S->top == S->base) return ERROR;
	*e = *--S->top;
	return OK;
}

Status GetTop(SqStack S, int* e) {
	// 若栈不空, 则用e返回s的栈顶元素, 并返回0K; 否则返回ERROR
	if (S.top == S.base) return ERROR;
	*e = *(S.top - 1);
	return OK;
}

Status DestroyStack(SqStack* S) {
	// 销毁栈S
	free(S->base);
	S->base = NULL;
	S->top = NULL;
	S->stacksize = 0;
	return OK;
}

int main() {
	SqStack S;
	if (InitStack(&S) != OK) {
		printf("Stack initialization failed.\n");
		return OVERFLOW;
	}
	int e; // 使用一个整数变量而不是指针
	Push(&S, 1);
	Push(&S, 2);
	Push(&S, 3);
	Push(&S, 4);
	Status status = Pop(&S, &e);
	if (status == OK) {
		// 删除的元素
		printf("Popped element: %d\n", e);
	}
	else {
		printf("Error: Stack is empty.\n");
	}
	if (GetTop(S, &e) == OK) {
		// 此时栈顶的元素
		printf("Top element: %d\n", e);
	}
	DestroyStack(&S);
	return 0;
}

使用数组实现一个栈

#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 101
int A[MAX_SIZE];
int top = -1;

void Push(int x)
{
	if (top == MAX_SIZE - 1)
	{
		printf("Error:stack overflow\n"); 
		return;
	}
	A[++top] = x;
}

void Pop() {
	if (top == -1) {
		printf("Error: No element to pop\n");
		return;
	}
	top--;
}

int Top()
{
	if (top != -1)
	{
		return A[top];
	}	
}

void Print()
{
	int i;
	printf("Stack: ");
	for (i = 0; i <= top; i++) {
		printf("%d ", A[i]);
	}
	printf("\n");
}

int main() {
	Push(2); Print(); 
	Push(5); Print(); 
	Push(10); Print(); 
	Pop(); Print(); 
	Push(12); Print();
	return 0;
}

使用链表实现一个栈

#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
 
typedef struct Node {
	int data;
	struct Node* link;
}Node;
struct Node* top;

void Push(int x) {
	Node* temp = (Node*)malloc(sizeof(Node));
	temp->data = x;
	temp->link = top;
	top = temp;
}

void Pop() {
	Node* temp;
	if (top == NULL) return;
	temp = top;
	top = top->link;
	free(temp);
}

int Top() {
	return top->data;
}

bool IsEmpty() {
	if (top == NULL) {
		return true;
	}
	return false;
}

void Print() {
	Node* temp = top;
	printf("List: ");
	while (temp != NULL) {
		printf("%d ", temp->data);
		temp = temp->link;
	}
	printf("\n");
}

int main() {
	top = NULL;
	printf("%d \n", IsEmpty());
	Push(2);
	Push(4);
	Push(1);
	Print();
	Pop();
	Print();
	printf("%d \n", Top());
	printf("%d \n", IsEmpty());
	return 0;
}

3.2 栈的应用举例

3.2.1 数制转换

十进制数N和其他d进制数的转换
N = ( N  div  d ) × d + N   m o d   d  (其中:div为整除运算,mod为求余运算) N = (N \text{ div } d) \times d + N \bmod d \text{ (其中:div为整除运算,mod为求余运算)} N=(N div d)×d+Nmodd (其中:div为整除运算,mod为求余运算)
在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>
#include <stdlib.h>

#define STACK_INIT_SIZE 100 //存储空间初始分配量
#define STACKINCREMENT 10 //存储空间分配增量

#define OK 1 //完成
#define OVERFLOW -1 //失败
#define ERROR -2 //错误

typedef int Status;
typedef struct {
	int* base; // 在栈构造之前和销毁之后,base的值为NULL
	int* top; // 栈顶指针
	int stacksize; //指示栈的当前可使用的最大容量
}SqStack;

Status InitStack(SqStack* S) {
	// 构造一个空栈S
	S->base = (int*) malloc(STACK_INIT_SIZE * sizeof(int));
	if (!S->base) exit(OVERFLOW);
	S->top = S->base;
	S->stacksize = STACK_INIT_SIZE;
	return OK;
}

Status StackEmpty(SqStack S) {
	return (S.top == S.base) ? OK : ERROR;
}

Status Push(SqStack* S, int e) {
	// 插入元素 e为新的栈顶元素
	if (S->top - S->base >= S->stacksize) { //栈满,追加存储空间
		S->base = (int*)realloc(S->base, (S->stacksize + STACKINCREMENT) * sizeof(int));
		if (!S->base) exit(OVERFLOW);
		S->top = S->base + S->stacksize;
		S->stacksize += STACKINCREMENT;
	}
	*S->top++ = e;
	return OK;
}

Status Pop(SqStack* S, int* e) {
	// 若栈不空,则删除s的栈顶元素,用e返回其值,并返回OK;否则返回 ERROR
	if (S->top == S->base) return ERROR;
	*e = *--S->top;
	return OK;
}

Status GetTop(SqStack S, int* e) {
	// 若栈不空, 则用e返回s的栈顶元素, 并返回0K; 否则返回ERROR
	if (S.top == S.base) return ERROR;
	*e = *(S.top - 1);
	return OK;
}

Status DestroyStack(SqStack* S) {
	// 销毁栈S
	free(S->base);
	S->base = NULL;
	S->top = NULL;
	S->stacksize = 0;
	return OK;
}

void conversion() {
	SqStack S;
	if (InitStack(&S) != OK) {
		printf("Stack initialization failed.\n");
	}
	int e, N;
	printf("请输入一个十进制数:");
	int num = scanf("%d", &N);
	if (num == 1) {
		while (N) {
			Push(&S, N % 8);
			N = N / 8;
		}
		while (StackEmpty(S) != OK) {
			Pop(&S, &e);
			printf("%d", e);
		}
		DestroyStack(&S);
	}
	
}

int main() {
	conversion();
	return 0;
}

3.2.2 括号匹配的检验

算法的设计思想:

  1. 凡出现左括弧,则进栈;

  2. 凡出现右括弧,首先检查栈是否空;

  • 若栈空,则表明“右括弧”多了
  • 否则和栈顶元素比较
    • 若相匹配,则“左括弧出栈”
    • 否则不匹配
  1. 表达式检验结束时
  • 若栈空, 则匹配正确

  • 否则表明“左括弧”多了

#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>
#include <stdlib.h>

#define OVERFLOW -1

#define STACK_INIT_SIZE 100 //存储空间初始分配量
#define STACKINCREMENT 10 //存储空间分配增量

typedef struct {
    char* base;
    char* top;
    int stackSize;
} Stack;

void InitStack(Stack* s) {
    s->base = (char*)malloc(100 * sizeof(char));
    if (!s->base) exit(1); // 分配内存失败
    s->top = s->base;
    s->stackSize = STACK_INIT_SIZE;
}

void Push(Stack* s, char elem) {
    if (s->top - s->base >= s->stackSize) { // 栈满,需要扩容
        s->base = (char*)realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(char));
        if (!s->base) exit(OVERFLOW); // 扩容失败
        s->top = s->base + s->stackSize;
        s->stackSize += STACKINCREMENT;
    }
    *(s->top++) = elem;
}

char Pop(Stack* s) {
    if (s->top == s->base) return '\0'; // 栈空
    return *(--s->top);
}

int StackEmpty(Stack s) {
    return s.top == s.base;
}

void DestroyStack(Stack* s) {
    free(s->base);
    s->base = NULL;
    s->top = NULL;
    s->stackSize = 0;
}

int CheckBrackets(const char* str) {
    Stack s;
    InitStack(&s);
    char c, topChar;

    while (*str) {
        switch (*str) {
        case '(':
        case '[':
        case '{':
            Push(&s, *str);
            break;
        case ')':
        case ']':
        case '}':
            if (StackEmpty(s)) {
                DestroyStack(&s);
                return 0; // 没有匹配的左括号
            }
            topChar = Pop(&s);
            if ((topChar == '(' && *str != ')') ||
                (topChar == '[' && *str != ']') ||
                (topChar == '{' && *str != '}')) {
                DestroyStack(&s);
                return 0; // 括号不匹配
            }
            break;
        }
        str++;
    }

    int isEmpty = StackEmpty(s);
    DestroyStack(&s);
    return isEmpty; // 如果栈为空,所有括号正确匹配
}

int main() {
    char expression[100];
    printf("Enter an expression: ");
    scanf("%99s", expression);

    if (CheckBrackets(expression)) {
        printf("The brackets are correctly matched.\n");
    }
    else {
        printf("The brackets are not matched.\n");
    }

    return 0;
}

3.2.3 迷宫求解

迷宫路径算法的基本思想是:

  • 若当前位置“可通”,则纳入路径继续前进
  • 若当前位置“不可通”,则后退,换向探索
  • 若四周均“不可通”,则从路径中删除
#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 100  // 堆栈最大容量
#define MAZE_SIZE 5  // 迷宫大小

#define OK 1 //完成
#define OVERFLOW -1 //失败
#define ERROR -2 //错误

typedef struct {
    int x;
    int y;
} PosType;

typedef struct {
    int ord;  // 通道块在路径上的序号
    PosType seat; // 通道块在迷宫中的坐标位置
    int di; // 从此通道块走向下一通道块的方向
} SElemType; // 栈的元素类型

typedef struct {
    SElemType* base;
    SElemType* top;
    int stacksize;
} Stack;

typedef int Status;
typedef int MazeType[MAZE_SIZE][MAZE_SIZE];


void InitStack(Stack* S) {
    S->base = (SElemType*)malloc(MAXSIZE * sizeof(SElemType));
    if (!S->base) exit(ERROR);
    S->top = S->base;
    S->stacksize = MAXSIZE;
}

Status Push(Stack* S, SElemType e) {
    if (S->top - S->base >= S->stacksize) {
        S->base = (SElemType*)realloc(S->base, (S->stacksize + 10) * sizeof(SElemType));
        if (!S->base) exit(ERROR);
        S->top = S->base + S->stacksize;
        S->stacksize += 10;
    }
    *S->top++ = e;
    return OK;
}


Status Pop(Stack* S, SElemType* e) {
    if (S->top == S->base) return ERROR;
    *e = *--S->top;
    return OK;
}

Status StackEmpty(Stack s) {
    return s.top == s.base;
}

// 检查当前位置是否可以通过
Status Pass(MazeType maze, PosType curpos) {
    // 检查坐标是否在迷宫范围内
    if (curpos.x < 0 || curpos.x >= MAZE_SIZE || curpos.y < 0 || curpos.y >= MAZE_SIZE) {
        return ERROR;  // 超出边界,不可通过
    }
    return maze[curpos.x][curpos.y] == 0;  // 返回1如果是通道,0如果是墙或已访问
}

// 留下足迹,标记位置已访问
Status FootPrint(MazeType maze, PosType curpos) {
    // 检查坐标是否在迷宫范围内
    if (curpos.x >= 0 && curpos.x < MAZE_SIZE && curpos.y >= 0 && curpos.y < MAZE_SIZE) {
        maze[curpos.x][curpos.y] = -1;  // 使用-1标记已访问
    }
}

// 标记位置为死胡同
void MarkPrint(MazeType maze, PosType pos) {
    // 检查坐标是否在迷宫范围内
    if (pos.x >= 0 && pos.x < MAZE_SIZE && pos.y >= 0 && pos.y < MAZE_SIZE) {
        maze[pos.x][pos.y] = 2;  // 使用2标记为死胡同
    }
}

PosType NextPos(PosType pos, int di) {
    PosType next = pos;
    switch (di) {
    case 1: next.y++; break;  // 向东
    case 2: next.x++; break;  // 向南
    case 3: next.y--; break;  // 向西
    case 4: next.x--; break;  // 向北
    }
    return next;
}

Status MazePath(MazeType maze, PosType start, PosType end) {
    // 若迷宫 maze 中存在从入口 start 到出口 end 的通道,则求得一条存放在栈中(从栈底到栈顶),并返回 TRUE; 否则返回 FALSE
    Stack s;
    InitStack(&s);
    PosType curpos = start; // 设定“当前位置”为“入口位置”
    int curstep = 1; // 探索第一步
    SElemType pop_elem;
    do {
        if (Pass(maze, curpos) == 1) { // 当前位置可以通过,即是未曾走到过的通道块
            FootPrint(maze, curpos); // 留下足迹
            SElemType e = { curstep, curpos, 1 };
            Push(&s, e); //  加入路径
            if (curpos.x == end.x && curpos.y == end.y) //  到达终点(出口)
                return OK;
            curpos = NextPos(curpos, 1);// 下一位置是当前位置的东邻
            curstep++;// 探索下一步
        }
        else { // 当前位置不能通过
            if (!StackEmpty(s)) {
                Pop(&s, &pop_elem);
                while (pop_elem.di == 4 && !StackEmpty(s)) {
                    MarkPrint(maze, pop_elem.seat); // 留下不能通过的标记,并退回一步
                    Pop(&s, &pop_elem);
                }
                if (pop_elem.di < 4) {
                    pop_elem.di++;
                    Push(&s, pop_elem); // 换下一个方向探索
                    curpos = NextPos(pop_elem.seat, pop_elem.di); // 设定当前位置是该新方向上的相邻块
                }
            }
        }
    } while (!StackEmpty(s));
}

int main() {
    MazeType maze = {
        {0, 1, 0, 0, 0},
        {0, 1, 1, 1, 0},
        {0, 0, 0, 1, 0},
        {0, 1, 0, 0, 0},
        {0, 0, 0, 1, 0}
    };
    PosType start = { 0, 0 };
    PosType end = { 4, 4 };
    MazePath(maze, start, end);
    printf("\nAfter MarkPrint:\n");
    for (int i = 0; i < MAZE_SIZE; i++) {
        for (int j = 0; j < MAZE_SIZE; j++) {
            if (maze[i][j] == -1) {
                printf("(%d,%d) ", i, j);
            }
        }
        printf("\n");
    }
    return 0;
}

3.2.4 表达式求值 - 波兰、逆波兰

//10以内计算
#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>
#include <stdlib.h>

#define OK 1 // 完成
#define OVERFLOW -1 // 失败
#define ERROR -2 // 错误
#define INF 1e9 // 不合法

#define STACK_INIT_SIZE 100 //存储空间初始分配量
#define STACKINCREMENT 10 //存储空间分配增量

typedef char SElemType;
typedef int Status;
typedef struct {
    SElemType* base;
    SElemType* top;
    int stackSize;
}Stack;

Status InitStack(Stack* S) {
    // 构造一个空栈S
    S->base = (SElemType*)malloc(STACK_INIT_SIZE * sizeof(SElemType));
    if (!S->base) exit(OVERFLOW);
    S->top = S->base;
    S->stackSize = STACK_INIT_SIZE;
    return OK;
}

Status Push(Stack* S, char e) {
    // 插入元素 e为新的栈顶元素
    if (S->top - S->base >= S->stackSize) { //栈满,追加存储空间
        S->base = (SElemType*)realloc(S->base, (S->stackSize + STACKINCREMENT) * sizeof(SElemType));
        if (!S->base) exit(OVERFLOW);
        S->top = S->base + S->stackSize;
        S->stackSize += STACKINCREMENT;
    }
    *S->top++ = e;
    return OK;
}

Status Pop(Stack* S, char* e) {
    // 若栈不空,则删除s的栈顶元素,用e返回其值,并返回OK;否则返回 ERROR
    if (S->top == S->base) return ERROR;
    *e = *--S->top;
    return OK;
}

Status DestroyStack(Stack* S) {
    // 销毁栈S
    free(S->base);
    S->base = NULL;
    S->top = NULL;
    S->stackSize = 0;
    return OK;
}

char GetTop(Stack S) {
    if (S.top == S.base) return ERROR;
    SElemType e = *(S.top - 1);
    return e;
}

int In(char c, const char* OP) {
    // 使用 strchr 函数检查 c 是否在 OP 字符串中
    return strchr(OP, c) != NULL;
}

char Precede(char a, char b)
{
    char x[10] = { '+','-','*','/','(',')','#' };
    char OP[10][10] = { {'>','>','<','<','<','>','>'},
                        {'>','>','<','<','<','>','>'},
                        {'>','>','>','>','<','>','>'},
                        {'>','>','>','>','<','>','>'},
                        {'<','<','<','<','<','=',' '},
                        {'>','>','>','>',' ','>','>'},
                        {'<','<','<','<','<',' ','='} };
    for (int i = 0; i < 7; i++)
    {
        if (a == x[i]) 
        {
            a = i;
        }
        if (b == x[i]) 
        {
            b = i;
        }
    }
    return OP[a][b];
}


char Operate(char p, char theta, char q)
{
    if (theta == '+')return p + q;
    else if (theta == '-')return q - p;
    else if (theta == '*')return p * q;
    else if (theta == '/')
    {
        if (p == 0)
        {
            printf("输入不合法!");
            return INF;
        }
        return q / p;
    }
    else return INF;
}

char EvaluateExpression() {
    // 算术表达式求值的算符优先算法。设OPTR和OPND分别为运算符栈和运算数栈,
    // OP 为运算符集合。
    const char* OP = "+-*/()#";
    char x;
    char theta, p, q;
    Stack OPTR, OPND;
    InitStack(&OPTR);
    Push(&OPTR, '#');
    InitStack(&OPND);
    char c = getchar();
    while (c != '#' || GetTop(OPTR) != '#') {
        printf("OPTR:"); for (int i = 0; i < OPTR.top - OPTR.base; i++)printf("%c ", OPTR.base[i]); puts("");
        printf("OPND:"); for (int i = 0; i < OPND.top - OPND.base; i++)printf("%d ", OPND.base[i]); puts("\n");

        if (!In(c, OP)) {  // 不是运算符则进栈OPND
            Push(&OPND, c - '0');
            c = getchar();
        } 
        else {
            switch (Precede(GetTop(OPTR), c))
            {
            case'<': // 栈顶元素优先权低
                Push(&OPTR, c);
                c = getchar();
                break;
            case'=': // 脱括号并接收下一字符
                Pop(&OPTR, &x);
                c = getchar();
                break;
            case'>': // 退栈并将运算结果入栈
                Pop(&OPTR, &theta);
                Pop(&OPND, &p);
                Pop(&OPND, &q);
                Push(&OPND, Operate(p, theta, q));
                break;
            default:
                break;
            }
        }
    }
    char res = GetTop(OPND);
    DestroyStack(&OPTR);
    DestroyStack(&OPND);
    return res;
}

int main() {
    printf("请输入一串表达式,以等号“#”结尾:");
    printf("最终结果为:%d", EvaluateExpression());
    return 0;
}

⭐️ 中缀、前缀、后缀

Order of operation:

  1. Parentheses (){} []

  2. Exponents (right to lett ) ^

  3. Multiplication and division (left to right)

  4. Addition and Subtraction (left to right)

中缀表达式 Infix : 运算符在运算数的中间

  • ​ <operand><operator><operand>
  • ​ 缺点:关系符号优先级和结合,所以对计算机来说却不好操作,在计算结果时,往往会将中缀表达式转成其它表达式来操作(一般转成后缀表达式)

前缀表达式 - 波兰表达式 Prefix

  • ​ <operator><operand><operand>

  • ​ 中缀表达式:(a + b) * c - d 转换为 前缀表达式(波兰表达式):- * + a b c d

  • ​ 特点:一个操作数只能和一个操作符进行结合, 连续出现的两个操作数和在它们之前且紧靠它们的运算符构成一个最小表达式

  • 前缀表达式的计算机求值:

    • 右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 和 次顶元素),并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果

    • 例如:(3+4)×5-6 对应的前缀表达式就是 - × + 3 4 5 6 , 针对前缀表达式求值步骤如下:

      • 从右至左扫描,将6、5、4、3压入堆栈
      • 遇到+运算符,因此弹出3和4(3为栈顶元素,4为次顶元素),计算出3+4的值,得7,再将7入栈
      • 接下来是×运算符,因此弹出7和5,计算出7×5=35,将35入栈
      • 最后是-运算符,计算出35-6的值,即29,由此得出最终结果

后缀表达式 - 逆波兰表达式 Postfix

  • ​ <operand><operand><operator>
  • ​ 中缀表达式 :(a + b) * c - d 转换为 后缀表达式(逆波兰表达式):a b + c * d -
  • ​ 特点: 运算符在式中出现的顺序恰为表达式的运算顺序;每个运算符和在它之前出现 且紧靠它的两个操作数构成一个最小表达式
  • 后缀表达式的计算机求值
    • 从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素 和 栈顶元素),并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果
    • 例如: (3+4)×5-6 对应的后缀表达式就是 3 4 + 5 × 6 - , 针对后缀表达式求值步骤如下:
      • 从左至右扫描,将3和4压入堆栈;
      • 遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈;
      • 将5入栈;
      • 接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈;
      • 将6入栈;
      • 最后是-运算符,计算出35-6的值,即29,由此得出最终结果

👉 逆波兰计算器简版

  • 计算器说明

    ​ 输入一个逆波兰表达式(后缀表达式), 使用栈(Stack),计算其结果

  • 代码思路

    计算后缀表达式无需考虑运算符优先级问题

    分为两种情况:

    ​ 遇到数:压入数栈

    ​ 遇到运算符:从数栈中弹出两个数,进行计算,计算结果压入数栈

    处理完表达式就代表计算完成

#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_SIZE 100

typedef struct {
    int top;
    int data[MAX_SIZE];
} Stack;

void push(Stack* s, int item) {
    if (s->top == MAX_SIZE - 1) {
        printf("Stack overflow\n");
        exit(1);
    }
    s->data[++s->top] = item;
}

int pop(Stack* s) {
    if (s->top == -1) {
        printf("Stack underflow\n");
        exit(1);
    }
    return s->data[s->top--];
}

int isOperator(char c) {
    return c == '+' || c == '-' || c == '*' || c == '/';
}

int calculate(int num1, int num2, char op) {
    switch (op) {
    case '+':
        return num1 + num2;
    case '-':
        return num1 - num2;
    case '*':
        return num1 * num2;
    case '/':
        return num1 / num2;
    default:
        printf("Invalid operator\n");
        exit(1);
    }
}

/*
	逆波兰计算器 - 整数
*/
int evaluateRPN(char* suffixExpression) {
    Stack stack;
    stack.top = -1;
    // strtok两个参数:要分割的字符串和分隔符,然后返回分割后的标记。
    char* token = strtok(suffixExpression, " ");
    while (token != NULL) {
        if (isOperator(token[0])) {
            // num2 先出栈,所以 num2 是减数或除数
            int num2 = pop(&stack);
            // num1 后出栈,所以 num1 是被减数或被除数
            int num1 = pop(&stack);
            int res = calculate(num1, num2, token[0]);
            push(&stack, res);
        }
        else {
            // 将字符串 token 转换为整数。这函数会忽略字符串前面的空白字符,直到遇到数字或正负号为止,然后将遇到的数字部分转换为整数。
            push(&stack, atoi(token));
        }
        // 在已经使用strtok函数分割过的字符串上继续分割,使用空格作为分隔符。传入NULL作为第一个参数表示继续从上一次的位置开始分割
        token = strtok(NULL, " ");
    }
    return pop(&stack);
}

int main() {
    char suffixExpression[] = "4 5 * 8 - 60 + 8 2 / +";
    int result = evaluateRPN(suffixExpression);
    printf("The result is: %d\n", result);
    return 0;
}

👉 中缀表达式转后缀表达式

  1. 初始化两个栈:运算符栈operStack和储存中间结果的栈tempStack;
  2. 从左至右扫描中缀表达式;
  3. 遇到操作数时,将其压tempStack;
  4. 遇到运算符时,比较其与operStack栈顶运算符的优先级:
    1. 如果operStack为空,或栈顶运算符为左括号“(”,则直接将此运算符入tempStack栈(分如下两种情况)
      1. operStack 栈顶为空:之前的优先级别高的运算已经处理完成,已经得到了一个结果,将当前运算符直接压入 operStack 栈即可
      2. operStack 栈顶为左括号:当从operStack 出栈,用于运算后,这对括号中的表达式的值也就计算出来了
    2. 如果当前运算符优先级比栈顶运算符的高,也将运算符压入tempStack(当前运算符优先级高,先执行运算)
    3. 否则,当前运算符优先级 <= 栈顶运算符优先级,将operStack栈顶的运算符弹出并压入tempStack中(operStack 栈顶运算符优先级高,先执行运算),再次转到(4.1)与operStack中新的栈顶运算符相比较(分如下两种情况);
      1. 一直循环,将 tempStack 栈顶元素取出,直到在 operStack 栈中找到比当前运算符优先级高的运算符,让其先执行运算
      2. 如果在 tempStack 栈中找不到比当前运算符优先级高的运算符,则会直接将 operStack 栈掏空,然后将当前运算符压入 tempStack 栈中(放在栈底)
  5. 遇到括号时:
    1. 如果是左括号“(”,则直接压入operStack,等待与其配对的右括号,因为括号中的表达式需要优先运算
    2. 如果是右括号“)”,则依次弹出operStack栈顶的运算符,并压入tempStack,直到遇到左括号为止,此时将这一对括号丢弃(此时括号内的运算完成,并将结果压入了tempStack)
  6. 重复步骤2至5,直到表达式的最右边
  7. 将operStack中剩余的运算符依次弹出并压入tempStack(operStack 栈中剩下的运算都是优先级相同的运算符,按顺序执行即可)
  8. 依次弹出tempStack中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

例子:1 + (( 2 + 3 )* 4 ) - 5

扫描到的元素储存中间结果的栈tempStack(栈底 -> 栈顶)运算符栈operStack(栈底 -> 栈顶)说明
11数字,直接入栈 - 3
+1+operStack 为空,直接入栈- 4.1.1
(1+ (左括号,直接入栈 - 5.1
(1+ ( (左括号,直接入栈 - 5.1
21 2+ ( (数字,直接入栈 - 3
+1 2+ ( ( +operStack 栈顶为左括号,直接入栈 - 4.1.2
31 2 3+ ( ( +数字,直接入栈 - 3
)1 2 3 ++ (右括号,弹出operStack,并压入tempStack,直到出现左括号 - 5.2
*1 2 3 +operStack 栈顶为左括号,直接入栈 - 4.1.2
41 2 3 + 4+ ( *数字,直接入栈 - 3
)1 2 3 + 4 *+右括号,弹出operStack,并压入tempStack,直到出现左括号 - 5.2
-1 2 3 + 4 * +-- 与 + 同级,operStack弹出+,+压入tempStack,operStack 为空则 - 压入operStack - 4.3
51 2 3 + 4 * + 5-数字,直接入栈 - 3
到达最右端1 2 3 + 4 * + 5 -将operStack中剩余的运算符依次弹出并压入tempStack - 8
#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h> // 用于isspace()函数

#define MAX_SIZE 100

int getPriority(char op) {
    switch (op) {
    case '+':
    case '-':
        return 1;
    case '*':
    case '/':
        return 2;
    default:
        return 0;
    }
}

// 检查字符串是否只包含有效的字符
int isValidExpression(const char* expr) {
    while (*expr) {
        if (!isdigit(*expr) && !isspace(*expr) && strchr("+-*/()", *expr) == NULL) {
            return 0; // 非法字符
        }
        expr++;
    }
    return 1; // 表达式有效
}

int infixToPostfix(const char* infix, char* postfix) {
    if (!isValidExpression(infix)) {
        return 0; // 表达式无效
    }

    char stack[MAX_SIZE];
    int top = -1;
    int j = 0;

    for (int i = 0; infix[i] != '\0'; i++) {
        char token = infix[i];
        if (isdigit(token)) {
            postfix[j++] = token;
        }
        else if (token == '(') {
            stack[++top] = token;
        }
        else if (token == ')') {
            while (top > -1 && stack[top] != '(') {
                postfix[j++] = stack[top--];
            }
            if (top == -1) return 0; // 没有匹配的'('
            top--; // 弹出'('
        }
        else {
            while (top > -1 && getPriority(stack[top]) >= getPriority(token)) {
                postfix[j++] = stack[top--];
            }
            if (token == ')') return 0; // ')' 不是运算符
            stack[++top] = token;
        }
    }

    while (top > -1) {
        postfix[j++] = stack[top--];
    }

    postfix[j] = '\0';
    return 1; // 成功转换
}

int main() {
    char infix[] = "1+((2+3)*4)-5";
    char postfix[MAX_SIZE] = { 0 };

    if (infixToPostfix(infix, postfix)) {
        printf("Infix Expression: %s\n", infix);
        printf("Postfix Expression: %s\n", postfix);
    }
    else {
        printf("Invalid expression.\n");
    }

    return 0;
}

👉 完整的逆波兰计算器

#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h> // 用于isspace()函数

#define MAX_SIZE 100

typedef struct {
    int top;
    double data[MAX_SIZE];
} Stack;

void push(Stack* s, double item) {
    if (s->top == MAX_SIZE - 1) {
        printf("Stack overflow\n");
        exit(1);
    }
    s->data[++s->top] = item;
}

double pop(Stack* s) {
    if (s->top == -1) {
        printf("Stack underflow\n");
        exit(1);
    }
    return s->data[s->top--];
}

int getPriority(char op) {
    switch (op) {
    case '+':
    case '-':
        return 1;
    case '*':
    case '/':
        return 2;
    default:
        return 0;
    }
}

// 判断字符是否为操作符
int is_operator(char c) {
    return (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')');
}

// 检查字符串是否只包含有效的字符
int isValidExpression(const char* expr) {
    while (*expr) {
        if (!isdigit(*expr) && !isspace(*expr) && strchr("+-*/().", *expr) == NULL) {
            return 0; // 非法字符
        }
        expr++;
    }
    return 1; // 表达式有效
}


int infixToPostfix(const char* infix, char* postfix) {
    if (!isValidExpression(infix)) {
        return 0; // 表达式无效
    }

    char stack[MAX_SIZE];
    int top = -1;
    int j = 0;

    for (int i = 0; infix[i] != '\0'; i++) {
        char token = infix[i];
        if (isdigit(token) || (token == '.' && i + 1 < strlen(infix) && isdigit(infix[i + 1]))) {
            // 复制数字到后缀表达式
            while (isdigit(token) || token == '.') {
                postfix[j++] = token;
                i++;
                token = infix[i];
            }
            postfix[j++] = ' '; // 添加空格以分隔数字
            if (is_operator(token) || (i == strlen(infix)))
            {
                i--;
            }
        }
        else if (token == '(') {
            stack[++top] = token;
        }
        else if (token == ')') {
            while (top > -1 && stack[top] != '(') {
                postfix[j++] = stack[top--];
                postfix[j++] = ' ';
            }
            if (top == -1) return 0; // 没有匹配的'('
            top--; // 弹出'('
        }
        else {
            if (isspace(token)) {
                continue;
            }
            while (top > -1 && getPriority(stack[top]) >= getPriority(token)) {
                postfix[j++] = stack[top--];
                postfix[j++] = ' ';
            }
            if (token == ')') return 0; // ')' 不是运算符
            top++;
            stack[top] = token;
        }
    }

    while (top > -1) {
        if (strchr("(",stack[top]))
        {
            return 0;
        }
        postfix[j++] = stack[top--];
        postfix[j++] = ' ';
    }

    postfix[j] = '\0';
    return 1; // 成功转换
}

double calculate(double num1, double num2, char op) {
    switch (op) {
    case '+':
        return num1 + num2;
    case '-':
        return num1 - num2;
    case '*':
        return num1 * num2;
    case '/':
        return num1 / num2;
    default:
        printf("Invalid operator\n");
        exit(1);
    }
}

/*
    逆波兰计算器 
*/
double evaluateRPN(char* suffixExpression) {
    Stack tempStack;
    tempStack.top = -1;
    // strtok两个参数:要分割的字符串和分隔符,然后返回分割后的标记。
    char* token = strtok(suffixExpression, " ");
    char* double_tpken;
    while (token != NULL) {
        if (is_operator(token[0])) {
            // num2 先出栈,所以 num2 是减数或除数
            double num2 = pop(&tempStack);
            // num1 后出栈,所以 num1 是被减数或被除数
            double num1 = pop(&tempStack);
            double res = calculate(num1, num2, token[0]);
            push(&tempStack, res);
        }
        else {
            // 将字符串 token 转换为double。这函数会忽略字符串前面的空白字符,直到遇到数字或正负号为止,然后将遇到的数字部分转换为整数。
            push(&tempStack, strtod(token, &double_tpken));
        }
        // 在已经使用strtok函数分割过的字符串上继续分割,使用空格作为分隔符。传入NULL作为第一个参数表示继续从上一次的位置开始分割
        token = strtok(NULL, " ");
    }
    return pop(&tempStack);
}

int main() {
    char infix[] = "(12.8 + 20) - 3.55 * 4 + 10 / 5.0";
    char postfix[MAX_SIZE] = { 0 };

    if (infixToPostfix(infix, postfix)) {
        printf("Infix Expression: %s\n", infix);
        printf("Postfix Expression: %s\n", postfix);
        double result = evaluateRPN(postfix);
        printf("The result is: %.2f\n", result);
    }
    else {
        printf("Invalid expression.\n");
    }

    return 0;
}

3.2.5 反转一个字符串或者反转一个链表

反转一个字符串

#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

typedef struct Node {
	int data;
	struct Node* link;
}Node;
struct Node* top = NULL;

void Push(int x) {
	Node* temp = (Node*)malloc(sizeof(Node));
	if (temp)
	{
		temp->data = x;
		temp->link = top;
		top = temp;
	}
}

void Pop() {
	Node* temp;
	if (top == NULL) return;
	temp = top;
	top = top->link;
	free(temp);
}

int Top() {
	if (top == NULL) {
		printf("Error: Stack is empty\n");
		return -1;  // Return an error value or handle error appropriately
	}
	return top->data;
}

bool IsEmpty() {
	if (top == NULL) {
		return true;
	}
	return false;
}

void Reverse(char* C, int n)
{
	int i;
	for (i = 0; i < n; i++)
	{
		Push(C[i]);
	}
	int j;
	for (j = 0; j < n; j++)
	{
		C[j] = Top();
		Pop();
	}
}

int main() {
	char C[51] = "Hello";
	Reverse(C, strlen(C));
	printf("Output = %s", C);
	return 0;
}

反转一个链表到链栈

#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>


typedef struct Node {
	int data;
	struct Node* link;
}Node;
Node* head; // Linked List
Node* top = NULL; // Stack

// Linked List

void Insert(int data, int n) {
	Node* temp1 = (Node*)malloc(sizeof(Node));
	if (temp1 != NULL) {

		temp1->data = data;
		temp1->link = NULL;

		if (n == 1) {
			temp1->link = head;
			head = temp1;
			return;
		}

		Node* temp2 = head;
		int i;
		for (i = 0; i < n - 2; i++) {
			temp2 = temp2->link;
		}
		temp1->link = temp2->link;
		temp2->link = temp1;

	}
}

void Delete(int n) {
	//if (head == NULL) return;
	Node* temp = head;
	if (n == 1) {
		head = temp->link;
		free(temp);
		return;
	}
	int i;
	for (i = 1; i < n - 1; i++) {
		temp = temp->link;
	}
	Node* temp1 = temp->link;
	temp->link = temp1->link;
	free(temp1);
}

//iteration
void Print(Node* headerNode) {
	Node* temp = headerNode;
	printf("List is: ");
	while (temp != NULL)
	{
		printf("%d ", temp->data);
		temp = temp->link;
	}
	printf("\n");
}


// Stack
void Push(int x) {
	Node* temp = (Node*)malloc(sizeof(Node));
	if (temp)
	{
		temp->data = x;
		temp->link = top;
		top = temp;
	}
}

void Pop() {
	Node* temp;
	if (top == NULL) return;
	temp = top;
	top = top->link;
	free(temp);
}

int Top() {
	if (top == NULL) {
		printf("Error: Stack is empty\n");
		return -1;  // Return an error value or handle error appropriately
	}
	return top->data;
}

bool IsEmpty() {
	if (top == NULL) {
		return true;
	}
	return false;
}


// 反转链表到栈
void ReverseToStack() {
	while (head != NULL) {
		Push(head->data);
		Node* temp = head;
		head = head->link;
		free(temp);
	}
}

int main() {
	Insert(2, 1);
	Insert(4, 2);
	Insert(6, 3);
	Insert(5, 4);
	Print(head);
	ReverseToStack();
	Print(top);
	return 0;
}

使用数组实现的栈反转一个链表

#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>


typedef struct Node {
	int data;
	struct Node* link;
}Node;
Node* head; // Linked List

// Stack
#define MAX_SIZE 101
Node* A[MAX_SIZE];
int top = -1;

// Linked List

void Insert(int data, int n) {
	Node* temp1 = (Node*)malloc(sizeof(Node));
	if (temp1 != NULL) {

		temp1->data = data;
		temp1->link = NULL;

		if (n == 1) {
			temp1->link = head;
			head = temp1;
			return;
		}

		Node* temp2 = head;
		int i;
		for (i = 0; i < n - 2; i++) {
			temp2 = temp2->link;
		}
		temp1->link = temp2->link;
		temp2->link = temp1;

	}
}

void Delete(int n) {
	//if (head == NULL) return;
	Node* temp = head;
	if (n == 1) {
		head = temp->link;
		free(temp);
		return;
	}
	int i;
	for (i = 1; i < n - 1; i++) {
		temp = temp->link;
	}
	Node* temp1 = temp->link;
	temp->link = temp1->link;
	free(temp1);
}

//iteration
void Print() {
	Node* temp = head;
	printf("List is: ");
	while (temp != NULL)
	{
		printf("%d ", temp->data);
		temp = temp->link;
	}
	printf("\n");
}


// Stack
void Push(Node* x)
{
	if (top == MAX_SIZE - 1)
	{
		printf("Error:stack overflow\n");
		return;
	}
	A[++top] = x;
}

void Pop() {
	if (top == -1) {
		printf("Error: No element to pop\n");
		return;
	}
	top--;
}

Node* Top()
{
	if (top != -1)
	{
		return A[top];
	}
	return;
}

void Reverse()
{
	if (head == NULL)
	{
		return;
	}
	Node* temp = head;
	while (temp != NULL)
	{
		Push(temp);
		temp = temp->link;
	}
	Node* temp1 = Top();
	head = temp1;
	Pop();
	while (top != -1)
	{
		temp1->link = Top();
		Pop();
		temp1 = temp1->link;
	}
	temp1->link = NULL;
}

int main() {
	Insert(1, 1);
	Insert(4, 2);
	Insert(6, 1);
	Insert(3, 3);
	Print();
	Reverse();
	Print();
	return 0;
}

3.3 栈与递归的实现

多个函数嵌套调用的规则是:后调用先返回此时的内存管理实行““栈式管理”

一个递归函数的运行过程类似于多个函数的嵌套调用,只是调用函数和被调用函数是同一个函数。

当在一个函数的运行期间调用另一个函数时,在运行该被调用函数之前,需先完成三件事:

  • 将所有的实在参数、返回地址等信息传递给被调用函数保存;
  • 为被调用函数的局部变量分配存储区;
  • 将控制转移到被调用函数的入口。

从被调用函数返回调用函数之前,应该完成:

  • 保存被调函数的计算结果;
  • 释放被调函数的数据区;
  • 依照被调函数保存的返回地址将控制转移到调用函数。

函数之间的信息传递和控制转移必须通过“栈”来实现,即系统将整个程序运行时所需的数据空间安排在一个栈中,每当调用一个函数时,就为它在栈顶分配一个存储区,每当从一个函数退出时,就释放它的存储区,则当前正运行的函数的数据区必在栈顶。

  • 递归过程指向过程中占用的数据区,称之为递归工作栈
  • 每一层的递归参数合成一个记录,称之为递归工作记录
  • 栈顶记录指示当前层的执行情况,称之为当前活动记录
  • 栈顶指针,称之为当前环境指针

汉诺塔:

汉诺塔问题的描述如下:有三根柱子,A、B、C。A柱子上从下往上按大小顺序叠放着n个圆盘,目标是将这n个圆盘移动到C柱子上。移动过程中必须遵守以下规则:

  • 一次只能移动一个圆盘。

  • 大圆盘不能叠放在小圆盘上。

  • 可以利用B柱子作为辅助柱子。

对于任何一个具体的步骤,实际上都需要进行3次移动:

  • 将上面的 n-1 个盘子从起始柱子(如 A)移到另一个柱子(如 B);
  • 将最大的盘子从起始柱子移到目标柱子(如 C);
  • 最后,将 n-1 个盘子从临时柱子(如 B)移到目标柱子(如 C)。
#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>

// 将n个盘子从start移动到end,通过temporary辅助
void towerOfHanoi(int n, char start, char end, char temporary) {
    if (n == 1) {
        printf("Move disk 1 from %c to %c\n", start, end);
        return;
    }

    // 首先将n-1个盘子从start移动到temporary
    towerOfHanoi(n - 1, start, temporary, end);

    // 然后将最大的盘子从start移动到end
    printf("Move disk %d from %c to %c\n", n, start, end);

    // 最后将n-1个盘子从temporary移动到end
    towerOfHanoi(n - 1, temporary, end, start);
}

int main() {
    int n; // 盘子的数量
    printf("Enter the number of disks: ");
    scanf("%d", &n);

    // 开始移动盘子,'A'是起始位置,'B'是辅助位置,'C'是目标位置
    towerOfHanoi(n, 'A', 'C', 'B');
    return 0;
}

3.4 队列 - Queue

3.4.1 抽象数据类型队列的定义

队列(queue)是一种先进先出(first in first out, FIFO)的线性表。它只允许在表的一端进行插入,而在另一端删除元素。

在队列中,允许插人的一端叫做队尾(rear),允许删除的一端则称为队头(front)。

在这里插入图片描述

队列的抽象数据类型的定义:

在这里插入图片描述

3.4.2 链队列–队列的链式表示和实现

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>
#include <stdlib.h>

#define MAXQSIZE 10
#define OK 1
#define OVERFLOW -1
#define ERROR -2

typedef struct Node {
    int data;
    struct Node* next;
}Node;
Node* front = NULL;
Node* rear = NULL;

void Enqueue(int x) {
    Node* temp = (Node*)malloc(sizeof(Node));
    if (!temp) {
        exit(1); // 内存分配失败,退出程序
    }
    temp->data = x;
    temp->next = NULL;
    if (front == NULL && rear == NULL) {
        front = rear = temp;
        return;
    }
    rear->next = temp;
    rear = temp;
}

void Dequeue() {
    Node* temp = front;
    if (front == NULL) return;
    if (front == rear) {
        front = rear = NULL;
    }
    else
    {
        front = front->next;
    }
    printf("Dequeue : %d\n", temp->data);
    free(temp);
}

int main() {
    Enqueue(1);
    Enqueue(2);
    Enqueue(3);
    Dequeue();
    Dequeue();
    Dequeue();
    return 0;
}

3.4.3 循环队列–队列的顺序表示和实现

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS *1
#include <stdio.h>
#include <stdlib.h>

#define MAXQSIZE 10
#define OK 1
#define OVERFLOW -1
#define ERROR -2

typedef int Status;
typedef struct {
    int* base;
    int front;
    int rear;
} SqQueue;

Status InitQueue(SqQueue* Q) {
    Q->base = (int*)malloc(MAXQSIZE * sizeof(int));
    if (!Q->base) exit(1); // 使用exit(1)表示错误退出
    Q->front = Q->rear = 0;
    return OK;
}

Status EnQueue(SqQueue* Q, int e) {
    if ((Q->rear + 1) % MAXQSIZE == Q->front) {
        return OVERFLOW; // 队列已满,返回OVERFLOW
    }
    Q->base[Q->rear] = e;
    Q->rear = (Q->rear + 1) % MAXQSIZE;
    return OK;
}

Status DeQueue(SqQueue* Q, int* e) {
    if (Q->front == Q->rear) {
        return ERROR; // 队列为空,返回ERROR
    }
    *e = Q->base[Q->front]; // 使用引用来修改e的值
    Q->front = (Q->front + 1) % MAXQSIZE;
    return OK;
}

void DestroyQueue(SqQueue* Q) {
    free(Q->base);
    Q->base = NULL;
    Q->front = Q->rear = 0;
}

int main() {
    SqQueue Q;
    if (InitQueue(&Q) == OK) {
        EnQueue(&Q, 1);
        EnQueue(&Q, 2);
        EnQueue(&Q, 3);
        int e;
        if (DeQueue(&Q, &e) == OK) {
            printf("%d ", e);
        }
        if (DeQueue(&Q, &e) == OK) {
            printf("%d ", e);
        }
        if (DeQueue(&Q, &e) == OK) {
            printf("%d", e);
        }
    }
    DestroyQueue(&Q);
    return 0;
}

参考:

教材:严蔚敏《数据结构》(C语言版).pdf

博客:栈的基本性质

视频:深入浅出数据结构、数据结构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1860030.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

畜牧机械5G智能制造工厂物联数字孪生平台,推进制造业数字化转型

畜牧机械5G智能制造工厂物联数字孪生平台&#xff0c;推进制造业数字化转型。畜牧机械5G智能制造工厂物联数字孪生平台&#xff0c;是近年来制造业数字化转型的杰出代表。工业物联数字孪生平台平台集成了物联网、大数据、云计算和人工智能等先进技术&#xff0c;通过高速、低延…

MES工业一体机的自动化控制技术

MES工业一体机是一种集成了物料管理、生产计划、设备管理、质量控制等功能于一身的智能化生产设备。其自动化控制技术是指通过计算机自动控制系统&#xff0c;实现对生产过程中各种参数的监测、调整和控制&#xff0c;从而提高生产效率、降低生产成本和提高产品质量的一种技术手…

Appium+python自动化(二十三)- 真假美猴王Monkeyrunner与Monkey傻傻的分不清楚(超详解)

简介 看《西游记》第五十七回&#xff0c;说是“六耳猕猴”化作孙悟空的摸样&#xff0c;伤了唐僧&#xff0c;后又和孙悟空大打出手…… 这位假孙悟空&#xff0c;实力不用多说了吧&#xff0c;和真孙悟空一般无二&#xff0c;大战孙悟空&#xff0c;闹到上天入地下海。 在唐僧…

【ai】李沐 动手深度学学v2 环境安装:anaconda3、pycharm、d2

cuda-toolkit cuda_12.5.0_windows_network.exe 官方课程网站 第二版资源下载release版本 pycharm版本 李沐 【动手学深度学习v2 PyTorch版】 课程笔记 CUDA 选择11, 实际下载 12.5.0

扫描电镜工作时镜筒内的一般真空度要求

扫描电镜&#xff08;Scanning Electron Microscope, SEM&#xff09;是一种强大的表面分析工具&#xff0c;它依赖于高度真空的环境来实现高质量的电子成像。在这篇文章中&#xff0c;我们将探讨扫描电镜在工作时所需的一般真空度要求&#xff0c;以及这些要求对成像质量的影响…

【最佳实践】Eslint 配置、使用技巧、代码检查的过程、肝这一篇就够了!

大家好&#xff0c;我是DX3906 目录 Eslint 配置 文件配置 .eslintrc.js 或者eslint.config.mjs 文件 eslintrc 总配置参考文档&#xff0c;按需使用&#xff1a; 在vscode中使用eslint插件 配置.eslintignore 文件 在setting中搜所eslint 代码检查的过程通常如下【面试…

基于java+springboot+vue实现的社团管理系统(文末源码+Lw)270

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对信息管理混乱&#xff0c;出错率高&#xff0c;信息安全性差&#…

革新校园环境:轻空间打造上海六十中学多功能气膜馆

在现代教育环境中&#xff0c;舒适、环保和多功能的建筑越来越受到重视。上海六十中学多功能气膜馆的建设正如火如荼地进行中&#xff0c;这个项目由轻空间&#xff08;江苏&#xff09;膜科技有限公司全力打造&#xff0c;将为学校师生带来全新的活动体验。 项目进展 自项目启…

Kotlin设计模式:深入解析Facade模式

Kotlin设计模式&#xff1a;深入解析Facade模式 在软件开发中&#xff0c;随着系统复杂度的增加&#xff0c;管理和使用多个相关接口变得越来越困难。这时候&#xff0c;Facade模式&#xff08;外观模式&#xff09;就显得尤为重要。本文将深入探讨Kotlin中的Facade模式&#…

史上最全涵盖在线离线nginx安装手册(含国产信创环境下麒麟V10)

下载安装包略 下载地址&#xff1a;http://nginx.org/download/nginx-版本.tar.gz 配合下载资源食用更佳 https://download.csdn.net/download/ProGram_BlackCat/89480431 安装 tar -zxvf nginx-1.16.1.tar.gz && cd nginx-1.16.1# 创建安装目录(默认路径↓) mkdir /u…

创建App

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在Django项目中&#xff0c;推荐使用App来完成不同模块的任务&#xff0c;通过执行如下命令可以启用一个应用程序。 python manage.py startapp app…

读AI新生:破解人机共存密码笔记11智能爆炸

1. 大猩猩问题 1.1. 大约1000万年前&#xff0c;现代大猩猩的祖先创造了进化出现代人类的遗传谱系 1.1.1. 它们的物种基本上没有未来&#xff0c;除了我们屈尊所允许它们拥有的未来 1.1.2. 我们不希望在超级智能机器面前处于类似的地位 1.2. 大猩猩问题就是人类是否能在一个…

LoadBalance 负载均衡

什么是负载均衡 负载均衡(Load Balance&#xff0c;简称 LB),是⾼并发,⾼可⽤系统必不可少的关键组件. 当服务流量增⼤时,通常会采⽤增加机器的⽅式进⾏扩容,负载均衡就是⽤来在多个机器或者其他资源中,按照⼀定的规则合理分配负载. 负载均衡的⼀些实现 服务多机部署时,开发⼈…

有重复字符串的排列组合

题目链接 有重复字符串的排列组合 题目描述 注意点 字符都是英文字母字符串长度在[1, 9]之间 解答思路 本题与无重复字符串的排列组合类似&#xff0c;区别是字符串中有重复字符&#xff0c;所以组合数量不会是固定的n * (n - 1) * (n - 2) * … * 1首先想到的仍然是深度优…

研究发现GPT-4o等较新的多模态AI模型的安全机制有不足之处

在 ChatGPT 和类似的生成式人工智能模型推出后&#xff0c;很多人都在强调安全问题&#xff0c;政府也参与其中&#xff0c;OpenAI 甚至成立了一个超级协调小组&#xff0c;以阻止未来的人工智能失控&#xff0c;但由于对人工智能安全的发展方向存在分歧&#xff0c;该小组于今…

【排序算法】—— 希尔排序

目录 一、希尔排序原理 二、希尔排序的思路 三、希尔排序为什么快 四、如何取增量 五、源码 希尔排序是简单插入排序的一种升级版&#xff0c;它也是用了插入的思想&#xff0c;而插入排序相比冒泡排序和选择排序的效率要高的多&#xff0c;再将它优化为希尔排序后效率跟原…

有哪些好用的骨传导耳机是值得推荐的?五款非常好用的骨传导耳机推荐!

作为一个数码博主&#xff0c;我以前接触过很多种不同型号的骨传导耳机产品&#xff0c;骨传导耳机在传输声音时不直接经过内耳膜和外耳道&#xff0c;而是通过振动骨骼来传导声音&#xff0c;说明我们的耳朵是开放式的状态&#xff0c;时刻耳道保持清爽&#xff0c;可以避免耳…

前端基础——自学习梳理

超文本协议HTML <!DOCTYPE HTML> <html><head><meta charset"utf-8"> <style> /*Css*/.sty1{height:100px;width:100px;background-color: red;margin-top: 10px;float:left;margin-left: 10px;box-shadow: 10px 10px 10px #0000…

uni微信小程序使用lottie

在uni插件市场找到 lottie-uni https://ext.dcloud.net.cn/plugin?id1044按照文档要求安装 HBuilderX 引入 下载或导入示例获取插件 import lottie from /common/lottie-miniprogram.jsindex.vue <template><uni-popupref"popup"type"center"ba…

C++ | Leetcode C++题解之第172题阶乘后的零

题目&#xff1a; 题解&#xff1a; class Solution { public:int trailingZeroes(int n) {int ans 0;while (n) {n / 5;ans n;}return ans;} };