【数据结构】循环链表

news2025/2/7 0:19:29

循环链表

  • 单链表局限性
  • 单向循环链表
  • 判断链表是否有环
    • 思路
    • code
  • 找到链表入口
    • 思路
      • 代码结构与逻辑
    • code

单链表局限性

单链表作为一种基本的数据结构,虽然在很多场景下都非常有用,但它也存在一些局限性:

  1. 单向访问:由于每个节点仅包含指向下一个节点的指针,因此只能从头节点开始按顺序向后遍历链表。这意味着如果要访问链表中距离头节点较远的节点,效率会比较低,时间复杂度为O(n),其中n是链表的长度。

  2. 不支持随机访问:与数组不同,不能通过索引直接访问元素。要在单链表中查找一个特定的元素,通常需要从头节点开始逐个检查节点,直到找到目标节点或到达链表末尾。

  3. 额外的空间开销:单链表中的每个节点除了存储实际数据外,还需要额外的空间来存储指向下一个节点的指针。这相对于连续存储的数据结构(如数组)来说,可能会导致更高的内存占用率。

  4. 插入和删除操作的复杂性:尽管在已知位置的情况下,单链表的插入和删除操作可以在O(1)时间内完成,但要找到这个位置却可能需要遍历链表,从而需要O(n)的时间。此外,进行这些操作时还需要小心处理边界情况,比如插入到链表头部或删除最后一个节点等。

  5. 缓存性能差:与连续存储的数据结构相比,链表的节点在内存中可能分散存储,这会影响CPU缓存的命中率,进而影响程序的整体性能。

综上所述,虽然单链表具有动态大小调整的优点,但在访问速度、空间利用率以及缓存友好性等方面存在一定的局限性。根据应用场景的不同,选择合适的数据结构非常重要。

单向循环链表

  • 循环链表(Circular Linked List)是另一种形式的链式存储结构。其特点是表中最后一个节点的指针域指向头节点,整个链表形成一个
  • 当链表遍历时,判别当前指针p是否指向表尾结点的终止条件不同。在单链表中,判别条件为 p!=NULLp->next!=NULL,而循环单链表的判别条件为p!=L p->next!=L

在这里插入图片描述

判断链表是否有环

在这里插入图片描述

思路

判断链表是否有环是一个经典的算法问题,通常有几种方法可以解决这个问题。最常用且效率较高的方法是“快慢指针”法(Floyd判圈算法),这种方法不仅能够判断链表中是否存在环,还能找到环的起点。以下是这种方法的基本思路:

快慢指针法

  1. 初始化两个指针:一个慢指针(slow)和一个快指针(fast)。慢指针每次只移动一步,而快指针每次移动两步。

  2. 同时移动两个指针遍历链表

    • 如果链表中存在环,那么快指针最终会追上慢指针(即两者相遇)。
    • 如果链表中不存在环,快指针将会首先到达链表的末尾(null),此时可以确定链表没有环。
  3. 判断是否相遇

    • 如果快指针和慢指针相遇,则表明链表中有环。
    • 如果快指针到达了链表的末尾(next为null),则说明链表没有环。

code

// 判断链表是否有环
int hasCycle(Node *head)
{
    // 定义快慢指针,初始都指向头节点
    Node *fast = head;
    Node *slow = head;
    // 当快指针和快指针的下一个节点不为空时,循环执行
    while (fast != NULL && fast->next != NULL)
    {
        // 快指针每次移动两步,慢指针每次移动一步
        fast = fast->next->next;
        slow = slow->next;
        // 如果快指针和慢指针相遇,说明链表有环,返回1
        if (fast == slow)
            return 1;
    }
    // 如果快指针和快指针的下一个节点为空,说明链表没有环,返回0
    return 0;
}

