数据结构基础内容-----第四章 栈与队列

news2024/11/25 6:39:16

文章目录

    • 栈的定义
    • 站的抽象数据类型
    • 两栈共享空间
    • 栈的作用
      • 递归的定义
    • 栈运算
  • 队列
    • 循环队列
    • 队列链式存储结构及实现

栈的定义

栈(Stack)是计算机科学中的一种抽象数据类型,它是一个只能在一端进行插入和删除操作的线性数据结构。栈按照后进先出(LIFO)的原则存储数据,即最后放入的元素最先被取出。类比物理世界中的堆叠物品,每次加入的物品都被放在上面,取出时也只能从上面取出,最后放入的物品最先被取出。

栈有两个基本的操作:

  • push:将元素压入栈顶。
  • pop:将栈顶元素弹出。

除此之外,栈还常常涉及到以下几个概念:

  • 栈顶(Top):表示栈顶元素所在位置的下标或指针。
  • 空栈(Empty):表示栈内没有任何元素。
  • 满栈(Full):表示栈已经无法再容纳新的元素。
  • 栈的大小(Size):表示栈可以容纳的元素的最大数量。
    栈的应用非常广泛,例如表达式求值、函数调用、回溯算法等都与栈密切相关。

站的抽象数据类型

**抽象数据类型(ADT)**是一种数学模型,它定义了数据的逻辑行为和操作。具体来说,ADT 描述了数据对象的属性和可对其执行的操作,并不考虑这些操作的具体实现方式。

栈(Stack)是一种常见的 ADT,其特点是后进先出(Last In First Out,LIFO),即最后加入的元素最先被取出。栈只支持两个基本操作:压入(Push)和弹出(Pop)。在栈中插入一个元素时,该元素会被放置在栈顶;在弹出一个元素时,栈顶的元素将被移除,并返回该元素的值。

根据这些基本操作,可以衍生出其他一些操作,例如查看栈顶元素(Top)、判断**栈是否为空(IsEmpty)**等。这些操作都可以通过压入和弹出操作来实现。

栈可以用于许多应用场景,例如函数调用时的内存分配、括号匹配、表达式求值等。在实际编程中,栈通常使用数组或链表来实现。

Push(item): 将一个元素item压入栈顶。
Pop(): 弹出栈顶元素,并将其返回。
Top(): 返回栈顶的元素,但不弹出该元素。
IsEmpty(): 判断栈是否为空,如果为空返回True,否则返回False。
Size(): 返回栈中元素的个数。

在这里插入图片描述
在实际编程中,栈通常使用数组或链表来实现。这里介绍一种用数组实现栈的方法,叫做顺序存储结构。

顺序存储结构是指将栈中的元素保存在连续的内存空间中,可以通过数组来实现。具体来说,我们需要定义一个数组、一个栈顶指针、以及一些相关的操作。

#define MAXSIZE 100

typedef struct {
    int data[MAXSIZE];
    int top; // 栈顶指针
} Stack;

void init(Stack *s) {
    s->top = -1;
}

int is_empty(Stack *s) {
    return s->top == -1;
}

int is_full(Stack *s) {
    return s->top == MAXSIZE - 1;
}

void push(Stack *s, int x) {
    if (is_full(s)) {
        printf("Stack Overflow!\n");
        return;
    }
    s->top++;
    s->data[s->top] = x;
}

int pop(Stack *s) {
    if (is_empty(s)) {
        printf("Stack Underflow!\n");
        return -1;
    }
    int x = s->data[s->top];
    s->top--;
    return x;
}

int top(Stack *s) {
    if (is_empty(s)) {
        printf("Stack Empty!\n");
        return -1;
    }
    return s->data[s->top];
}

int size(Stack *s) {
    return s->top + 1;
}

两栈共享空间

两栈共享空间是指在一个数组中实现两个栈的数据结构。这种实现方法可以节省空间,因为两个栈共用一个数组。

具体实现方法是将数组分成两个部分,**一个部分为第一个栈的存储区域,另一个部分为第二个栈的存储区域。**两个栈的起始位置分别从数组的两端开始向中间靠拢。当两个栈的栈顶指针相遇时,表示两个栈都满了。

