【优选算法】分治 {三分快排:三指针优化,随机选key,快速选择算法;归并排序:统计数组中的逆序对,统计数组中的翻转对;相关编程题解析}

一、经验总结

1.1 三分快排

优化一:三指针优化

之前学习的快速排序无法妥善处理相等或重复序列的排序问题(有序且三数取中无效),使快速排序的效率无法达到最优。

为了解决重复序列的问题,我们将原先的双指针法(前后指针)优化为三指针,将数组划分成三块:

  • [0, left]:< key
  • [left+1, right-1]:==key
  • [riight, n-1]:> key
  • 其中left标记<key区间的最右侧;i负责从左向右遍历数组;right标记>key区间的最左侧;

之后,再利用分治思想将<key和>key的部分进行排序即可,所有==key的部分已经移动到了最终的位置上。完美的解决了重复序列的问题。

举个极端一点的例子,对于全体重复的序列,原先需要partition n次,每次都要将区间遍历一遍是一个典型的复杂度为O(N^2)的算法。现在,仅需要partition一次就可以将所有数字归入==key的区间,不再有<key和>key的部分排序结束,复杂度降为O(N)。

优化二:随机选key

之前我们使用的是取最左(右)值为key、三数取中为key。实际上随机取key可以使数组划分的更为均匀,每个区间都是等概率划分的。使快速排序的时间复杂度更接近于O(NlogN)。

三分快排的应用:快速选择算法

快速选择算法是解决Topk问题的最优方案,之前学习过的利用堆解决Topk问题时间复杂度为O(NlogK),已经相当高效了。但是快速选择算法可以将时间复杂度优化为O(N)。

快速选择算法的原理是基于三分快排的,但并不需要将数组完全排序,而是将数组划分为三块以后,将三块区间内元素的个数与k比较,再进行递归分割,直到将最小(最大)的前k个数全部移动到数组前面(后面)。

Topk问题又分前k小(大)、第k小(大)。前k小只需要将最小的前k个数全部移动到数组前面即可,<key区间内的元素个数只要==k就可以返回。而第k小不仅要移动最小的前k个数,还必须找到第k个,即第k个数必须刚好落在==key的区间内才能返回。


1.2 归并排序

利用归并排序统计数组中的逆序对

所谓逆序对是指前大后小的一对数,利用归并排序统计逆序对可以将暴力解法的时间复杂度O(N^2),优化为O(NlogN)。算法思路如下:

  1. 将数组从中间一分为二,先统计左右区间内的逆序对,并进行排序。
  2. 然后再归并左右区间的过程中,统计一左一右跨两个区间的逆序对,有两个策略可供选择
    1. 升序排序:以右区间中的元素cur2为基点,在左区间中找大于cur2的元素cur1,因为是升序所以左区间之后的元素都大。
    2. 降序排序:以左区间中的元素cur1为基点,在右区间中找小于cur1的元素cur2,因为是降序所以右区间之后的元素都小
  3. 颠来倒去其实都是在cur1 > cur2的时候,统计逆序对的数量,只是基点不同:先左后右是降序,先右后左是升序
  4. 逆序对的判定规则与左右区间归并的比较规则相同,所以可以在左右区间归并的过程中顺道统计逆序对的个数。

利用归并排序统计数组中的翻转对

不同于逆序对,翻转对要求前一个数大于后一个数的两倍。翻转对的判定规则与左右区间归并的比较规则不同,也就不能顺道了。但是翻转对的判定与统计仍然可以利用归并排序的分治和左右区间有序的条件,只是需要在左右区间归并之前,先一步进行一左一右的翻转对统计即可。

在统计一左一右跨两个区间的翻转对时,算法规律和逆序对相同:在cur1/2 > cur2(乘法改除法防溢出)的时候,统计翻转对的数量。先左后右是降序,先右后左是升序。算法还可以使用同向双指针进行优化,只需要将左右两个区间遍历一遍O(N),就可以完成统计。不会影响整体归并排序的复杂度O(NlogN)。


