数据结构——(双)链表

文章目录

1. 定义

2. 双链表和单链表的区别

3. 代码示例

3.1 双链表节点和结构定义

3.2 初始化双链表

3.3 返回双链表的长度

3.4 在指定位置插入元素

3.5 在末尾插入元素

3.6 删除指定位置的元素并返回被删除的元素

3.7 删除末尾元素

3.8 获取指定位置的元素

3.9 修改指定位置的元素

3.10 释放双链表内存

4. 流程

5. 完整代码


1. 定义

双链表(Doubly Linked List)是一种链表数据结构,其中每个节点不仅包含一个数据域,还包含两个指针域,一个指向前驱节点,一个指向后继节点。相比单链表,双链表可以更方便地进行双向遍历,插入和删除操作也更高效。

双链表的定义和操作

  1. 节点的定义:每个节点包含数据部分和指向前一个节点和后一个节点的指针部分。
  2. 头节点和尾节点:链表的头节点是链表的起点,尾节点是链表的终点,通过头节点可以遍历整个链表。
  3. 双向链接:每个节点都有指向前一个节点和后一个节点的指针,因此可以双向遍历链表。
  4. 动态扩展:节点在内存中不必是连续的,可以动态分配和释放内存。

2. 双链表和单链表的区别

结构上的区别

  • 单链表

    • 每个节点包含一个数据域和一个指向下一个节点的指针。
    • 只有从头节点向下遍历的方向,没有指向前一个节点的指针。
    • 头节点没有前驱节点,尾节点的指针指向 NULL
  • 双链表

    • 每个节点包含一个数据域、一个指向下一个节点的指针和一个指向前一个节点的指针。
    • 可以从任一节点双向遍历,即可以向前遍历到头节点,也可以向后遍历到尾节点。
    • 头节点的前驱指针指向 NULL,尾节点的后继指针指向 NULL

单链表的优缺点

优点

  1. 简单性:实现和维护相对简单,节点只需要存储一个指针,内存开销小。
  2. 内存占用较少:每个节点只包含一个指针,因此内存占用较双链表少。
  3. 插入和删除操作效率较高:在已知前驱节点的情况下,插入和删除节点的时间复杂度为 O(1)。

缺点

  1. 查找效率较低:从头节点遍历到目标节点的时间复杂度为 O(n)。
  2. 只能单向遍历:不能从尾节点向前遍历到头节点,灵活性差。

双链表的优缺点

优点

  1. 双向遍历:可以从任一节点双向遍历,即可以向前遍历到头节点,也可以向后遍历到尾节点。
  2. 删除节点更方便:在已知节点本身的情况下,删除节点时不需要知道其前驱节点,因为可以通过节点本身直接访问其前驱节点和后继节点。
  3. 灵活性高:在某些情况下,双向遍历能够更方便地实现算法,比如在某些需要回溯的场景下。

缺点

  1. 内存占用较大:每个节点包含两个指针,内存占用较单链表多。
  2. 实现和维护相对复杂:双向链表的实现和维护比单链表更复杂,需要处理更多的指针操作。
  3. 插入和删除操作相对复杂:插入和删除操作需要同时更新前驱和后继节点的指针,操作较为繁琐。

3. 代码示例

3.1 双链表节点和结构定义

#include <stdio.h>
#include <stdlib.h>// 双链表节点结构定义
typedef struct DNode {int data;             // 节点存储的数据struct DNode *prev;   // 指向前一个节点的指针struct DNode *next;   // 指向后一个节点的指针
} DNode;// 双链表结构定义
typedef struct {DNode *head;  // 双链表头节点指针DNode *tail;  // 双链表尾节点指针size_t size;  // 双链表中的节点个数
} DoublyLinkedList;
  • DNode 结构体定义了双链表的节点,包含数据域 data 和指向前一个节点的指针 prev 以及指向后一个节点的指针 next
  • DoublyLinkedList 结构体定义了双链表,包含指向头节点的指针 head 和指向尾节点的指针 tail,以及链表中节点的个数 size

