【基础算法总结】分治--快排+归并

news2024/9/30 10:55:35

目录

  • 一,分治算法介绍
  • 二,算法原理和代码实现
    • 75.颜色划分
    • 912.排序数组-快速排序
    • 215.数组中的第k个最大元素(快速选择算法)
    • LCR159.最小的k个数(快速选择算法)
    • 912.排序数组-归并排序
    • LCR170.数组中的逆序对
    • 315.计算右侧小于当前元素的个数
    • 493.翻转对
  • 三,算法总结

一,分治算法介绍

分治是一类十分重要的算法,"分治"顾名思义就是分而治之,把一个大问题分成若干个相同或是相似的子问题 ,再把这些小问题继续划分成若干个相同或是相似的更小子问题…直到最小子问题不可再划分。接着通过解决最小子问题进而解决了上一层更小子问题…又进而解决了大问题。这个过程就是 – 递归

我们学过的快速排序和归并排序就是非常典型也是非常重要的分治。但是它们的分治思想不仅仅用于排序上,在解决其他问题时也是非常有效的。下面介绍的若干道题目就是使用快排和归并的核心思想解决的,让大家加深对它们的理解

二,算法原理和代码实现

75.颜色划分

在这里插入图片描述
在这里插入图片描述

这道题十分经典,它的代码是快速排序的核心代码之一(因为快排有多种实现方式,核心代码也有多种),它的本质就是数组分三块(数组划分)
本题是为下面几题做铺垫的,不是用分治算法,而是使用三指针
(1) 首先是三个指针的作用:
i:遍历扫描数组,初始化为0
left:标记0区域的最右侧,初始化为-1
right:标记2区域的最左侧,初始化为最后一个元素的下一个位置
(2) 这三个下标把数组分为4部分:
[0,left] :全是0
[left+1,i-1]:全是1
[i,right-1]:待扫描的元素
[right, n-1]:全是2
在这里插入图片描述
(3) 在扫描过程中:
a. 当arr[i] == 0时:swap(arr[++left],arr[i++]),把0放入指定区域,并完成指针移动
b. 当arr[i] == 1时:i++
c. 当arr[i] == 2时:swap(arr[–right],arr[i]),把2放入指定区域,但是i不能移动,因为交换后 i 所指的依旧是待扫描的
(4) 当 i 与 right 相遇后,待扫描区间不存在了,此时遍历结束

代码实现:

class Solution 
{
public:
    void sortColors(vector<int>& nums)
    {
        int n = nums.size();
        int left = -1, right = n, i = 0;
        // 当i >= right时,说明待扫描区域已经扫描完了,可以结束了
        while(i < right)
        {
            if(nums[i] == 0) swap(nums[++left], nums[i++]);
            else if(nums[i] == 1) i++;
            else swap(nums[--right], nums[i]);
        }
    }
};

912.排序数组-快速排序

在这里插入图片描述
在这里插入图片描述

算法原理:

这道题可以使用我们以前学过的快速排序的算法(二路快排)解决,但是当数据中有很多重复值时,使用以前的算法时间复杂度会退化成 O(N^2)
我们上一题介绍的"数组分三块"(三路快排)的思想就可以很好的解决这个问题,原因是当数组里的数据都是 key 时,进行一次划分后就变成一块区域了(就是等于key的,没有大于或是小于key的),此时没有左右区间,就不用进行递归了,直接结束。这里的时间复杂度是 O(N) 级别

这里还可以进行优化:用随机的方式选择基准元素
在这里插入图片描述

代码实现:

class Solution 
{
public:
    vector<int> sortArray(vector<int>& nums) 
    {
        srand(time(NULL)); // 种随机数种子
        qsort(nums, 0, nums.size()-1);
        return nums;
    }