二、相关编程题

2.1 三分快排

2.1.1 颜色分类

题目链接

75. 颜色分类 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int n = nums.size();
        int left = -1, i = 0, right = n;
        while (i < right) {
            if (nums[i] == 0) {
                if (++left != i)
                    swap(nums[left], nums[i]);
                ++i;
            } else if (nums[i] == 1) {
                ++i;
            } else if (nums[i] == 2 && --right != i) {
                swap(nums[i], nums[right]);
            }
        }
    }
};

2.1.2 优化快速排序

题目链接

912. 排序数组 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        srand(time(nullptr));
        QuickSort(nums, 0, nums.size()); //注意区间是左闭右开
        return nums;
    }

    void QuickSort(vector<int>& nums, int begin, int end)
    {
        if(end - begin < 2) return;
        int key = nums[rand()%(end-begin)+begin]; //随机取key
        int left = begin-1, i = begin, right = end;
        while(i < right)
        {
            if(nums[i] < key)   swap(nums[++left], nums[i++]);
            else if(nums[i] == key) ++i;
            else swap(nums[--right], nums[i]);
        }
        //left和right都是闭端点
        QuickSort(nums, begin, left+1); //left做end需要+1(右开)
        QuickSort(nums, right, end); //right做begin不需要+1(左闭)
    }
};

2.1.3 数组中的第k个最大元素

题目链接

215. 数组中的第K个最大元素 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

//快速选择算法 O(N)
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        srand(time(nullptr));
        return QuickSelect(nums, 0, nums.size()-1, k); //注意区间是左闭右闭
    }

    int QuickSelect(vector<int>& nums, int begin, int end, int k)
    {
        // 当区间内只有一个元素时,直接返回这个元素
        if(begin == end) return nums[begin];
        // 随机选key
        int key = nums[rand()%(end-begin+1)+begin]; 
        // 将区间内的元素划分成三块
        int left = begin-1, i = begin, right = end+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]);
        }
        //核心逻辑
        if(end-right+1 >= k) //c>=k
            return QuickSelect(nums, right, end, k); 
        else if(end-left >= k) //b+c>=k
            return key;
        else    
            return QuickSelect(nums, begin, left, k-(end-left)); //找k-b-c大的数
    }
};

//堆算法 O(NlogK)
class Solution {
public:K
    int findKthLargest(vector<int>& nums, int k) {
        vector<int> leastHeap(k);
        for(int i = 0; i < k; ++i)
        {
            leastHeap[i] = nums[i];
        }

        for(int i = k-2/2; i >= 0; --i)
        {
            AdjustDown(leastHeap, i);
        }

        for(int i = k; i < nums.size(); ++i)
        {
            if(nums[i] > leastHeap[0]) 
            {
                leastHeap[0] = nums[i];
                AdjustDown(leastHeap, 0);
            }
        }

        return leastHeap[0];
    }

    void AdjustDown(vector<int>& nums, int root) {
        int parent = root;
        int child = parent * 2 + 1;
        int n = nums.size();
        while (child < n) {
            if (child + 1 < n && nums[child + 1] < nums[child]) {
                ++child;
            }
            if (nums[child] < nums[parent]) {
                swap(nums[child], nums[parent]);
                parent = child;
                child = parent * 2 + 1;
            } else {
                break;
            }
        }
    }
};

2.1.4 最小的k个数

题目链接

LCR 159. 库存管理 III - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:
    vector<int> inventoryManagement(vector<int>& stock, int cnt) {
        srand(time(nullptr));
        if(cnt > 0)
        	QuickSelect(stock, 0, stock.size()-1, cnt);
        return vector<int> (stock.begin(), stock.begin()+cnt);
    }

    void QuickSelect(vector<int>& nums, int begin, int end, int k)
    {
        // 当区间内只有一个元素时,直接返回
        if(begin == end) return;
        // 随机选key
        int key = nums[rand()%(end-begin+1)+begin]; 
        // 将区间内的元素划分成三块
        int left = begin-1, i = begin, right = end+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]);
        }
        //核心逻辑
        if(left-begin+1 > k) //a>k
            QuickSelect(nums, begin, left, k); 
        else if(right-begin >= k) //a+b>=k
            return;
        else    
            QuickSelect(nums, right, end, k-(right-begin)); 
    }
};