int main(int argc, char const *argv[])
{
    // 初始化链表
    Node *list = InitList();

    // 获取尾节点
    Node *tail = GetTail(list);

    tail = InsertTail(tail, 1);
    tail = InsertTail(tail, 2);
    tail = InsertTail(tail, 3);
    Node *three = tail; // 保存第三个节点的地址
    tail = InsertTail(tail, 4);
    tail = InsertTail(tail, 5);
    tail = InsertTail(tail, 6);
    tail = InsertTail(tail, 7);
    tail->next = three; // 将尾节点的next指向第三个节点,形成环

    if (hasCycle(list))
        printf("链表有环\n");
    else
        printf("链表没有环\n");

    printf("打印链表:\n");
    PrintList(list); // 遍历链表
    return 0;
}

/* 判断链表是否有环 */

找到链表入口

思路

您的代码实现了一个检测链表中环的存在并找到环入口的算法。下面是对您提供的代码的详细分析和解释:

代码结构与逻辑

  1. 初始检查

    • 首先检查链表是否为空或者只有一个节点(head == NULL || head->next == NULL),在这种情况下,直接返回NULL,因为这样的链表不可能有环。
  2. 快慢指针初始化

    • 定义两个指针:fastslow,均指向链表的头节点。这两个指针用于遍历链表,其中fast每次移动两步,而slow每次移动一步。
  3. 循环查找环

    • while循环中,通过移动fastslow来遍历链表。如果链表中存在环,那么fastslow最终会在环内某个位置相遇。
    • 如果fastfast->nextNULL,则表示链表没有环,函数返回NULL
  4. 确认环的存在

    • fast == slow时,表明链表中有环,跳出循环。
  5. 计算环的长度(非必要步骤):

    • 声明一个新的节点p,先指向fast,然后让p一直一定,当p再次指向相遇的节点时,说明环的长度已经计算完毕。
  6. 寻找环的入口

    • 接着,代码将fast重新指向链表头部,并让fast先走之前计算得到的环长度步数。
    • 然后,同时移动fastslow,每次各走一步,当它们相遇时的位置就是环的起始节点。

code

  • 思路:快慢指针,快指针每次走两步,慢指针每次走一步,如果快指针追上了慢指针,说明链表有环。
/**
 * @description: 判断链表是否有环
 * 思路:快慢指针,快指针每次走两步,慢指针每次走一步,如果快指针追上了慢指针,说明链表有环。
 */

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

typedef int ElemType; // 定义元素类型

typedef struct node // 定义节点类型
{
    ElemType data;
    struct node *next;
} Node;

/* 初始化一个单链表-造一个头节点 */
Node *InitList()
{
    Node *head = (Node *)malloc(sizeof(Node)); // 为头节点分配内存
    head->data = 0;                            // 头节点的数据域为0
    head->next = NULL;                         // 头节点的指针域为空
    return head;                               // 返回头节点
}

// 初始化节点(带节点数据域参数)
Node *InitListWithElem(ElemType e)
{
    Node *node = (Node *)malloc(sizeof(node)); // 为节点分配内存
    node->data = e;                            // 节点的数据域为e
    node->next = NULL;                         // 节点的指针域为空
    return node;                               // 返回节点
}

/*单链表 - 头插法*/
int InsertHead(Node *L, ElemType e)
{
    Node *p = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
    p->data = e;                            // 在新节点的数据域存入数据e
    p->next = L->next;                      // 新节点的指针域指向头节点的下一个节点(把L的NULL复制给新节点)
    L->next = p;                            // 头节点的指针域指向新节点
    return 1;                               // 返回1表示成功
}
/* 单链表 - 遍历 */
void TraverseList(Node *L)
{
    Node *p = L->next; // 从头节点的下一个节点开始遍历
    while (p != NULL)  // 遍历到链表末尾
    {
        printf("%d ", p->data); // 输出节点的数据域,这里是%d,因为ElemType是int类型
        p = p->next;            // 移动到下一个节点
    }
    printf("\n"); // 换行
}

/* 单链表 - 尾插法 */
// 获取尾节点地址
Node *GetTail(Node *List)
{
    Node *p = List;         // 从头节点开始遍历
    while (p->next != NULL) // 遍历到链表末尾
    {
        p = p->next; // 移动到下一个节点
    }
    return p; // 返回尾节点
}