需要注意的是,在进行入栈和出栈操作时,需要判断哪个栈的栈顶指针需要移动。具体判断方法是,如果要对第一个栈进行入栈操作,则将元素插入到第一个栈的栈顶,并将第一个栈的栈顶指针加1;如果要对第二个栈进行入栈操作,则将元素插入到第二个栈的栈顶,并将第二个栈的栈顶指针减1。出栈操作也是类似的。

以下是一个简单的示例代码,实现了两个栈在同一个数组内共享空间的功能:

#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 100

typedef struct {
    int array[MAX_SIZE];
    int top1;
    int top2;
} TwoStacks;

void initStacks(TwoStacks *stacks) {
    stacks->top1 = -1;
    stacks->top2 = MAX_SIZE;
}

void push1(TwoStacks *stacks, int item) {
    if (stacks->top1 < stacks->top2 - 1) {
        stacks->top1++;
        stacks->array[stacks->top1] = item;
    } else {
        printf("Stack Overflow\n");
        exit(1);
    }
}

void push2(TwoStacks *stacks, int item) {
    if (stacks->top1 < stacks->top2 - 1) {
        stacks->top2--;
        stacks->array[stacks->top2] = item;
    } else {
        printf("Stack Overflow\n");
        exit(1);
    }
}

int pop1(TwoStacks *stacks) {
    if (stacks->top1 >= 0) {
        int item = stacks->array[stacks->top1];
        stacks->top1--;
        return item;
    } else {
        printf("Stack Underflow\n");
        exit(1);
    }
}

int pop2(TwoStacks *stacks) {
    if (stacks->top2 < MAX_SIZE) {
        int item = stacks->array[stacks->top2];
        stacks->top2++;
        return item;
    } else {
        printf("Stack Underflow\n");
        exit(1);
    }
}

int main() {
    TwoStacks stacks;
    initStacks(&stacks);

    push1(&stacks, 1);
    push2(&stacks, 2);
    push1(&stacks, 3);
    push2(&stacks, 4);
    push1(&stacks, 5);

    printf("%d\n", pop1(&stacks)); // Output: 5
    printf("%d\n", pop2(&stacks)); // Output: 4
    printf("%d\n", pop1(&stacks)); // Output: 3
    printf("%d\n", pop2(&stacks)); // Output: 2
    printf("%d\n", pop1(&stacks)); // Output: 1

    return 0;
}

栈的作用

栈还可以用于解决一些特定的编程问题,例如表达式求值、递归算法、回溯算法等等。在这些情况下,栈可以帮助我们存储和跟踪程序运行时的状态信息,以便于算法的正确执行。

这里是用栈做的小的运用

#include <stdio.h>
#include <stdlib.h>

#define MAX_STACK_SIZE 100

// 定义栈结构体
typedef struct {
    int data[MAX_STACK_SIZE];
    int top;
} Stack;

// 初始化栈
void init_stack(Stack *s) {
    s->top = -1;
}

// 判断栈是否为空
int is_empty(Stack *s) {
    return s->top == -1;
}

// 判断栈是否已满
int is_full(Stack *s) {
    return s->top == MAX_STACK_SIZE - 1;
}

// 入栈操作
void push(Stack *s, int value) {
    if (is_full(s)) {
        printf("Stack is full.\n");
        exit(1);
    }
    s->data[++(s->top)] = value;
}

// 出栈操作
int pop(Stack *s) {
    if (is_empty(s)) {
        printf("Stack is empty.\n");
        exit(1);
    }
    return s->data[(s->top)--];
}

// 递归函数
int factorial(int n) {
    Stack s;
    init_stack(&s);

    int result = 1;
    push(&s, n); // 入栈
    while (!is_empty(&s)) {
        int x = pop(&s); // 出栈
        if (x == 1) {
            break;
        }
        result *= x;
        push(&s, x - 1); // 入栈
    }

    return result;
}

// 示例程序
int main() {
    int n = 5;
    printf("%d! = %d\n", n, factorial(n));
    return 0;
}