2.2 归并排序

2.2.1 归并排序

题目链接

912. 排序数组 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        vector<int> tmp(nums.size()); //辅助数组在递归外创建效率更高
        MergeSort(nums, 0, nums.size(), tmp);
        return nums;
    }

    void MergeSort(vector<int>& nums, int begin, int end, vector<int>& tmp)
    {
        if(end-begin < 2) return;
        int begin1 = begin;
        int end1 = begin+(end-begin)/2;
        int begin2 = end1;
        int end2 = end;
        MergeSort(nums, begin1, end1, tmp);
        MergeSort(nums, begin2, end2, tmp);
        //归并左右两顺序区间
        int i = begin;
        while(begin1 < end1 && begin2 < end2)
        {
            if(nums[begin1] <= nums[begin2])
            {
                tmp[i++] = nums[begin1++];
            }
            else
            {
                tmp[i++] = nums[begin2++];
            }
        }
        while(begin1 < end1)
        {
            tmp[i++] = nums[begin1++];
        }
        while(begin2 < end2)
        {
            tmp[i++] = nums[begin2++];
        }
		
        for(int i = begin; i < end; ++i)
        {
            nums[i] = tmp[i];
        }
    }
};

2.2.2 数组中的逆序对

题目链接

LCR 170. 交易逆序对的总数 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:
    int reversePairs(vector<int>& record) {
        vector<int> tmp(record.size());
        return MergeSort(record, 0, record.size(), tmp);
    }
    
    int MergeSort(vector<int>& nums, int begin, int end, vector<int>& tmp)
    {
        //如果区间内的元素个数小于2,返回0个逆序对
        if(end-begin < 2) return 0;
        //将区间从中间划分成左右两个区间
        int begin1 = begin;
        int end1 = begin+(end-begin)/2;
        int begin2 = end1;
        int end2 = end;
        int cnt = 0;
		//左区间的个数+排序;右区间的个数+排序
        cnt += MergeSort(nums, begin1, end1, tmp);
        cnt += MergeSort(nums, begin2, end2, tmp);
		//一左一右的个数+归并排序
		//策略一:以cur2为基点,在之前找大
        int i = begin;
        while(begin1 < end1 && begin2 < end2)
        {
            if(nums[begin1] <= nums[begin2])
            {
                tmp[i++] = nums[begin1++];
            }
            else
            {
                cnt += end1-begin1;
                tmp[i++] = nums[begin2++];
            }
        }
        //策略二:以cur1为基点在之后找小
        // while(begin1 < end1 && begin2 < end2)
        // {
        //     if(nums[begin1] > nums[begin2])
        //     {
        //         cnt += end2 - begin2;
        //         tmp[i++] = nums[begin1++];
        //     }
        //     else
        //     {
        //         tmp[i++] = nums[begin2++];
        //     }
        // }
        while(begin1 < end1)
        {
            tmp[i++] = nums[begin1++];
        }
        while(begin2 < end2)
        {
            tmp[i++] = nums[begin2++];
        }
		//将归并排序好的区间元素拷贝回原数组
        for(int i = begin; i < end; ++i)
        {
            nums[i] = tmp[i];
        }
        return cnt; //返回的就是区间内的逆序对总数
    }
};

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

题目链接

315. 计算右侧小于当前元素的个数 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
    vector<int> tmp1, tmp2;
