算法思想总结:分治思想

news2024/11/27 17:54:52

一、颜色划分

. - 力扣(LeetCode)

class Solution {
public:
    void sortColors(vector<int>& nums) 
    {
       //三路划分的思想
       int n=nums.size();
       int left=-1, right=n,cur=0;
       while(cur<right)
       {
        if(nums[cur]==0) swap(nums[++left],nums[cur++]);
        else if(nums[cur]==1) ++cur;
        else swap(nums[--right],nums[cur]);
       }  
    }
};

二、快排优化(三路划分+随机key)

. - 力扣(LeetCode)

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) 
    {
        //三路划分+随机取key
       srand((unsigned int)time(NULL));//时间种子
       qsort(nums,0,nums.size()-1);
       return nums;
    }

    void qsort(vector<int>& nums,int begin,int end)
    {
        if(begin>=end) return;
       int key=nums[rand()%(end-begin+1)+begin];
       int left=begin-1,right=end+1,cur=begin;
       while(cur<right)
       {
        if(nums[cur]<key) swap(nums[++left],nums[cur++]);
        else if(nums[cur]==key) ++cur;
        else  swap(nums[--right],nums[cur]);
       }
       qsort(nums,begin,left);
       qsort(nums,right,end);
    }
};

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

. - 力扣(LeetCode)

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) 
    {
        //第k大  堆排
        //第k小
        //前k大  //快速选择算法
        //前k小
          //三路划分 
      srand(time(NULL));//时间种子
       return qsort(nums,0,nums.size()-1,k);
    }

     int qsort(vector<int>& nums,int begin,int end,int k)
    {
        if(begin>=end) return nums[begin];
        //1随机选择基准元素
       int key=nums[rand()%(end-begin+1)+begin];
       //2根据基准元素将数组分三块
       int left=begin-1,right=end+1,cur=begin;
       while(cur<right)
       {
        if(nums[cur]<key) swap(nums[++left],nums[cur++]);
        else if(nums[cur]==key) ++cur;
        else  swap(nums[--right],nums[cur]);
       }
       //3.开始分情况讨论
       int c=end-right+1;
       int b=right-1-(left+1)+1;
       if(c>=k) return qsort(nums,right,end,k);
       else if(b+c>=k) return key;
       else return qsort(nums,begin,left,k-b-c);
    }
};

四、库存管理(Top-k)

. - 力扣(LeetCode)

  

class Solution {
public:
    //最小的k个数
    vector<int> inventoryManagement(vector<int>& nums, int k)
    {
        srand(time(NULL));//时间种子
        qsort(nums,0,nums.size()-1,k);
        return {nums.begin(),nums.begin()+k};
    }
 
     void qsort(vector<int>& nums,int begin,int end,int k)
     {
      if(begin>=end) return;
      int key=nums[rand()%(end-begin+1)+begin];
      int left=begin-1,right=end+1,cur=begin;
      while(cur<right)
      {
        if(nums[cur]<key) swap(nums[++left],nums[cur++]);
        else if(nums[cur]==key) ++cur;
        else  swap(nums[--right],nums[cur]);
      }
        int a=left+1,b=right-left+1;
        if(a>k) qsort(nums,begin,left,k);
        else if(a+b>=k) return;
        else qsort(nums,right,end,k-a-b); 
    }
};

五、归并排序

. - 力扣(LeetCode)

class Solution {
public:
    //需要一个辅助数组
    vector<int> temp;//辅助数组
    vector<int> sortArray(vector<int>& nums) 
    {
        int n=nums.size();
        temp.resize(n);
        MergeSort(nums,0,n-1);
        return nums;
    }
    void MergeSort(vector<int>& nums,int left,int right)
    {
       if(left>=right) return;
       //1、找到中间位置
       int mid=(left+right)>>1;
       //2 排好左区间,排好右区间
       MergeSort(nums,left,mid);
       MergeSort(nums,mid+1,right);
       //3.进行归并
       int i=left;//帮助还原
       int cur1=left,cur2=mid+1;
       while(cur1<=mid&&cur2<=right) 
            temp[i++]=nums[cur1]<nums[cur2]?nums[cur1++]:nums[cur2++];
        //4、处理没有归并的区间
        while(cur1<=mid)   temp[i++]=nums[cur1++];
        while(cur2<=right) temp[i++]=nums[cur2++];
        //5、还原
       for(int j=left;j<=right;++j) nums[j]=temp[j];
    }
};