    void qsort(vector<int>& nums, int l, int r)
    {
        if(l >= r) return;

        // 数组分三块(三路快排)
        int key = getRandom(nums, l, r);
        int i = l, left = l-1, right = r+1; // 注意这里的初始化值
        while(i < right)
        {
            if(nums[i] < key) swap(nums[++left], nums[i++]);
            else if(nums[i] == key) i++;
            else swap(nums[--right], nums[i]);
        }
        
		// 走完一遍"数组分三块"时,i与right已经重合了
        // [l, left] [left+1, right-1] [right, r]
        qsort(nums, l, left);
        qsort(nums, right, r);
    }

    int getRandom(vector<int>& nums, int left, int right)
    {
        int r = rand();
        return nums[r % (right - left + 1) + left];
    }
};

215.数组中的第k个最大元素(快速选择算法)

在这里插入图片描述
在这里插入图片描述

topK问题是一类很重要的问题,一般有4种问法
a. 找第K大
b. 找第K小
c. 找前K大
d. 找前K小
解决这类问题有两种算法:
(1) 堆结构 – O(N * logN)
(2) 快速选择算法(基于快排) – O(N)

算法原理:

这道题是基于上一题的快排算法内容,由前文可知,基准值 key 把数组分为三块区域,从左往右依次是小于key,等于key,大于key。所以我们只需要确定本题中要找的的第 k 大元素落在哪个区域,就在哪块区域里找,其余两块区域不需要再考虑了

所以接下来的重点就是要讨论如何确定第 k 大元素落在哪个区域
假设数组中的三块区域的元素的个数分别是 a,b,c。分三种情况讨论
在这里插入图片描述
(1) 如果是落在最右边的区域,则 c >= k
此时只需要去 [right, r] 区域,继续找第 k 大元素就好了

(2) 如果是落在中间的区域,则 b+c >= k
此时第 k 大元素一定是在中间的区域,直接返回 key 即可

(3) 如果(1)(2)都不成立,此时要去 [l, left] 区域,找第 k-b-c 大的元素

代码实现:

class Solution 
{
public:
    int findKthLargest(vector<int>& nums, int k) 
    {
        srand(time(NULL));
        return qsort(nums, 0, nums.size()-1, k);
    }

    int qsort(vector<int>& nums, int l, int r, int k)
    {
        if(l == r) return nums[l];

        // 找数组里的随机数做基准值
        int key = getRandom(nums, l, r);
        
        // 数组分三块
        int left = l - 1, right = r + 1, i = l;
        while(i < right)
        {
            if(nums[i] < key) swap(nums[++left], nums[i++]);
            else if(nums[i] == key) i++;
            else swap(nums[--right], nums[i]);
        }

        // 分情况讨论
        int c = r - right + 1, b = right - left -1;
        if(c >= k) return qsort(nums, right, r, k);
        else if(b + c >= k) return key;
        else return qsort(nums, l, left, k-b-c);
    }

    int getRandom(vector<int>& nums, int left, int right)
    {
        return nums[rand() % (right - left + 1) + left];
    }
};

LCR159.最小的k个数(快速选择算法)

在这里插入图片描述
在这里插入图片描述

算法原理:

这道题也是一道topk问题,解法也有多种:一是直接排序,再取出前k个元素,时间复杂度O(N * ogN)二是使用堆结构,时间复杂度O(N * logN)三是使用快速选择算法,时间复杂度O(N)

这里只介绍快速选择算法。本题的算法原理和上一题基本上是一模一样的,此处不再详细分析了。简略分析如下
在这里插入图片描述

细节问题:

根据算法原理可知,快速选择完之后,我们并没有把数组排序,只是把最小的k个数扔到了数组前面,数组还是无序的

代码实现:

class Solution 
{
public:
    vector<int> inventoryManagement(vector<int>& stock, int cnt) 
    {
        srand(time(NULL));
        qsort(stock, 0, stock.size()-1, cnt);

        return {stock.begin(), stock.begin() + cnt};
    }