/**
 * @Description:单链表 - 尾插法插入数据
 * @param {Node} *tail   尾节点
 * @param {ElemType} e   插入的数据
 * @return {*}           返回新的尾节点
 */
Node *InsertTail(Node *tail, ElemType e)
{
    Node *p = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
    p->data = e;                            // 在新节点的数据域存入数据e
    tail->next = p;                         // 尾节点的指针域指向新节点
    p->next = NULL;                         // 新节点的指针域为空
    return p;                               // 返回新的尾节点
}

/**
 * @Description:单链表 - 在链表尾部插入节点
 * @param {Node} *tail   链表尾部节点
 * @param {Node} *node   要插入的节点
 * @return {Node *}      插入节点后的链表尾部节点
 */
Node *InsertTailWithNode(Node *tail, Node *node)
{
    tail->next = node; // 尾节点的指针域指向要插入的节点
    node->next = NULL; // 要插入的节点的指针域为空
    return node;       // 返回新的尾节点
}

/**
 * @Description:单链表 - 在指定位置插入数据
 * @param {Node} *L     单链表的头节点
 * @param {int} pos     位置
 * @param {ElemType} e  插入的数据
 * @return {*}
 */
int InsertPosNode(Node *L, int pos, ElemType e)
{
    // 用来保存插入位置的前驱节点
    Node *p = L; // 从头节点开始遍历
    int i = 0;
    // 遍历链表-找到插入位置的前驱节点
    while (i < pos - 1) // 遍历到插入位置的前驱节点
    {
        p = p->next; // 移动到下一个节点
        i++;
        if (p == NULL) // 判断是否到达链表末尾
        {
            printf("插入位置不合法\n");
            return 0;
        }
    }

    Node *newnode = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
    newnode->data = e;                            // 在新节点的数据域存入数据e
    newnode->next = p->next;                      // 新节点的指针域指向插入位置的前驱节点的下一个节点
    p->next = newnode;                            // 插入位置的前驱节点的指针域指向新节点
    return 1;
}

/**
 * @Description:单链表 - 删除指定位置的节点
 * @param {Node} *L 单链表的头节点
 * @param {int} pos 位置
 * @return {*}       返回1表示成功
 */
int DeletePosNode(Node *L, int pos)
{
    // 用来保存删除位置的前驱节点
    Node *p = L; // 从头节点开始遍历
    int i = 0;
    // 遍历链表-找到删除节点的前驱节点
    while (i < pos - 1) // 遍历到删除位置的前驱节点
    {
        p = p->next; // 移动到下一个节点
        i++;
        if (p == NULL) // 判断是否到达链表末尾
        {
            printf("删除位置不合法\n");
            return 0;
        }
    }
    if (p->next == NULL) // 判断删除位置是否合法
    {
        printf("删除位置不合法\n");
        return 0;
    }
    Node *q = p->next; // 保存要删除的节点的地址
    p->next = q->next; // 删除节点的前驱节点的指针域 指向 删除节点的下一个节点
    free(q);           // 释放删除节点的内存

    return 1; // 返回1表示成功
}

int GetListLength(Node *L)
{
    int length = 0;
    Node *p = L; // 从头节点开始遍历,头节点算在内

    while (p != NULL)
    {
        p = p->next;
        length++;
    }
    return length;
}

void FreeList(Node *L)
{
    Node *p = L->next; // 从头节点的下一个节点开始遍历,头节点不需要释放
    Node *q = NULL;    // 用来保存下一个节点的地址,q能掌握下一个节点的地址,这是灵魂所在
    while (p != NULL)
    {
        q = p->next; // 保存下一个节点的地址
        free(p);     // 释放当前节点的内存
        p = q;       // 移动到下一个节点
    }
    L->next = NULL; // 头节点的指针域为空
}