public:
    vector<int> countSmaller(vector<int>& nums) {
        tmp1.resize(nums.size()); //用于归并排序nums
        tmp2.resize(nums.size()); //用于归并index,并不是排序,只是执行和nums同样的操作
        vector<int> ret(nums.size(), 0); //结果数组
        vector<int> index(nums.size()); //用于映射每个元素的原始下标
        for(int i = 0; i < nums.size(); ++i)
        {
            index[i] = i;
        }
        MergeSort(nums, 0, nums.size(), ret, index);
        return ret;
    }

    void MergeSort(vector<int>& nums, int begin, int end, vector<int>& ret, vector<int>& index)
    {
        if(end - begin < 2) return;
		//将数组从中间分成两个区间
        int begin1 = begin;
        int end1 = begin1+(end-begin)/2;
        int begin2 = end1;
        int end2 = end;
		//先分别处理左右区间内的个数
        MergeSort(nums, begin1, end1, ret, index);
        MergeSort(nums, begin2, end2, ret, index);
		//再处理一左一右的个数
        int i = begin;
        while(begin1 < end1 && begin2 < end2)
        {
            if(nums[begin1] > nums[begin2]) //降序排序
            {
                ret[index[begin1]] += end2-begin2; //注意:1.获取元素的原始下标 2.+=可能在左右区间中已经统计过了
                tmp1[i] = nums[begin1];
                tmp2[i++] = index[begin1++]; //nums数组中的元素移动到哪,index数组中的原始下标就移动到哪
            }
            else
            {
                tmp1[i] = nums[begin2];
                tmp2[i++] = index[begin2++];
            }
        }

        while(begin1 < end1)
        {
            tmp1[i] = nums[begin1];
            tmp2[i++] = index[begin1++];
        }

        while(begin2 < end2)
        {
            tmp1[i] = nums[begin2];
            tmp2[i++] = index[begin2++];
        }

        for(int i = begin; i < end; ++i)
        {
            nums[i] = tmp1[i];
            index[i] = tmp2[i];
        }

    }
};

2.2.4 翻转对

题目链接

493. 翻转对 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
    vector<int> tmp;
public:
    int reversePairs(vector<int>& nums) {
        int n = nums.size();
        tmp.resize(n);
        return MergeSort(nums, 0, n);
    }   

    int MergeSort(vector<int>& nums, int begin, int end)
    {
        if(end-begin < 2) return 0;
		//将数组从中间划分成左右两个区间
        int begin1 = begin;
        int end1 = begin1+(end-begin)/2;
        int begin2 = end1;
        int end2 = end;
        int cnt = 0;
        //分别去左右区间统计翻转对并进行排序
        cnt += MergeSort(nums, begin1, end1);
        cnt += MergeSort(nums, begin2, end2);
		//统计一左一右跨两个区间的翻转对
        int cur1 = begin1, cur2 = begin2;
        while(cur1 < end1 && cur2 < end2)
        {
            //策略一:在cur1后面找*2都比它小的数
            if(nums[cur1]/2.0 > nums[cur2]) //乘法改除法,防溢出
            {
                cnt += end2-cur2; //由于是降序,所以之后的都小
                ++cur1;
            }
            else
                ++cur2;
            //策略二:在cur2前面找/2都比它大的数
            // if(nums[cur1]/2.0 > nums[cur2])
            // {
            //     cnt += end1-cur1; //由于是升序,所以之后的都大
            //     ++cur2;
            // }
            // else
            //     ++cur1;
        }

        int i = begin;
        while(begin1 < end1 && begin2 < end2)
        {
            //降序排序
            if(nums[begin1] >= nums[begin2])
            {
                tmp[i++] = nums[begin1++];
            }
            else
            {
                tmp[i++] = nums[begin2++];
            }
            //升序排序
            // if(nums[begin1] <= nums[begin2])
            // {
            //     tmp[i++] = nums[begin1++];
            // }
            // else
            // {
            //     tmp[i++] = nums[begin2++];
            // }
        }

        while(begin1 < end1)
        {
            tmp[i++] = nums[begin1++];
        }
        while(begin2 < end2)
        {
            tmp[i++] = nums[begin2++];
        }

        for(int i = begin; i < end; ++i)
        {
            nums[i] = tmp[i];
        }
        return cnt;
    }
};

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

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

相关文章

