算法训练营Day10

#数据结构 #队列 #栈

开源学习资料

Feeling and experiences:

队列与栈的理论基础:

在初学完数据结构以后,我对栈和队列的底层只有一个初步的认知:

队列(Queue)和栈(Stack)都是数据结构中的逻辑结构,它们描述了元素之间的相对关系而不涉及具体的物理存储。

队列(先进先出):类比于现实生活中的排队,先来的排最前面,完事后第一个出列。

栈(先进后出):类比于叠盘子,叠好后,最面上的盘子是最后放上去的。

之前刷了力扣上的题,很多时候都用到了栈和队列的原理

对它们就有了更深刻的认识:

我接触到的,栈的应用:
1. 函数调用: 栈常常用于实现函数调用的调用栈,其中每次函数调用都将其局部变量和返回地址压入栈中,函数返回时再弹出。
2. 表达式求值: 栈可以用于计算表达式的值,特别是中缀表达式到后缀表达式的转换,以及后缀表达式的求值。
3. 回溯算法: 在深度优先搜索(DFS)等算法中,栈用于保存当前路径,以便在需要回溯时能够恢复状态。(回溯的精髓)

我接触到的,队列的应用:
1. 广度优先搜索: 队列常用于广度优先搜索算法,确保按照层级顺序访问节点。(经常刷题遇到)
2. 任务调度: 队列可用于实现任务调度系统,确保任务按照先后顺序执行。
3. 缓冲区: 在计算机网络和操作系统中,队列被广泛用作缓冲区,用于存储和管理数据。

以上是我对栈和队列的认识。

Java中,(在不手动模拟栈和队列的情况下),通常会用到Queue(队列)与Stact(栈)这两个接口。这两个接口定义了操作队列和栈的标准方法。

Queue(队列)主要方法包括:

offer(E,e):将元素插入队列,成功返回true,失败返回false。

poll():检查并移除队列的头部元素,队列为空时返回null。

peek():检查但不移除队列的头部元素,队列为空时返回null。

Stack(栈)主要方法包括:

push(E item):将元素压入栈。

pop():弹出栈顶元素。

peek():查看栈顶元素但不移除。

补充:Queue与Stack接口在Java中都扩展了Collection接口,继承了一些通用的集合方法,比如size(),isEmpty(),contains()等。

还有队列中的双端队列Deque(继承了Queue接口)

在这里记录一下它的主要方法:

addFirst(E e):在队列开头插入元素。

addLast(E e):在队列尾部插入元素。

removeFirst():移除并返回队列的第一个元素。

getFirst():返回队列的第一个元素,但不移除。

熟练运用以上方法,解题就不用重新模拟了。

用栈实现队列:力扣题目链接

请你仅使用两个栈实现先入先出队列。

队列应当支持一般队列支持的所有操作(pushpoppeekempty

class MyQueue {

    Stack<Integer> stackIn;
    Stack<Integer> 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 <stdio.h>
#include <stdlib.h>

// 定义栈结构体
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)的栈

支持普通栈的全部四种操作(pushtoppopempty)。 

先用Java的方法:

class MyStack {
    Queue<Integer> queue1;
    Queue<Integer> queue2;


    public MyStack() {
        queue1 = new ArrayDeque<Integer>();
        queue2 = new ArrayDeque<Integer>();
    }
    
    public void push(int x) {
        queue2.offer(x);
        while (!queue1.isEmpty()) {
            queue2.offer(queue1.poll());
        }
        Queue<Integer> 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 <stdio.h>
#include <stdlib.h>

// 创建节点
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!


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值