递归的定义

递归是一种在函数定义中使用自身的技术。在递归中,一个函数调用自身,直到达到某个终止条件为止。通常情况下,递归用于解决可以被分割成相同模式的子问题的问题,这些问题可以通过不断地调用函数来得到解决。

递归函数通常包括两部分:基本情况和递归情况。基本情况是指函数能够直接处理的情况,不需要再次调用自身来解决。递归情况则是指函数需要将问题分解成更小的子问题,并通过调用自身来解决它们。

递归的优点是可以使代码更简洁、易读、易于理解,但同时也可能会导致复杂度高、栈溢出等问题。因此,在使用递归时需要谨慎考虑其使用场景和实现方式。

总之学会递归就要学会递归

在这里插入图片描述
逆波兰表示法
在这里插入图片描述

波兰表示法

在这里插入图片描述

栈运算

使用栈可以很方便地实现算术表达式的求值。具体步骤如下:

  1. 创建两个栈:操作数栈和运算符栈。

  2. 从左到右扫描表达式中的每个字符。

  3. 如果当前字符是数字,则将其压入操作数栈。

  4. 如果当前字符是运算符,则判断运算符栈是否为空,如果不为空,则比较该运算符与运算符栈顶的运算符的优先级,如果该运算符的优先级小于或等于运算符栈顶的运算符,则弹出运算符栈顶的运算符和操作数栈中的两个元素,进行相应的运算,并将结果压入操作数栈。否则将该运算符压入运算符栈。

  5. 如果当前字符是左括号,则将其压入运算符栈。

  6. 如果当前字符是右括号,则循环弹出运算符栈顶的运算符和操作数栈中的两个元素,进行相应的运算,并将结果压入操作数栈,直到遇到左括号为止。

  7. 循环执行步骤 3-6,直到扫描完整个表达式。

  8. 循环弹出运算符栈顶的运算符和操作数栈中的两个元素,进行相应的运算,并将结果压入操作数栈,直到运算符栈为空。

  9. 操作数栈中最后剩下的元素即为表达式的计算结果。

这个方法可以实现任意长度的算术表达式的求值,包括含有加减乘除、括号等多种运算符的复杂表达式。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>   // 需要使用 isdigit 函数

#define MAX_STACK_SIZE 100   // 定义栈的最大容量

int stack[MAX_STACK_SIZE];   // 定义操作数栈
char opstack[MAX_STACK_SIZE];   // 定义运算符栈
int top = -1;   // 操作数栈顶指针
int optop = -1;   // 运算符栈顶指针

int pop() {   // 弹出操作数栈顶元素
    if (top == -1) {
        printf("Stack underflow!\n");
        exit(1);
    }
    return stack[top--];
}

char oppop() {   // 弹出运算符栈顶元素
    if (optop == -1) {
        printf("Stack underflow!\n");
        exit(1);
    }
    return opstack[optop--];
}

void push(int value) {   // 将元素压入操作数栈
    if (top >= MAX_STACK_SIZE - 1) {
        printf("Stack overflow!\n");
        exit(1);
    }
    stack[++top] = value;
}

void oppush(char value) {   // 将元素压入运算符栈
    if (optop >= MAX_STACK_SIZE - 1) {
        printf("Stack overflow!\n");
        exit(1);
    }
    opstack[++optop] = value;
}

int priority(char op) {   // 定义运算符优先级
    if (op == '*' || op == '/') {
        return 2;
    } else if (op == '+' || op == '-') {
        return 1;
    } else {
        return 0;
    }
}