    void qsort(vector<int>& nums, int l, int r, int k)
    {
        if(l >= r) return;

        // 在数组里找随机值做key
        int key = getRandom(nums, l, r);

        // 数组分三块
        int left = l - 1, right = r + 1, i = l;
        while(i < right)
        {
            if(nums[i] < key) swap(nums[++left], nums[i++]);
            else if(nums[i] == key) i++;
            else swap(nums[--right], nums[i]);
        }

        // 分情况讨论
        // [l, left] [left+1, right-1] [right, r]
        int a = left - l + 1, b = right - left -1;
        if(a > k) qsort(nums, l, left, k);
        else if(a+b >= k) return;
        else qsort(nums, right, r, k-a-b);
    }

    int getRandom(vector<int>& nums, int left, int right)
    {
        return nums[rand() % (right - left + 1) + left];
    }
};

912.排序数组-归并排序

算法原理:

归并排序的大致过程
先取中间点把数组分为两个区间,要把数组排有序,只要左区间有序,右区间有序了,数组就有序了。所以再递归左区间,取中间点把左区间分又为左右两个区间…一直递归,直到左右区间只剩一个元素不可再分割了,这一层进行回退归并过程,归并的核心就是合并两个有序数组,一直归并到第一层,再进行递归右区间…
图解如下:
在这里插入图片描述

细节问题:

这里执行归并操作合并两个有序数组时需要创建临时数组,有两种创建方式:
(1) 边递归边创建
在这里插入图片描述
(2) 提前创建好,并且开好空间(推荐)
在这里插入图片描述

代码实现:

class Solution 
{
    vector<int> tmp; // 定义为全局
public:
    vector<int> sortArray(vector<int>& nums) 
    {
        tmp.resize(nums.size()); // 提前开空间
        mergeSort(nums, 0, nums.size() - 1);
        return nums;
    }

    void mergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return;

        // 找中间点
        int mid = (right + left ) / 2;
        //int mid = (left + right) >> 1;

        // 把左右区间排序
        // [left, mid] [mid+1, right]
        mergeSort(nums, left, mid);
        mergeSort(nums, mid+1, right);

        // 合并两个有序数组
        int cur1 = left, cur2 = mid+1, i = 0;
        while(cur1 <= mid && cur2 <= right)
            tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++]; 
        // 处理没有遍历完的数据
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        while(cur2 <= right) tmp[i++] = nums[cur2++];

        // 还原
        for(int i = left; i <= right; i++)
            nums[i] = tmp[i-left];
    }
};

LCR170.数组中的逆序对

在这里插入图片描述
在这里插入图片描述

算法原理:

这道题比较难。在使用归并分治思想解决这个问题前,先来搞定几个铺垫知识:
(1) 总对数 = 左半区间逆序对个数a + 右半区间逆序对个数b + 左选一个右选一个组成的逆序对个数c
再对第一个知识进行延伸:
(2) 在左半区间找出逆序对个数a后,进行排序,右半区间找出逆序对个数b后,也进行排序,最后再左选一个右选一个组成的逆序对个数c,也跟着排序,此时 a + b + c = 总对数

有了上面两点铺垫知识,就可以引出归并排序的算法了:
把整个数组按中点分成两部分,可以在递归中完成左右两区间逆序对个数的计算,同时进行排序,核心过程是如何计算左选一个右选一个组成的逆序对个数,加排序?如果数组有序,可以统计出一大堆

策略1:用升序,找到该数之前,有多少个数比我大。盯着 cur2 看。
此时是在归并过程中,已经定义了 cur1 和 cur2,分情况讨论:
(1) nums[cur1] <= nums[cur2] -> 此时不能确定左边有多少个数比 nums[cur2]大,cur1++
(2) nums[cur1] > nums[cur2] -> ret += (mid - cur1 + 1) 个对,一次统计出来一堆,再 cur2++
在这里插入图片描述

拓展内容:

策略1中,能否用降序,找到该数之前,有多少个数比我大。也盯着 cur2 看? 不可行
原因:此时在归并过程中,[left, cur1-1] 区间的数都比 nums[cur1] 要大,[mid+1, cur2-1] 区间的数都比 nums[cur2] 要大,如果我们计算 cur1-1 - left +1 的个数,那在 cur1++ 后又要重新计算前面的个数了
在这里插入图片描述

策略2 :用降序,找到该数之后,有多少个数比我小。盯着 cur1 看
(1) nums[cur1] <= nums[cur2] -> 此时不能确定左边有多少个数比nums[cur2]小,cur2++
(2) nums[cur1] > nums[cur2] -> ret += (right - cur2 + 1)个对,一次统计出来一堆,再 cur1++
在这里插入图片描述

代码实现:
这里用的是策略1的升序。

class Solution 
{
    vector<int> tmp;
public:
    int reversePairs(vector<int>& nums) 
    {
        tmp.resize(nums.size());
        return mergeSort(nums, 0, nums.size()-1);
    }

    int mergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return 0;

        int ret = 0;
        // 计算中点
        int mid = (left + right) >> 1;

        // 左边的个数+排序  右边的个数+排序
        // [left, mid] [mid+1, right]
        ret += mergeSort(nums, left, mid);
        ret += mergeSort(nums, mid+1, right);

        // 一左一右的个数
        int cur1 = left, cur2 = mid+1, i = 0;
        while(cur1 <= mid && cur2 <= right)
        {
            if(nums[cur1] <= nums[cur2])
            {
                // 排序+向后移
                tmp[i++] = nums[cur1++];
            }
            else
            {
                // 排序+统计个数
                ret += (mid - cur1 + 1);
                tmp[i++] = nums[cur2++];
            }
        }

        // 处理剩余的数据
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        while(cur2 <= right) tmp[i++] = nums[cur2++];

        // 还原
        for(int j = left; j <= right; j++)
            nums[j] = tmp[j-left];

        return ret;
    }
};

策略1和策略2的代码差异:
策略1:升序

在这里插入图片描述

策略2:降序

在这里插入图片描述

315.计算右侧小于当前元素的个数

在这里插入图片描述
在这里插入图片描述

算法原理:

这题的本质和上一题一样也是计算逆序对的个数,所以也是使用归并分治的思想
因为这道题是求右侧小于当前元素的个数,所以用的是上一题的策略2降序
在这里插入图片描述

但是这道题与上一题不同的是
我们统计出个数之后,不是直接 ret+= 返回,而是要把个数存入另一个数组的和这个元素对应(原始下标)的位置上
所以这里我们还要解决一个问题就是:
当统计出 nums[cur1] 的后面有多少个元素比它小之后,还要能找到这个元素的原始下标(在递归过程中cur1是会移动的)
解决方法:
搞一个与原数组nums同规模的 index 数组,里面的值存的是原数组中每个元素的下标。然后不管nums数组里的元素怎么移动,index 数组里面的值都与它绑定一起移动
在这里插入图片描述

代码实现:

class Solution 
{
    vector<int> ret;
    vector<int> index;
    int tmpNums[500010];
    int tmpIndex[500010];
public:
    vector<int> countSmaller(vector<int>& nums) 
    {
        int n = nums.size();
        ret.resize(n);
        index.resize(n);

        // 记录原始下标
        for(int i = 0; i < n; i++)
            index[i] = i;

        mergeSort(nums, 0, n-1);
        return ret;
    }

    void mergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return;

        // 找中点
        int mid = (left + right) >> 1;
        // [left, mid] [mid+1, right]

        // 左右区间的个数
        mergeSort(nums, left, mid);
        mergeSort(nums, mid+1, right);

