C/C++每日一练:合并K个有序链表

        本篇博客将探讨如何 “合并K个有序链表” 这一经典问题。本文将从题目要求、解题思路、过程解析和相关知识点逐步展开,同时提供详细注释的代码示例。


链表(Linked List)

         链表是一种线性数据结构,由一系列节点(Node)通过指针链接在一起。与数组不同,链表中的元素在内存中不需要连续存储,每个节点包含两部分:

  • 数据部分:存储节点的值或数据。
  • 指针部分:存储指向下一个节点的地址(单链表)或上一个和下一个节点的地址(双向链表)。

         链表的类型主要有以下几种:

  • 单链表:每个节点只指向下一个节点。
  • 双向链表:每个节点既有指向下一个节点的指针,也有指向上一个节点的指针。
  • 循环链表:链表的最后一个节点指向链表的头节点,形成循环。

         链表的特点:

  • 动态大小:可以根据需要动态地增加或减少元素,无需预分配存储空间。
  • 插入/删除效率高:在链表的任意位置进行插入或删除操作只需修改指针,不涉及大量元素的移动,效率较高。
  • 随机访问效率低:由于链表不支持直接访问任意位置的元素,需要通过遍历来查找特定位置的节点。

         如下图所示:


题目要求

        给定 k 个有序的链表,要求将它们合并成一个有序链表,并返回最终的结果链表。

输入

  • 一个包含 k 个链表的数组,其中每个链表均已按升序排列。

输出

  • 一个合并后的链表,且链表内元素按升序排列。

约束

  1. k >= 1
  2. 每个链表的长度可能不同,且长度总和不超过 10^5。
  3. 链表节点的值范围在 [-10^4, 10^4]。

解题思路

方法一:逐一合并

  • 将所有链表两两合并。
  • 时间复杂度为 O(k⋅n),其中 n 为平均每个链表的长度。

方法二:使用最小堆(优先队列)

  • 利用最小堆维护每个链表当前节点的最小值,逐步提取并加入结果链表。
  • 时间复杂度为 O(n⋅log⁡k)。

方法三:分治合并

  • 将 k 个链表两两分组合并。
  • 类似归并排序,时间复杂度为 O(n⋅log⁡k)。

以下主要以 方法二:使用最小堆方法三:分治合并 为例进行解析。


过程解析

方法一:逐一合并

思路
         将第一个链表作为初始结果链表,然后逐个将剩余的链表与当前结果链表进行合并,直到所有链表合并完毕。

实现步骤

  1. 初始化结果链表为第一个链表。
  2. 遍历链表数组,调用合并两个链表的函数,将当前链表与结果链表合并。
  3. 返回最终结果链表。

优点

  • 实现简单,逻辑清晰,适合入门时使用。

缺点

  • 效率较低,尤其在链表数量较多或链表长度较大时。

方法二:使用最小堆

思路
        利用最小堆(优先队列)维护链表当前节点的最小值,每次从堆中提取最小值并将其加入结果链表,同时推进对应链表的指针。

实现步骤

  1. 创建一个最小堆,将所有链表的头节点加入堆中。
  2. 反复提取堆顶最小值,将其加入结果链表。
  3. 如果提取的节点有下一个节点,将下一个节点加入堆中。
  4. 堆为空时,所有节点已处理完,返回结果链表。

优点

  • 在链表数量较多时表现优秀。
  • 保证结果链表的构建是高效的。

缺点

  • 实现稍复杂,需要熟悉堆的数据结构。

方法三:分治合并

思路
        通过分治思想,将链表分成两组,递归地合并每组链表,直到最终只剩一个合并后的链表。

实现步骤

  1. 将链表数组两两分组,递归合并每组链表。
  2. 每次合并两个链表使用合并两个有序链表的逻辑。
  3. 最终返回唯一的合并链表。

优点

  • 效率高,适合大规模链表数量。
  • 思路清晰,类似归并排序的合并逻辑。

缺点

  • 实现需要递归,可能在栈深度受限的系统中受到限制。

三种方法的比较