int evaluate(char *expression) {   // 表达式求值函数
    int i, len, operand1, operand2;
    char c, op;

    len = strlen(expression);
    for (i = 0; i < len; i++) {
        c = expression[i];

        if (isdigit(c)) {   // 如果是数字字符,将其转换为整数并压入操作数栈
            push(c - '0');
        } else if (c == '(') {   // 如果是左括号,将其压入运算符栈
            oppush(c);
        } else if (c == ')') {   // 如果是右括号,则弹出运算符栈顶的运算符和操作数栈中的两个元素,进行相应的运算,并将结果压入操作数栈,直到遇到左括号为止
            while (opstack[optop] != '(') {
                operand2 = pop();
                operand1 = pop();
                op = oppop();
                switch (op) {
                    case '+': push(operand1 + operand2); break;
                    case '-': push(operand1 - operand2); break;
                    case '*': push(operand1 * operand2); break;
                    case '/': 
                        if (operand2 == 0) {   // 处理除数为0的情况
                            printf("Division by zero!\n");
                            exit(1);
                        }
                        push(operand1 / operand2); 
                        break;
                }
            }
            oppop();   // 弹出左括号
        } else if (c == '+' || c == '-' || c == '*' || c == '/') {   // 如果是运算符,则比较该运算符与运算符栈顶的运算符的优先级,如果该运算符的优先级小于或等于运算符栈顶的运算符,则弹出运算符栈顶的运算符和操作数栈中的两个元素,进行相应的运算,并将结果压入操作数栈。否则将该运算符压入运算符栈。
            while (optop >= 0 && priority(c) <= priority(opstack[optop]))
            {
operand2 = pop();
operand1 = pop();
op = oppop();
switch (op) {
case '+': push(operand1 + operand2); break;
case '-': push(operand1 - operand2); break;
case '*': push(operand1 * operand2); break;
case '/':
if (operand2 == 0) {   // 处理除数为0的情况
printf("Division by zero!\n");
exit(1);
}
push(operand1 / operand2);
break;
}
}
oppush(c);
} else if (c == ' ') {   // 如果是空格,则忽略
continue;
} else {   // 如果不是数字、括号或运算符,则输入的表达式有误
printf("Invalid expression!\n");
exit(1);
}
}

while (optop >= 0) {   // 处理剩余的运算符
    operand2 = pop();
    operand1 = pop();
    op = oppop();
    switch (op) {
        case '+': push(operand1 + operand2); break;
        case '-': push(operand1 - operand2); break;
        case '*': push(operand1 * operand2); break;
        case '/': 
            if (operand2 == 0) {   // 处理除数为0的情况
                printf("Division by zero!\n");
                exit(1);
            }
            push(operand1 / operand2); 
            break;
    }
}

return pop();   // 返回操作数栈顶元素,即为表达式求值结果
}

int main() {
char expression[100];

printf("Please enter an arithmetic expression:\n");
fgets(expression, 100, stdin);

printf("The result is: %d\n", evaluate(expression));

return 0;
}



队列

队列是一种常见的数据结构,它按照先进先出(FIFO)的原则来存储和访问元素。队列通常用于在多个处理单元之间分配任务或缓冲数据。

在队列中,新元素被添加到队列的尾部,而从队列中删除元素时则从队列的头部开始进行。这意味着最先添加的元素会最先被取出,而最后添加的元素则会最后被取出。这种方式确保了队列中的元素按照添加顺序进行处理。

队列的基本操作包括入队(将元素添加到队列尾部)和出队(从队列头部删除元素)。此外,队列还可以支持其他操作,如查看队列头部元素、检查队列是否为空等。

在这里插入图片描述

队列的实现

#include <stdio.h>
#include <stdlib.h>

#define MAX_QUEUE_SIZE 100

// 定义一个队列结构体
struct queue {
    int items[MAX_QUEUE_SIZE];
    int front, rear;
};

// 初始化队列
struct queue* create_queue() {
    struct queue* q = (struct queue*)malloc(sizeof(struct queue));
    q->front = -1;
    q->rear = -1;
    return q;
}

// 检查队列是否为空
int is_empty(struct queue* q) {
    if (q->rear == -1)
        return 1;
    else
        return 0;
}

// 检查队列是否已满
int is_full(struct queue* q) {
    if (q->front == 0 && q->rear == MAX_QUEUE_SIZE - 1)
        return 1;
    else if (q->front == q->rear + 1)
        return 1;
    else
        return 0;
}