// 查找倒数第k个节点
int findNodeFS(Node *L, int k)
{
    Node *fast = L->next;
    Node *slow = L->next;

    for (int i = 0; i < k; i++)
    {
        fast = fast->next;
    }

    while (fast != NULL)
    {
        fast = fast->next;
        slow = slow->next;
    }

    printf("倒数第%d个节点值为:%d\n", k, slow->data);
    return 1;
}

// 查找两个节点共同后缀的起始位置
Node *findIntersectionNode(Node *headA, Node *headB)
{
    if (headA == NULL || headB == NULL)
    {
        return NULL;
    }

    Node *p = headA;
    int lenA = 0;
    int lenB = 0;

    // 遍历链表A,获取链表A的长度
    while (p != NULL)
    {
        p = p->next;
        lenA++;
    }
    // 遍历链表B,获取链表B的长度
    p = headB;
    while (p != NULL)
    {
        p = p->next;
        lenB++;
    }

    Node *fast; // 快指针
    Node *slow; // 慢指针
    int step;   // 两个单词之间数量的差值,可以用于快指针先走的步数
    if (lenA > lenB)
    {
        step = lenA - lenB;
        fast = headA;
        slow = headB;
    }
    else
    {
        step = lenB - lenA;
        fast = headB;
        slow = headA;
    }
    // 让快指针先走step步
    for (int i = 0; i < step; i++)
    {
        fast = fast->next;
    }
    // 快慢指针同步走,直到指向同一个节点退出循环
    while (fast != slow)
    {
        fast = fast->next;
        slow = slow->next;
    }
    return fast;
}

// 函数:RemoveEqualNodes
// 功能:删除链表中与给定值相等的节点
// 参数:Node *L:链表头指针,int n:链表的长度
// 返回值:无
void RemoveEqualNodes(Node *L, int n)
{
    // TODO: 实现删除链表中与给定值相等的节点的功能
    Node *p = L;                                   // 定义一个指针p,指向链表的头节点
    int index;                                     // 定义一个变量index,作为数组下标使用
    int *q = (int *)malloc(sizeof(int) * (n + 1)); // 在堆内存中分配一个数组,用来存储已经出现过的绝对值

    /* 遍历数组,初始化为0 */
    for (int i = 0; i < n + 1; i++)
    {
        *(q + i) = 0; // 初始化为0,表示没有出现过这个绝对值
    }

    while (p->next != NULL)
    {
        // 获取绝对值
        index = abs(p->next->data); // 计算当前节点的绝对值,作为数组下标使用

        if (*(q + index) == 0) // 如果这个绝对值没有出现过
        {
            *(q + index) = 1; // 标记为已经出现过
            p = p->next;      // 移动到下一个节点
        }
        else // 如果这个绝对值已经出现过,删除当前节点
        {
            Node *tempNode = p->next; // 保存要删除的节点的地址
            p->next = tempNode->next; // 删除当前节点
            free(tempNode);           // 释放当前节点的内存
        }
    }
    free(q); // 释放数组的内存
}

/**
 * @description: 反转链表
 * @param {Node} *head  头节点
 * @return {*}          返回反转后的头节点
 * note:
 * 空指针检查:检查head是否为NULL,避免非法访问。
 * 直接操作原头节点:反转完成后,将原头节点的next指向反转后的首节点(prev),无需新建头节点。
 * 处理所有边界条件:链表为空(head->next为NULL)时,循环不会执行,直接返回head。
 *
 * 创建的三个节点是first,second,third 局部指针变量,不需要free释放内存
 * first->next 或 first->data 是通过指针访问节点的成员。
 * 直接写 first 表示操作指针本身(例如赋值或比较)。
 */
Node *ReverseList(Node *head)
{
    if (head == NULL)
    {
        return NULL; // 处理空头节点情况
    }
    Node *first = NULL;        // 定义一个指针first,指向空NULL,代表反转之后的尾
    Node *second = head->next; // 定义一个指针second,指向头节点的下一个节点,代表当前节点
    Node *third = NULL;        // 定义一个指针third

    while (second != NULL)
    {
        third = second->next; // 将third指向second的下一个节点,保存下一个节点的地址
        second->next = first; // 将当前节点的next指针指向first,实现反转
        first = second;       // 将first指向second,移动到下一个节点,指针的赋值操作
        second = third;       // 将second指向third,移动到下一个节点
    }

    head->next = first; // 头节点的next指针指向first,实现反转

    return head; // 返回新的头节点
}