方法时间复杂度空间复杂度适用场景实现复杂度
逐一合并O(k⋅n)O(k \cdot n)O(k⋅n)O(n)O(n)O(n)链表数量较少时简单
使用最小堆O(n⋅log⁡k)O(n \cdot \log k)O(n⋅logk)O(k)O(k)O(k)链表数量较多时中等
分治合并O(n⋅log⁡k)O(n \cdot \log k)O(n⋅logk)O(log⁡k)O(\log k)O(logk)大规模链表合并中等
  • 如果链表数量很少,逐一合并的实现最简单,适合初学者练习。
  • 如果链表数量较多且长度较短,最小堆法表现较优。
  • 如果链表数量和长度都较多,分治合并法综合性能最好。

相关知识点

  1. 链表操作

    • 基本操作:插入、删除、遍历。
    • 注意指针的动态分配与释放。
  2. 优先队列

    • C++ STL 中的std::priority_queue。
    • 自定义排序方式。
  3. 分治策略

    • 递归与归并思想。

示例代码

C代码实现:逐一合并

#include <stdio.h>  // 包含标准输入输出头文件,提供 printf、scanf 等函数
#include <stdlib.h> // 包含动态内存分配和其他实用功能函数,如 malloc 和 free

// 定义链表节点结构
typedef struct ListNode {
    int val;               // 节点的值
    struct ListNode* next; // 指向下一个节点的指针
} ListNode;

// 合并两个有序链表的函数
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    // 如果 l1 为空,直接返回 l2
    if (!l1) return l2;
    // 如果 l2 为空,直接返回 l1
    if (!l2) return l1;

    // 比较两个链表头节点的值,递归合并较小值的后续节点
    if (l1->val < l2->val) {
        l1->next = mergeTwoLists(l1->next, l2); // l1 较小,递归处理 l1->next
        return l1;                              // 返回 l1 作为新的头节点
    } else {
        l2->next = mergeTwoLists(l1, l2->next); // l2 较小,递归处理 l2->next
        return l2;                              // 返回 l2 作为新的头节点
    }
}

// 合并 K 个有序链表的函数
ListNode* mergeKLists(ListNode** lists, int k) {
    // 如果链表数组为空,直接返回 NULL
    if (k == 0) return NULL;
    // 初始化结果链表为第一个链表
    ListNode* mergedList = lists[0];

    // 遍历链表数组,从第二个链表开始逐一合并
    for (int i = 1; i < k; ++i) {
        mergedList = mergeTwoLists(mergedList, lists[i]); // 合并当前结果链表和下一个链表
    }

    // 返回最终合并完成的结果链表
    return mergedList;
}

// 辅助函数:打印链表
void printList(ListNode* head) {
    // 遍历链表,打印每个节点的值
    while (head) {
        printf("%d -> ", head->val); // 输出当前节点的值
        head = head->next;          // 移动到下一个节点
    }
    printf("NULL\n"); // 链表结束后打印 NULL
}

// 辅助函数:创建链表节点
ListNode* createNode(int val) {
    // 动态分配内存为新节点
    ListNode* node = (ListNode*)malloc(sizeof(ListNode));
    node->val = val;   // 设置节点值
    node->next = NULL; // 初始化下一个节点指针为空
    return node;       // 返回新节点的指针
}

// 主函数测试
int main() 
{
    // 构建测试链表 1:1 -> 4 -> 5
    ListNode* list1 = createNode(1);
    list1->next = createNode(4);
    list1->next->next = createNode(5);

    // 构建测试链表 2:1 -> 3 -> 4
    ListNode* list2 = createNode(1);
    list2->next = createNode(3);
    list2->next->next = createNode(4);

    // 构建测试链表 3:2 -> 6
    ListNode* list3 = createNode(2);
    list3->next = createNode(6);

    // 创建链表数组存储所有链表
    ListNode* lists[] = {list1, list2, list3};

    // 合并链表
    ListNode* mergedList = mergeKLists(lists, 3);

    // 输出结果链表
    printf("Merged List: ");
    printList(mergedList);

    return 0; // 程序正常结束
}