java项目之智能家居系统源码(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的智能家居系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于Springboot的智能家居系…

Redis:redis基础

Redis Remote Dictionary Service即远程字典服务 一个基于内存的key-value结构数据库,在开发中常常作为缓存存储不经常被改变的数据 基于内存存储,读写性能高 在企业中应用广泛 Redis介绍 用C语言开发的开源高性能键值对数据库,可以达到10w的qps,可以存储丰富的value类型…

IDEA中各种Maven相关问题(文件飘红、下载依赖和启动报错)

错误情况 包名、类名显示红色、红色波浪线&#xff0c;大量依赖提示不存在&#xff08;程序包xxx不存在&#xff09; 工程无法启动 一、前提条件 1、使用英文原版IDEA 汉化版的可能有各种奇怪的问题。建议用IDEA英文版&#xff0c;卸载重装。 2、下载maven&#xff0c;配置环…

评测 香橙派OrangePi在智能交通上的应用

1、OrangePi应用场景 关于 Orange Pi AI Pro 开发板是香橙派联合华为精心打造的高性能 AI 开发板&#xff0c;其搭载了昇腾 AI 处理器&#xff0c;可提供 8TOPS INT8 的计算能力&#xff0c;内存提供了 8GB 和 16GB两种版本。可以实现图像、视频等多种数据分析与推理计算&#…

12-常用类

1. 包装类 针对八种基本数据类型封装的相应的引用类型。 有了类的特点&#xff0c;就可以调用类中的方法。&#xff08;为什么要封装&#xff09; 基本数据类型包装类booleanBooleanchar CharacterbyteByteshortShortintIntegerlongLongfloatFloatdoubleDouble 1.1 …

seRsync + Rsync 实时同步

1&#xff0c;结构图 2&#xff0c;节点A 2.1 安装rsync yum install -y rsync2.2 安装seRsync 下载这个压缩包sersync2.5.4_64bit_binary_stable_final.tar.gz 解压后&#xff0c;将sersync2复制到系统可执行程序路径&#xff1a;/usr/local/bin/&#xff1b;创建sersync配…

Visual Studio 的使用

目录 1. 引言 2. 安装和配置 2.1 系统要求 2.2 安装步骤 2.3 初次配置 3. 界面介绍 3.1 菜单栏和工具栏 3.2 解决方案资源管理器 3.3 编辑器窗口 3.4 输出窗口 3.5 错误列表 3.6 属性窗口 4. 项目管理 4.1 创建新项目 4.2 导入现有项目 4.3 项目属性配置 5. 代…

SpringSecurity6从入门到实战之SpringSecurity快速入门

SpringSecurity6从入门到实战之SpringSecurity快速入门 环境准备 依赖版本号springsecurity6.0.8springboot3.0.12JDK17 这里尽量与我依赖一致,免得在学习过程中出现位置的bug等 创建工程 这里直接选择springboot初始化快速搭建工程,导入对应的jdk17进行创建 直接勾选一个web…

QtCreator调试运行工程报错,无法找到相关库的的解决方案

最新在使用国产化平台做qt应用开发时&#xff0c;总是遇到qtcreator内调试运行 找不到动态库的问题&#xff0c;为什么会出现这种问题呢&#xff1f;明明编译的时候能够正常通过&#xff0c;运行或者调试的时候找不到相关的库呢&#xff1f;先说结论&#xff0c;排除库本身的问…

计算机网络7——网络安全1 概述与加密

文章目录 一、网络安全问题概述1、计算机网络面临的安全性威胁2、安全的计算机网络3、数据加密模型 二、两类密码体制1、对称密钥密码体制2、公钥密码体制 随着计算机网络的发展&#xff0c;网络中的安全问题也日趋严重。当网络的用户来自社会各个阶层与部门时&#xff0c;大量…

Raven2掠夺者2渡鸦2账号怎么验证 注册怎么验证账号教程

《渡鸦2》作为韩国孕育的次世代MMORPG手游巨制&#xff0c;是《Raven》系列辉煌传奇的最新篇章&#xff0c;它在暗黑奇幻的广袤天地间再度挥洒创意&#xff0c;深度融合前所未有的游戏机制与海量新颖内容&#xff0c;为该类型游戏树立了崭新的里程碑。公测日期锁定在2024年5月2…

2024 在Pycharm管理数据库

2024 在Pycharm管理数据库 Pycharm 社区版DataBase Navigator 数据库管理插件(Plugins)安装使用(sqlite为例添加数据) 文章目录 2024 在Pycharm管理数据库一、Pycharm数据库配置1、Database Navigator插件安装2、连接数据库 二、数据库使用1、插件自带基本操作2、控制台操作 …

LAMP集群分布式实验报告

前景&#xff1a; 1.技术成熟度和稳定性&#xff1a; LAMP架构&#xff08;Linux、Apache、MySQL、PHP&#xff09;自1998年提出以来&#xff0c;经过长时间的发展和完善&#xff0c;已经成为非常成熟和稳定的Web开发平台。其中&#xff0c;Linux操作系统因其高度的灵活性和稳…

恢复视频3个攻略:从不同情况下的恢复方法到实践!

随着科技的进步&#xff0c;我们的生活被各种各样的数字内容所包围&#xff0c;其中&#xff0c;视频因其独特的记录性质&#xff0c;承载着许多重要的资料。但不管是自媒体人还是普通人日常生活随手一拍&#xff0c;都会遇到误删视频的情况。为了帮助您找回手机视频&#xff0…

2024全新升级版家政服务小程序源码 支持家政预约+上门服务+SAAS系统+可二开

随着科技的飞速发展&#xff0c;家政服务行业也迎来了数字化转型的浪潮。为了满足市场日益增长的需求&#xff0c;分享一款2024全新升级版的家政服务小程序源码。该源码不仅支持家政预约和上门服务&#xff0c;还集成了SAAS系统&#xff0c;并支持二次开发&#xff0c;为用户带…

【机器学习300问】103、简单的经典卷积神经网络结构设计成什么样?以LeNet-5为例说明。

一个简单的经典CNN网络结构由&#xff1a;输入层、卷积层、池化层、全连接层和输出层&#xff0c;这五种神经网络层结构组成。它最最经典的实例是LeNet-5&#xff0c;它最早被设计用于手写数字识别任务&#xff0c;包含两个卷积层、两个池化层、几个全连接层&#xff0c;以及最…

企业级OV SSL证书的应用场景和加密手段

为了保护数据传输的安全性与用户隐私&#xff0c;企业级OVSSL&#xff08;Organization Validation SSL&#xff09;证书成为众多企业的首选安全解决方案。本文将深入探讨OVSSL证书的应用场景及其实现数据加密的核心手段&#xff0c;为企业构建坚不可摧的在线信任桥梁提供指南。…

【面试干货】约瑟夫问题

【面试干货】约瑟夫问题 1、实现思想2、代码实现 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 约瑟夫问题 是一个经典的数学问题&#xff0c;描述如下&#xff1a;编号为1, 2, …, n的n个人按顺时针方向围坐一圈&#xff0c;从第1个人开始…

实战教程:使用Go的net/http/fcgi包打造高性能Web应用

实战教程&#xff1a;使用Go的net/http/fcgi包打造高性能Web应用 简介理解FCGI协议FastCGI工作原理CGI与FastCGI对比其他接口对比 初步使用net/http/fcgi包设置和配置基础环境一个简单的FastCGI应用示例本地测试与调试 深入net/http/fcgi包的高级用法探索net/http/fcgi的主要功…

《最新出炉》系列入门篇-Python+Playwright自动化测试-46-鼠标滚轮操作

宏哥微信粉丝群&#xff1a;https://bbs.csdn.net/topics/618423372 有兴趣的可以扫码加入 1.简介 有些网站为了节省流量和资源&#xff0c;提高加载效率&#xff0c;采用的是动态加载&#xff08;懒加载&#xff09;的&#xff0c;也就是当拖动页面右侧滚动条后会自动加载网…