六、交易逆序对的总数

. - 力扣(LeetCode)

升序版本:

class Solution {
public:
    vector<int> temp;//标记数组
     //1、升序,有多少个数比当前位置大
    int reversePairs(vector<int>& nums) 
    {
        int n=nums.size();
        temp.resize(n);
        return MergeSort(nums,0,n-1);
    }

    int MergeSort(vector<int>& nums,int left,int right)
    {
        if(left>=right) return 0;
        int mid=(right+left)>>1;
        int ret=0;
        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]) temp[i++]=nums[cur1++];
            else 
            {
                ret+=mid-cur1+1;
                temp[i++]=nums[cur2++];
            }
           }
        while(cur1<=mid) temp[i++]=nums[cur1++];
        while(cur2<=right) temp[i++]=nums[cur2++];
        //开始还原
        for(int j=left;j<=right;++j) nums[j]=temp[j-left];
        return ret;
    }
};

降序版本:

class Solution {
public:
    vector<int> temp;//标记数组
     //1、升序,前面有多少个数比当前位置大
     //2、降序,后面有多少数比我当前位置小
    int reversePairs(vector<int>& nums) 
    {
        int n=nums.size();
        temp.resize(n);
        return MergeSort(nums,0,n-1);
    }

    int MergeSort(vector<int>& nums,int left,int right)
    {
        if(left>=right) return 0;
        int mid=(right+left)>>1;
        int ret=0;
        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]) temp[i++]=nums[cur2++];
            else 
            {
                ret+=right-cur2+1;//说明我cur1后面的位置都比你大
                temp[i++]=nums[cur1++];
            }
           }
        while(cur1<=mid) temp[i++]=nums[cur1++];
        while(cur2<=right) temp[i++]=nums[cur2++];
        //开始还原
        for(int j=left;j<=right;++j) nums[j]=temp[j-left];
        return ret;
    }
};

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

. - 力扣(LeetCode)

class Solution {
public:
     vector<int> ret;//记录返回的结果
     vector<int> index;//建立一个下标数组和nums数据绑定
     vector<int> numstemp;
     vector<int> indextemp;
    vector<int> countSmaller(vector<int>& nums) 
    {
      int n=nums.size();
      ret.resize(n);
      index.resize(n);
      numstemp.resize(n);
      indextemp.resize(n);
      //初始化index数组
      for(int i=0;i<n;++i) index[i]=i;//存当前位置对应的下标
      Mersort(nums,0,n-1);
      return ret;
    }

     void Mersort(vector<int>& nums,int left,int right)
     {
        if(left>=right) return;
        int mid=(left+right)>>1;
        Mersort(nums,left,mid);
        Mersort(nums,mid+1,right);
        //开始归并  升序,有多少个数比我当前的位置小
        int cur1=left,cur2=mid+1,i=left;
        while(cur1<=mid&&cur2<=right)
        {
            if(nums[cur1]>nums[cur2])
            {
                ret[index[cur1]]+=right-cur2+1;
                numstemp[i]=nums[cur1];
                indextemp[i++]=index[cur1++];
            }
            else
            {
                 numstemp[i]=nums[cur2];
                indextemp[i++]=index[cur2++];
            }
        }
        //填没有进去的区间
        while(cur1<=mid) 
        {
            numstemp[i]=nums[cur1];
            indextemp[i++]=index[cur1++];
        }
        while(cur2<=right)
        {
             numstemp[i]=nums[cur2];
             indextemp[i++]=index[cur2++];
        }
       //还原
       for(int j=left;j<=right;++j)
       {
        nums[j]=numstemp[j];
        index[j]= indextemp[j];
       }

     }
};

八、翻转对

. - 力扣(LeetCode)

升序版本:前面有多少元素的一半比我大(升序) 固定cur2  找cur1

class Solution {
public:
    vector<int> temp;//标记数组
     //1、升序,前面有多少个数比当前位置大
     //2、降序,后面有多少数比我当前位置小
    int reversePairs(vector<int>& nums) 
    {
        int n=nums.size();
        temp.resize(n);
        return MergeSort(nums,0,n-1);
    }

