【数据结构】线性表——栈与队列

写在前面

栈和队列的关系链表和顺序表的关系差不多,不存在谁替代谁,只有双剑合璧才能破敌万千~~😎😎


文章目录

  • 写在前面
  • 一、栈
    • 1.1栈的概念及结构
    • 1.2、栈的实现
      • 1.2.1、栈的结构体定义
      • 1.2.2、栈的初始化栈
      • 1.2.3、入栈
      • 1.2.4、出栈
      • 1.2.5、 获取栈顶元素
      • 1.2.6、 获取栈中有效元素个数
      • 1.2.7、检测栈是否为空,如果为空返回非零结果,如果不为空返回0
      • 1.2.8、销毁栈
  • 二、队列
    • 2.1队列的概念及结构
    • 2.2、队列的实现
      • 2.2.1、队列的结构体定义
      • 2.2.2、初始化队列
      • 2.2.3、队尾入队列
      • 2.2.4、队头出队列
      • 2.2.5、检测队列是否为空,如果为空返回非零结果,如果非空返回0
      • 2.2.6、获取队列头部元素
      • 2.2.7、获取队列队尾元素
      • 2.2.8、获取队列中有效元素个数
      • 2.2.9、销毁队列
  • 三、栈与队列的相互实现
    • 3.1、栈模拟实现队列
    • 3.2、队列模拟实现栈
  • 四、循环队列
    • 4.1、循环队列的结构体定义
    • 4.2、循环队列的初始化
    • 4.3、循环队列的判断是否为空
    • 4.4、循环队列的判断是否满了
    • 4.5、循环队列的入队列
    • 4.6、循环队列的出队列
    • 4.7、循环队列获取头队列元素
    • 4.7、循环队列获取尾队列元素
    • 4.7、循环队列的销毁


一、栈

1.1栈的概念及结构

:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶

在这里插入图片描述ps:图片来源51CTO

1.2、栈的实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。

  • 栈的实现逻辑与顺序表的大差不差,只是需要规定规定入队列与出队列需要遵守遵守后进先出即可

1.2.1、栈的结构体定义

typedef int stackData;
typedef struct stack
{stackData* val;int size;int cakacity;
}stack;
  • typedef int stackData;把顺序表结构的类型重命名为:stackData。若将来如果要改变数据栈内容的结构类型。可以极为方便的改变。
  • 在结构体中定义了。栈的总体大小和当前容量。以便确认是否需要扩容。
  • 定义的stackData*用于接收动态开辟的内存。
  • size是栈空间的大小。
  • capacity是栈空间当前的元素数量。

1.2.2、栈的初始化栈

void SKinit(stack* head) {head->val = (stackData*)malloc(sizeof(stackData) * 4);assert(head->val);head->size = 4;//栈存储空间的大小head->top = 0;//top是栈顶元素的下一个位置
}
  • 因为栈的结构体是用户自己开辟没有进行栈存储空间内存划分的。所以需要把栈的表存储空间进行初始化。
  • 默认开辟两个空间。
  • 在成功开辟后。把size设置为2。top 设置为0(此时栈空间内没有存储有效数据)。

1.2.3、入栈