int DeleteMidNode(Node *head)
{
    Node *fastNode = head->next; // 快指针,先走一步,后面每次走两步
    Node *slowNode = head;       // 慢指针,每次走一步
    /* 当快指针的下一个或者下一个的下一个是NULL,说明快指针已经走到了链表的末尾 */
    while (fastNode != NULL && fastNode->next != NULL) // 快指针走到链表末尾时,慢指针指向的就是链表中间节点
    {
        fastNode = fastNode->next->next; // 快指针每次走两步
        slowNode = slowNode->next;       // 慢指针每次走一步
    }
    // 删除中间节点
    Node *tempNode = slowNode->next; // 保存要删除的节点的地址
    slowNode->next = tempNode->next; // 删除当前节点
    free(tempNode);                  // free(tempNode) 释放的是 tempNode 所指向的内存,也就是被删除节点的堆内存
    return 1;                        // 删除成功返回1
}

/**
 * notes:问题:我自己创建了一个tempNode,然后free了tempNode,那么被删除的那个节点,没有被free,那么在内存里还存在被删除的节点吗?下面是deepseek的回答:
 * tempNode 的作用:tempNode 是一个指针,它指向的是 slowNode->next,也就是链表中要被删除的节点。tempNode 本身只是一个指针变量,它存储的是被删除节点的内存地址。
 * free(tempNode) 的作用:free(tempNode) 释放的是 tempNode 所指向的内存,也就是被删除节点的内存。free 并不会释放 tempNode 这个指针变量本身(指针变量是存储在栈上的),而是释放指针所指向的堆内存。
 * 被删除节点的内存状态:当你调用 free(tempNode) 后,被删除节点的内存会被操作系统标记为“可回收”,这意味着这块内存不再属于你的程序,操作系统可以将其重新分配给其他部分使用。因此,被删除的节点在内存中不再有效。
 *
 * 总结:tempNode 会随着函数结束自动销毁;被删除的节点是通过 free 函数释放的内存。
 */

// 重新排列链表
void reOrderList(Node *head)
{
    // TODO: 实现重新排列链表的功能
    Node *fast = head; // 快指针,不需要从head->next开始,因为要找到中间节点(偶数个节点时,中间节点是中间两个节点的前一个节点,奇数个节点时,中间节点是中间那个节点)
    Node *slow = head;
    while (fast != NULL && fast->next != NULL) // 快指针走到链表末尾时,慢指针指向的就是链表中间节点
    {
        fast = fast->next->next;
        slow = slow->next;
    }

    Node *first = NULL;        // 用来保存反转后的链表的头节点
    Node *second = slow->next; // 从中间节点开始反转
    Node *third = NULL;        // 用来保存下一个节点的地址
    slow->next = NULL;         // 中间节点的next指向NULL,从中间断开链表,分成两个链表,再合并两个链表

    while (second != NULL)
    {
        third = second->next; // 保存下一个节点的地址
        second->next = first; // 反转
        first = second;       // 移动到下一个节点
        second = third;       // 移动到下一个节点
    }

    // 合并两个链表
    Node *p1 = head->next; // 从头节点的下一个节点开始遍历
    Node *q1 = first;      // 从反转后的链表的头节点开始遍历
    Node *p2, *q2;
    while ((p1 != NULL) && (q1 != NULL)) // 当两个链表都没有遍历完时,交替合并两个链表
    {
        p2 = p1->next; // 保存p1的下一个节点的地址
        q2 = q1->next; // 保存q1的下一个节点的地址

        p1->next = q1; // 交替合并两个链表,p1和q1交替连接,p2和q2交替连接,直到有一个链表遍历完为止
        q1->next = p2; // 交替合并两个链表,p1和q1交替连接,p2和q2交替连接,直到有一个链表遍历完为止

        p1 = p2; // 移动到下一个节点
        q1 = q2; // 移动到下一个节点
    }
}