    int MergeSort(vector<int>& nums,int left,int right)
    {
        if(left>=right) return 0;
        int mid=(right+left)>>1;
        int ret=0;
        ret+=MergeSort(nums,left,mid);
        ret+=MergeSort(nums,mid+1,right);
        //归并之前先统计翻转对
         int cur1=left,cur2=mid+1;
        //升序  锁定cur2,然后统计cur1的一半是否大于cur2
         while(cur2<=right)
         {
            while(cur1<=mid&&nums[cur2]>=nums[cur1]/2.0) ++cur1;
            //此时cur2的位置和cur2后面的位置都符合条件
            ret+=mid-cur1+1;
            ++cur2;
         }
        //开始进行归并
        cur1=left,cur2=mid+1;//恢复cur1和cur2为初始位置
        int i=left;
        while(cur1<=mid&&cur2<=right)
           temp[i++]=nums[cur1]<nums[cur2]?nums[cur1++]:nums[cur2++];//降序
        while(cur1<=mid) temp[i++]=nums[cur1++];
        while(cur2<=right) temp[i++]=nums[cur2++];
        //开始还原
        for(int j=left;j<=right;++j) nums[j]=temp[j];
        return ret;
    }
};

降序版本:后面有多少元素的两倍比我小(降序) 固定cur1  找cur2

class Solution {
public:
    vector<int> temp;//标记数组
     //1、升序,前面有多少个数比当前位置大
     //2、降序,后面有多少数比我当前位置小
    int reversePairs(vector<int>& nums) 
    {
        int n=nums.size();
        temp.resize(n);
        return MergeSort(nums,0,n-1);
    }

    int MergeSort(vector<int>& nums,int left,int right)
    {
        if(left>=right) return 0;
        int mid=(right+left)>>1;
        int ret=0;
        ret+=MergeSort(nums,left,mid);
        ret+=MergeSort(nums,mid+1,right);
        //归并之前先统计翻转对
         int cur1=left,cur2=mid+1;
        //降序  锁定cur1,然后统计cur2位置的2倍是否小于cur1
         while(cur1<=mid)
         {
            while(cur2<=right&&nums[cur2]>=nums[cur1]/2.0) ++cur2;
            //此时cur2的位置和cur2后面的位置都符合条件
            ret+=right-cur2+1;
            ++cur1;
         }
        //开始进行归并
        cur1=left,cur2=mid+1;//恢复cur1和cur2为初始位置
        int i=0;
        while(cur1<=mid&&cur2<=right)
           temp[i++]=nums[cur1]>nums[cur2]?nums[cur1++]:nums[cur2++];//降序
        while(cur1<=mid) temp[i++]=nums[cur1++];
        while(cur2<=right) temp[i++]=nums[cur2++];
        //开始还原
        for(int j=left;j<=right;++j) nums[j]=temp[j-left];
        return ret;
    }
};

九、区间和的个数

. - 力扣(LeetCode)

class Solution {
public:
    vector<long> dp;
    vector<long> temp;
    int lower, upper;
    int countRangeSum(vector<int>& nums, int _lower, int _upper)
    {
        //设置一个前缀和数组dp 则问题等价于求所有下标为(i,j)满足dp[j]-dp[i]属于lower和upper区间
        //此时是有左右两段的,左右两段是分别有序的,对前缀和数组排序并不会修改数组中元素的值,只是改变了元素位置,如对leftright=35位置的前缀和排序,排序后前缀和35位置的数还是原来35位置的数,只是排列变化了
        //排列并不会影响前缀和
        lower = _lower;
        upper = _upper;
        int n = nums.size();
        dp.resize(n+1);//补了0减少了边界的处理前缀和 presum[i..j] = presum[j] - presum[i - 1] 所以补了0为了减少边界处理
        temp.resize(n+1);
        for(int i=1;i<=n;++i) dp[i]=dp[i-1]+nums[i-1];
        return MergeSort(0,n);
    }

    int MergeSort(int left, int right)
    {
        if (left == right) return 0;
        int mid = (left + right) >> 1;
        int ret = 0;
        ret += MergeSort(left, mid);
        ret += MergeSort(mid + 1, right);
        //开始找符合条件的的区间 //升序,固定k 然后用两个指针去索引找到符合要求的地方
        int k = left, l = mid + 1, r = mid + 1;
        while (k <= mid)
        {
            while (l <= right && dp[l] - dp[k] < lower) ++l;
            while (r <= right && dp[r] - dp[k] <= upper) ++r;
            ret += (r - l);
            ++k;
        }
        //进行合并
        int cur1 = left, cur2 = mid + 1, i = left;
        while (cur1 <= mid && cur2 <= right)
            temp[i++] = dp[cur1] < dp[cur2] ? dp[cur1++] : dp[cur2++];
        //4、处理没有归并的区间
        while (cur1 <= mid)   temp[i++] = dp[cur1++];
        while (cur2 <= right) temp[i++] = dp[cur2++];
        //5、还原
        for (int j = left; j <= right; ++j) dp[j] = temp[j];
        return ret;
    }
};