        // 统计个数放入原始位置
        int cur1 = left, cur2 = mid+1, i = 0;
        while(cur1 <= mid && cur2 <= right) // 降序
        {
            if(nums[cur1] <= nums[cur2])
            {
                tmpNums[i] = nums[cur2];
                tmpIndex[i++] = index[cur2++];
            }
            else
            {
                ret[index[cur1]] += right - cur2 + 1; 
                tmpNums[i] = nums[cur1];
                tmpIndex[i++] = index[cur1++];
            }
        }

        // 处理剩下的排序过程
        while(cur1 <= mid)
        {
            tmpNums[i] = nums[cur1];
            tmpIndex[i++] = index[cur1++];
        }
        while(cur2 <= right)
        {
            tmpNums[i] = nums[cur2];
            tmpIndex[i++] = index[cur2++];
        }

        // 还原
        for(int j = left; j <= right; j++)
        {
            nums[j] = tmpNums[j-left];
            index[j] = tmpIndex[j-left];
        }
    }
};

493.翻转对

在这里插入图片描述
在这里插入图片描述
算法原理:

这道题是个逆序对的变式题,但是它不能和 [LCR170.数组中的逆序对] 一样在归并过程中统计翻转对的个数。因为在那道题中的 nums[i] > nums[j] 与归并过程比较时(合并两个有序数组)是一样的,但是这道题的比较是 nums[i] > 2 * nums[j],与归并过程比较时不同

解决方法就是
要在归并过程之前计算翻转对的个数。因为我们要利用这两个数组有序的特性
策略1:计算当前元素后面,有多少元素的两倍比我小,此时降序
此时让 cur1 和 cur2 指向两个区间的开始,盯着 cur1 的元素,先固定cur1不动
(1) 若 nums[cur1] <= 2 * nums[cur2],由于是降序数组,只有 cur2向后移时,才能让nums[cur1] > 2 * nums[cur2]
(2) cur2++,直到 nums[cur1] > 2 * nums[cur2] ,此时 ret += right - cur2 + 1
(3) 再让cur1++,注意此时 cur2 还是一直往后走
(4) 直到 cur1 或 cur2 走出区间
在这里插入图片描述
策略2:计算当前元素之前,有多少元素的一半比我大,此时升序
在这里插入图片描述

代码实现:

class Solution 
{
    int tmp[50010];
public:
    int reversePairs(vector<int>& nums) 
    {
        return mergeSort(nums, 0, nums.size()-1);
    }

    int mergeSort(vector<int>& nums, int left, int right)
    {
        if(left >= right) return 0;

        // 计算中点
        int mid = (left + right) >> 1;
        // [left, mid] [mid+1, right]

        int ret = 0;
        // 计算左右区间的个数
        ret += mergeSort(nums, left, mid);
        ret += mergeSort(nums, mid+1, right);

        // 计算一左一右的个数
        int cur1 = left, cur2 = mid+1, i = left;
        while(cur1 <= mid)
        {
            while(cur2 <= right && nums[cur2] >= nums[cur1] / 2.0) cur2++;
            if(cur2 > right) break;
            ret += right - cur2 + 1;
            cur1++;
        }

        // 合并两个有序数组
        cur1 = left, cur2 = mid+1;
        while(cur1 <= mid && cur2 <= right) // 降序
            tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur2++] : nums[cur1++];
        
        // 处理剩下的数据
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        while(cur2 <= right) tmp[i++] = nums[cur2++];

        // 还原
        for(int j = left; j <= right; j++)
            nums[j] = tmp[j];

        return ret;
    }
};

策略1和策略2的代码差异:
策略1:降序

在这里插入图片描述
在这里插入图片描述

策略2:升序

在这里插入图片描述
在这里插入图片描述

三,算法总结

上面的若干道题都是基于快排和归并的思想解决的,所以最重要的还是要理解这两种排序算法。
如果想要更加详细的学习两种排序算法,请点击下面两篇文章:
(1) 归并排序和计数排序
(2) 快速排序和冒泡排序

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

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