void SKpush(stack* pHead, stackData x) {assert(pHead);stack* head = pHead;if (head->top == head->size) {//判断是否需要扩容stackData* p1 = (stackData*)realloc(head->val,sizeof(stackData) * head->size * 2);assert(p1);head->val = p1;head->size *= 2;}head->val[head->top] = x;head->top++;}
  • 与顺序表尾插增添并无二样。

1.2.4、出栈

stackData SKPop(stack* pHead) {assert(pHead);stack* head = pHead;stackData date = head->val[head->top - 1];head->top--;return date;
}
  • 只要把当前数据。进行减减。无需把栈中的内容进行删除。因为再次入栈数据,就会把之前的数据覆盖。

1.2.5、 获取栈顶元素

stackData StackTop(stack* ps) {assert(ps);return ps->val[ps->top - 1];
}
  • 访问数组而已。
  • 因为top是栈顶元素的下一个元素,所以top-1就是栈顶当前元素的下标。

1.2.6、 获取栈中有效元素个数

int SKsize(stack* pHead) {return pHead->top;
}
  • 只需要返回top就知道栈空间有多少元素了

1.2.7、检测栈是否为空,如果为空返回非零结果,如果不为空返回0

int SKEmpty(stack* pHead) {return pHead->top == 0;
}
  • top也代表栈空间有多少元素,只需要把top0比较即可。

1.2.8、销毁栈

void SKdestory(stack* pHead) {while (pHead->top) {SKPop(pHead);}
}
  • 遍历出栈即可完成销毁。

二、队列

2.1队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列是先进先出FIFO(First In First Out)的原则。

  • 入队列:进行插入操作的一端称为队尾
  • 出队列:进行删除操作的一端称为队头

在这里插入图片描述

2.2、队列的实现

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

在这里插入图片描述
即:在单链表中只能使用尾插和头删进行数据的更改

2.2.1、队列的结构体定义

typedef int queData;
typedef struct QueNode {queData data;struct qnode* next;
}qnode;
typedef struct Queue {qnode* head;qnode* tail;
}que;
  • typedef int queData;链表结构的类型重命名为:queData。若将来如果要改变链表内容的结构类型,就可以极为方便的改变。
  • 在结构体QueNode中定义了链表存储的内容存储下个节点的指针
  • 为了更方便使用链表结构体,把链表结构体重命名为qnode
  • 在定义完成链表的结构体后,我们需要定义队列的结构体,用来确保每次访问队列只能访问到链表的头节点尾节点
  • 队列结构体struct Queue是用来管理链表的,所以成员变量类型都是qnode*head代表头节点,tail代表尾节点。

2.2.2、初始化队列

void QueInit(que** queue) {que* p1 = (que*)malloc(sizeof(que));assert(p1);p1->head = NULL;p1->tail = NULL;*queue = p1;
}
  • 动态开辟一个空队列。先把队列设置为空,把前后指针设置为NULL

2.2.3、队尾入队列

void QuePush(que* queue, queData x) {assert(queue);qnode* p1 = (qnode*)malloc(sizeof(qnode));assert(p1);p1->next = NULL;p1->data = x;if (queue->head == NULL) {queue->head = p1;queue->tail = p1;}else {queue->tail->next = p1;queue->tail = p1;}}
  • 首先动态开辟一个链表节点。
  • 把入队节点的next赋为空值,并且把需要插入的x值传递给新开辟的链表节点p1->data。完成入队数据的处理。
  • 在入队列之前,我们需要判断队列是否为NULL,如NULL说明队列的链表里面目前没有一个节点,那就需要把p1赋值head作为队列的队头
  • 判断了队列不为NULL,则直接把队尾的next指向p1,之后更新队尾完成入队。

2.2.4、队头出队列

qnode* QuePop(que* queue) {assert(queue);if (queue->head == NULL) {return NULL;}qnode* p1 = queue->head;queue->head = queue->head->next;if (queue->head == NULL) {queue->tail = NULL;}return p1;
}
  • 在出队列之前,我们需要判断队列是否为NULL,如NULL说明队列的链表里面目前没有一个节点,那就需返回NULL即可。
  • 判断了队列不为NULL,用一个指针p1记录原对头,之后把队头的head指针指向next节点更新新队头后返回指针p1 完成出队。
  • 如果把队列的节点全部出完后,就需要把headtail赋为NULL

2.2.5、检测队列是否为空,如果为空返回非零结果,如果非空返回0

bool QueEmpty(que* queue) {assert(queue);if (queue->head == NULL && queue->tail == NULL) {return true;}return false;
}
  • 如果队头和队尾指针同时NULL则说明队列为空。否则肯定不为空。

2.2.6、获取队列头部元素

queData QueueFront(que* queue){assert(queue);assert(!QueEmpty(queue));return queue->head->data;
}
  • 直接返回队头的data

2.2.7、获取队列队尾元素

queData QueueBack(que* queue) {assert(queue);assert(!QueEmpty(queue));//如果队列为空还哪有说明头部元素return queue->tail->data;
}
  • 直接返回队尾的data

2.2.8、获取队列中有效元素个数

int QueSize(que* queue) {assert(queue);qnode* p1 = queue->head;int sz = 0;while (p1 != NULL) {sz++;p1 = p1->next;}return sz;
}
  • 遍历++即可

2.2.9、销毁队列

void QueDestory(que* queue) {assert(queue);while (!QueEmpty(queue)) {free(QuePop(queue));}
}
  • 循环出队列即可。

三、栈与队列的相互实现

3.1、栈模拟实现队列

  • 队列的特性是先入先出。即:FIFO(First In First Out)的原则。
  • 栈的特性是后入先出。即:LIFO(Last In First Out)的原则。
  • 我们可以利用两栈后入先出的特性,先用栈1把入队列的值存起来,在存起来后,就把栈1的值出栈到栈2中进行保存,这样就完成栈中底部的值变为顶部的值了。(如下图)在这里插入图片描述
  • 在上图中可以看到,当栈2出栈的时候,就变成了栈1底部的数据先出,即:先入先出
  • 所以我们只需要创建两个栈一个栈负责入数据栈1一个栈负责出数据栈2,为了不影响先后顺序,我们必须等到栈2为空的时候才栈1的数据导入进去。

示例代码

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int stackData;
typedef struct stack
{stackData* val;int size;int top;
}stack;//栈的结构体typedef struct {stack* stack1;//接收数据stack* stack2;//出数据
} MyQueue;//栈模拟的队列//栈函数的实现
void SKinit(stack** head) {*head = (stack*)malloc(sizeof(stack));assert(*head);(*head)->val = (stackData*)malloc(sizeof(stackData) * 4);assert((*head)->val);(*head)->size = 4;(*head)->top = 0;
}
//入栈
void SKpush(stack* pHead, stackData x) {assert(pHead);stack* head = pHead;if (head->top == head->size) {stackData* p1 = (stackData*)realloc(head->val,sizeof(stackData) * head->size * 2);assert(p1);head->val = p1;head->size *= 2;}head->val[head->top] = x;head->top++;}
//出栈
stackData SKPop(stack* pHead) {assert(pHead);stack* head = pHead;stackData date = head->val[head->top - 1];head->top--;return date;
}
//销毁
void SKdestory(stack* pHead) {while (pHead->top) {SKPop(pHead);}
}
//大小
int SKsize(stack* pHead) {return pHead->top;
}
//判断为空
int SKEmpty(stack* pHead) {return pHead->top == 0;
}
stackData StackTop(stack* ps) {assert(ps);return ps->val[ps->top - 1];
}//队列函数的实现// 创建一个队列
MyQueue* myQueueCreate() {MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));  // 为队列分配内存SKinit(&obj->stack1);  // 初始化栈1,用于接收数据SKinit(&obj->stack2);  // 初始化栈2,用于出数据return obj;  // 返回队列对象
}// 入队操作,将元素x压入栈1
void myQueuePush(MyQueue* obj, int x) {SKpush(obj->stack1, x);  // 直接将元素压入栈1
}// 出队操作,从栈2中弹出元素
int myQueuePop(MyQueue* obj) {// 如果栈2为空,将栈1中的元素移动到栈2if (obj->stack2->top == 0) {// 从栈1依次弹出元素并压入栈2while (!SKEmpty(obj->stack1)) {stackData n = SKPop(obj->stack1);SKpush(obj->stack2, n);}}// 从栈2中弹出元素return SKPop(obj->stack2);
}// 查看队列头部元素
int myQueuePeek(MyQueue* obj) {// 如果栈2为空,将栈1中的元素移动到栈2if (obj->stack2->top == 0) {while (!SKEmpty(obj->stack1)) {stackData n = SKPop(obj->stack1);SKpush(obj->stack2, n);}}// 返回栈2的栈顶元素return StackTop(obj->stack2);
}// 判断队列是否为空
bool myQueueEmpty(MyQueue* obj) {return (obj->stack2->top == 0) && (obj->stack1->top == 0);  // 栈1和栈2都为空时,队列为空
}// 销毁队列,释放所有资源
void myQueueFree(MyQueue* obj) {// 一直出队直到队列为空while (!myQueueEmpty(obj)) {myQueuePop(obj);}// 释放栈1和栈2的内存free(obj->stack1->val);free(obj->stack2->val);free(obj->stack1);free(obj->stack2);// 释放队列对象free(obj);
}