C代码实现:最小堆解法

 示例代码

#include <stdio.h>  // 包含标准输入输出头文件
#include <stdlib.h> // 包含动态内存分配函数

// 定义链表节点结构
typedef struct ListNode {
    int val;               // 节点值
    struct ListNode* next; // 指向下一个节点的指针
} ListNode;

// 定义小顶堆结构
typedef struct MinHeap {
    ListNode** array; // 存储链表节点的指针数组
    int size;         // 当前堆的大小
    int capacity;     // 堆的容量
} MinHeap;

// 创建小顶堆
MinHeap* createMinHeap(int capacity) {
    // 分配堆结构和数组的内存
    MinHeap* heap = (MinHeap*)malloc(sizeof(MinHeap));
    heap->array = (ListNode**)malloc(sizeof(ListNode*) * capacity);
    heap->size = 0;       // 初始化堆的大小为 0
    heap->capacity = capacity; // 设置堆容量
    return heap;          // 返回堆的指针
}

// 交换两个链表节点的指针
void swap(ListNode** a, ListNode** b) {
    ListNode* temp = *a;
    *a = *b;
    *b = temp;
}

// 堆化调整函数(维护堆的性质)
void heapify(MinHeap* heap, int i) {
    int smallest = i;            // 假设当前节点为最小值
    int left = 2 * i + 1;        // 左子节点索引
    int right = 2 * i + 2;       // 右子节点索引

    // 如果左子节点更小,更新最小值索引
    if (left < heap->size && heap->array[left]->val < heap->array[smallest]->val)
        smallest = left;

    // 如果右子节点更小,更新最小值索引
    if (right < heap->size && heap->array[right]->val < heap->array[smallest]->val)
        smallest = right;

    // 如果最小值不是当前节点,交换并递归调整
    if (smallest != i) {
        swap(&heap->array[i], &heap->array[smallest]);
        heapify(heap, smallest);
    }
}

// 将节点插入到堆中
void insertHeap(MinHeap* heap, ListNode* node) {
    // 将新节点放在堆末尾
    heap->array[heap->size++] = node;
    int i = heap->size - 1; // 当前节点的索引

    // 向上调整节点位置以维护堆的性质
    while (i && heap->array[(i - 1) / 2]->val > heap->array[i]->val) {
        swap(&heap->array[i], &heap->array[(i - 1) / 2]);
        i = (i - 1) / 2; // 移动到父节点
    }
}

// 从堆中提取最小值节点
ListNode* extractMin(MinHeap* heap) {
    if (heap->size == 0) return NULL; // 堆为空时返回 NULL

    // 获取堆顶最小值
    ListNode* root = heap->array[0];
    // 将堆末尾的节点移到堆顶
    heap->array[0] = heap->array[--heap->size];
    // 调整堆以恢复堆的性质
    heapify(heap, 0);
    return root; // 返回提取的最小值节点
}

// 合并 K 个有序链表的函数
ListNode* mergeKLists(ListNode** lists, int k) {
    // 创建最小堆
    MinHeap* heap = createMinHeap(k);

    // 将每个链表的头节点加入堆中
    for (int i = 0; i < k; ++i) {
        if (lists[i]) insertHeap(heap, lists[i]);
    }

    // 创建结果链表的哑节点(dummy 节点)
    ListNode dummy;
    ListNode* tail = &dummy; // 结果链表的尾部指针
    dummy.next = NULL;

    // 从堆中逐一提取最小值节点并加入结果链表
    while (heap->size > 0) {
        // 提取堆中的最小值节点
        ListNode* minNode = extractMin(heap);
        // 将最小值节点连接到结果链表
        tail->next = minNode;
        tail = tail->next; // 更新尾部指针

        // 如果提取的节点还有下一个节点,将其加入堆中
        if (minNode->next) insertHeap(heap, minNode->next);
    }

    // 释放堆的内存
    free(heap->array);
    free(heap);
    return dummy.next; // 返回结果链表的头节点
}