相关文章

POLYGON Nature - Low Poly 3D Art by Synty 树木植物

一个低多边形资源包,包含可以添加到现有多边形风格游戏中的树木、植物、地形、岩石、道具和特效 FX 资源。 为 POLYGON 系列提供混合样式树这一新增功能。弥合 POLYGON 与更传统的层级资源之间的差距。还提供了一组经典的 POLYGON 风格的树木和植被以满足你的需求。 该包还附带…

Mac安装Manim并运行

1.在macOS上创建Python虚拟环境&#xff0c;可以使用venv模块&#xff0c;这是Python自带的库&#xff0c;也可以使用conda。以下是使用venv创建和使用Python虚拟环境的步骤&#xff1a; 打开终端。 创建一个新的目录来存放你的项目&#xff0c;并进入该目录&#xff1a; mk…

Spring Security中自定义cors配置

一、为什么要自定义cors配置 在使用Spring框架时&#xff0c;Spring Security组件提供了简便的cors配置方案&#xff0c;使程序开发者可以快速的实现“同源安全策略”。关于cors&#xff0c;可以参数之前的一篇文章--关于Spring Security的CORS_springsecurity cors-CSDN博客 由…

excel 处理数据的常用场景之考勤表的制作

文章目录 预备知识数据有效性excel的图表知识 考勤表效果图开始制作1. 首先我们去分析这个表格&#xff0c;我们把它分为三个部分如下&#xff1a;2. 开始第一个部分的制作3. 开始制作第二部分。4. 第三部分也是最复杂的&#xff0c;因为会用到几个公式&#xff0c;我这里下面会…

入职2年的程序员,被劝退了!年纪大了,感觉好绝望!

入职2年的程序员&#xff0c;今天被劝退了&#xff01;年纪大了&#xff0c;感觉好绝望&#xff01; 我的朋友是一位程序员&#xff0c;毕业后去了BAT企业&#xff0c;前2年去了一家国企&#xff0c;至今刚满2年&#xff0c;刚进去绩效领导给打了C&#xff0c;现在被边缘化&…

详解Java之继承与多态

目录 继承 派生类和基类各部分执行顺序 protected 访问权限总结 final关键字 组合 多态 向上转型 向下转型 动态绑定 静态绑定 方法重载 方法重写 super关键字 super和this的对比 在构造方法中调用重写方法 继承 继承是为了解决多个类具有一些相同的属性和方…

DotNetty ChannelRead接收数据为null

问题&#xff1a;C#使用Dotnetty和Java netty服务器通讯&#xff0c;结果能正确发送数据到服务器&#xff0c;却始终接收不到服务器返回的数据。 解决&#xff1a;一定一定要注意服务器和客户端使用的编码一定要完全一样才行 我先前在客户端添加了StringDecoder,服务器却没有…

AI如此迅猛,你还能跟上吗?快来试试这个对口型新神器!

国庆即将到来&#xff0c;宇航祝大家国庆节快乐&#xff01;最近感觉有些寒意了&#xff0c;气温忽高忽低的&#xff0c;大家一定要注意&#xff0c;别感冒了。 今天给大家分享一个即梦的新功能 : 对口型&#xff0c;也是最近即梦不声不响新上线的功能。 一、 功能介绍 即梦的…

【C++】多态(上)

个人主页~ 多态 一、多态的概念二、多态的定义以及实现1、多态的构成条件2、虚函数3、虚函数的重写虚函数重写的两个特殊情况①协变② 析构函数的重写 4、C11的override和final&#xff08;1&#xff09;final&#xff08;2&#xff09;override 5、重载、重写、隐藏的对比 三、…

普中51单片机

参考&#xff1a;51单片机快速入门教程2022&#xff08;普中51开发板A2新版&#xff09;--绍兴文理学院元培学院《单片机原理与应用》课程建设_哔哩哔哩_bilibili 1.以管理员启动&#xff0c;破解