// 判断链表是否有环
int hasCycle(Node *head)
{
    // 定义快慢指针,初始都指向头节点
    int fast_count = 0;
    int slow_count = 0;
    Node *fast = head;
    Node *slow = head;
    // 当快指针和快指针的下一个节点不为空时,循环执行
    while (fast != NULL && fast->next != NULL)
    {
        // 快指针每次移动两步,慢指针每次移动一步
        fast = fast->next->next;
        slow = slow->next;

        fast_count += 2;
        slow_count++;
        // 如果快指针和慢指针相遇,说明链表有环,返回1
        if (fast == slow)
        {
            // printf("环的个数为:%d\n", (fast_count - slow_count)); // 计算环的个数
            return 1;
        }
    }
    // 如果快指针和快指针的下一个节点为空,说明链表没有环,返回0
    return 0;
}

// 找到链表环的入口
Node *FindCycleListEntry(Node *head)
{
    if (head == NULL || head->next == NULL)
        return NULL; // 链表为空或只有一个节点,肯定无环

    // 定义快慢指针,初始都指向头节点
    Node *fast = head;
    Node *slow = head;

    // 当快指针和快指针的下一个节点不为空时,循环执行
    while (fast != NULL && fast->next != NULL)
    {
        // 快指针每次移动两步,慢指针每次移动一步
        fast = fast->next->next;
        slow = slow->next;

        // 如果快指针和慢指针相遇,说明链表有环,返回1
        if (fast == slow)
        {
            break; // 跳出循环
        }
    }

    // 如果没有环,返回NULL
    if (fast == NULL || fast->next == NULL)
        return NULL; // 链表为空或只有一个节点,肯定无环

    // 计算环的长度
    int LoopLength = 0; // 环的长度
    Node *p = fast;     // 保存相遇的节点

    // 当p再次指向相遇的节点时,说明环的长度已经计算完毕
    do
    {
        LoopLength++;
        p = p->next; // 移动到下一个节点
    } while (p != fast);

    // 让快指针重新指向头节点,慢指针指向头节点
    fast = head;
    slow = head;
    // 让快指针先走环的长度步数
    for (int i = 0; i < LoopLength; i++)
    {
        fast = fast->next;
    }
    // 快慢指针同步走,直到指向同一个节点退出循环
    while (fast != slow)
    {
        fast = fast->next;
        slow = slow->next;
    }
    return fast; // 返回环的入口节点
}

int main()
{
    // 初始化链表
    Node *list = InitList();

    // 获取尾节点
    Node *tail = GetTail(list);

    tail = InsertTail(tail, 1);
    tail = InsertTail(tail, 2);
    tail = InsertTail(tail, 3);
    Node *CycleEntry = tail; // 保存环的入口节点
    tail = InsertTail(tail, 4);
    tail = InsertTail(tail, 5);
    tail = InsertTail(tail, 6);
    tail = InsertTail(tail, 7);
    tail = InsertTail(tail, 8);
    tail->next = CycleEntry; // 将尾节点的next指向环的入口节点,形成环

    Node *p = FindCycleListEntry(list);
    printf("环的入口节点为:%d\n", p->data); // 输出环的入口节点的值

    return 0;
}

/* 找到链表环的入口 */

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

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

相关文章

ImGui 学习笔记(二)—— 多视口

在计算机图形学中&#xff0c;视口&#xff08;Viewport&#xff09;是一个可观察的多边形区域。 将物体渲染至图像的过程中&#xff0c;会用两种区域表示。世界坐标窗口是用户所关注的区域&#xff08;即用户想要可视化的东西&#xff09;&#xff0c;坐标系由应用程序确定。…

安装和卸载RabbitMQ