3.2 初始化双链表

// 初始化双链表
void initDoublyLinkedList(DoublyLinkedList *list) {list->head = NULL; // 初始化头节点为空list->tail = NULL; // 初始化尾节点为空list->size = 0;    // 初始化节点个数为0
}

  • initDoublyLinkedList 函数将双链表的头节点和尾节点设置为 NULL,并将节点个数初始化为 0。

3.3 返回双链表的长度

// 返回双链表的长度
size_t getLength(const DoublyLinkedList *list) {return list->size; // 返回双链表的节点个数
}
  •  getLength 函数返回双链表中的节点个数。

3.4 在指定位置插入元素

// 在指定位置插入元素
void insertAt(DoublyLinkedList *list, size_t index, int element) {// 检查插入位置是否有效if (index > list->size) {return; // 忽略无效的插入位置}// 创建新节点DNode *newNode = (DNode *)malloc(sizeof(DNode));newNode->data = element;newNode->prev = NULL;newNode->next = NULL;// 如果插入位置是头节点if (index == 0) {newNode->next = list->head;if (list->head != NULL) {list->head->prev = newNode;}list->head = newNode; // 新节点成为头节点if (list->tail == NULL) {list->tail = newNode; // 如果链表为空,新节点也是尾节点}} else if (index == list->size) {// 如果插入位置是尾节点newNode->prev = list->tail;if (list->tail != NULL) {list->tail->next = newNode;}list->tail = newNode; // 新节点成为尾节点if (list->head == NULL) {list->head = newNode; // 如果链表为空,新节点也是头节点}} else {// 找到插入位置的前一个节点DNode *current = list->head;for (size_t i = 0; i < index - 1; i++) {current = current->next;}// 插入新节点newNode->next = current->next;newNode->prev = current;if (current->next != NULL) {current->next->prev = newNode;}current->next = newNode;}list->size++; // 更新节点个数
}
  • insertAt 函数在双链表的指定位置插入一个新的元素。如果插入位置是 0,则新节点成为头节点。如果插入位置是链表的末尾,则新节点成为尾节点。否则,通过遍历找到插入位置的前一个节点,并在该位置插入新节点。更新链表的节点个数。

3.5 在末尾插入元素

// 在末尾插入元素
void insertEnd(DoublyLinkedList *list, int element) {insertAt(list, list->size, element); // 在链表末尾插入元素
}
  • insertEnd 函数在双链表末尾插入一个新的元素,通过调用 insertAt 函数实现。

3.6 删除指定位置的元素并返回被删除的元素

// 删除指定位置的元素并返回被删除的元素
int deleteAt(DoublyLinkedList *list, size_t index) {// 检查删除位置是否有效if (index >= list->size) {return -1; // 忽略无效的删除位置}int deletedElement;// 如果删除位置是头节点if (index == 0) {DNode *temp = list->head;list->head = temp->next;if (list->head != NULL) {list->head->prev = NULL;} else {list->tail = NULL; // 如果链表只有一个节点}deletedElement = temp->data;free(temp); // 释放被删除节点的内存} else if (index == list->size - 1) {// 如果删除位置是尾节点DNode *temp = list->tail;list->tail = temp->prev;if (list->tail != NULL) {list->tail->next = NULL;} else {list->head = NULL; // 如果链表只有一个节点}deletedElement = temp->data;free(temp); // 释放被删除节点的内存} else {// 找到删除位置的前一个节点DNode *current = list->head;for (size_t i = 0; i < index - 1; i++) {current = current->next;}// 删除节点DNode *temp = current->next;current->next = temp->next;if (temp->next != NULL) {temp->next->prev = current;}deletedElement = temp->data;free(temp); // 释放被删除节点的内存}list->size--; // 更新节点个数return deletedElement;
}
  • deleteAt 函数删除双链表中指定位置的节点,并返回该节点的值。如果删除位置是 0,则直接删除头节点。如果删除位置是链表的末尾,则删除尾节点。否则,通过遍历找到删除位置的前一个节点,并删除该位置的节点。更新链表的节点个数。

