数据结构:栈和队列

文章目录

  • 一、栈
    • 1.栈的概念及结构
      • 1.栈的概念及结构
      • 2.栈的实现
    • 2.栈的顺序表实现
      • 1.栈的结构体和实现的功能函数
      • 2.栈的初始化,入栈和出栈操作
      • 3.栈的其他操作
    • 3.栈的链表实现
      • 1.栈的结构体和实现的功能函数
      • 2.栈功能函数的实现
  • 二、队列
    • 1.队列的概念及结构
      • 1.队列的概念及结构
      • 2.队列的实现
    • 2.队列的顺序表实现(循环队列)
      • 1.循环队列分析
      • 2.循环队列的结构体和实现的功能函数
      • 2.循环队列初始化和插入
      • 2.循环队列的其他操作
    • 3.队列的链表实现
      • 1.队列的结构体和实现的功能函数
      • 2.队列功能函数的实现
  • 二、栈和队列应用实列:实现简单计算器
    • 1.问题分析
    • 1.代码实现
  • 总结


一、栈

1.栈的概念及结构

1.栈的概念及结构

栈是一种特殊的线性表,只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端其称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出的原则。
在这里插入图片描述

2.栈的实现

栈的实现一般可以使用数组或者链表实现
栈的数组实现:
在这里插入图片描述
栈的链表实现:
在这里插入图片描述
对比两种方式的插入和删除:数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。

2.栈的顺序表实现

1.栈的结构体和实现的功能函数

typedef int STDataType;
typedef struct Stack
{STDataType* data;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);// 获取栈中有效元素个数 
bool StackEmpty(Stack* ps);// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
void StackDestroy(Stack* ps);// 销毁栈 

这里我们使用动态开辟的结构,保证栈的空间足够。数组实现我们需要一个变量来保存栈顶元素。栈顶元素也是我们栈中有效元素的个数。

2.栈的初始化,入栈和出栈操作

// 初始化
void StackInit(Stack* ps)
{ps->top = 0;//指向栈顶的位置置为数组的起始位置ps->capacity = 0;//把容量进行初始化ps->data = NULL;//把数据区进行初始化
}
// 入栈
void StackPush(Stack* ps, STDataType data)
{assert(ps);if (ps->top == ps->capacity)//判断空间是否已满,已满就进行扩容{int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//产生新的容量Stack* p = (Stack*)realloc(ps->data, sizeof(Stack)* newcapacity);//进行扩容if (p == NULL)//判断是否扩容成功{perror("realloc");exit(-1);}ps->data = p;//指向扩容后的地址ps->capacity = newcapacity;//更新容量为新的容量}ps->data[(ps->top)++] = data;//把数据弹入栈顶
}
// 出栈 
void StackPop(Stack* ps)
{assert(ps);if (ps->top == 0)//判断是否还有元素{return;}ps->top--;//弹出栈顶元素
}

这里初始化,入栈和出栈操作和顺序表的操作没什么区别。

3.栈的其他操作

// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{assert(ps);return ps->data[(ps->top) - 1];//栈顶的前一个位置为我们的栈顶元素,因为我们设置的起始位置从0开始
}
// 获取栈中有效元素个数 
int StackSize(Stack* ps)
{assert(ps);return ps->top;//栈顶元素就是栈中有效元素个数
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool StackEmpty(Stack* ps)
{assert(ps);return ps->top == 0;//返回判断结果,等于0则代表没有元素,则返回真。反之则为假。
}
// 销毁栈 
void StackDestroy(Stack* ps)
{assert(ps);free(ps->data);//释放掉我们开辟的数据空间ps->data = NULL;//把指向我们开辟数据的空间指向空ps->top = 0;ps->capacity = 0;
}

这里销毁注意我们的数据区的也要进行空间释放,防止造成空间泄露。
测试代码:

void test1()
{Stack ps;StackInit(&ps);StackPush(&ps, 1);StackPush(&ps, 2);StackPush(&ps, 3);StackPush(&ps, 4);StackPush(&ps, 5);printf("栈顶:%d\n", StackTop(&ps));// 获取栈顶元素 printf("个数:%d\n", StackSize(&ps));// 获取栈中有效元素个数if (!StackEmpty(&ps)){printf("Stack is not NULL\n");}StackPop(&ps);printf("栈顶:%d\n", StackTop(&ps));// 获取栈顶元素 if (!StackEmpty(&ps)){printf("Is not Empty\n");}StackDestroy(&ps);
}
int main()
{test1();//test2();//test3();//test4();//test5();return 0;
}

在这里插入图片描述

3.栈的链表实现

1.栈的结构体和实现的功能函数

typedef int STDataType;
typedef struct StackNode
{STDataType data;struct StackNode* next;  //记录下一个区域的指针
}StackNode;
typedef struct Stack//头节点
{int size;//记录元素的个数StackNode head;
}Stack;
void StackInit(Stack* ps);// 初始化栈 
void StackPush(Stack* ps, STDataType data);// 入栈 
void StackPop(Stack* ps);// 出栈 
STDataType StackTop(Stack* ps);// 获取栈顶元素 
int StackSize(Stack* ps);// 获取栈中有效元素个数 
bool StackEmpty(Stack* ps);// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
void StackDestroy(Stack* ps);// 销毁栈 

我们这里和实现双链表一样,设置了一个特殊的头节点。头节点比正常节点多了一个变量用来记录栈中的元素个数,可以避免返回栈的元素个数时对栈进行遍历。

2.栈功能函数的实现

// 初始化栈 
void StackInit(Stack* ps)
{ps->size = 0;//初始元素为0ps->head.next = NULL;//无元素时头节点的下一个指向空ps->head.data = 0;
}
// 入栈 
void StackPush(Stack* ps, STDataType data)
{assert(ps);StackNode* add = (StackNode*)malloc(sizeof(StackNode));//创建节点if (add == NULL)//判断节点是否创建成功{perror("malloc");exit(-1);}add->data = data;//给节点赋上数据StackNode* pos = &(ps->head);//要取地址add->next = pos->next;//进行头插pos->next = add;ps->size++;
}
// 出栈 
void StackPop(Stack* ps)
{assert(ps);StackNode* pos = &(ps->head);if (pos->next == NULL){return;}StackNode* del = pos->next;//进行头删pos->next = del->next;free(del);ps->size--;
}
// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{assert(ps);StackNode* pos = (&(ps->head))->next;if (pos == NULL)//判断头指针的下一个是否为空{return;}return pos->data;
}
// 获取栈中有效元素个数 
int StackSize(Stack* ps)
{assert(ps);return ps->size;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool StackEmpty(Stack* ps)
{assert(ps);return ps->size == 0;
}
// 销毁栈 
void StackDestroy(Stack* ps)
{assert(ps);StackNode* pos = &(ps->head);if (pos->next == NULL)//判断头指针的下一个是否为空{return;}StackNode* del = pos->next;while (del != NULL){pos->next = del->next;free(del);del = pos->next;}
}

这里就是沿用链表的操作,注意释放节点时避免节点丢失。
测试函数:

void test2()
{Stack ps;StackInit(&ps);StackPush(&ps, 1);StackPush(&ps, 2);StackPush(&ps, 3);StackPush(&ps, 4);StackPush(&ps, 5);printf("栈顶:%d\n", StackTop(&ps));// 获取栈顶元素 printf("个数:%d\n", StackSize(&ps));// 获取栈中有效元素个数if (!StackEmpty(&ps)){printf("Stack is not NULL\n");}StackPop(&ps);printf("栈顶:%d\n", StackTop(&ps));// 获取栈顶元素 StackDestroy(&ps);
}
int main()
{//test1();test2();//test3();//test4();//test5();return 0;
}

在这里插入图片描述

二、队列

1.队列的概念及结构

1.队列的概念及结构

队列只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的特点。进行插入操作的一端称为队尾 ,进行删除操作的一端称为队头。
在这里插入图片描述

2.队列的实现

队列的实现一般可以使用数组或者链表实现
队列的链表实现:
在这里插入图片描述
对比两种方式的插入和删除:使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,需要频繁移动数据,效率会比较低。

2.队列的顺序表实现(循环队列)

由于队列使用数组需要扩容和频繁移动数据,这样的结构并不常用,所以我们用顺序表实现循环的队列。

1.循环队列分析

我们假设数组的大小有五个元素
我们如何判断队列中的元素是否已经满了呢?
在这里插入图片描述
用头位置等于尾位置吗?
在这里插入图片描述
上述一个元素也没有会不会直接判断为数组已满呢?
我们的解决办法是保证一个位置为空,当尾位置等于头位置时即为队列满,即队尾不存储数据。
在这里插入图片描述

2.循环队列的结构体和实现的功能函数

#define MAXNUM 5
typedef int QDataType;
typedef struct QListNode
{QDataType data[5];int head;//对头元素int end;//队尾元素
}QNode;
void QueueInit(QNode* q);// 初始化队列 
void QueuePush(QNode* q, QDataType data);// 队尾入队列 
void QueuePop(QNode* q);// 队头出队列 
QDataType QueueFront(QNode* q);// 获取队列头部元素 
QDataType QueueBack(QNode* q);// 获取队列队尾元素 
int QueueSize(QNode* q);// 获取队列中有效元素个数 
int QueueEmpty(QNode* q);// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
void QueueDestroy(QNode* q);// 销毁队列 

2.循环队列初始化和插入

// 初始化队列 
void QueueInit(QNode* q)
{q->head = 0;q->end = 0;
}
// 队尾入队列 
void QueuePush(QNode* q, QDataType data)
{assert(q);if ((q->end - q->head) == (MAXNUM-1))//尾元素和首元素相差最大数量减一个元素代表队列已满{printf("队列已满,无法插入\n");return;}q->data[(q->end) % MAXNUM] = data;q->end++;
}

我们对队列进行插入时要对队尾元素进行取模运算。防止插入时造成越界。

2.循环队列的其他操作

// 队头出队列 
void QueuePop(QNode* q)
{assert(q);if (q->head == q->end)//尾元素和首元素相同证明队列中没有元素{printf("没有元素可以出队\n");return;}q->head++;
}
// 获取队列头部元素 
QDataType QueueFront(QNode* q)
{assert(q);if (q->head == q->end && q->end != 0)//尾元素和首元素相同证明队列中没有元素{printf("没有元素可以查看\n");return -1;}return q->data[(q->head) % MAXNUM];
}
// 获取队列队尾元素 
QDataType QueueBack(QNode* q)
{assert(q);if (q->head == q->end && q->end != 0)//尾元素和首元素相同证明队列中没有元素{printf("没有元素可以查看\n");return -1;}return q->data[(q->end - 1) % MAXNUM];
}
// 获取队列中有效元素个数 
int QueueSize(QNode* q)
{assert(q);return q->end - q->head;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(QNode* q)
{assert(q);return q->head == q->end;
}
// 销毁队列 
void QueueDestroy(QNode* q)
{assert(q);q->head = 0;q->end = 0;
}

我们返回队头队尾数据要看尾是否在0位置,为0代表一个元素还没插入。我们进行插入返回元素的操作都需要进行取模操作!!!
测试函数:

void test3()
{QNode qu;QueueInit(&qu);int i = 0;for(i = 0; i < 5; i++){QueuePush(&qu, i);}printf("队头:%d\n", QueueFront(&qu));printf("队尾:%d\n", QueueBack(&qu));QueuePop(&qu);printf("队头:%d\n", QueueFront(&qu));printf("个数:%d\n", QueueSize(&qu));printf("为空:%d\n", QueueEmpty(&qu));QueuePop(&qu);QueuePop(&qu);QueuePop(&qu);QueuePop(&qu);QueueDestroy(&qu);
}
int main()
{//test1();//test2();test3();//test4();//test5();return 0;
}

在这里插入图片描述

3.队列的链表实现

1.队列的结构体和实现的功能函数

typedef int QDataType;
typedef struct QListNode
{QDataType data;struct QListNode* next;
}QNode;
// 队列的结构 
typedef struct Queue
{QNode* head;QNode* end;
}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);// 获取队列中有效元素个数 
int QueueEmpty(Queue* q);// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
void QueueDestroy(Queue* q);// 销毁队列 

我们设置了一个结构体用来存储头节点和尾节点,目的是为了减少遍历。

2.队列功能函数的实现

 //初始化队列 
void QueueInit(Queue* q)
{assert(q);q->head = NULL;q->end = NULL;
}
// 队尾入队列 
void QueuePush(Queue* q, QDataType data)
{assert(q);QNode* pos = (QNode*)malloc(sizeof(QNode));if (pos == NULL){perror("malloc");exit(-1);}pos->data = data;pos->next = NULL;if (q->end == NULL){q->head = pos;q->end = pos;}else{q->end->next = pos;q->end = q->end->next;}
}
// 队头出队列 
void QueuePop(Queue* q)
{assert(q);QNode* del = q->head;if (q->head == q->end && q->end != NULL)//当头指针等于尾指针时证明队列中没有元素{printf("没有元素可以出队\n");return;}q->head = q->head->next;free(del);
}
// 获取队列头部元素 
QDataType QueueFront(Queue* q)
{assert(q);if (q->head == q->end && q->end != NULL)//当头指针等于尾指针时证明队列中没有元素{printf("没有元素可以查看\n");return -1;}return q->head->data;
}
// 获取队列队尾元素 
QDataType QueueBack(Queue* q)
{assert(q);if (q->head == q->end && q->end != NULL)//当头指针等于尾指针时证明队列中没有元素{printf("没有元素可以查看\n");return -1;}return q->end->data;
}
// 获取队列中有效元素个数 
int QueueSize(Queue* q)
{assert(q);int size = 0;QNode* pos = q->head;while (pos != q->end){size++;pos = pos->next;}return size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{assert(q);return q->head == q->end;
}
// 销毁队列 
void QueueDestroy(Queue* q)
{assert(q);QNode* del = q->head;while (del != q->end){q->head = del->next;free(del);del = q->head;}free(q->head);q->head = NULL;q->end = NULL;
}

测试函数:

void test4()
{Queue qu;QueueInit(&qu);int i = 0;for(i = 0; i < 5; i++){QueuePush(&qu, i);}printf("队头:%d\n", QueueFront(&qu));printf("队尾:%d\n", QueueBack(&qu));QueuePop(&qu);printf("队头:%d\n", QueueFront(&qu));printf("个数:%d\n", QueueSize(&qu));printf("为空:%d\n", QueueEmpty(&qu));QueuePop(&qu);QueuePop(&qu);QueuePop(&qu);QueuePop(&qu);QueueDestroy(&qu);
}
int main()
{//test1();//test2();//test3();test4();//test5();return 0;
}

在这里插入图片描述

二、栈和队列应用实列:实现简单计算器

计算10+(10+20*30)*4-50

1.问题分析

我们计算需要进行优先级比较,是否含有括号,数据的存储等,我们需要两个栈来进行存储,一个符号栈,一个数字栈。
在这里插入图片描述
思路分析:
在这里插入图片描述

1.代码实现

void Count(Stack* num, int headsign)
{int n1 = 0;//用于计算的变量1int n2 = 0;//用于计算的变量2//已经是指针了,不可以在取地址了n2 = StackTop(num);//n2先进性出栈StackPop(num);//数字出栈n1 = StackTop(num);//n2出栈是为了防止除法是顺序错乱StackPop(num);//数字出栈int sum = 0;//用来存储两个值的结果switch (headsign)//判断符号{case '+':sum = n1 + n2;break;case '-':sum = n1 - n2;break;case '*':sum = n1 * n2;break;case '/':sum = n1 / n2;break;default:exit(-1);//未知符号,程序退出}//入数字栈StackPush(num, sum);
}
void Match_Brace(Stack* sign, Stack* num)//开始匹配左括号
{int headsign = 0;//存储栈顶的元素符号headsign = StackTop(sign);//获取栈顶元素符号while(!StackEmpty(sign))//符号栈不为空则一直进行循环, 直到在左括号处结束{if (headsign == '(')//如果为左括号则直接出栈结束{StackPop(sign);//符号出栈break;}else{//计算Count(num, headsign);//计算函数StackPop(sign);//符号出栈}headsign = StackTop(sign);//获取栈顶元素符号}
}
int Priority(int symbol)
{switch (symbol)//判断符号{case '(':return 0;case '+':case '-':return 1;case '*':case '/':return 2;default:exit(-1);//未知符号,程序退出}
}
void Match_Symbols(Stack* sign, Stack* num, int symbol)
{if (StackEmpty(sign) || symbol == '(')//如果栈为空或者为左括号,则直接入栈{StackPush(sign, symbol);//入栈return;}int headsign = 0;//存储栈顶的元素符号headsign = StackTop(sign);//获取栈顶元素符号if (Priority(symbol) > Priority(headsign))//优先级比较,该符号优先级高则直接入栈{StackPush(sign, symbol);//入栈return;}while(Priority(symbol) <= Priority(headsign))//直到优先级高于栈顶元素,停止循环{//计算Count(num, headsign);//计算函数StackPop(sign);//栈顶符号出栈if (StackEmpty(sign))//栈为空则退出循环{break;}headsign = StackTop(sign);//获取栈顶元素符号}StackPush(sign, symbol);//入栈
}
void test5()
{Stack num;//存储数字所使用的栈Stack sign;//存储算数符号所用的栈StackInit(&num);//对数字栈进行初始化StackInit(&sign);//对符号栈进行初始化char* s = "10+(10+20*30)*4-50";//要计算的表达式int i = 0;//用来判断表达式是否已到结尾int sum = 0;//用来存储一个整形数据int flag = 0;//用来判断是否取完一个整形元素while (s[i] != '\0'){if (isdigit(s[i]))//判断是否为数字{sum = sum * 10 + (s[i] - '0');//更新sum的值flag = 1;//把标志位置为1,为后面判断是否入栈准备}else{if (flag == 1)//判断该数字是否以入栈{//入数字栈StackPush(&num, sum);flag = 0;//更新标志位sum = 0;//更新sum值,防止下次计算时出错}if (s[i] == ')')//开始匹配左括号{//进行出栈匹配左括号Match_Brace(&sign, &num);}else//字符为( + - * /{//进行优先级比较Match_Symbols(&sign, &num,s[i]);}}i++;}if (flag == 1)//判断该数字是否以入栈{//入数字栈StackPush(&num, sum);flag = 0;//更新标志位sum = 0;//更新sum值,防止下次计算时出错}int headsign = 0;//存储栈顶的元素符号while (!StackEmpty(&sign))//符号栈不为空则一直进行计算{headsign = StackTop(&sign);//获取栈顶元素符号//计算Count(&num, headsign);StackPop(&sign);//符号出栈}printf("%d\n", StackTop(&num));StackDestroy(&num);//销毁数字栈StackDestroy(&sign);//销毁字符栈
}
int main()
{//test1();//test2();//test3();//test4();test5();return 0;
}

在这里插入图片描述
注意函数对指针的二次传参不需要在进行取地址,在计算机中计算机识别的是字符,所以我们需要一个字符一个字符的进行,这时间需要我们判断这个数字到底几位数,需要我们一个临时量,也可以用库函数atoi实现。其他按照思路可以轻而易举的实现。

总结

栈和队列都是含有限制的线性表。前面的知识扎实的话实现栈和队列没有一点问题。都是顺序表和链表的其中一部分。

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

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

相关文章

【使用教程】在Ubuntu下运行CANopen通信PMM伺服电机使用教程(NimServoSDK_V2.0.0)

本教程将指导您在Ubuntu操作系统下使用NimServoSDK_V2.0.0来运行CANopen通信的PMM系列一体化伺服电机。我们将介绍必要的步骤和命令&#xff0c;以确保您能够成功地配置和控制PMM系列一体化伺服电机。 NimServoSDK_V2.0.0是一款用于PMM一体化伺服电机的软件开发工具包。它提供了…

Harmony OS教程学习笔记

基础知识 1.如何修改程序启动的第一个页面&#xff1f; 不想使用创建的默认的页面&#xff0c;这时需要修改启动页面&#xff0c;修改的地方在EntryAbility文件中的onWindowStageCreate方法中。 onWindowStageCreate(windowStage: window.WindowStage) {// Main window is cr…

Golang Gorm 更新字段 save update updates

更新和删除操作的前提条件都是要在找到数据的情况下&#xff0c;先要查询到数据才可以做操作。 更新的前提的先查询到记录&#xff0c;Save保存所有字段&#xff0c;用于单个记录的全字段更新它会保控所有字段&#xff0c;即使零值也会保存。 在更新和删除之前&#xff0c;要利…

远程线程注入(简单样例以及原理)

远程线程注入(简单样例以及原理) 注入的目标是将我们的代码注入到目标进程的地址空间中 注入通常可以根据注入的内容分为两种类型&#xff1a; shellcode注入 &#xff1a;这种注入是将我们的代码直接注入到目标内存中&#xff0c;这就要保证我们的代码在贴到其他地址上后仍…

交叉熵--损失函数

目录 交叉熵&#xff08;Cross Entropy&#xff09; 【预备知识】 【信息量】 【信息熵】 【相对熵】 【交叉熵】 交叉熵&#xff08;Cross Entropy&#xff09; 是Shannon信息论中一个重要概念&#xff0c; 主要用于度量两个概率分布间的差异性信息。 语言模型的性能…

Azure静态网站托管

什么是静态网站托管 Azure Blob的静态网站托管是一项功能&#xff0c;它允许开发人员在Azure Blob存储中托管和发布静态网站。通过这个功能&#xff0c;您可以轻松地将静态网页、图像、视频和其他网站资源存储在Azure Blob中&#xff0c;并直接通过提供的URL访问这些资源。 官…

使用VisualStudio制作上位机(一)

文章目录 使用VisualStudio制作上位机(一)写在前面第一部分:创建应用程序第二部分:GUI主界面设计使用VisualStudio制作上位机(一) Author:YAL 写在前面 1.达到什么目的呢 本文主要讲怎么通过Visual Studio 制作上位机,全文会以制作过程来介绍怎么做,不会去讲解具体…

css 实现svg动态图标效果

效果演示&#xff1a; 实现思路&#xff1a;主要是通过css的stroke相关属性来设置实现的。 html代码: <svgt"1692441666814"class"icon"viewBox"0 0 1024 1024"version"1.1"xmlns"http://www.w3.org/2000/svg"p-id"…

操作符详解(1)

1. 操作符分类&#xff1a; 算术操作符 移位操作符 位操作符 赋值操作符 单目操作符 关系操作符 逻辑操作符 条件操作符 逗号表达式 下标引用、函数调用和结构成员 2. 算术操作符 - * / % 1. 除了 % 操作符之外&#xff0c;其他的几个操作符可以作用于整数和浮点数。 2. 对…

Redis中的缓存穿透、雪崩、击穿的原因以及解决方案

一&#xff0c;什么是缓存穿透、雪崩、击穿&#xff1f; 1、缓存穿透&#xff1a; 是指用户查询数据&#xff0c;在数据库没有&#xff0c;自然在缓存中也不会有。这样就导致用户查询的时候&#xff0c;在缓存中找不到&#xff0c;每次都要去数据库再查询一遍&#xff0c;然后…

宇宙原理:黑洞基础。

宇宙原理&#xff1a;黑洞基础TOC 黑洞的数理基础&#xff1a;一个由满数组成的数盘&#xff0c;经过自然演进&#xff0c;将会逐步稀疏化、最终会向纯数方案发展&#xff1b;纯数方案虽然只有{2}、无数&#xff08;虚拟&#xff09;、{0,1,2,3}&#xff08;虚拟&#xff09;、…

1.82亿南沙区“数字城市”城市运营中心建设项目采购需求

1. 总体技术需求 项目整体框架 遵循“一个中枢、两大中台、基础底座”的总体架构&#xff0c;构建全区城市治理中枢&#xff0c;实现连通上下、衔接左右、分拨处置和协同指挥等作用。 本项目系统的总体架构分为一个中枢、两大中台、基础底座共五部分&#xff0c;在政务云平台…

netty(一):NIO——处理消息边界

处理消息边界 为什么要处理边界 因为会存在半包和粘包的问题 1.客户端和服务端约定一个固定长度 优点&#xff1a;简单 缺点&#xff1a;可能造成浪费 2.客户端与服务端约定一个固定分割符 *缺点 效率低 3.先发送长度&#xff0c;再发送数据 TLV格式&#xff1a; type…

【ROS】参数服务器--理论模型与参数操作(C++)

一、概念介绍 参数服务器在ROS中主要用于实现不同节点之间的数据共享。参数服务器相当于是独立于所有节点的一个公共容器&#xff0c;可以将数据存储在该容器中&#xff0c;被不同的节点调用&#xff0c;当然不同的节点也可以往其中存储数据。 作用&#xff1a;存储一些多节点…

C语言:库函数atoi及其模拟实现

atoi&#xff1a; atof是C语言标准库中的一个函数&#xff0c;用于将字符串转换为对应的浮点数/整形数。 函数接受一个参数str&#xff0c;该参数是一个指向以null结尾的字符串的指针。atof函数会尝试将这个字符串转换为一个浮点数&#xff0c;并返回转换后的结果。 要注意的是…

飞机打方块(五)游戏音乐

一、新建节点 1.在Start场景中新建Music节点&#xff0c;绑定canvas 2.在Game场景中新建Music节点 3.新建节点 4.新建Music脚本&#xff0c;绑定Canvas Music.ts const { ccclass, property } cc._decorator;ccclass export default class NewClass extends cc.Component {p…

*args无疑是解决函数重载的一大创新利器--我用可变数量参数解决了函数重载问题

需求分析 最近遇到这样一个需求&#xff1a;根据用户传递的不同参数数量执行不同的功能。我这几天一直在思考这个问题&#xff1a;如何根据参数数量去执行不同的功能&#xff0c;最初的设想是把不需要的参数设置为NONE或者""再或者" "(后两者引号均表示传空…

Flink 流式读写文件、文件夹

文章目录 一、flink 流式读取文件夹、文件二、flink 写入文件系统——StreamFileSink三、查看完整代码 一、flink 流式读取文件夹、文件 Apache Flink针对文件系统实现了一个可重置的source连接器&#xff0c;将文件看作流来读取数据。如下面的例子所示&#xff1a; StreamExe…

【无标题】QT应用编程: QtCreator配置Git版本控制(码云)

QT应用编程: QtCreator配置Git版本控制(码云) 感谢&#xff1a;DS小龙哥的文章&#xff0c;这篇主要参考小龙哥的内容。 https://cloud.tencent.com/developer/article/1930531?areaSource102001.15&traceIdW2mKALltGu5f8-HOI8fsN Qt Creater 自带了git支持。但是一直没…

软件测试学术顶会——ISSTA 2023 论文(网络安全方向)清单、摘要与总结

总结 本次会议涵盖的安全研究主题广泛,包括源代码分析、二进制代码分析、恶意软件检测、漏洞检测、模糊测试、程序验证等。一些热门的研究方向包括:基于机器学习的漏洞检测、大型语言模型在软件安全中的应用、区块链智能合约安全分析。这些方向都在最近几年持续发展。一些较冷门…