// 向队列尾部插入元素
void enqueue(struct queue* q, int value) {
    if (is_full(q)) {
        printf("Queue is full.\n");
    } else {
        if (q->front == -1) {
            q->front = 0;
        }
        q->rear = (q->rear + 1) % MAX_QUEUE_SIZE;
        q->items[q->rear] = value;
        printf("Enqueued item: %d\n", value);
    }
}

// 从队列头部删除元素并返回该元素
int dequeue(struct queue* q) {
    int item;
    if (is_empty(q)) {
        printf("Queue is empty.\n");
        item = -1;
    } else {
        item = q->items[q->front];
        if (q->front == q->rear) {
            q->front = -1;
            q->rear = -1;
        } else {
            q->front = (q->front + 1) % MAX_QUEUE_SIZE;
        }
    }
    return item;
}

// 显示队列中所有元素
void display(struct queue* q) {
    int i;
    if (is_empty(q)) {
        printf("Queue is empty.\n");
    } else {
        printf("Queue contains the following items:\n");
        for (i = q->front; i <= q->rear; i++) {
            printf("%d ", q->items[i]);
        }
        printf("\n");
    }
}

// 主函数用于演示队列操作
int main() {
    struct queue* q = create_queue();

    // 向队列添加元素
    enqueue(q, 1);
    enqueue(q, 2);
    enqueue(q, 3);
    enqueue(q, 4);

    // 显示队列中的元素
    display(q);

    // 从队列删除元素
    int removed_item = dequeue(q);
    printf("Removed item: %d\n", removed_item);

    // 显示更新后的队列
    display(q);
    
    return 0;
}

循环队列

循环队列是一种使用固定大小的环形缓冲区来实现队列的数据结构。在循环队列中,队列的尾部连接到了队列的头部,使得元素可以按照先进先出(FIFO)的顺序被访问。

在循环队列中,需要维护两个指针:一个指向队列的头部(front),另一个指向队列的尾部(rear)。当向队列中插入元素时,将元素添加到尾部指针所指向的位置,并将尾部指针后移。同样地,当从队列中删除元素时,将元素从头部指针所指向的位置删除,并将头部指针后移。由于循环队列是环形的,当指针到达缓冲区的尾部时,它会返回缓冲区的开头。

循环队列相对于普通队列的好处在于,它可以有效地利用内存空间并避免因为队列的头部或尾部始终在数组的末尾而导致的空间浪费。此外,在循环队列中,我们可以更高效地判断队列是否已满或为空。

#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 5  // 定义最大队列长度为5

typedef struct {
    int front;  // 队头指针
    int rear;   // 队尾指针
    int data[MAX_SIZE];  // 队列数组
} Queue;

/**
 * 初始化队列
 * @param q 待初始化的队列
 */
void init(Queue *q) {
    q->front = q->rear = 0;
}

/**
 * 判断队列是否为空
 * @param q 待判断的队列
 * @return 如果队列为空,则返回1;否则返回0。
 */
int is_empty(Queue *q) {
    return q->front == q->rear;
}

/**
 * 判断队列是否已满
 * @param q 待判断的队列
 * @return 如果队列已满,则返回1;否则返回0。
 */
int is_full(Queue *q) {
    return (q->rear + 1) % MAX_SIZE == q->front;
}

/**
 * 入队操作
 * @param q 队列指针
 * @param x 入队元素
 * @return 成功入队则返回1,否则返回0。
 */
int enqueue(Queue *q, int x) {
    if (is_full(q)) {  // 如果队列已满,则无法入队
        printf("队列已满,无法入队。\n");
        return 0;
    } else {
        q->data[q->rear] = x;         // 将元素插入队尾
        q->rear = (q->rear + 1) % MAX_SIZE;  // 队尾指针加1,注意取模
        return 1;
    }
}

/**
 * 出队操作
 * @param q 队列指针
 * @param x 出队元素的存储地址
 * @return 成功出队则返回1,否则返回0。
 */
int dequeue(Queue *q, int *x) {
    if (is_empty(q)) {  // 如果队列为空,则无法出队
        printf("队列为空,无法出队。\n");
        return 0;
    } else {
        *x = q->data[q->front];          // 取出队头元素
        q->front = (q->front + 1) % MAX_SIZE;  // 队头指针加1,注意取模
        return 1;
    }
}