【开源免费】基于SpringBoot+Vue.JS校园资料分享平台(JAVA毕业设计)

本文项目编号 T 059 &#xff0c;文末自助获取源码 \color{red}{T059&#xff0c;文末自助获取源码} T059&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

【抖音矩阵源码开发:短视频矩阵SEO技术深度解析与实践指南】

开发部署短视频矩阵源码的主要步骤如下&#xff1a; 开发和部署短视频矩阵平台涉及一系列精细化的技术步骤&#xff0c;旨在确保系统的高效构建与稳定运行。该流程起始于开发环境的精心部署&#xff0c;涵盖PHP环境的搭建、集成开发环境&#xff08;IDE&#xff09;的配置以及…

STM32编码器接口笔记

1. 引言 在现代控制系统中&#xff0c;编码器扮演着非常重要的角色。它就像一个精密的测量工具&#xff0c;可以告诉我们机械部件的位置和运动状态。在STM32微控制器中&#xff0c;编码器接口可以轻松地与各种编码器连接&#xff0c;实现精确的控制。我将在这里探讨STM32编码器…

string类的使用(上)

目录 1.string类的概念 2.string的构造函数&#xff08;实现初始化&#xff09; 3.对string类容量获取和操作 4.string类对象的访问和遍历 4.1operator[] 4.2begin和end 4.3 rbegin 和 rend 4.4迭代器分类 4.5范围for&#xff08;自动实现遍历&#xff09; 5.string类…

打造高效舒适的气膜网球馆—轻空间

气膜网球馆&#xff0c;作为现代运动设施的创新选择&#xff0c;其成本构成涵盖多个重要方面&#xff0c;确保为运动者提供最佳体验。 一、膜材选择 膜材是气膜网球馆的核心&#xff0c;品质不同直接影响成本。高品质膜材不仅增强了耐用性&#xff0c;也能有效阻挡外界气候影响…

【展会回顾】阿尔泰科技2024上海工博会精彩瞬间,圆满收官 ,感恩遇见 ,共赴新程!

9月24日&#xff0c;以“工业聚能 新质领航”为主题的第二十四届中国国际工业博览会&#xff08;以下称“工博会”&#xff09;在国家会展中心&#xff08;上海&#xff09;举办&#xff0c;展示了一批代表中国工业领域的最新技术与前沿成果。 本届工博会的主题“工业聚能&…

Pycharm 本地搭建 stable-diffusion-webui

一、下载工程源码 Github官方连接 https://github.com/AUTOMATIC1111/stable-diffusion-webui 二、Pycharm打开工程 1、设置环境 文件-设置-项目-Python解析器-添加解释器-添加本地解释器 Conda环境-创造新环境-Python版本3.10 注意一定要选择Python3.10版本&#xff0c;否…

uni-app canvas文本自动换行

封装 支持单行文本超出换行。多行文本顺位排版 // 填充自动换行的文本function fillFeedText({ctx, text, x, y, maxWidth, lineHeight, color, size}) {// 文本配置ctx.setFontSize(size);ctx.setFillStyle(color);// 计算文本换行宽高&#xff0c;换行逻辑const words text…

JavaScript中的函数定义

第8章 函数 在JS中函数即对象&#xff0c;程序可以随意操控他们。可以把函数赋值给变量&#xff0c;或者作为参数传递给其他函数。因为函数就是对象&#xff0c;所以可以给他们设置属性&#xff0c;甚至调用他们的方法。 JavaScript的函数可以嵌套在其他函数中定义&#xff0…

初识C语言(四)

目录 前言 十一、常见关键字&#xff08;补充&#xff09; &#xff08;1&#xff09;register —寄存器 &#xff08;2&#xff09;typedef类型重命名 &#xff08;3&#xff09;static静态的 1、修饰局部变量 2、修饰全局变量 3、修饰函数 十二、#define定义常量和宏…