#数据结构 #队列 #栈
开源学习资料
Feeling and experiences:
在初学完数据结构以后,我对栈和队列的底层只有一个初步的认知:
队列(Queue)和栈(Stack)都是数据结构中的逻辑结构,它们描述了元素之间的相对关系而不涉及具体的物理存储。
队列(先进先出):类比于现实生活中的排队,先来的排最前面,完事后第一个出列。
栈(先进后出):类比于叠盘子,叠好后,最面上的盘子是最后放上去的。
之前刷了力扣上的题,很多时候都用到了栈和队列的原理
对它们就有了更深刻的认识:
我接触到的,栈的应用:
1. 函数调用: 栈常常用于实现函数调用的调用栈,其中每次函数调用都将其局部变量和返回地址压入栈中,函数返回时再弹出。
2. 表达式求值: 栈可以用于计算表达式的值,特别是中缀表达式到后缀表达式的转换,以及后缀表达式的求值。
3. 回溯算法: 在深度优先搜索(DFS)等算法中,栈用于保存当前路径,以便在需要回溯时能够恢复状态。(回溯的精髓)
我接触到的,队列的应用:
1. 广度优先搜索: 队列常用于广度优先搜索算法,确保按照层级顺序访问节点。(经常刷题遇到)
2. 任务调度: 队列可用于实现任务调度系统,确保任务按照先后顺序执行。
3. 缓冲区: 在计算机网络和操作系统中,队列被广泛用作缓冲区,用于存储和管理数据。
以上是我对栈和队列的认识。
Java中,(在不手动模拟栈和队列的情况下),通常会用到Queue(队列)与Stact(栈)这两个接口。这两个接口定义了操作队列和栈的标准方法。
offer(E,e):将元素插入队列,成功返回true,失败返回false。
poll():检查并移除队列的头部元素,队列为空时返回null。
peek():检查但不移除队列的头部元素,队列为空时返回null。
push(E item):将元素压入栈。
pop():弹出栈顶元素。
peek():查看栈顶元素但不移除。
补充:Queue与Stack接口在Java中都扩展了Collection接口,继承了一些通用的集合方法,比如size(),isEmpty(),contains()等。
还有队列中的双端队列Deque(继承了Queue接口)
在这里记录一下它的主要方法:
addFirst(E e):在队列开头插入元素。
addLast(E e):在队列尾部插入元素。
removeFirst():移除并返回队列的第一个元素。
getFirst():返回队列的第一个元素,但不移除。
熟练运用以上方法,解题就不用重新模拟了。
请你仅使用两个栈实现先入先出队列。
队列应当支持一般队列支持的所有操作(push
、pop
、peek
、empty
)
class MyQueue {
Stack stackIn;
Stack stackOut;
public MyQueue() {
stackIn = new Stack<>(); // 负责进栈
stackOut = new Stack<>(); // 负责出栈
}
public void push(int x) {
stackIn.push(x);
}
public int pop() {
dumpstackIn();
return stackOut.pop();
}
public int peek() {
dumpstackIn();
return stackOut.peek();
}
public boolean empty() {
return stackIn.isEmpty() && stackOut.isEmpty();
}
// 如果stackOut为空,那么将stackIn中的元素全部放到stackOut中
private void dumpstackIn(){
if (!stackOut.isEmpty()) return;
while (!stackIn.isEmpty()){
stackOut.push(stackIn.pop());
}
}
}
用Java写的话就非常简单,因为可以直接用栈的方法来模拟队列。
顺便复习数据结构,这里再用C来写一下:
//先来模拟栈
typedef struct Stack{
int *stk;
int stkSize; //栈的长度
int stkCapacity; //栈的容量
} Stack;
Stack* stackCreate(int capacity){
Stack* sta = malloc(sizeof(Stack)); //给栈找一块空间
sta->stk = malloc(sizeof(int)*capacity); //给栈分配空间大小
sta->stkSize = 0;
sta->stkCapacity = capacity;
return sta;
}
//压栈
void stackPush(Stack* obj,int x){
obj->stk[obj->stkSize] = x;
obj->stkSize++; //多一个元素则增加一个空间
}
//出栈
void stackPop(Stack*obj){
obj->stkSize--;
}
//访问栈顶元素
int stackTop(Stack* obj){
return obj->stk[obj->stkSize-1];
}
//判断栈是否为空
bool stackEmpty(Stack* obj){
return obj->stkSize == 0;
}
//清楚栈
void stackFree(Stack*obj){
free(obj->stk);
}
//创建队列的结构体,用两个栈来模拟
typedef struct MyQueue{
Stack* inStack;
Stack* outStack;
}MyQueue;
MyQueue* myQueueCreate() {
MyQueue* ret = malloc(sizeof(MyQueue));
ret->inStack = stackCreate(100);
ret->outStack = stackCreate(100);
return ret;
}
void in2out(MyQueue* obj) {
while (!stackEmpty(obj->inStack)) {
stackPush(obj->outStack, stackTop(obj->inStack));
stackPop(obj->inStack);
}
}
void myQueuePush(MyQueue* obj, int x) {
stackPush(obj->inStack, x);
}
int myQueuePop(MyQueue* obj) {
if (stackEmpty(obj->outStack)) {
in2out(obj);
}
int x = stackTop(obj->outStack);
stackPop(obj->outStack);
return x;
}
int myQueuePeek(MyQueue* obj) {
if (stackEmpty(obj->outStack)) {
in2out(obj);
}
return stackTop(obj->outStack);
}
bool myQueueEmpty(MyQueue* obj) {
return stackEmpty(obj->inStack) && stackEmpty(obj->outStack);
}
void myQueueFree(MyQueue* obj) {
stackFree(obj->inStack);
stackFree(obj->outStack);
}
代码量一下就上来了
在数据结构中,对于栈的结构体搭建并模拟,一般会使用base指针和top指针:
#include
#include
// 定义栈结构体
typedef struct {
int *base; // 栈底指针
int *top; // 栈顶指针
int size; // 栈的容量
} Stack;
// 初始化栈
void initializeStack(Stack *stack, int size) {
stack->base = (int *)malloc(size * sizeof(int));
if (stack->base == NULL) {
exit(EXIT_FAILURE);
}
stack->top = stack->base;
stack->size = size;
}
// 判断栈是否为空
int isEmpty(Stack *stack) {
return stack->top == stack->base;
}
// 判断栈是否已满
int isFull(Stack *stack) {
return stack->top - stack->base == stack->size;
}
// 入栈操作
void push(Stack *stack, int value) {
if (isFull(stack)) {
exit(EXIT_FAILURE);
}
*(stack->top) = value;
(stack->top)++;
}
// 出栈操作
int pop(Stack *stack) {
if (isEmpty(stack)) {
exit(EXIT_FAILURE);
}
(stack->top)--;
return *(stack->top);
}
// 销毁栈
void destroyStack(Stack *stack) {
free(stack->base);
stack->base = stack->top = NULL;
stack->size = 0;
}
请你仅使用两个队列实现一个后入先出(LIFO)的栈
支持普通栈的全部四种操作(push
、top
、pop
和 empty
)。
先用Java的方法:
class MyStack {
Queue queue1;
Queue queue2;
public MyStack() {
queue1 = new ArrayDeque();
queue2 = new ArrayDeque();
}
public void push(int x) {
queue2.offer(x);
while (!queue1.isEmpty()) {
queue2.offer(queue1.poll());
}
Queue temp = queue1;
queue1 = queue2;
queue2 = temp;
}
public int pop() {
return queue1.poll();
}
public int top() {
return queue1.peek();
}
public boolean empty() {
return queue1.isEmpty();
}
}
用C语言写一遍:
#define LEN 20
typedef struct queue {
int *data;
int head;
int rear;
int size;
} Queue;
typedef struct {
Queue *queue1, *queue2;
} MyStack;
Queue *initQueue(int k) {
Queue *obj = (Queue *)malloc(sizeof(Queue));
obj->data = (int *)malloc(k * sizeof(int));
obj->head = -1;
obj->rear = -1;
obj->size = k;
return obj;
}
void enQueue(Queue *obj, int e) {
if (obj->head == -1) {
obj->head = 0;
}
obj->rear = (obj->rear + 1) % obj->size;
obj->data[obj->rear] = e;
}
int deQueue(Queue *obj) {
int a = obj->data[obj->head];
if (obj->head == obj->rear) {
obj->rear = -1;
obj->head = -1;
return a;
}
obj->head = (obj->head + 1) % obj->size;
return a;
}
int isEmpty(Queue *obj) {
return obj->head == -1;
}
MyStack *myStackCreate() {
MyStack *obj = (MyStack *)malloc(sizeof(MyStack));
obj->queue1 = initQueue(LEN);
obj->queue2 = initQueue(LEN);
return obj;
}
void myStackPush(MyStack *obj, int x) {
if (isEmpty(obj->queue1)) {
enQueue(obj->queue2, x);
} else {
enQueue(obj->queue1, x);
}
}
int myStackPop(MyStack *obj) {
if (isEmpty(obj->queue1)) {
while (obj->queue2->head != obj->queue2->rear) {
enQueue(obj->queue1, deQueue(obj->queue2));
}
return deQueue(obj->queue2);
}
while (obj->queue1->head != obj->queue1->rear) {
enQueue(obj->queue2, deQueue(obj->queue1));
}
return deQueue(obj->queue1);
}
int myStackTop(MyStack *obj) {
if (isEmpty(obj->queue1)) {
return obj->queue2->data[obj->queue2->rear];
}
return obj->queue1->data[obj->queue1->rear];
}
bool myStackEmpty(MyStack *obj) {
if (obj->queue1->head == -1 && obj->queue2->head == -1) {
return true;
}
return false;
}
void myStackFree(MyStack *obj) {
free(obj->queue1->data);
obj->queue1->data = NULL;
free(obj->queue1);
obj->queue1 = NULL;
free(obj->queue2->data);
obj->queue2->data = NULL;
free(obj->queue2);
obj->queue2 = NULL;
free(obj);
obj = NULL;
}
同样在数据结构搭建单端的链队列,一般的写法是(在VsCode上进行了模拟):
#include
#include
// 创建节点
typedef int Elemtype;
typedef struct queueNode
{
Elemtype data;
struct queueNode *next;
}queueNode,*queuePtr;
// 创建队列结构
typedef struct
{
queuePtr front;
queuePtr rear;
}linkQueue;
//链队列的建立
void initQueue(linkQueue*Q){
Q->front = Q->rear = (queuePtr)malloc(sizeof(queueNode));
if(!Q->front) exit(0);
Q->front->next = NULL;
}
//实现链队列的入队
void enQueue(linkQueue *Q,int e)
{
queuePtr P =(queuePtr)malloc(sizeof(queueNode));
if(!P) exit(0);
P->data = e;
P->next = NULL;
Q->rear->next =P;
Q->rear = P;
}
//实现链队列的出队
void deQueue(linkQueue *Q,int *e){
if(Q->front == Q->rear) return;
queuePtr P = Q->front->next;
*e = P->data;
Q->front->next = P->next;
if(Q->rear == P) Q->rear = Q->front;
free(P);
return;
}
//实现判断队列是否为空
int queueEmpty(linkQueue *Q){
return Q->front == Q->rear;
}
//实现队列销毁
void destroyQueue(linkQueue *Q){
while(Q->front){
Q->rear == Q->front->next;
free(Q->front);
Q->front = Q->rear;
}
}
int main(){
linkQueue Q;
initQueue(&Q);
//模拟入队
enQueue(&Q,1);
enQueue(&Q,3);
enQueue(&Q,7);
enQueue(&Q,10);
//模拟出队
int e;
deQueue(&Q,&e);
printf("%d\n",e);
deQueue(&Q,&e);
printf("%d\n",e);
return 0;
}
今天的学习主要是能更清晰栈和队列的底层原理。 (为后面的题做好准备)
熟练运用自己学习的语言的API,后面的题目会涉及到栈和队列的运用。
寒灯纸上
梨花雨凉~
Fighting!