int main() {
    Queue q;            // 定义队列
    init(&q);           // 初始化队列

    enqueue(&q, 1);     // 入队元素1
    enqueue(&q, 2);     // 入队元素2
    enqueue(&q, 3);     // 入队元素3
    enqueue(&q, 4);     // 入队元素4
    enqueue(&q, 5);     // 入队元素5,此时队列已满,5入不了队列
    enqueue(&q, 6);     // 入队元素6,此时无法入队

    int x;              // 定义变量x,用于存储出队元素
    dequeue(&q, &x);    // 出队操作,取出队头元素1
    printf("出队元素:%d\n", x);

    dequeue(&q, &x);    // 出队操作,取出队头元素2
    printf("出队元素:%d\n", x);

    enqueue(&q, 6);     // 入队新元素6

    dequeue(&q, &x);    // 出队操作,取出队头元素3
    printf("出队元素:%d\n", x);

    dequeue(&q, &x);    // 出队操作,取出队头元素4
    printf("出队元素:%d\n", x);

    dequeue(&q, &x);    // 出队操作,取出队头元素6
    printf("出队元素:%d\n", x);

    dequeue(&q, &x);    //
    dequeue(&q, &x);    // 出队操作,此时队列为空,无法出队
printf("出队元素:%d\n", x);

return 0;
}

//程序有瑕疵,这里仅是抛砖作用。

队列链式存储结构及实现

首先,定义一个结构体来表示链表中的节点,包含数据域和指针域:

typedef struct ListNode {
    int data;
    struct ListNode* next;
} ListNode;

其中 data 表示节点中存储的数据,next 是指向下一个节点的指针。

接着,定义一个结构体来表示队列,包含头指针和尾指针:

typedef struct Queue {
    ListNode* front; // 队头指针
    ListNode* rear;  // 队尾指针
} Queue;

其中 front 指向队头节点,rear 指向队尾节点。

创建空队列时,可以将 front 和 rear 初始化为 NULL。

入队操作时,需要新建一个节点,并将其插入到队尾:

void enQueue(Queue* q, int value) {
    ListNode* node = (ListNode*)malloc(sizeof(ListNode));
    node->data = value;
    node->next = NULL;

    if (q->rear == NULL) { // 队列为空时
        q->front = q->rear = node;
    } else {
        q->rear->next = node;
        q->rear = node;
    }
}

出队操作时,需要删除队头节点,并更新队头指针:

int deQueue(Queue* q) {
    if (q->front == NULL) { // 队列为空时
        return -1;
    }

    ListNode* node = q->front;
    int value = node->data;

    if (q->front == q->rear) { // 队列只有一个节点时
        q->front = q->rear = NULL;
    } else {
        q->front = q->front->next;
    }

    free(node);
    return value;
}

小例子

#include <stdio.h>
#include <stdlib.h>

// 链表节点结构体
typedef struct ListNode {
    int data;
    struct ListNode* next;
} ListNode;

// 队列结构体
typedef struct Queue {
    ListNode* front; // 队头指针
    ListNode* rear;  // 队尾指针
} Queue;

// 初始化队列
void initQueue(Queue* q) {
    q->front = NULL;
    q->rear = NULL;
}

// 入队操作
void enQueue(Queue* q, int value) {
    ListNode* node = (ListNode*)malloc(sizeof(ListNode));
    node->data = value;
    node->next = NULL;

    if (q->rear == NULL) { // 队列为空时
        q->front = q->rear = node;
    } else {
        q->rear->next = node;
        q->rear = node;
    }
}

// 出队操作
int deQueue(Queue* q) {
    if (q->front == NULL) { // 队列为空时
        return -1;
    }

    ListNode* node = q->front;
    int value = node->data;

    if (q->front == q->rear) { // 队列只有一个节点时
        q->front = q->rear = NULL;
    } else {
        q->front = q->front->next;
    }

    free(node);
    return value;
}