3.7 删除末尾元素

// 删除末尾元素并返回被删除的元素
int deleteEnd(DoublyLinkedList *list) {return deleteAt(list, list->size - 1); // 删除链表末尾的元素
}
  • deleteEnd 函数删除双链表末尾的节点,通过调用 deleteAt 函数实现。

3.8 获取指定位置的元素

// 获取指定位置的元素
int getElementAt(const DoublyLinkedList *list, size_t index) {// 检查索引位置是否有效if (index >= list->size) {return -1; // 返回无效的索引}// 遍历找到指定位置的节点DNode *currentNode = list->head;for (size_t i = 0; i < index; i++) {currentNode = currentNode->next;}return currentNode->data; // 返回指定位置的元素
}
  • getElementAt 函数返回双链表中指定位置的元素。通过遍历找到指定位置的节点,并返回该节点的值。

3.9 修改指定位置的元素

// 修改指定位置的元素
void modifyAt(DoublyLinkedList *list, size_t index, int newValue) {// 检查修改位置是否有效if (index >= list->size) {return; // 忽略无效的修改位置}// 遍历找到指定位置的节点DNode *currentNode = list->head;for (size_t i = 0; i < index; i++) {currentNode = currentNode->next;}currentNode->data = newValue; // 修改节点的值
}
  • modifyAt 函数修改双链表中指定位置的元素值。通过遍历找到指定位置的节点,并修改该节点的值。

3.10 释放双链表内存

// 释放双链表内存
void destroyDoublyLinkedList(DoublyLinkedList *list) {// 遍历释放每个节点的内存DNode *currentNode = list->head;while (currentNode != NULL) {DNode *temp = currentNode;currentNode = currentNode->next;free(temp); // 释放每个节点的内存}list->head = NULL; // 头节点置为空list->tail = NULL; // 尾节点置为空list->size = 0;    // 节点个数置为0
}
  • destroyDoublyLinkedList 函数释放双链表使用的内存。通过遍历链表,逐个释放每个节点的内存。将头节点和尾节点指针置为空,节点个数置为 0。

4. 流程

初始化双链表

  • 判断链表是否为空。
  • 如果为空,设置头指针和尾指针为NULL。
  • 如果不为空,创建头节点和尾节点。

插入节点

  • 判断插入位置是在头部、尾部还是中间。
  • 如果是头部,设置新节点为头节点并更新头节点的前驱指针。
  • 如果是尾部,设置新节点为尾节点并更新尾节点的后继指针。
  • 如果是在中间,找到插入位置前一个节点,设置新节点的前驱和后继指针,并更新前一个节点和后一个节点的指针。
  • 更新链表长度。

删除节点

  • 判断删除位置是在头部、尾部还是中间。
  • 如果是头部,找到头节点并更新头节点指针。
  • 如果是尾部,找到尾节点并更新尾节点指针。
  • 如果是在中间,找到要删除节点的前一个节点,更新前一个节点和后一个节点的指针。
  • 释放被删除节点的内存。
  • 更新链表长度。

遍历链表

  • 从头节点开始。
  • 判断当前节点是否为NULL。
  • 如果不为NULL,访问节点数据并移动到下一个节点,重复判断。
  • 如果为NULL,遍历结束。

修改节点数据

  • 找到目标节点。
  • 更新节点数据。

 

5. 完整代码

代码包括双链表的基本操作,包括初始化、插入、删除、获取和修改元素,以及释放链表的内存.