// 辅助函数:打印链表
void printList(ListNode* head) {
    while (head) {
        printf("%d -> ", head->val); // 打印节点值
        head = head->next;          // 移动到下一个节点
    }
    printf("NULL\n"); // 链表结束
}

// 辅助函数:创建链表节点
ListNode* createNode(int val) {
    ListNode* node = (ListNode*)malloc(sizeof(ListNode)); // 分配内存
    node->val = val;   // 设置节点值
    node->next = NULL; // 初始化下一个指针
    return node;
}

// 主函数测试
int main() 
{
    // 构建测试链表 1:1 -> 4 -> 5
    ListNode* list1 = createNode(1);
    list1->next = createNode(4);
    list1->next->next = createNode(5);

    // 构建测试链表 2:1 -> 3 -> 4
    ListNode* list2 = createNode(1);
    list2->next = createNode(3);
    list2->next->next = createNode(4);

    // 构建测试链表 3:2 -> 6
    ListNode* list3 = createNode(2);
    list3->next = createNode(6);

    // 创建链表数组
    ListNode* lists[] = {list1, list2, list3};

    // 合并链表
    ListNode* mergedList = mergeKLists(lists, 3);

    // 输出结果链表
    printf("Merged List: ");
    printList(mergedList);

    return 0; // 程序结束
}

补充

小顶堆性质

          小顶堆(Min-Heap) 是一种完全二叉树,它具有以下性质:

堆的结构性质:

  • 小顶堆是一棵完全二叉树,即树是从左到右逐层填满的,只有最后一层可能不满,但节点必须从左向右连续排列。

堆的值性质:

  • 每个节点的值都小于或等于其子节点的值。
  • 即:对于任意节点 i,有:
A[i] ≤ A[2i+1](左子节点值)
A[i] ≤ A[2i+2](右子节点值)

        由于这两个性质,堆的最小值始终存储在根节点(即数组的第一个位置)。

        数组表示:堆可以使用数组表示,将完全二叉树的节点按层序遍历的顺序存储:

索引:     0   1   2   3   4   5
值:       10  15  20  30  40  25

在数组中,可以通过以下规则找到父子节点的关系:

  • 父节点索引: parent(i) = (i−1) / 2
  • 左子节点索引: left(i) = 2i+1
  • 右子节点索引: right(i) = 2i+2

示例:一个满足小顶堆性质的完全二叉树:

        10
       /  \
      15   20
     / \   /
    30 40 25
heapify 函数的作用
void heapify(MinHeap* heap, int i);
  • i: 要调整的节点在堆数组中的索引。
  • heap: 表示一个小顶堆,包含节点数组和堆的大小。

         heapify 函数是小顶堆的调整函数,用来维护堆的性质(即每个节点的值都不大于其子节点的值)。它的作用是:

  • 从索引 i 开始,将子树调整为满足小顶堆性质。
  • 如果某节点不满足小顶堆性质,则通过交换该节点和其较小子节点的值,并递归调整子树,直到整个堆满足小顶堆性质。
实现逻辑
  1. 假设当前节点的值是最小的(设索引为 smallest)。
  2. 比较当前节点和其左、右子节点的值:
    • 如果左子节点更小,更新 smallest为左子节点索引。
    • 如果右子节点更小,更新 smallest为右子节点索引。
  3. 如果 smallest 发生变化(当前节点不是最小值),交换当前节点和 smallest的值。
  4. 递归调用 heapify,确保调整后的子树也满足小顶堆性质。
示例说明

        假设有以下堆数组,表示一个不完全满足小顶堆性质的堆:

索引:      0   1   2   3   4   5
值:        40  15  20  30  10  25

         对应的堆结构:

        40
       /  \
      15   20
     / \   /
    30  10 25

调用 heapify(heap, 0)

  1. 当前节点: 40(索引 0)。
  2. 左子节点:15(索引 1)。
  3. 右子节点:20(索引 2)。
  4. 最小值为左子节点 15,交换 4015

调整后堆数组:

索引:      0   1   2   3   4   5
值:        15  40  20  30  10  25