十,总结

 分治思想的典型应用就是快速排序和归并排序

1,快速排序本身相当于一个前序遍历,最好的时间复杂度是NlogN 最差的时间复杂度是N^2 ,最坏的情况是出现在(1)以最左侧或最右侧为基准值的时候,凑巧又接近有序(2)大量重复元素。为了解决这个问题衍生出了优化思路:三组划分+随机取key。并且这种方式还可以解决top-k问题,并且时间复杂度是o(N)比堆排序还优秀,我们称之为快速选择算法。

2,归并排序的本质就是将问题划分成无数个合并两个有序数组的子问题。是一个典型的后序遍历,时间复杂度是NlogN.我们发现他有一个特点就是:在归并之前,两个数组是有序的,这个时候我们可以利用他的单调性去做文章。博主总结出来这类的做题思路大概就是,当这个题目涉及到数组种前面的元素和后面的元素存在某种数值上的大小关系(这样才会和单调性建立联系)时,就可以利用归并排序去帮助我们解决。具体需要升序还是降序需要根据题目而定。

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

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

相关文章

Java常用数据结构与集合

数据结构 数组&#xff1a; 内存地址连续检索效率高(可以通过下标访问成员)增删操作效率低(保证数据越界的问题,需动态扩容)长度固定&#xff0c;扩容的需要新的数组复制或者Arrays类的copyOf方法 链表 内存地址不连续查询快删除慢&#xff0c;因为需要移动指针又分双向链表…

【LAMMPS学习】八、基础知识(2.6)Lammps中的Walls墙

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

【opencv】示例-neural_network.cpp 使用机器学习模块创建并训练一个简单的多层感知机(神经网络)模型...

#include <opencv2/ml/ml.hpp> // 引入OpenCV的机器学习模块using namespace std; // 使用标准命名空间 using namespace cv; // 使用OpenCV命名空间 using namespace cv::ml; // 使用OpenCV机器学习命名空间int main() {//创建随机训练数据Mat_<float> data(100, …

密码学 | 椭圆曲线 ECC 密码学入门(二)

目录 4 椭圆曲线&#xff1a;更好的陷门函数 5 奇异的对称性 6 让我们变得奇特 ⚠️ 原文地址&#xff1a;A (Relatively Easy To Understand) Primer on Elliptic Curve Cryptography ⚠️ 写在前面&#xff1a;本文属搬运博客&#xff0c;自己留着学习。如果你和我一样…

TDengine taosAdapter启用HTTPS

HTTPS &#xff08;Hypertext Transfer Protocol Secure &#xff09;&#xff0c;是以安全为目标的 HTTP 通道&#xff0c;在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性 。HTTPS 在HTTP 的基础下加入SSL&#xff0c;HTTPS 的安全基础是 SSL&#xff0c;因此加…

LinkedList部分底层源码分析

JDK版本为1.8.0_271&#xff0c;以插入和删除元素为例&#xff0c;LinkedList部分源码如下&#xff1a; //属性&#xff0c;底层结构为双向链表 transient Node<E> first; //记录第一个结点的位置 transient Node<E> last; //记录最后一个结点的尾元素 transient …

Testng测试框架(2)-测试用例@Test

测试方法用 Test 进行注释&#xff0c;将类或方法标记为测试的一部分。 Test() public void aFastTest() {System.out.println("Fast test"); }import org.testng.annotations.Test;public class TestExample {Test(description "测试用例1")public void…

日期时间总结 java

日期时间 1 Date基础2 SimpleDateFormat3 currentTimeMillis4 Calendar5 时间转化5.1 date 时间戳5.2 date String5.3 cal date 6 其他7 练习题题1 1 Date基础 .getYear() .getMonth() 1 .getDate() 几号 .toLocaleString() 转换为本地时区 getTime()&#xff1a;返回自1970年…

git am XXX.patch 文件内容解析

git am XXX.patch 文件内容解析 打补丁的两种方式&#xff1a; 1.patch XXX.patch 2.git am XXX.patch 例如&#xff1a; diff --git a/drivers/crypto/se/ce.c b/drivers/crypto/se/ce.c index e6f68286d4ce6..de1bcb46fbe6b 100644 --- a/drivers/crypto/se/ce.cb/drive…