我的飞书:https://rvg7rs2jk1g.feishu.cn/docx/SUWXdDb0UoCV86xP6b3c7qtMn6b 使用Ubuntu环境进行安装 一、安装Erlang 在安装RabbitMQ之前,我们需要先安装Erlang,RabbitMQ需要Erlang的语言支持 #安装Erlang sudo apt-get install erlang 在安装的过程中,会弹出一段信息,此…

Apache HttpClient

HttpClient是apache组织下面的一个用于处理HTTP请求和响应的来源工具&#xff0c;是一个在JDK基础类库是做了更好的封装的类库。 HttpClient 使用了连接池技术来管理 TCP 连接&#xff0c;这有助于提高性能并减少资源消耗。连接池允许 HttpClient 复用已经建立的连接&#xff0…

Golang 并发机制-6:掌握优雅的错误处理艺术

并发编程可能是提高软件系统效率和响应能力的一种强有力的技术。它允许多个工作负载同时运行&#xff0c;充分利用现代多核cpu。然而&#xff0c;巨大的能力带来巨大的责任&#xff0c;良好的错误管理是并发编程的主要任务之一。 并发代码的复杂性 并发编程增加了顺序程序所不…

react使用DatePicker日期选择器

1、引入&#xff1a;npm i day 2、页面引入&#xff1a; import dayjs from dayjs; 3、使用 <DatePicker onChange{onChange} value{datas ? dayjs(datas) : null} /> 4、事件 const onChange (date, dateString) > {setInput(dateString)setDatas(dateString)…

深度学习 Pytorch 基础网络手动搭建与快速实现

为了方便后续练习的展开&#xff0c;我们尝试自己创建一个数据生成器&#xff0c;用于自主生成一些符合某些条件、具备某些特性的数据集。 导入相关的包 # 随机模块 import random# 绘图模块 import matplotlib as mpl import matplotlib.pyplot as plt# 导入numpy import nu…

保姆级教程Docker部署KRaft模式的Kafka官方镜像

目录 一、安装Docker及可视化工具 二、单节点部署 1、创建挂载目录 2、运行Kafka容器 3、Compose运行Kafka容器 4、查看Kafka运行状态 三、集群部署 四、部署可视化工具 1、创建挂载目录 2、运行Kafka-ui容器 3、Compose运行Kafka-ui容器 4、查看Kafka-ui运行状态 …

51单片机看门狗系统

在 STC89C52 单片机中&#xff0c;看门狗控制寄存器的固定地址为 0xE1。此地址由芯片厂商在硬件设计时确定&#xff0c;但是它在头文件中并未给出&#xff0c;因此在使用看门狗系统时需要声明下这个特殊功能寄存器 sfr WDT_CONTR 0xE1; 本案将用一个小灯的工作状况来展示看门…

RNN/LSTM/GRU 学习笔记

文章目录 RNN/LSTM/GRU一、RNN1、为何引入RNN&#xff1f;2、RNN的基本结构3、各种形式的RNN及其应用4、RNN的缺陷5、如何应对RNN的缺陷&#xff1f;6、BPTT和BP的区别 二、LSTM1、LSTM 简介2、LSTM如何缓解梯度消失与梯度爆炸&#xff1f; 三、GRU四、参考文献 RNN/LSTM/GRU …

Android记事本App设计开发项目实战教程2025最新版Android Studio

平时上课录了个视频&#xff0c;从新建工程到打包Apk&#xff0c;从头做到尾&#xff0c;没有遗漏任何实现细节&#xff0c;欢迎学过Android基础的同学参加&#xff0c;如果你做过其他终端软件开发&#xff0c;也可以学习&#xff0c;快速上手Android基础开发。 Android记事本课…

【R语言】获取数据

R语言自带2种数据存储格式&#xff1a;*.RData和*.rds。 这两者的区别是&#xff1a;前者既可以存储数据&#xff0c;也可以存储当前工作空间中的所有变量&#xff0c;属于非标准化存储&#xff1b;后者仅用于存储单个R对象&#xff0c;且存储时可以创建标准化档案&#xff0c…