3.2、队列模拟实现栈

  • 队列的特性是先入先出。即:FIFO(First In First Out)的原则。
  • 栈的特性是后入先出。即:LIFO(Last In First Out)的原则。
  • 我们可以利用两个队列进行互相入队,把队列中的最后一个元素进行返回,这样就完成栈的先入先出的原则了。(如下图)在这里插入图片描述
  • 至此,再下一次出栈时候只需要判断哪个队列不为空,就循环出队列到另一个队列中,这样就可以模拟实现栈了。

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>// 定义栈中存储的数据类型
typedef int stackData;// 栈的结构体定义
typedef struct stack {stackData* val;  // 动态数组,用来存储栈的元素int size;        // 栈的容量int top;         // 栈顶元素的索引
} stack;// 初始化栈函数
void SKinit(stack** head) {*head = (stack*)malloc(sizeof(stack));assert(*head);  // 确保内存分配成功(*head)->val = (stackData*)malloc(sizeof(stackData) * 4);assert((*head)->val);  // 确保内存分配成功(*head)->size = 4;(*head)->top = 0;
}// 入栈操作
void SKpush(stack* pHead, stackData x) {assert(pHead);  // 确保栈指针有效stack* head = pHead;// 如果栈满,进行扩容if (head->top == head->size) {// 重新分配内存,栈容量加倍stackData* p1 = (stackData*)realloc(head->val, sizeof(stackData) * head->size * 2);assert(p1); head->val = p1; head->size *= 2;  }head->val[head->top] = x;head->top++;  
}// 出栈操作
stackData SKPop(stack* pHead) {assert(pHead);  // 确保栈指针有效stack* head = pHead;stackData date = head->val[head->top - 1];head->top--;return date;
}// 销毁栈,释放栈中所有数据
void SKdestory(stack* pHead) {while (pHead->top) {SKPop(pHead);  // 依次出栈,直到栈为空}
}// 获取栈的大小(栈中的元素个数)
int SKsize(stack* pHead) {return pHead->top;  // 栈顶指针即为栈的大小
}// 判断栈是否为空
int SKEmpty(stack* pHead) {return pHead->top == 0;  // 如果栈顶指针为0,则栈为空
}// 获取栈顶元素
stackData StackTop(stack* ps) {assert(ps);  // 确保栈指针有效return ps->val[ps->top - 1];  // 返回栈顶元素
}// 队列结构体定义
typedef struct {stack* stack1;  // 用于接收数据(入队)stack* stack2;  // 用于出数据(出队)
} MyQueue;// 创建队列
MyQueue* myQueueCreate() {// 为队列分配内存MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));// 初始化两个栈SKinit(&obj->stack1);  // 用于接收数据SKinit(&obj->stack2);  // 用于出数据return obj;  // 返回队列对象
}// 入队操作,将元素x压入栈1
void myQueuePush(MyQueue* obj, int x) {SKpush(obj->stack1, x);  // 将数据压入栈1
}// 出队操作,从栈2弹出元素
int myQueuePop(MyQueue* obj) {// 如果栈2为空,将栈1中的元素转移到栈2if (obj->stack2->top == 0) {// 将栈1中的元素依次弹出并压入栈2while (!SKEmpty(obj->stack1)) {stackData n = SKPop(obj->stack1);SKpush(obj->stack2, n);}}// 从栈2中弹出元素并返回return SKPop(obj->stack2);
}// 查看队列头部元素
int myQueuePeek(MyQueue* obj) {// 如果栈2为空,将栈1中的元素转移到栈2if (obj->stack2->top == 0) {while (!SKEmpty(obj->stack1)) {stackData n = SKPop(obj->stack1);SKpush(obj->stack2, n);}}// 返回栈2的栈顶元素return StackTop(obj->stack2);
}// 判断队列是否为空
bool myQueueEmpty(MyQueue* obj) {return (obj->stack2->top == 0) && (obj->stack1->top == 0);  // 栈1和栈2都为空时,队列为空
}// 销毁队列,释放所有资源
void myQueueFree(MyQueue* obj) {// 一直出队直到队列为空while (!myQueueEmpty(obj)) {myQueuePop(obj);}// 释放栈1和栈2的内存free(obj->stack1->val);free(obj->stack2->val);free(obj->stack1);free(obj->stack2);// 释放队列对象free(obj);
}

四、循环队列

在操作系统的生产者消费者模型中可以就会使用循环队列环形队列可以使用数组实现,也可以使用循环链表实现。

因为是环形队列,所以不存在扩容队列的情况,本篇笔记以数组实现为例。
以数组为数据存储模型比链表实现方便的多。

4.1、循环队列的结构体定义

typedef struct {int* a;int front;int rear;int k;//开辟的数组大小
} MyCircularQueue;
  • 首先创建一个数组,用来存储队列的数据。

  • 定义两个指针,用来指向队列数据中的队头和队尾。

  • front:只需要保存队头元素在数组中的下标即可

  • rear:保存队尾元素的下一个下标,这样可以保证队列位空时rearfront的下标相同,队列满时frontrear的下一位。如下图在这里插入图片描述

  • 为了达到上图的效果,需要在初始化时候把数组开大1个空间。多开的空间作为数组的预留位置。

  • 定义一个整形变量k用来保存队列大小。

4.2、循环队列的初始化

MyCircularQueue* myCircularQueueCreate(int k) {//传入开辟队列的大小MyCircularQueue* que = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));assert(que);que->a = (int*)malloc(sizeof(int) * (k + 1));que->k = k;que->front = 0;que->rear = 0;return que;
}
  • 动态开辟一个队列结构体
  • 动态开辟一个队列空间,其中队列空间需要比k大一个1
  • 并且把结构体中的其他值进行初始化赋值

4.3、循环队列的判断是否为空

bool IsEmpty(MyCircularQueue* obj) {return (obj->front == obj->rear);
}
  • 根据我们设计逻辑,只有obj->front == obj->rear时候队列位空

4.4、循环队列的判断是否满了

bool IsFull(MyCircularQueue* obj) {return (obj->rear + 1) % (obj->k + 1) == obj->front;
}
  • 根据我们设计逻辑,如果rear + 1front的情况就说明队列满了。

  • 解析(obj->rear + 1) % (obj->k + 1) == obj->front假设当前容量是:3。k == 3):

    • 第一种只入队列且未满的情况
      • 只入队列的情况下说明front值是未进行改变的,且rear值是最后元素下标的下一位(如下图)。在这里插入图片描述这时候判断,队列是否未满,只需要(rear +1) % (k + 1)== front小数模大数结果还是本身
    • 第二种只入队列且已经满的情况
      • 只入队列的情况下说明front值是未进行改变的,且rear值是最后元素下标的下一位(如下图)。在这里插入图片描述在队列满的情况下,当前的rear的值是:3rear + 1就超出了数组的大小(如下图)在这里插入图片描述但是在循环队列rear + 1是代表着数组原始起点的(如下逻辑图)在这里插入图片描述为了达到rear+1就能回到数组起始位置,进行rear+1 % k + 1rear+1的值:4k+1的值也为:4。相同的值同时模的到的值是0。此时我们只需要判断下标0处是否也是front的值就可以判断出队列是否为满了(obj->rear + 1) % (obj->k + 1) == obj->front。(如上图)
    • 第三种入满队列后了一些且未满的情况
      • 此时队列的front元素进行了改变,不再是0下标的值,此时我们可以和第二种结合理解。先画出此时的数组的物理结构图逻辑结构图。(如下图)在这里插入图片描述为了使rear + 1回到逻辑结构中的下一个位置,我们使用第二种中介绍的方法,使用(obj->rear + 1) % (obj->k + 1)方法,在把rear + 1回到数组起始位置,之后判断此时的front是否也是此下标。