对应堆结构:

        15
       /  \
      40   20
     / \   /
    30  10 25

递归调用 heapify(heap, 1):

  • 当前节点:40(索引 1)。
  • 左子节点:30(索引 3)。
  • 右子节点:10(索引 4)。
  • 最小值为右子节点 10,交换 4010

调整后堆数组:

索引:      0   1   2   3   4   5
值:        15  10  20  30  40  25

对应堆结构:

        15
       /  \
      10   20
     / \   /
    30  40 25

heapify(heap, 4) 不再需要调整,因为 40 没有子节点。

最终堆数组:

索引:      0   1   2   3   4   5
值:        15  10  20  30  40  25

 最终堆结构:

        15
       /  \
      10   20
     / \   /
    30  40 25

C++代码实现:分治解法

#include <stdio.h>  // 包含标准输入输出头文件
#include <stdlib.h> // 包含动态内存分配函数

// 定义链表节点结构
typedef struct ListNode {
    int val;               // 节点值
    struct ListNode* next; // 指向下一个节点的指针
} ListNode;

// 合并两个有序链表的函数
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    // 如果 l1 为空,直接返回 l2
    if (!l1) return l2;
    // 如果 l2 为空,直接返回 l1
    if (!l2) return l1;

    // 比较两个链表的头节点,选择较小值作为合并后链表的头节点
    if (l1->val < l2->val) {
        l1->next = mergeTwoLists(l1->next, l2); // 递归处理 l1->next 和 l2
        return l1;                              // 返回 l1 作为当前链表头
    } else {
        l2->next = mergeTwoLists(l1, l2->next); // 递归处理 l1 和 l2->next
        return l2;                              // 返回 l2 作为当前链表头
    }
}

// 分治法合并 K 个有序链表
ListNode* mergeKListsDivideAndConquer(ListNode** lists, int left, int right) {
    // 如果左边界等于右边界,表示只剩下一个链表
    if (left == right) return lists[left];
    // 如果左边界大于右边界,返回 NULL
    if (left > right) return NULL;

    // 计算中间位置
    int mid = left + (right - left) / 2;

    // 递归地合并左半部分链表
    ListNode* l1 = mergeKListsDivideAndConquer(lists, left, mid);
    // 递归地合并右半部分链表
    ListNode* l2 = mergeKListsDivideAndConquer(lists, mid + 1, right);

    // 合并左右两部分链表
    return mergeTwoLists(l1, l2);
}

// 主函数入口,用于调用分治法合并链表
ListNode* mergeKLists(ListNode** lists, int k) {
    // 如果链表数组为空,直接返回 NULL
    if (k == 0) return NULL;
    // 调用分治法进行合并
    return mergeKListsDivideAndConquer(lists, 0, k - 1);
}

// 辅助函数:打印链表
void printList(ListNode* head) {
    while (head) {
        printf("%d -> ", head->val); // 打印当前节点的值
        head = head->next;          // 移动到下一个节点
    }
    printf("NULL\n"); // 链表结束
}

// 辅助函数:创建链表节点
ListNode* createNode(int val) {
    ListNode* node = (ListNode*)malloc(sizeof(ListNode)); // 分配内存
    node->val = val;   // 设置节点值
    node->next = NULL; // 初始化指针
    return node;       // 返回新节点
}

