【数据结构】队列和栈

大家中秋节快乐,玩了好几天没有学习,今天分享的是栈以及队列的相关知识,以及栈和队列相关的面试题

1.栈

1.1栈的概念及结构

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

1.2栈的实现

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

栈的接口函数

// 初始化栈
voidStackInit(Stack*ps); 
// 入栈
voidStackPush(Stack*ps, STDataTypedata); 
// 出栈
voidStackPop(Stack*ps); 
// 获取栈顶元素
STDataTypeStackTop(Stack*ps); 
// 获取栈中有效元素个数
intStackSize(Stack*ps); 
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 intStackEmpty(Stack*ps); 
// 销毁栈
voidStackDestroy(Stack*ps); 

栈的实现

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>typedef struct Stack//定义一个栈的结构体变量	
{int * a;int top; // 栈顶int capacity; // 容量
}Stack;
void StackInit(Stack* ps)
{assert(ps);//断言,防止为空指针ps->a = NULL;//所指向的地址为空ps->capacity = ps->top = 0;//容量和栈中元素个数均为0
}
void StackPush(Stack* ps, int data)
{assert(ps);if (ps->capacity == ps->top)//如果栈中的元素个数等于栈的容量时考虑扩容,{int newcapcity = ps->capacity == 0 ? 4 : ps->capacity * 2;//如果刚开始时都等于0,就先给4个空间大小,后面如果满的话,容量扩大1倍int* newnode = (int*)realloc(ps->a,sizeof(int)* newcapcity);//申请空间,将申请好的空间首地址传给newnode指针assert(newnode);//断言,防止malloc失败ps->a = newnode;//将newnode保存的申请空间的首地址传给ps->a,让ps->a指向创建好的空间ps->capacity = newcapcity;//容量大小更新为新容量大小}ps->a[ps->top] = data;//像存数组一样存数据ps->top++;//指向下一个
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{assert(ps);return ps->top ==0;//ps->top为栈中元素个数.==0栈中无元素,无元素要返回1, 无元素ps->t0p==0,这个表达式结果是1,返回1;}
// 出栈
void StackPop(Stack* ps)
{assert(ps);assert(!StackEmpty(ps));//防止栈内无元素,继续出栈ps->top--;
}
// 获取栈顶元素
int StackTop(Stack* ps)
{assert(ps);assert(!StackEmpty(ps));return ps->a[ps->top - 1];//ps->top为栈中元素个数,由于数组下标是从0开始,所以栈顶元素下标为ps->top-1;}
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{assert(ps);return ps->top;}
// 销毁栈
void StackDestroy(Stack* ps)
{assert(ps);free(ps->a);//free掉动态申请的内存ps->a = NULL;//防止野指针ps->capacity = ps->top = 0;//容量和栈中元素个数置为0}

栈的功能测试

int main()
{Stack st;StackInit(&st);StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);while (!StackEmpty(&st)){printf("%d",  StackTop(&st));StackPop(&st);}StackDestroy(&st);}

在这里插入图片描述
实现了栈的后入先出

2.队列

2.1队列的概念及结构

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

队列的实现

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

队列的接口函数

// 初始化队列
voidQueueInit(Queue*q); 
// 队尾入队列
voidQueuePush(Queue*q, QDataTypedata); 
// 队头出队列
voidQueuePop(Queue*q); 
// 获取队列头部元素
QDataTypeQueueFront(Queue*q); 
// 获取队列队尾元素
QDataTypeQueueBack(Queue*q); 
// 获取队列中有效元素个数
intQueueSize(Queue*q); 
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 intQueueEmpty(Queue*q); 
// 销毁队列
voidQueueDestroy(Queue*q);

队列的实现

typedef struct QListNode
{struct QListNode* next;//保存结点的下一个结点的地址int  data;//该节点的数据
}QNode;
typedef struct Queue
{QNode* front;QNode* tail;
}Queue;//定义一个队列结构体,指向队列的前结点和尾结点
// 初始化队列
void QueueInit(Queue* q)
{assert(q);q->front = q->tail = NULL;//头节点尾结点置为NULL}
// 队尾入队列
void QueuePush(Queue* q, int data)
{assert(q);QNode* newnode = (QNode*)malloc(sizeof(QNode));//新结点申请空间assert(newnode);//防止申请失败newnode->next = NULL;//新节点的下一个结点的地址为空,不保存newnode->data = data;//新结点的数据if (q->front == NULL)//没有一个结点{q->front = q->tail = newnode;//就让指向头节点和指向尾结点的指针指向新结点}else//有结点{q->tail->next = newnode;//新结点尾插到后面q->tail = newnode;//移动指向尾结点的指针到队列末尾结点,也就是新结点}}// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{return q->front == NULL;//如果没有结点,则q->front==NULL,表达式成立返回1,表明队列为空}// 队头出队列
void QueuePop(Queue* q)
{assert(q);assert(!QueueEmpty(q));//防止队列为空在出数据if (q->front->next == NULL)//如果只有一个结点{q->front = q->tail ==NULL;//那就把这个结点置空,指向头结点指针和指向尾结点的指针指向空}else{QNode* next = q->front->next;//保存下一个结点的地址free(q->front);//从头结点开始释放一个结点,也就是头删q->front = next;//指向头结点的指针移动到下一个位置}}
// 获取队列头部元素
int QueueFront(Queue* q)
{assert(q);assert(q->front);//防止头节点为空return q->front->data;//头结点数据}
// 获取队列队尾元素
int QueueBack(Queue* q)
{assert(q);assert(q->tail);//防止尾节点为空return q->tail->data;//尾节点数据}
// 获取队列中有效元素个数
int QueueSize(Queue* q)
{int size = 0;//记录元素个数变量assert(q);QNode* cur = q->front;//遍历队列的指针先指向头while (cur){size++;//遍历记数cur = cur->next;}return size;//返回有效数据个数
}
// 销毁队列
void QueueDestroy(Queue* q)
{assert(q);QNode* cur = q->front;//遍历队列的指针while (cur){QNode* next = cur->next;//保存下一个节点的地址free(cur);//释放掉当前cur指针指向当前位置的空间cur = next;//指向下一个位置}q->front = q->tail = NULL;//防止野指针}

队列功能测试

int main()
{Queue st;QueueInit(&st);QueuePush(&st, 1);QueuePush(&st, 2);QueuePush(&st, 3);QueuePush(&st, 4);while (!QueueEmpty(&st)){printf("%d ", QueueFront(&st));QueuePop(&st);}QueueDestroy(&st);}

在这里插入图片描述

3.栈和队列面试题

20.有效的括号
在这里插入图片描述

思路:定义一个栈,将之前的功能都添在前面,使用栈解决这个问题,就是遍历这个字符串,如果是左括号的话,就入栈,然后s++,遇到右括号的话就取出栈顶元素,和这个右括号匹配,匹配上了就出栈栈顶元素,然后s++;没匹配上说明匹配不上,直接return false;当不是左括号的时候,出现右括号时,可能栈里还没有左括号,此时也匹配不上,直接return false;当遍历完s字符串后(s字符串一直是左括号),此时也属于匹配不上,就是判断栈中是否有元素,有元素都是左括号,然后就判空函数返回0==false,(当然定义栈需要初始化栈,和销毁栈)。

代码实现:

typedef struct Stack
{char* a;int top; // 栈顶int capacity; // 容量
}Stack;
void StackInit(Stack* ps)
{assert(ps);ps->a = NULL;ps->capacity = ps->top = 0;
}
void StackPush(Stack* ps, int data)
{assert(ps);if (ps->capacity == ps->top){int newcapcity = ps->capacity == 0 ? 4 : ps->capacity * 2;char* newnode = (char*)realloc(ps->a,sizeof(char) * newcapcity);assert(newnode);ps->a = newnode;ps->capacity = newcapcity;}ps->a[ps->top] = data;ps->top++;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{assert(ps);return ps->top == 0;}
// 出栈
void StackPop(Stack* ps)
{assert(ps);assert(!StackEmpty(ps));ps->top--;
}
// 获取栈顶元素
char StackTop(Stack* ps)
{assert(ps);assert(!StackEmpty(ps));return ps->a[ps->top - 1];}
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{assert(ps);return ps->top;}
// 销毁栈
void StackDestroy(Stack* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->capacity = ps->top = 0;}bool isValid(char * s){
Stack st;
StackInit(&st);
while(*s)
{if(*s=='['||*s=='('||*s=='{')//左括号入栈{StackPush(&st,*s);s++;//移动到下一个字符位置}else{if(StackEmpty(&st))//可能出现无左括号return false;char top=StackTop(&st);//获取栈顶元素if(*s==']'&&top=='['||*s=='}'&&top=='{'||*s==')'&&top=='(')//匹配上就出栈{	StackPop(&st);s++;//移动下一个字符位置}elsereturn false;//匹配不上直接return false}}
int ret=StackEmpty(&st);// s字符串全是左括号,全部入栈,栈内不为空return 0匹配不上
StackDestroy(&st);//销毁栈
return ret;}

225.用队列实现栈
在这里插入图片描述
思路:队列是先进先出,而栈是后进先出,要用两个队列实现栈,一个队列是空的,然后要出栈栈顶元素,也就是队尾元素,可以先将队尾元素的前面的所有元素都入另一个空的队列,然后在pop这个队尾的元素,就能实现后进的先出,由于两个队列构成的栈,将一个队列中的元素入另一个队列,肯定不是出栈。
1.入栈函数的实现
如果哪个队列不为空就把元素入哪个队列中,保证一个队列为空,刚开始的时候,两个队列都为空,入哪个队列都行,在第二次入队列时候,就能保证元素都入不为空的队列了
2.出栈函数的实现
当保证一个队列为空的时候,要实现对应的后入的先出,就可以将非空队列的除队尾元素其他的都入另一个队列中,当非空队列只剩一个元素时,也就是后入的这个元素,将这个元素出队列,并且不入另一个队列,就相当于出栈,出队列前用一个变量存储这个队尾元素,也就是栈顶元素。
3.返回栈顶元素函数
使用定义好的QueueBack函数返回队尾元素,也就是栈顶元素,==注意肯定返回的是非空队列的队尾元素,也就是栈顶元素
4.判断栈为空的函数
使用定义好的QueueEmpty函数,return QueueEmpty(第一个队列地址)&&QueueEmpty(第二个队列地址),当两个队列都为空的时候,QueueEmpty函数就返回1 ,return 1;表示栈为空,如果有一个队列不为空的话,与的结果就是0, return 0,就是栈不为空。
5.释放栈的函数
使用QueueDestroy,销毁两个队列,然后free掉动态申请来的空间。


//队列功能的实现
typedef struct QListNode
{struct QListNode* next;int data;
}QNode;typedef struct Queue
{QNode* front;QNode* tail;
}Queue;
void QueueInit(Queue* q)
{assert(q);q->front = q->tail = NULL;}
// 队尾入队列
void QueuePush(Queue* q, int x)
{assert(q);QNode* newnode = (QNode*)malloc(sizeof(QNode));assert(newnode);newnode->data =x;newnode->next = NULL;if (q->tail == NULL){q->tail = q->front = newnode;}else{q->tail->next = newnode;q->tail = newnode;}}
bool QueueEmpty(Queue* q)
{assert(q);return q->front == NULL;}// 队头出队列
void QueuePop(Queue* q)
{assert(q);assert(!QueueEmpty(q));if (q->front->next == NULL){free(q->tail);q->tail = q->front = NULL;}else{QNode* next = q->front->next;free(q->front);q->front = next;}}
// 获取队列头部元素
int QueueFront(Queue* q)
{assert(q);assert(q->front);return q->front->data;}
// 获取队列队尾元素
int QueueBack(Queue* q)
{assert(q);assert(q->tail);return q->tail->data;}
// 获取队列中有效元素个数
int QueueSize(Queue* q)
{assert(q);int size = 0;QNode* cur = q->front;while (cur){size++;cur = cur->next;}return size;}
// 销毁队列
void QueueDestroy(Queue* q)
{assert(q);QNode* cur = q->front;while (cur){QNode* next = cur->next;free(cur);cur = next;}q->front = q->tail = NULL;}
//队列功能实现到这里
typedef struct {
Queue a;
Queue b;   } MyStack;//定义栈MyStack* myStackCreate() {
MyStack* obj=(MyStack*)malloc(sizeof(MyStack));//给栈申请动态空间
if(obj==NULL){perror("malloc fail");}
QueueInit(&obj->a);//栈中两个队列的初始化
QueueInit(&obj->b);
return obj;//返回申请栈空间的地址}void myStackPush(MyStack* obj, int x)//入栈函数{
if(!QueueEmpty(&obj->a))//哪个队列不为空就入哪个队列
{QueuePush(&obj->a,x);}
else
{QueuePush(&obj->b,x);}}int myStackPop(MyStack* obj) 
{Queue* empty=&obj->a;//不知道哪个为空的队列,先随便保存一个Queue* nonempty=&obj->b;if(!QueueEmpty(&obj->a))//如果a队列不是空的,就将队列b的地址保存在空的指针里面{empty=&obj->b;nonempty=&obj->a;}while(QueueSize(nonempty)>1)//当非空的队列只剩下一个元素时,队尾元素,也就是栈顶元素{QueuePush(empty,QueueFront(nonempty));//将非空队列的除队尾元素全部入到另一个空的队列中QueuePop(nonempty);//队头元素出队列}int ret=QueueFront(nonempty);//循环结束,只剩下队尾元素,将队尾元素保存在变量中QueuePop(nonempty);//队尾元素出队列,并且不进另一个队列,相当于出栈return ret;//返回栈顶元素
}int myStackTop(MyStack* obj) {
if(QueueEmpty(&obj->a))
{return  QueueBack(&obj->b);}
else
{return  QueueBack(&obj->a);//哪个队列不为空,直接使用QueueBack返回不为空队列的队尾元素}
}bool myStackEmpty(MyStack* obj) 
{
return QueueEmpty(&obj->a)&&QueueEmpty(&obj->b);}void myStackFree(MyStack* obj) {QueueDestroy(&obj->a);QueueDestroy(&obj->b);free(obj);}

232.用栈实现队列
在这里插入图片描述
思路:使用两个栈实现队列,栈为后入先出,队列为后入后出,当要出队头元素,也就是栈底元素时,可以将栈顶元素一个接一个放入另一个栈中popst,然后栈底元素到另一个栈就变成了栈顶元素,然后就可以实现队头元素,也就是栈底元素先出栈。
1.入队列函数的实现
使用 StackPush函数将数据入到栈pushst中
2.出队列函数实现
将pushst栈中的栈顶元素一个接一个全部入到栈popst中,将pushst栈中的元素全部pop掉,此时popst栈顶的元素就是队头元素,用一个变量保存他,然后将popst栈顶元素pop掉,return 栈顶元素。
3.返回队列开头的元素的函数
和出队列函数大致相同,这个不需要pop掉队头元素
4.判断队列为空函数
使用StackEmpty函数,return
StackEmpty(&obj->popst)&&StackEmpty(&obj->pushst);当两个栈都为空的时候返回1 ,表示队列为空,只要有一个不为空的话返回0,表示队列不为空。
5.释放队列函数
使用StackDestroy函数销毁两个栈,然后free掉动态开辟的内存。

typedef struct Stack
{int* a;int top; // 栈顶int capacity; // 容量
}Stack;
void StackInit(Stack* ps)//初始化栈
{ps->a = NULL;ps->top = 0;ps->capacity = 0;
}
void StackPush(Stack* ps, int data)//入栈
{assert(ps);if (ps->capacity == ps->top){int newcapcity = ps->capacity == 0 ? 4 : ps->capacity * 2;int* tmp = (int*)realloc(ps->a, sizeof(int) * newcapcity);if (tmp == NULL){perror("realloc fail");}else{ps->a = tmp;ps->capacity = newcapcity;}}ps->a[ps->top] = data;ps->top++;}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{assert(ps);return ps->top ==0;}
// 出栈
void StackPop(Stack* ps)
{assert(ps);assert(!StackEmpty(ps));ps->top--;}
// 获取栈顶元素
int StackTop(Stack* ps)
{assert(ps);assert(!StackEmpty(ps));return ps->a[ps->top - 1];}
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{assert(ps);return ps->top;}// 销毁栈
void StackDestroy(Stack* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->top = ps->capacity = 0;}typedef struct {
Stack popst;
Stack pushst;} MyQueue;//定义队列MyQueue* myQueueCreate() {MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));//动态给队列申请空间StackInit(&obj->popst);   //初始化两个栈StackInit(&obj->pushst); return obj;//返回队列的地址}void myQueuePush(MyQueue* obj, int x) {StackPush(&obj->pushst,x);//入队列都入到pushst栈中}int myQueuePop(MyQueue* obj) {
if(StackEmpty(&obj->popst))//如果popst栈中为空的话
{while(StackSize(&obj->pushst))//将pushst栈中的元素全部入到popst栈中
{StackPush(&obj->popst,StackTop(&obj->pushst));//栈顶元素一个接一个放到popst的栈中StackPop(&obj->pushst);//栈顶元素出栈
}}
int ret=StackTop(&obj->popst);//变量接收popst栈顶元素的值,然后pop掉
StackPop(&obj->popst);
return ret;//返回队列头元素,也就是popst栈顶元素}int myQueuePeek(MyQueue* obj) //与上一个函数同理
{if(StackEmpty(&obj->popst))
{while(StackSize(&obj->pushst))
{StackPush(&obj->popst,StackTop(&obj->pushst));StackPop(&obj->pushst);
}}
int ret=StackTop(&obj->popst);return ret;}bool myQueueEmpty(MyQueue* obj) {return StackEmpty(&obj->popst)&&StackEmpty(&obj->pushst);}void myQueueFree(MyQueue* obj) 
{
StackDestroy(&obj->popst);
StackDestroy(&obj->pushst);
free(obj);}

622.设计循环队列
在这里插入图片描述
思路:用数组实现这个队列较简单,在开辟空间大小时,需要k个空间,我们给他开辟k+1个空间,如果尾的下一个是头的话,就说明队列满了,如果头和尾在一个地方,则队列为空,获取队首元素就是返回obj->a[obj->head]即可,获取队尾元素一般要找到obj->tail-1的位置,因为tail是后加,当存最后一个后,他的tail+1;插入元素,就让obj->a[obj->tail]=value;然后tail++;删除一个元素就让head++就行。
注意边界:
检查队列是否满的边界处理:
在这里插入图片描述
插入元素的边界处理:在这里插入图片描述
删除元素边界处理:
在这里插入图片描述
获取尾部元素的边界处理
在这里插入图片描述

typedef struct {int*a;//指向队列空间的指针int k;//队列空间大小int head;//队列头下标int tail;//队列尾下标} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));//给描述队列的变量创建空间
obj->a=(int *)malloc(sizeof(int)*(k+1));//给队列创建空间
obj->k=k;//队列空间大小赋值
obj->head=obj->tail=0;//初始化队列队尾队头下标
return obj;//返回创建队列信息的地址
}bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->head==obj->tail;//空的话,头下标等于尾下标
}bool myCircularQueueIsFull(MyCircularQueue* obj) {int next=obj->tail+1;//记录尾下标的下一个下标if(obj->tail==obj->k)//边界处理next=0;return next==obj->head;//相等说明tail对应的下一个元素是head,表示已经满了}bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))//满的话直接返回return false;
obj->a[obj->tail]=value;//插入元素
obj->tail++;//尾下标更新+1
if(obj->tail==obj->k+1)//边界处理
obj->tail=0;
return true;//插入成功
}bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))//空的不能删除return false
return false;
obj->head++;     头下标更新+1if(obj->head==obj->k+1)//边界处理
obj->head=0;
return true;  //删除成功return true}int myCircularQueueFront(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))//空的话返回-1;return -1;return obj->a[obj->head];//不空返回头下标对应的元素}int myCircularQueueRear(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))//空的话返回-1;return -1; int prev=obj->tail-1;//记录尾下标的上一个下标if(prev==-1)//边界处理prev=obj->k; return obj->a[prev];//返回队列尾元素}void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);free(obj);}

在这里插入图片描述
先free掉obj的话,obj->a指针中存放的队列的地址置为随机值,永远free不了obj->a,存在内存泄漏,所以先free obj->a,然后free obj.

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

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

相关文章

MySQL数据查询性能如何分析--Explain介绍说明

1、Explain是什么 Explain是MySQL执行查看执行计划命令的指令&#xff0c;使用EXPLAIN关键字可以模拟优化器执行SQL查询语句&#xff0c;从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。 2、Explain官网介绍 http://dev.mysql.com/doc/refma…

【MySQL】数据类型(二)

文章目录 一. char字符串类型二. varchar字符串类型2.1 char和varchar比较 三. 日期和时间类型四. enum和set类型4.1 set的查询 结束语 一. char字符串类型 char (L) 固定长度字符串 L是可以存储的长度&#xff0c;单位是字符&#xff0c;最大长度是255 MySQL中的字符&#xff…

Makefile学习

一、Makefile的介绍 1.1 什么是Makefile 相信在Linux系统中经常会用到make这个命令来编译程序&#xff0c;而执行make命令所依赖的文件便是Makefile文件&#xff0c;make命令通过Makefile文件编写的内容对程序进行编译。make命令根据文件更新的时间戳来决定哪些文件需要重新编…

纯css html 真实水滴效果

惯例,不多说直接上图 秉承着开源精神,我们将这段代码无私地分享给大家&#xff0c;因为我们深信&#xff0c;信息的共享和互相学习是推动科技进步的关键。我们鼓励大家在使用这段代码的同时&#xff0c;也能够将其中的原理、思想和经验分享给更多的人。 这份代码是我们团队用心…

一百八十六、大数据离线数仓完整流程——步骤五、在Hive的DWS层建动态分区表并动态加载数据

一、目的 经过6个月的奋斗&#xff0c;项目的离线数仓部分终于可以上线了&#xff0c;因此整理一下离线数仓的整个流程&#xff0c;既是大家提供一个案例经验&#xff0c;也是对自己近半年的工作进行一个总结。 二、数仓实施步骤 &#xff08;五&#xff09;步骤五、在Hive的…

Purism 推出注重隐私的 Linux 平板电脑

导读一款昂贵的 Linux 平板电脑&#xff0c;注重安全和隐私。让我们拭目以待。 Purism 是一家日益流行的计算机硬件产品制造商&#xff0c;专门提供配备注重隐私的开源 Linux 发行版的笔记本电脑、台式机和移动设备。 最近&#xff0c;他们发布了一款新产品 Librem 11 平板电…

ARM底层汇编基础指令

汇编语言的组成 伪操作 不参与程序执行&#xff0c;但是用于告诉编译器程序怎么编译.text .global .end .if .else .endif .data 汇编指令 编译器将一条汇编指令编译成一条机器码&#xff0c;在内存里一条指令占4字节内存&#xff0c;一条指令可以实现一个特定的功能 伪指令 不…

嵌入式Linux应用开发-基础知识-第十六章GPIO和Pinctrl子系统的使用

嵌入式Linux应用开发-基础知识-第十六章GPIO和Pinctrl子系统的使用 第十六章 GPIO 和 Pinctrl 子系统的使用16.1 Pinctrl 子系统重要概念16.1.1 引入16.1.2 重要概念16.1.3 示例16.1.4 代码中怎么引用pinctrl 16.2 GPIO子系统重要概念16.2.1 引入16.2.2 在设备树中指定引脚16.2…

软件设计模式系列之二十一——观察者模式

1 观察者模式的定义 观察者模式&#xff08;Observer Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象之间建立一对多的依赖关系&#xff0c;当一个对象的状态发生变化时&#xff0c;所有依赖于它的对象都会得到通知并自动更新。这个模式也被称为发布-订阅模式…

数据结构 | 二叉树

基本形状 可参照 数据结构&#xff1a;树(Tree)【详解】_数据结构 树_UniqueUnit的博客-CSDN博客 二叉树的性质 三种顺序遍历

BUUCTF reverse wp 51 - 55

findKey shift f12 找到一个flag{}字符串, 定位到关键函数, F5无效, 大概率是有花指令, 读一下汇编 这里连续push两个byte_428C54很奇怪, nop掉下面那个, 再往上找到函数入口, p设置函数入口, 再F5 LRESULT __stdcall sub_401640(HWND hWndParent, UINT Msg, WPARAM wPara…

Kafka(一)使用Docker Compose安装单机Kafka以及Kafka UI

文章目录 Kafka中涉及到的术语Kafka镜像选择Kafka UI镜像选择Docker Compose文件Kafka配置项说明KRaft vs Zookeeper和KRaft有关的配置关于Controller和Broker的概念解释Listener的各种配置 Kafka UI配置项说明 测试Kafka集群Docker Compose示例配置 Kafka中涉及到的术语 对于…

Spring5应用之AOP切入点详解

作者简介&#xff1a;☕️大家好&#xff0c;我是Aomsir&#xff0c;一个爱折腾的开发者&#xff01; 个人主页&#xff1a;Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏&#xff1a;Spring5应用专栏_Aomsir的博客-CSDN博客 文章目录 前言切入点详解切…

简单三步 用GPT-4和Gamma自动生成PPT PDF

1. 用GPT-4 生产PPT内容 我想把下面的文章做成PPT&#xff0c;请你给出详细的大纲和内容 用于谋生的知识&#xff0c;学生主要工作是学习&#xff0c;成年人的工作是养家糊口&#xff0c;这是基本的要求&#xff0c;在这之上&#xff0c;才能有更高的追求。 不要短期期望过高…

C#(CSharp)入门教程

目录 C#的第一个程序 变量 折叠代码 变量类型和声明变量 获取变量类型所占内存空间&#xff08;sizeof&#xff09; 常量 转义字符 隐式转换 显示转换 异常捕获 运算符 算术运算符 布尔逻辑运算符 关系运算符 位运算符 其他运算符 字符串拼接 …

计算机图像处理-高斯滤波

高斯滤波 高斯滤波是一种线性平滑滤波&#xff0c;适用于消除高斯噪声&#xff0c;广泛应用于图像处理的减噪过程。通俗的讲&#xff0c;高斯滤波就是对整幅图像进行加权平均的过程&#xff0c;每一个像素点的值&#xff0c;都由其本身和邻域内的其他像素值经过加权平均后得到…

Ubuntu部署运行ORB-SLAM2

ORB-SLAM2是特征点法的视觉SLAM集大成者&#xff0c;不夸张地说是必学代码。博主已经多次部署运行与ORB-SLAM2相关的代码&#xff0c;所以对环境和依赖很熟悉&#xff0c;对整个系统也是学习了几个月&#xff0c;一行行代码理解。本次在工控机上部署记录下完整的流程。 ORB-SLA…

koa基础应用

不要把koa想得太复杂&#xff0c;他就是一个Node框架而已。 在本地应用安装好Node和koa后&#xff0c;先实现一下简单的服务app.js&#xff0c;代码如下&#xff1a; const Koa require(koa) const app new Koa(); app.use(async (context) > {context.body hello Koa …

企业风险管理策略终极指南

企业风险管理不一定是可怕的。企业风险管理是一个模糊且难以定义的主题领域。它涵盖了企业的多种风险和程序&#xff0c;与传统的风险管理有很大不同。 那么&#xff0c;企业风险管理到底是什么&#xff1f;在本文中&#xff0c;我们将确定它是什么&#xff0c;提出两种常见的…

B058-SpringBoot

目录 springboot概念与作用入门案例springboot运行方式热部署配置文件Profile多环境支持整合测试-springboot-testSpringboot-web1.返回json数据2.返回页面&#xff08;模板技术&#xff09;thymeleaf1.导入thymeleaf依赖2.模板文件3.controller4.启动类 SSM整合1.导包2.项目目…