4.5、循环队列的入队列

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if (myCircularQueueIsFull(obj)) {return false;}else {obj->a[obj->rear] = value;obj->rear++;obj->rear = (obj->rear) % (obj->k + 1);return true;}
}
  • 入队列返回结果:如果成功插入则返回真
  • 入队列前,需要检查队列是否为满。
  • 如果队列不为满,只需要在rear位置中,存放插入的数据即可。
  • 在每次插入完成后,rear数据都需要进行逻辑后移一位。在逻辑后移中,需要分成2中情况
    • 第一种rear不在数组最后一位(如下图)在这里插入图片描述此时小数模大数,结果还是本身。不受到(obj->rear) % (obj->k + 1)的影响。
    • 第二种rear在数组最后一位(如下图)在这里插入图片描述rear+1的值:4k+1的值也为:4。相同的值同时模的到的值是0(obj->rear) % (obj->k + 1)即:把rear的值重新返回到数组头部。(如下图)
      在这里插入图片描述
  • 只有每次都对rear取模才能完成循环队列的逻辑。

4.6、循环队列的出队列

bool myCircularQueueDeQueue(MyCircularQueue* obj) {if (IsEmpty(obj)) {return false;}else {obj->front++;obj->front = (obj->front) % (obj->k + 1);return true;}
}
  • 出队列返回结果:如果成功出队则返回真
  • 出队列前,需要检查队列是否为空。
  • 出队列需要把front下标++,把队列的队头在数组中往后移动一个位置,这样就完成了出队。
  • front在每次插入完成后,front数据都需要进行逻辑后移一位。在逻辑后移中,也分成2中情况,但是这两种情况与入队列的read相同,这里不做详细说明。