// 主函数测试
int main() 
{
    // 构建测试链表 1:1 -> 4 -> 5
    ListNode* list1 = createNode(1);
    list1->next = createNode(4);
    list1->next->next = createNode(5);

    // 构建测试链表 2:1 -> 3 -> 4
    ListNode* list2 = createNode(1);
    list2->next = createNode(3);
    list2->next->next = createNode(4);

    // 构建测试链表 3:2 -> 6
    ListNode* list3 = createNode(2);
    list3->next = createNode(6);

    // 创建链表数组
    ListNode* lists[] = {list1, list2, list3};

    // 调用分治法合并链表
    ListNode* mergedList = mergeKLists(lists, 3);

    // 打印结果链表
    printf("Merged List: ");
    printList(mergedList);

    return 0; // 程序正常结束
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络复习1——导言和概论

网络简史 1946年&#xff0c;美国物理学家莫奇利任总设计师研制成功世界上第一台电子管计算机ENIAC&#xff08;这标志着人类自学会使用工具的漫长岁月中&#xff0c;终于拥有了可以替代人类脑力劳动的“工具”&#xff09; 1969年9月2日&#xff0c;以雷克雷洛克为首的约20名…

详解桥接模式

引言 在开发过程中&#xff0c;可能会遇到系统设计有多种维度变化的情况&#xff0c;比如我们想画一幅五彩斑斓的画&#xff0c;需要用到12个颜色&#xff0c;但是需要粗细不同的线条&#xff08;粗、中、细&#xff09;&#xff0c;如果用蜡笔&#xff0c;就需要粗中细三种蜡笔…

MySQL笔记-启动时log报错Table ‘mysql.user‘ doesn‘t exist

安装好mysql后&#xff0c;正常使用&#xff08;使用的是rpm版安装的&#xff09; service mysqld start | stop | restart 不会出现这个问题。 我遇到的情况是在凝思操作系统上&#xff0c;已经存在了一个mysql。网上查找了一些资料&#xff0c;卸载&#xff0c;后可能卸载…

【SpringBoot+Vue】x-admin管理系统跟做

技术栈 前端技术说明Vue前端框架Vuex全局状态管理框架ElementUI前端UI框架Axios前端HTTP框架vue-element-admin项目脚手架 后端技术说明SpringBoot容器MVC框架MyBatisORM框架MyBatis-plusMyBatis增强工具Redis非关系型数据库 数据库准备 SET NAMES utf8mb4; SET FOREIGN_KE…

AI智算-正式上架GPU资源监控概览 Grafana Dashboard

下载链接 https://grafana.com/grafana/dashboards/22424-ai-gpu-20241127/

异步处理优化:多线程线程池与消息队列的选择与应用

目录 一、异步处理方式引入 &#xff08;一&#xff09;异步业务识别 &#xff08;二&#xff09;明确异步处理方式 二、多线程线程池&#xff08;Thread Pool&#xff09; &#xff08;一&#xff09;工作原理 &#xff08;二&#xff09;直面优缺点和适用场景 1.需要快…

用到动态库的程序运行过程

当我们写好了一段代码然后编译运行后会生成可执行文件&#xff0c;该文件会存在磁盘的当前目录下&#xff0c;而当我们开始运行这段程序时&#xff0c;操作系统&#xff08;加载器&#xff09;需要将其从磁盘加载进内存然后执行相关操作&#xff0c;而对于用到动态库的程序&…

Windows使用多个JDK的方法

原文网址&#xff1a;Windows使用多个JDK的方法-CSDN博客 简介 本文介绍Windows如何使用多个JDK。 原先已经有了JDK8&#xff0c;现在想用JDK21。但有的项目依然是JDK8&#xff0c;所以两个JDK需要共存。 解决方案 第一步&#xff1a;改环境变量 右键此电脑> 属性>…

RDIFramework.NET CS敏捷开发框架 SOA服务三种访问(直连、WCF、WebAPI)方式

1、介绍 在软件开发领域&#xff0c;尤其是企业级应用开发中&#xff0c;灵活性、开放性、可扩展性往往是项目成功的关键因素。对于C/S项目&#xff0c;如何高效地与后端数据库进行交互&#xff0c;以及如何提供多样化的服务访问方式&#xff0c;是开发者需要深入考虑的问题。…

《数字图像处理基础》学习07-图像几何变换之最近邻插值法放大图像

目录 一&#xff0c;概念 二&#xff0c;题目及matlab实现 1&#xff0c;解题思路 2&#xff0c;matlab实现 1&#xff09;matlab思路 2&#xff09;完整代码 三&#xff0c;放大图像及matlab实现 一&#xff0c;概念 通过上一篇&#xff0c;我已经学习了使用最邻近插…

LWIP和FATFS 实现 FTP 服务端

目录 一、前言 二、LWIP 和 FTP 简介 1.LWIP 2.FTP 三、实现 FTP 服务端的主要步骤 1.初始化 LWIP 2.创建 FTP 服务器任务 3.处理客户端连接 4.实现 FTP 命令处理 5.文件系统操作 6.错误处理和日志记录 四、示例代码 1.创建FTP任务 2. FTP任务代码 3.处理交互数据…

PyCharm中Python项目打包并运行到服务器的简明指南

目录 一、准备工作 二、创建并设置Python项目 创建新项目 配置项目依赖 安装PyInstaller 三、打包项目 打包为可执行文件 另一种打包方式(使用setup.py) 四、配置服务器环境 五、上传可执行文件到服务器 六、在服务器上运行项目 配置SSH解释器 配置部署 上传代…

PHP 方头像转为圆图

业务需要把创建海报上的用户头像由方形转为圆形&#xff0c;前端的样式设置不能用。 故采用GD的函数来对方图进行裁剪处理为圆图。 目录 裁剪函数 本地图片 远程图片 效果 参考文章 总结 裁剪函数 从网上找的一个裁剪图片的函数。 代码如下&#xff1a; /* * 将图片切…

Java--数组的定义与使用

1.数组的基本概念 1.1为什么用数组 在程序设计中,每一个数据总是对应一个变量.当数据量越大,就需要更多的变量来存储.我们将相同类型的数据存储到一个集合中,就可以更方便我们对数据进行访问,同时可以减少不断定义变量.这个集合就叫做数组 1.2数组的定义 数组是一种基本的数…

手机实时提取SIM卡打电话的信令声音-蓝牙电话如何适配eSIM卡的手机

手机实时提取SIM卡打电话的信令声音 --蓝牙电话如何适配eSIM卡的手机 一、前言 蓝牙电话的海外战略中&#xff0c;由于海外智能手机市场中政策的差异性&#xff0c;对内置eSIM卡的手机进行支持是非常合理的需求。Android系列手机中&#xff0c;无论是更换通信运营商&#xf…

《操作系统 - 清华大学》6 -4:局部页面置换算法:时钟页面置换算法 (Clock)

文章目录 1.时钟置换算法的工作原理2.时钟置换算法的具体实现过程3. 时钟置换算法示例 1.时钟置换算法的工作原理 需要考虑有没有新的办法&#xff0c;既能有 LRU 算法这种效果&#xff0c;产生缺页次数比较少&#xff0c;同时实现的效率比较简洁和方便&#xff0c;有点类似于…

Centos7安装MySQL8.0详细教程(压缩包安装方式)

本章教程&#xff0c;主要介绍如何在Centos7上安装MySQL8.0版本数据库&#xff08;压缩包安装方式&#xff09; 一、卸载系统自带的 Mariadb 1、查询 rpm -qa|grep mariadb2.、卸载 如果有查询结果&#xff0c;就进行卸载&#xff0c;没有就跳过该步骤。 rpm -e --nodeps mar…

brew安装mongodb和php-mongodb扩展新手教程

1、首先保证macos下成功安装了Homebrew&#xff0c; 在终端输入如下命令&#xff1a; brew search mongodb 搜索是不是有mongodb资源&#xff0c; 演示效果如下&#xff1a; 2、下面来介绍Brew 安装 MongoDB&#xff0c;代码如下&#xff1a; brew tap mongodb/brew brew in…

Flink四大基石之CheckPoint(检查点) 的使用详解

目录 一、Checkpoint 剖析 State 与 Checkpoint 概念区分 设置 Checkpoint 实战 执行代码所需的服务与遇到的问题 二、重启策略解读 重启策略意义 代码示例与效果展示 三、SavePoint 与 Checkpoint 异同 操作步骤详解 四、总结 在大数据流式处理领域&#xff0c;Ap…

springboot旅游管理系统的设计与实现

springboot旅游管理系统的设计与实现 如需源码pc端&#x1f449;&#x1f449;&#x1f449;资源 手机端&#x1f449;&#x1f449;&#x1f449;资源 摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于…