本地部署开源免费文件传输工具LocalSend并实现公网快速传送文件

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-X4xB3gSR3z2VUfmN {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

IDEA 设置类注释模板作者、日期、描述等信息(推荐标准!)

idea注释模版配置 idea作为越来越多程序员使用的开发工具&#xff0c;平时的代码注释也非常的关键&#xff0c;类上注释和方法上注释每次换电脑或者新同事入职都要统一修改&#xff0c;找了网上好多教程都写的乱七八糟的啥都有&#xff0c;为方便统一就自己写一个操作方法&…

【word2pdf】Springboot word转pdf(自学使用)

文章目录 概要整体介绍具体实现官网pom文件增加依赖 遇到的问题本地运行OK&#xff0c;发布到Linux报错还是本地OK&#xff0c;但是Linux能运行的&#xff0c;但是中文乱码 小结 概要 Springboot word 转 pdf 整体介绍 搜了一下&#xff0c;发现了能实现功能的方法有四种 U…

ppt技巧:如何将Word文档大纲中导入到幻灯片中?

在PowerPoint中&#xff0c;将Word文档的大纲导入到新的幻灯片是一种非常实用的技巧。以下是详细的步骤&#xff1a; 首先&#xff0c;需要打开PowerPoint软件并打开原始的幻灯片文件。 在PowerPoint的顶部【开始】菜单栏中&#xff0c;找到并点击“新建幻灯片”按钮&#xff0…

《黑马点评》Redis高并发项目实战笔记(上)P1~P43

P1 Redis企业实战课程介绍 P2 短信登录 导入黑马点评项目 首先在数据库连接下新建一个数据库hmdp&#xff0c;然后右键hmdp下的表&#xff0c;选择运行SQL文件&#xff0c;然后指定运行文件hmdp.sql即可&#xff08;建议MySQL的版本在5.7及以上&#xff09;&#xff1a; 下面这…

本科大学生计算机毕业设计案例:遗失物品信息管理系统

设计需求&#xff1a; 客户需求&#xff1a; 项目所用技术&#xff1a; 后端&#xff1a;springBoot,mybatisPlus,springSecurity,Swagger2 前端&#xff1a;vue-element-admin,elementUi 数据库&#xff1a;mysql&#xff0c;redis 数据库表设计&#xff1a; 关键代码展示&a…

使用geneHapR进行基因单倍型分析(以vcf文件为例)

前记 在群体基因组学研究中&#xff0c;我们常常需要知道一些位点的变异情况&#xff0c;以便于根据对应的表型信息估算这些位点的效应&#xff0c;同时了解这些位点在不同亚群之间的变化情况。这个时候我们就需要进行单倍型分析(Haplotype Analysis)&#xff0c;单倍型分析是研…

第07-1章 计算机网络相关概念

7.1 本章目标 了解网络协议的概念了解网络体系结构熟悉ISO/OSI参考模型以及每一层的功能掌握TCP/IP模型各层的主要协议及其功能熟练掌握IP地址、子网规划等相关内容 7.2 网络协议的概念 7.2.1 概念介绍 &#xff08;1&#xff09;网络协议&#xff1a;计算机网络和分布系统中…

AndroidAutomotive模块介绍(三)CarService服务

前言 上一篇文档总结 Android Automotive 框架的 APP 和 API 部分内容&#xff0c;本篇文档将会继续根据 Android Automotive 框架结构&#xff0c;总结 Framework 层 CarService 服务的内容。 本文档对 Android Automotive Framework 层服务将会按照如下顺序展开描述&#x…

4. Django 探究FBV视图

4. 探究FBV视图 视图(Views)是Django的MTV架构模式的V部分, 主要负责处理用户请求和生成相应的响应内容, 然后在页面或其他类型文档中显示. 也可以理解为视图是MVC架构里面的C部分(控制器), 主要处理功能和业务上的逻辑. 我们习惯使用视图函数处理HTTP请求, 即在视图里定义def…

一、flask入门和视图

run启动参数 模板渲染 后端给前端页面传参 前端页面设置css from flask import Flask, render_template,jsonify# 创建flask对象 app Flask(__name__)# 视图函数 路由route app.route("/") def hello_world():# 响应&#xff0c;返回给前端的数据return "h…