4.7、循环队列获取头队列元素

int myCircularQueueFront(MyCircularQueue* obj) {if (IsEmpty(obj)) {return -1;}return obj->a[obj->front];
}
  • 先判断是否为空,不为空直接返回队头元素即可。

4.7、循环队列获取尾队列元素

int myCircularQueueRear(MyCircularQueue* obj) {if (IsEmpty(obj)) {return -1;}//return obj->a[obj->rear - 1];return obj->a[((obj->rear - 1) + (obj->k + 1)) % (obj->k+1)];
}
  • 正常来说是:先判断是否为空,不为空直接返回rear - 1的位置元素即可。

  • 但是有一种特殊情况即:rear是数组首元素下标时,rear - 1就造成了越界访问。在这里插入图片描述

  • 为了避免越界访问的情况,必须确保在数组首元素时,返回的是数组尾元素的值(可以使用分支语句处理)。这里使用取模处理:(k = 3)

    • 我们知道元素本身模本身的结果为:0,并且小数模大数结果为:小数本身
    • 所以当rear-1时,如果在非首元素位置中,通过分配率((obj->rear - 1) + (obj->k + 1)) % (obj->k+1)可以变成(obj->rear - 1) % (obj->k+1),结果是obj->rear - 1本身
    • 如果在非首元素位置中时,(obj->rear - 1) + (obj->k + 1)的值:-1 + 4 == 3。这时候我们用得到的结果对k+1进行取模就变成了小数模大数结果为:小数本身。这时候rear的结果是数组尾元素的下标返回的结果就是队列的尾节点元素在这里插入图片描述