int main() {
    Queue q;
    initQueue(&q);

    // 入队操作
    enQueue(&q, 1);
    enQueue(&q, 2);
    enQueue(&q, 3);

    // 出队操作
    int value1 = deQueue(&q);
    int value2 = deQueue(&q);
    int value3 = deQueue(&q);

    printf("%d %d %d\n", value1, value2, value3); // 输出:1 2 3

    return 0;
}

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

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

相关文章

tomcat what

tomcat是什么 对于tomcat是什么有什么作用。曾经看到一个大神是这样解释tomcat的&#xff0c;现在分享给大家 内容大体是&#xff1a; 我家有一台机器&#xff0c;可以把石头变成金子。你快递给我一箱石头&#xff0c;让我把它们变成一箱金子再快递给你。 这个机器就是web项…

【Python从入门到进阶】21、爬虫相关概念介绍

接上篇《20、HTML页面结构的介绍》 上一篇我们正式进入了Python爬虫的实战教程&#xff0c;主要讲解了要爬取的HTML页面的结构。本篇我们来介绍爬虫的相关概念。 一、什么是互联网爬虫 如果我们把互联网比作一张大的蜘蛛网&#xff0c;那一台计算机上的数据便是蜘蛛网上的一个…

如何自己搭建chatgpt镜像网站

前沿 总所周知&#xff0c;访问以及注册chatgpt都是比较困难的&#xff0c;如何能畅游chatgpt而不受魔法的限制呢&#xff1f;还好openai给出了非常不错的api能使我们快速搭建一个镜像网站 准备工作 首先得准备一台服务器 这里推荐使用海外vps来进行搭建&#xff0c;缺点就是…

java基础入门-16-【阶段项目(综合练习doudizhu游戏)】

Java基础入门-16-【阶段项目(综合练习&doudizhu游戏)】 25、阶段项目(综合练习&doudizhu游戏)斗地主小游戏斗地主游戏1(控制台版)步骤一:准备牌步骤二:洗牌步骤三:发牌步骤四:看牌步骤五:给牌进行排序(两种方式排序)方式一:利用序号进行排序方式二:给每一…

阻焊设计~焊盘阻焊开窗、阻焊桥

阻焊设计 焊盘阻焊开窗 阻焊开窗应比焊盘尺寸大6mils以上&#xff08;单边3mils&#xff09;&#xff0c;见下图&#xff1a; 阻焊桥 a) 相邻的SMD焊盘&#xff0c;SMD焊盘和插件孔、SMD焊盘和过孔、过孔与过孔之间需要保留阻焊桥&#xff1b;最小阻焊桥宽度2mils &#x…

vue3.0与vue2.0

一、生命周期的变化 1.vue2.响应式架构 2.vue3.0 响应式架构图 Vue3.0响应式框架在设计上&#xff0c;将视图渲染和数据响应式完全分离开来。将响应式核心方法effect从原有的Watcher中抽离。这样&#xff0c;当我们只需要监听数据响应某种逻辑回调(例如监听某个text属性的变化…

每日学术速递5.28

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CL 1.Improving Factuality and Reasoning in Language Models through Multiagent Debate 标题&#xff1a;通过多主体辩论改进语言模型中的事实性和推理 作者&#xff1a;Yilun Du,…

创建型设计模式02-工厂方法模式

✨作者&#xff1a;猫十二懿 ❤️‍&#x1f525;账号&#xff1a;CSDN 、掘金 、个人博客 、Github &#x1f389;公众号&#xff1a;猫十二懿 工厂方法模式 1、工厂方法模式介绍 工厂方法模式&#xff08;Factory Method Pattern&#xff09;是一种常用的对象创建型设计模式…

MAC中文版 FCPX V10.6.6 专属视频剪辑后期工具安装教程

Final Cut Pro X简介 Final Cut Pro X又名FCPX,是MAC上非常不错的视频非线性剪辑软件,它剪辑速度超凡,具有先进的调色功能、HDR 视频支持&#xff0c;以及 ProRes RAW&#xff0c;让剪辑、音轨、图形特效、整片输出&#xff0c;支持主流的摄像机格式,是专业视频剪辑领域的王者…