#include <stdio.h>
#include <stdlib.h>// 双链表节点结构定义
typedef struct DNode {int data;           // 节点存储的数据struct DNode *prev; // 指向前一个节点的指针struct DNode *next; // 指向后一个节点的指针
} DNode;// 双链表结构定义
typedef struct {DNode *head; // 双链表头节点指针DNode *tail; // 双链表尾节点指针size_t size; // 双链表中的节点个数
} DoublyLinkedList;// 初始化双链表
void initDoublyLinkedList(DoublyLinkedList *list) {list->head = NULL; // 初始化头节点为空list->tail = NULL; // 初始化尾节点为空list->size = 0;    // 初始化节点个数为0
}// 返回双链表的长度
size_t getLength(const DoublyLinkedList *list) {return list->size; // 返回双链表的节点个数
}// 在指定位置插入元素
void insertAt(DoublyLinkedList *list, size_t index, int element) {if (index > list->size) {return; // 忽略无效的插入位置}DNode *newNode = (DNode *)malloc(sizeof(DNode)); // 创建新节点newNode->data = element;newNode->prev = NULL;newNode->next = NULL;if (index == 0) { // 插入位置是头节点newNode->next = list->head;if (list->head != NULL) {list->head->prev = newNode;}list->head = newNode;if (list->tail == NULL) {list->tail = newNode;}} else if (index == list->size) { // 插入位置是尾节点newNode->prev = list->tail;if (list->tail != NULL) {list->tail->next = newNode;}list->tail = newNode;if (list->head == NULL) {list->head = newNode;}} else {DNode *current = list->head;for (size_t i = 0; i < index - 1; i++) {current = current->next;}newNode->next = current->next;newNode->prev = current;if (current->next != NULL) {current->next->prev = newNode;}current->next = newNode;}list->size++; // 更新节点个数
}// 在末尾插入元素
void insertEnd(DoublyLinkedList *list, int element) {insertAt(list, list->size, element); // 在链表末尾插入元素
}// 删除指定位置的元素并返回被删除的元素
int deleteAt(DoublyLinkedList *list, size_t index) {if (index >= list->size) {return -1; // 忽略无效的删除位置}int deletedElement;if (index == 0) { // 删除位置是头节点DNode *temp = list->head;list->head = temp->next;if (list->head != NULL) {list->head->prev = NULL;} else {list->tail = NULL; // 如果链表只有一个节点}deletedElement = temp->data;free(temp); // 释放被删除节点的内存} else if (index == list->size - 1) { // 删除位置是尾节点DNode *temp = list->tail;list->tail = temp->prev;if (list->tail != NULL) {list->tail->next = NULL;} else {list->head = NULL; // 如果链表只有一个节点}deletedElement = temp->data;free(temp); // 释放被删除节点的内存} else {DNode *current = list->head;for (size_t i = 0; i < index - 1; i++) {current = current->next;}DNode *temp = current->next;current->next = temp->next;if (temp->next != NULL) {temp->next->prev = current;}deletedElement = temp->data;free(temp); // 释放被删除节点的内存}list->size--; // 更新节点个数return deletedElement;
}// 删除末尾元素并返回被删除的元素
int deleteEnd(DoublyLinkedList *list) {return deleteAt(list, list->size - 1); // 删除链表末尾的元素
}// 获取指定位置的元素
int getElementAt(const DoublyLinkedList *list, size_t index) {if (index >= list->size) {return -1; // 返回无效的索引}DNode *currentNode = list->head;for (size_t i = 0; i < index; i++) {currentNode = currentNode->next;}return currentNode->data; // 返回指定位置的元素
}// 修改指定位置的元素
void modifyAt(DoublyLinkedList *list, size_t index, int newValue) {if (index >= list->size) {return; // 忽略无效的修改位置}DNode *currentNode = list->head;for (size_t i = 0; i < index; i++) {currentNode = currentNode->next;}currentNode->data = newValue; // 修改节点的值
}// 释放双链表内存
void destroyDoublyLinkedList(DoublyLinkedList *list) {DNode *currentNode = list->head;while (currentNode != NULL) {DNode *temp = currentNode;currentNode = currentNode->next;free(temp); // 释放每个节点的内存}list->head = NULL; // 头节点置为空list->tail = NULL; // 尾节点置为空list->size = 0;    // 节点个数置为0
}// 打印双链表中的所有元素
void printDoublyLinkedList(const DoublyLinkedList *list) {DNode *currentNode = list->head;while (currentNode != NULL) {printf("%d ", currentNode->data);currentNode = currentNode->next;}printf("\n");
}// 主函数测试双链表操作
int main() {DoublyLinkedList myList; // 声明双链表initDoublyLinkedList(&myList); // 初始化双链表printf("初始化双链表成功!\n");insertEnd(&myList, 1); // 链表尾部插入元素1insertEnd(&myList, 2); // 链表尾部插入元素2insertEnd(&myList, 3); // 链表尾部插入元素3printf("向双链表插入了3个元素\n");printDoublyLinkedList(&myList); // 打印链表中的元素printf("双链表长度为: %zu\n", getLength(&myList)); // 获取双链表长度insertAt(&myList, 1, 4); // 在索引1处插入元素4printf("在索引1处插入元素4\n");printDoublyLinkedList(&myList); // 打印链表中的元素printf("双链表长度为: %zu\n", getLength(&myList)); // 再次获取双链表长度printf("索引1处的元素为: %d\n", getElementAt(&myList, 1)); // 获取索引1处的元素modifyAt(&myList, 0, 5); // 修改索引0处的元素printf("把索引0处的元素修改为5\n");printDoublyLinkedList(&myList); // 打印链表中的元素printf("删除索引1处的元素,该元素值是: %d\n", deleteAt(&myList, 1)); // 删除索引1处的元素printDoublyLinkedList(&myList); // 打印链表中的元素destroyDoublyLinkedList(&myList); // 销毁双链表printf("双链表销毁成功!\n");return 0;
}
  • 初始化双链表initDoublyLinkedList 函数初始化双链表,将头节点和尾节点设置为 NULL,节点个数初始化为 0。
  • 返回双链表的长度getLength 函数返回双链表中的节点个数。
  • 在指定位置插入元素insertAt 函数在双链表的指定位置插入一个新节点,根据插入位置调整前后节点的指针。
  • 在末尾插入元素insertEnd 函数在双链表末尾插入一个新节点,调用 insertAt 函数实现。
  • 删除指定位置的元素deleteAt 函数删除双链表中指定位置的节点,并返回该节点的值,根据删除位置调整前后节点的指针。
  • 删除末尾元素deleteEnd 函数删除双链表末尾的节点,调用 deleteAt 函数实现。
  • 获取指定位置的元素getElementAt 函数返回双链表中指定位置的元素,通过遍历找到目标节点。
  • 修改指定位置的元素modifyAt 函数修改双链表中指定位置的节点的值,通过遍历找到目标节点并更新其数据。
  • 释放双链表内存destroyDoublyLinkedList 函数释放双链表中的所有节点内存,并将链表恢复到初始状态。
  • 打印双链表中的所有元素printDoublyLinkedList 函数遍历双链表并打印每个节点的数据。

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

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

相关文章

AI网络爬虫019:搜狗图片的时间戳反爬虫应对策略

文章目录 一、介绍二、输入内容三、输出内容一、介绍 如何批量爬取下载搜狗图片搜索结果页面的图片?以孙允珠这个关键词的搜索结果为例: https://pic.sogou.com/pics? 翻页规律如下: https://pic.sogou.com/napi/pc/searchList?mode=2&start=384&xml_len=48&am…

C语言作业7 指针实现strlen,strcpy,strcmp和strstr功能

1、自定义函数(my_strlen)实现strlen函数的功能 2、自定义函数(my_strcpy)实现strcpy函数的功能 3、自定义函数(my_strcmp)实现strcmp函数的功能 4、自定义函数(my_strcat)实现strcat函数的功能 5、自定义函数(my_strstr)实现求src字符串中是否包含子串dest字符串 #include &l…

【C++】入门基础(一)

目录 一.命名空间&#xff1a;namespace 1.namespace的价值 2.namespace的定义 3.namespace的使用方法 3.1 域解析运算符:: 3.2 using展开 3.3 using域解析运算符 二.输入输出 三.缺省参数 四.函数重载 1.参数类型不同 2.参数个数不同 3.参数顺序不同 一.命名空间&…

开源项目的认识理解

目录 开源项目有哪些机遇与挑战&#xff1f; 1.开源项目的发展趋势 2.开源的经验分享&#xff08;向大佬请教与上网查询&#xff09; 3.开源项目的挑战 开源项目有哪些机遇与挑战&#xff1f; 1.开源项目的发展趋势 1. 持续增长与普及 - 开源项目将继续增长&#xff0c…

从小主机到第一台自组装NAS:升级与优化记录

目录 前言硬件系统安装的波折过程问题解决系统安装 套件/dockerjellyfin 功耗测试刚安装好系统插上缓存盘且运行了更多的套件和 docker 容器之后 温度场景一场景二场景三 后记参考 在使用了一年的小主机 NAS 后&#xff0c;我决定自己组装并安装新 NAS。本文详细记录了硬件选择…

烟雾自动监测报警摄像机

当今社会&#xff0c;安全意识日益增强&#xff0c;各种智能监测技术也在不断创新发展。烟雾自动监测报警摄像机作为其中的一种重要应用&#xff0c;正在为人们的生活和财产安全提供更加全面的保护。烟雾自动监测报警摄像机集成了先进的传感器技术和智能算法&#xff0c;能够高…

图像搜索技术在司法证据分析中的应用:思通数科大模型的创新实践

引言 在司法侦查过程中&#xff0c;图像和视频证据的分析对于案件的侦破至关重要。随着人工智能技术的快速发展&#xff0c;图像搜索技术已成为司法领域的关键工具。本文将探讨如何结合思通数科的大模型&#xff0c;利用图像搜索技术对案件中的图片或视频证据进行深度分析&…

同时用到,网页,java程序,数据库的web小应用

具体实现功能&#xff1a;通过网页传输添加用户的请求&#xff0c;需要通过JDBC来向 MySql 添加一个用户数据 第一步&#xff0c;部署所有需要用到的工具 IDEA(2021.1),Tomcat(9),谷歌浏览器&#xff0c;MySql,jdk(17) 第二步&#xff0c;创建java项目&#xff0c;提前部署数…

ADC 性能规格-静态性能- (2) - 偏移误差( offset error)和满标度增益误差(full scale gain error)

偏移误差(Offset error) 失调(Offset) 定义:失调是指ADC输出数字代码中零位与实际模拟输入零位之间的差异。简单来说,就是当输入信号为零时,ADC输出的数字代码并不一定是零,这个偏差就是失调。影响:失调会影响ADC的整体精度,因为它在整个输入范围内引入了一个固定的偏…

智慧水利引领行业转型:探讨智慧水利解决方案在水务管理、灾害预警及水资源保护中的前沿应用与挑战

本文关键词&#xff1a;智慧水利、智慧水利工程、智慧水利发展前景、智慧水利技术、智慧水利信息化系统、智慧水利解决方案、数字水利和智慧水利、数字水利工程、数字水利建设、数字水利概念、人水和协、智慧水库、智慧水库管理平台、智慧水库建设方案、智慧水库解决方案、智慧…

ExcelToDB2:批量导入Excel到IBM DB2数据库的自动化工具

ExcelToDB2&#xff1a;批量导入Excel到IBM DB2数据库的自动化工具 简介 ExcelToDB2是一个可以批量导入Excel到IBM DB2数据库的自动化工具。支持将xls/xlsx/xlsm/xlsb/csv/txt/xml格式的Excel文件导入到IBM DB2等多种原生及国产数据库。自动化是其最大的特点&#xff0c;因为它…

MVPT: Multitask Vision-Language Prompt Tuning

摘要 提示调整(Prompt Tuning)是一种针对特定任务的学习提示向量的调节&#xff0c;已成为一种数据高效和参数高效的方法&#xff0c;用于使大型预训练的视觉语言模型适应多个下游任务。然而&#xff0c;现有的方法通常是从头开始独立地学习每个任务的提示向量&#xff0c;从而…

docker-compose安装PolarDB-PG数据库

文章目录 一. Mac1.1 docker-compose.yaml1.2 部署1.3 卸载4. 连接 二. Win102.1 docker-compose.yaml2.2 部署2.3 卸载 参考官方文档 基于单机文件系统部署 一. Mac 1.1 docker-compose.yaml mkdir -p /Users/wanfei/docker-compose/polardb-pg && cd /Users/wanfei…

开放式耳机哪款性价比高?这五款超值精品不容错过

喜欢进行户外运动的小伙伴们&#xff0c;应该都很需要一款既可以匹配运动场景&#xff0c;又兼顾音质体验的无线蓝牙耳机吧。而开放式耳机拥有佩戴舒适牢固&#xff0c;不堵塞耳部&#xff0c;不影响外部声音传入耳部的优点&#xff0c;完全可以成为运动健身人士户外运动的好伴…

【JavaScript 算法】深度优先搜索:探索所有可能的路径

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、算法原理二、算法实现三、应用场景四、优化与扩展五、总结 深度优先搜索&#xff08;Depth-First Search, DFS&#xff09;是一种用于遍历或搜索图或树数据结构的算法。该算法尽可能深入图的分支&#xff0c;探索所有可…

Qt图片缩放显示

在Qt中&#xff0c;如果你想显示图片的像素或者对图片进行缩放显示&#xff0c;可以使用 QImage 类来处理图片数据&#xff0c;并使用 QLabel 或自定义的 QWidget 来显示图片&#xff0c;但是很难通过鼠标进行缩放显示 QGraphicsView可以实现此功能 在Qt中&#xff0c;QGraphi…

《Windows API每日一练》9.2.1 菜单

■和菜单有关的概念 窗口的菜单栏紧挨着标题栏下面显示。这个菜单栏有时叫作程序的“主菜单”或“顶级菜单“&#xff08;top-level menu&#xff09;。顶级菜单中的菜单项通常会激活下拉菜单&#xff08;drop-downmenu&#xff09;&#xff0c;也 叫“弹出菜单”&#xff08;…

流程图怎么做?有三种制作方法

流程图怎么做&#xff1f;在日常生活和工作中&#xff0c;流程图作为一种直观展示步骤、流程或决策路径的工具&#xff0c;扮演着不可或缺的角色。它不仅能够帮助我们理清思路、规划任务&#xff0c;还能促进团队协作与沟通。那么&#xff0c;如何高效地绘制流程图呢&#xff1…

2024年最新PyCharm保姆级安装教程

PyCharm是一款专为Python开发者设计的集成开发环境&#xff08;IDE&#xff09;&#xff0c;旨在帮助用户在使用Python语言开发时提高效率。 PyCharm作为一款强大的Python IDE&#xff0c;其主要作用在于提供了一整套可以帮助Python开发者提高开发效率的工具。这些工具包括但不…

2024 /7/14 H3U与MD600Modbus通讯应用指导

目录 步骤一&#xff1a;硬件接线 步骤二&#xff1a;变频器参数设置 步骤三&#xff1a;软件PLC程序配置 注意事项&#xff1a; 步骤一&#xff1a;硬件接线 PLC侧485端子 MD600变频器侧485端子 …