4.7、循环队列的销毁

void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);free(obj);
}
  • 干脆利落的销毁了用于存储队列元素的数组队列结构体的两个动态开辟空间~

以上就是本章所有内容。若有勘误请私信不才。万分感激💖💖 如果对大家有帮助的话,就请多多为我点赞收藏吧~~~💖💖
请添加图片描述

ps:表情包来自网络,侵删🌹

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

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

相关文章

A030-基于Spring boot的公司资产网站设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600…

企业生产环境-麒麟V10(ARM架构)操作系统部署kafka高可用集群

前言&#xff1a;Apache Kafka是一个分布式流处理平台&#xff0c;由LinkedIn开发并捐赠给Apache软件基金会。它主要用于构建实时数据流管道和流应用。Kafka具有高吞吐量、可扩展性和容错性的特点&#xff0c;适用于处理大量数据。 以下是Kafka的一些核心概念和特性&#xff1…

供应链管理、一件代发系统功能及源码分享 PHP+Mysql

随着电商行业的不断发展&#xff0c;传统的库存管理模式已经逐渐无法满足市场需求。越来越多的企业选择“一件代发”模式&#xff0c;即商家不需要自己储备商品库存&#xff0c;而是将订单直接转给供应商&#xff0c;由供应商直接进行发货。这种方式极大地降低了企业的运营成本…