Java经典笔试题—day14

Java经典笔试题—day14 &#x1f50e;选择题&#x1f50e;编程题&#x1f36d;计算日期到天数转换&#x1f36d;幸运的袋子 &#x1f50e;结尾 &#x1f50e;选择题 (1)定义学生、教师和课程的关系模式 S (S#,Sn,Sd,Dc,SA &#xff09;&#xff08;其属性分别为学号、姓名、所…

【数据湖仓架构】数据湖和仓库:范式简介

是时候将数据分析迁移到云端了——您选择数据仓库还是数据湖解决方案&#xff1f;了解这两种方法的优缺点。 数据分析平台正在转向云环境&#xff0c;例如亚马逊网络服务、微软 Azure 和谷歌云。云环境提供了多种好处&#xff0c;例如可扩展性、可用性和可靠性。此外&#xff0…

SpringMVC bean加载控制 -- SpringMVC入门保姆级教程(二)

文章目录 前言二、SpringMVC bean 加载控制1.bean加载控制2.添加Spring开发环境3.SpringMVC bean加载控制4.SpringMVC bean控制相关知识点 总结 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类的博客&#xff0c;方便日后回顾。当然&#xff0c;如果能…

丝印设计~丝印内容、大小、距离、排列,位置

丝印设计 丝印的内容包括&#xff1a; a) PCB的名称/PCB的版本号 b) 元器件外形框 c) 元器件的序号 d) 元器件的极性和方向标志 e) 条码框 f) 插入PCB的名称&#xff08;母板&#xff09; g) 插针的位置序号 h) 安装孔位置代号 i) 元器件第1脚的位置代号 j) 过板方向 k) 光纤盘…

分治入门+例题

目录 &#x1f947;2.3.2 合并排序 &#x1f947;2.3.3 快速排序 &#x1f33c;P1010 [NOIP1998 普及组] 幂次方 &#x1f333;总结 形象点&#xff0c;分治正如“凡治众如治寡&#xff0c;分数是也”&#xff0c;管理少数几个人&#xff0c;即可统领全军 本质&#xff…

js数据类型和六种运算结果为false的情况

数据类型 number&#xff1a;数字&#xff08;整数、小数、NaN(Not a Number)&#xff09; string&#xff1a;字符串、单双引皆可 boolean&#xff1a;布尔。true、false null&#xff1a;对象为空 undefined&#xff1a;当声明的变量初始化时&#xff0c;该变量的默认值…

vuex五大核心、辅助函数

一、vuex五大核心 分别为&#xff1a;state、getters、mutations、actions、modules state&#xff1a;用来存放我们的状态&#xff0c;也可以理解数据&#xff0c;类似与data中定义的数据&#xff1b;mutations:可以获取state里面的状态&#xff0c;进行修改&#xff0c;他只…

Hadoop---10、生产调优手册

1、HDFS—核心参数 1.1 NameNode 内存生产配置 1、NameNode内存计算 每个文件块大概占用150byte&#xff0c;一台服务器128G内存为例&#xff0c;能储存多少文件块呢&#xff1f; 12810241024*1024/150Byte ≈ 9.1 亿 G M KB Byte 2、Hadoop2.x系列&#xff0c;配置 NameNode…

leetcode506.相对名次

题目描述跳转leetcode详情 给你一个长度为 n 的整数数组 score &#xff0c;其中 score[i] 是第 i 位运动员在比赛中的得分。所有得分都 互不相同 。 运动员将根据得分 决定名次 &#xff0c;其中名次第 1 的运动员得分最高&#xff0c;名次第 2 的运动员得分第 2 高&#xff0…

day1 -- 资料库管理系统DBMS

学习目标&#xff1a; 了解什么是Mysql 如何安装Mysql 学习内容&#xff1a; 资料库概念资料库管理系统概念资料库管理系统分类SQL是什么安装Mysql启动Mysql并建立一张测试表格 正文部分 资料库 比如你喜欢摘抄优美的词句&#xff0c;那些优美的词句就是一种文字资料&#xf…