为什么在springboot中使用autowired的时候它黄色警告说不建议使用字段注入

byType找到多种实现类导致报错 Autowired: 通过byType 方式进行装配, 找不到或是找到多个&#xff0c;都会抛出异常 我们在单元测试中无法进行字段注入 字段注入通常是 private 修饰的&#xff0c;Spring 容器通过反射为这些字段注入依赖。然而&#xff0c;在单元测试中&…

Unity游戏(Assault空对地打击)开发(6) 鼠标光标的隐藏

前言 鼠标光标在游戏界面太碍眼了&#xff0c;要隐藏掉。 详细操作 新建一个脚本HideCursor&#xff0c;用于隐藏/取消隐藏光标。 写入以下代码。 意义&#xff1a;游戏开始自动隐藏光标&#xff0c;按Esc&#xff08;隐藏<-->显示&#xff09;。 using System.Collectio…

哪些专业跟FPGA有关?

FPGA产业作为近几年新兴的技术领域&#xff0c;薪资高、待遇好&#xff0c;吸引了大量的求职者。特别是对于毕业生&#xff0c;FPGA领域的岗位需求供不应求。那么&#xff0c;哪些专业和FPGA相关呢&#xff1f; 哪些专业跟FPGA有关&#xff1f; 微电子学与固体电子学、微电子科…

UE5 蓝图学习计划 - Day 14:搭建基础游戏场景

在上一节中&#xff0c;我们 确定了游戏类型&#xff0c;并完成了 项目搭建、角色蓝图的基础设置&#xff08;移动&#xff09;。今天&#xff0c;我们将进一步完善 游戏场景&#xff0c;搭建 地形、墙壁、机关、触发器 等基础元素&#xff0c;并添加角色跳跃功能&#xff0c;为…

ZooKeeper单节点详细部署流程

ZooKeeper单节点详细部署流程 文章目录 ZooKeeper单节点详细部署流程 一.下载稳定版本**ZooKeeper**二进制安装包二.安装并启动**ZooKeeper**1.安装**ZooKeeper**2.配置并启动**ZooKeeper** ZooKeeper 版本与 JDK 兼容性3.检查启动状态4.配置环境变量 三.可视化工具管理**Zooke…

Python----Python高级(并发编程:进程Process,多进程,进程间通信,进程同步,进程池)

一、进程Process 拥有自己独立的堆和栈&#xff0c;既不共享堆&#xff0c;也不共享栈&#xff0c;进程由操作系统调度&#xff1b;进程切换需要的资源很最大&#xff0c;效率低。 对于操作系统来说&#xff0c;一个任务就是一个进程&#xff08;Process&#xff09;&#xff…

ComfyUI安装调用DeepSeek——DeepSeek多模态之图形模型安装问题解决(ComfyUI-Janus-Pro)

ComfyUI 的 Janus-Pro 节点&#xff0c;一个统一的多模态理解和生成框架。 试用&#xff1a; https://huggingface.co/spaces/deepseek-ai/Janus-1.3B https://huggingface.co/spaces/deepseek-ai/Janus-Pro-7B https://huggingface.co/spaces/deepseek-ai/JanusFlow-1.3B 安装…

UE求职Demo开发日志#22 显示人物信息,完善装备的穿脱

1 创建一个人物信息显示的面板&#xff0c;方便测试 简单弄一下&#xff1a; UpdateInfo函数&#xff1a; 就是获取ASC后用属性更新&#xff0c;就不细看了 2 实现思路 在操作目标为装备栏&#xff0c;或者操作起点为装备栏时&#xff0c;交换前先判断能否交换&#xff08;只…

如何安装PHP依赖库 更新2025.2.3

要在PHP项目中安装依赖&#xff0c;首先需要确保你的系统已经安装了Composer。Composer是PHP的依赖管理工具&#xff0c;它允许你声明项目所需的库&#xff0c;并管理它们。以下是如何安装Composer和在PHP项目中安装依赖的步骤&#xff1a; 一. 安装Composer 对于Windows用户…