机器学习 ---线性回归

目录 摘要&#xff1a; 一、简单线性回归与多元线性回归 1、简单线性回归 2、多元线性回归 3、残差 二、线性回归的正规方程解 1、线性回归训练流程 2、线性回归的正规方程解 &#xff08;1&#xff09;适用场景 &#xff08;2&#xff09;正规方程解的公式 三、衡量…

Unity类银河战士恶魔城学习总结(P127 Stat ToolTip属性提示)

【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili 教程源地址&#xff1a;https://www.udemy.com/course/2d-rpg-alexdev/ 本章节实现了把鼠标放到属性上面就会显示属性的作用 UI_StatToolTip.cs 这段代码实现了一个UI提示框&#xff08;ToolTip&#xff09;功能…

C/C++语言基础--initializer_list表达式、tuple元组、pair对组简介

本专栏目的 更新C/C的基础语法&#xff0c;包括C的一些新特性 前言 initializer_list表达式、tuple元组、pair对组再C日常还是比较常用的&#xff0c;尤其是对组在刷算法还是挺好用的&#xff0c;这里做一个简介&#xff1b;这三个语法结合C17的结构化绑定会更好用&#xff…

Python_爬虫1_Requests库入门

目录 Requests库 7个主要方法 Requests库的get()方法 Response对象的属性 爬取网页的通用代码框架 理解requests库的异常 HTTP协议及Requests库方法 HTTP协议 HTTP协议采用URL作为定位网络资源的标识。 HTTP协议对资源的操作 理解PATCH和PUT的区别 HTTP协议与Requse…

视频流媒体播放器EasyPlayer.js RTSP播放器视频颜色变灰色/渲染发绿的原因分析

EasyPlayer.js RTSP播放器属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#xff0c;无须安装任何插件&#xff0c;起播快、延迟低、兼容性强&#xff0c;使用非常便捷。 EasyPlayer.js播放器不仅支持H.264与H.265视频编码格式&#xff0…

[JAVA]有关MyBatis环境配置介绍

什么是MyBatis环境配置&#xff1f; MyBatis是基于JDBC对数据库进行操作&#xff0c;在我们进行数据操作时&#xff0c;我们需要告诉MyBatis我们连接哪个数据库&#xff0c;ip地址&#xff0c;数据库名称&#xff0c;用户名密码等。以此来进行环境配置。 首先&#xff0c;MyB…

微搭低代码入门05循环

目录 1 for 循环2 while 循环3 do...while 循环4 break 语句5 循环展示组件总结 在编程中&#xff0c;循环是一种非常强大的控制结构&#xff0c;它允许我们重复执行一段代码直到满足某个条件为止。在微搭中&#xff0c;我们一般用循环来处理我们数据库返回的结果。 在微搭中&a…

11.13机器学习_线性回归

十 集成学习方法之随机森林 机器学习中有一种大类叫集成学习&#xff08;Ensemble Learning&#xff09;&#xff0c;集成学习的基本思想就是将多个分类器组合&#xff0c;从而实现一个预测效果更好的集成分类器。集成算法可以说从一方面验证了中国的一句老话&#xff1a;三个…

活动|华院计算作为联盟理事单位出席进博会全球人工智能合作论坛

第七届中国国际进口博览会&#xff08;进博会&#xff09;于11月5日至10日在上海举行&#xff0c;作为本次进博会的重要配套活动&#xff0c;首届人工智能全球合作论坛也于9日圆满落幕。本次论坛由全球招商中心委员会、人工智能全球合作论坛组委会主办&#xff0c;中国国际科技…

2、开发工具和环境搭建

万丈高楼平地起&#xff0c;学习C语言先从安装个软件工具开始吧。 1、C语言软件工具有两个作用 1、编辑器 -- 写代码的工具 2、编译器 -- 将代码翻译成机器代码0和1 接下来我们介绍两种C语言代码工具&#xff1a;devcpp 和 VS2019&#xff0c;大家可以根据自己的喜好安装。 dev…

【Qt实现虚拟键盘】

Qt实现虚拟键盘 &#x1f31f;项目分析&#x1f31f;实现方式&#x1f31f;开发流程 &#x1f31f;项目分析 需求&#xff1a;为Linux环境下提供可便捷使用的虚拟键盘OS环境&#xff1a;Windows 7/11、CentOS 7开发语言&#xff1a;Qt/C IDE&#xff1a;QtCreator 、Qt5.14.2功…

领夹麦克风哪个品牌好,手机领夹麦克风哪个牌子好,选购推荐

​无线麦克风凭借其无与伦比的便携性与灵活性&#xff0c;成为在演讲、表演以及会议等多种场合中不可或缺的有力帮手。它挣脱了线缆的束缚&#xff0c;使得声音的传播更加自由自在。其操作十分简便&#xff0c;只需简单配对就能投入使用&#xff0c;从而可以轻松地适应各类场景…

ADC输出码和输入电压转换关系

ADC输出码和输入电压转换关系 转换公式&#xff1a;ADC输出码(Vin / Vref) *2n 。其中Vin 是输入ADC芯片的电压&#xff0c;Vref是参考电压&#xff0c;n是ADC芯片的位数。 举个例子MS5182是一个16bit的ADC&#xff08;21665536&#xff09;&#xff0c;参考电压Vref4.096V&a…

IROS讲座:如何写出受欢迎的论文

讲座原名称&#xff1a;How to write papers people love reading 时间地点&#xff1a;2024年10月中旬&#xff0c;阿布扎比国家展览中心&#xff0c;阿联酋 演讲嘉宾照片&#xff1a; 以下是拍摄的部分PPT&#xff0c;并添加了中文笔记&#xff1a;

【Docker】Mac安装Docker Desktop导致磁盘剩余空间较少问题如何解决?

目录 一、背景描述 二、解决办法 三、清理效果 四、理论参考 解决方法 1. 清理未使用的 Docker 镜像、容器和卷 2. 查看 Docker 使用的磁盘空间 3. 调整 Docker 的存储位置 4. 增加磁盘空间 5. 调整 Docker Desktop 配置 6. 使用 Docker 清理工具&#xff08;例如 D…

Tiktok对接和内容发布申请流程

这段时间在搞AI生成视频&#xff0c;希望用户能一键发布到Tiktok&#xff0c;因此研究了一下Tiktok的开发者申请流程&#xff0c;发现好复杂&#xff0c;同时也发现Tiktok的开发也跟我一样&#xff0c;挺草台班子的 0、流程简述 废话不多说&#xff0c;Tiktok的开发者申请和…

Android ART知多少?

Android 虚拟机 ART&#xff08;Android Runtime&#xff09;是 Android 平台上的应用程序运行时环境&#xff0c;用于执行应用程序的字节码。ART 自 Android 5.0&#xff08;Lollipop&#xff09;开始取代了 Dalvik&#xff0c;成为 Android 的默认运行时环境。本文将从以下几…