首页 > 其他分享 >master公式 归并排序 小和问题 逆序对问题 荷兰国旗问题 快排

master公式 归并排序 小和问题 逆序对问题 荷兰国旗问题 快排

时间:2023-08-27 17:34:32浏览次数:29  
标签:return nums int 复杂度 快排 vector master 数组 逆序

递归行为时间复杂度计算:master公式

T(N) = a * T(N/b) + O(Nd)

N:母问题规模

a:子问题计算次数

N/b:子问题规模

O(Nd):每次递归除子问题外其他操作时间复杂度

1)log(b,a) > d  :  T(N) = O(Nlog(b,a)

2)log(b,a) < d  :  T(N) = O(Nd

3)log(b,a) == d  :  T(N) = O(Nd * logN) 

使用master公式的前提:子问题等规模

如使用递归求数组最大值:

// 将数组分为左右两个子数组,分别求两个子数组最大值,返回两个最大值中更大者
int process(vector<int>& nums, int l,int r){
    if(l == r){  // 递归结束条件
        return nums[l];
    }
    int m = l + ((r - l) >> 1); // 括号保证 >> 运算先于 +
    int leftMax = process (nums, l, m);
    int rightMax = process (nums, m+1, r); //是 m+1 不是 m
    return max(leftMax, rightMax);
}

int getMax(vector<int>& nums){
    return process(nums, 0, nums.size() - 1);
}
/*
此递归中 a = 2 ,b = 2, d = 0
log(b,a) > d  :  T(N) = O(N ^ log(b,a)) = O(N)
此递归算法的时间复杂度与遍历数组找最大值算法相同
如果递归算法改为:
计算数组前2/3部分和后2/3部分最大值,返回两个最大值中更大者
由于子问题都是 2*N/3 规模,依然可以使用master公式
*/

归并排序

1)整体就是一个简单递归,左边排好序,右边排好序,让其整体有序

2)让其整体有序的过程采用了外排序的方法(排到外部数组里再拷贝回来)

时间复杂度O(N*logN)  空间复杂度O(N)

void merge(vector<int>& nums, int l, int m, int r){
    // 存放两半数组merge后结果,最后拷贝到nums数组
    vector<int> help(r - l + 1, 0);
    // 三个指针,分别指向help数组,左右两半数组
    int i = 0;
    int p1 = l;
    int p2 = m +1;
    // 两半数组中最小的放入help
    while(p1 <= m && p2 <= r){
        help[i++] = nums[p1] <= nums[p2] ? nums[p1++] : nums[p2++];
    }
    // 两半数组中一个已遍历完,另一个数组中还未遍历的依次拷贝到help
    while(p1 <= m){
        help[i++] = nums[p1++];
    }
    while(p2 <= r){
        help[i++] = nums[p2++];
    }
    // 将排好序的help数组拷贝到nums相应位置
    for(i = 0; i < help.size(); i++){
        nums[l + i] = help[i];
    }
}

void process(vector<int>& nums, int l,int r){
    if(l == r){  // 递归结束条件
        return;
    }
    int m = l + ((r - l) >> 1);
    // 先把左右两半部分排好序
    process(nums, l, m);
    process(nums, m+1, r);  // 是 m + 1 不是 m
    // 左右两半部分都已排好序,最后merge在一起
    merge(nums, l, m, r);
}

int mergeSort(vector<int>& nums){
    process(nums, 0, nums.size() - 1);
}
/*
计算mergeSort时间复杂度:master公式
a = 2 ,b = 2, d = 1
log(b,a) == d  :  T(N) = O((N ^ d) * logN) = O(N*logN)
*/

为什么选择、冒泡、插入排序时间复杂度为O(N^2),归并排序为O(N*logN):

因为它们浪费了许多比较行为,以选择排序为例:

第一次在0~N-1范围比较,只搞定了一个数

第二次在1~N-1范围比较,只搞定了一个数

第三次在2~N-1范围比较,只搞定了一个数。。。

每次比较都是独立的,第一次比较的结果不会用到第二次比较中去

而归并排序没有浪费比较行为:

归并排序每次比较的信息留下来了,每次比较得到一个有序的部分

这个有序的部分会在下次比较里与另一个有序部分merge出一个更大有序部分

每次比较后的信息是一层层往下传递的

小和问题

在一个数组中,每一个数的左边比当前数小的所有数累加起来,叫做这个数组的小和。求一个数组的小和。

例子:[1,3,4,2,5] 

1左边比1小的数,没有

3左边比3小的数,1

4左边比4小的数,1,3

2左边比2小的数,1

5左边比5小的数,1,3,4,2

所以小和为1+1+3+1+1+3+4+2=16

使用归并排序,边排序边求小和。

与归并排序唯一的不同是:

只有在左边与右边相等时把右边的数移到help数组

即只有左边数比右边小时才把左边数记到help数组,同时计算小和(因为此时左边数比所有右边数都小)

int merge(vector<int>& nums, int l, int m, int r){
    vector<int> help(r - l + 1, 0);
    int i = 0;
    int p1 = l;
    int p2 = m + 1;
    int res = 0;
    while(p1 <= m && p2 <= r){
        /*
        找左边数小于右边数,找到则左边数小于右边数及右边数以右所有数
        左右边数相等时让右边数右移增大,因为我们找的是左边数小于右边数
        也只有这样,右边数以右的所有数大于左边数的事实才能被检测到
        */
        res += nums[p1] < nums[p2] ? nums[p1] * (r - p2 + 1) : 0;
        help[i++] = nums[p1] < nums[p2] ? nums[p1++] : nums[p2++];
    }
    while(p1 <= m){
        help[i++] = nums[p1++];
    }
    while(p2 <= r){
        help[i++] = nums[p2++];
    }
    for(i = 0; i < help.size(); i++){
        nums[l + i] = help[i];
    }
    return res;
}

int process(vector<int>& nums, int l,int r){
    if(l == r){
        return 0;
    }
    int m = l + ((r - l) >> 1);
    return process(nums, l, m) + process(nums, m+1, r) + merge(nums, l, m, r);
}

int smallSum(vector<int>& nums){
    if(nums.empty() || nums.size() < 2){
        return 0;
    }
    return process(nums, 0, nums.size() - 1);
}

逆序对问题

在一个数组中,左边的数如果比右边的数大,则这两个数构成一个逆序对,请打印所有的逆序对。

边排序边检测:

小和问题是左边的数比右边所有数小,则。。。

逆序对问题是左边的数比右边的数大,则。。。

int merge(vector<int>& nums, int l, int m, int r){
    vector<int> help(r - l + 1, 0);
    int i = 0;
    int p1 = l;
    int p2 = m + 1;
    int res = 0;
    while(p1 <= m && p2 <= r){
        /*
        左边数比右边数大,则左边数及左边数以右所有数都大于右边数
        因为要找左边数大于右边数,所以左右边数相等时左边数右移增大
        只有这样,左边数以右所有数大于右边数这一事实才能被检测到
        */
        res += nums[p1] > nums[p2] ? (m - p1 + 1) : 0;
        help[i++] = nums[p1] <= nums[p2] ? nums[p1++] : nums[p2++];
    }
    while(p1 <= m){
        help[i++] = nums[p1++];
    }
    while(p2 <= r){
        help[i++] = nums[p2++];
    }
    for(i = 0; i < help.size(); i++){
        nums[l + i] = help[i];
    }
    return res;
}

int process(vector<int>& nums, int l,int r){
    if(l == r){
        return 0;
    }
    int m = l + ((r - l) >> 1);
    return process(nums, l, m) + process(nums, m+1, r) + merge(nums, l, m, r);
}

int inversePair(vector<int>& nums){
    if(nums.empty() || nums.size() < 2){
        return 0;
    }
    return process(nums, 0, nums.size() - 1);
}

荷兰国旗问题

给定一个数组arr和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于数组的数放在数组的右边。要求额外空间复杂度O(1),时间复杂度O(N)

维护一个 < 区域右边界指针和 > 区域左边界指针

初始边界分别在数组首元素左边和最后一个元素右边

从左到右遍历arr[i]:

1)arr[i] < num : arr[i]和 < 区域下一个元素交换,<区域右扩,i++

2)arr[i] == num :i++

3)arr[i] > num :arr[i]和 > 区域前一个元素交换,i原地不动

当i和有边界重合,算法结束

void swap(vector<int>& nums, int i, int j){
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}
void flag(vector<int>& nums, int value){
    int less = -1;
    int more = nums.size();
    int i = 0;
    while(i < more){
        if(nums[i] < value){
            swap(nums, i++, ++less);
        }
        else if(nums[i] == value){
            i++;
        }
        else{
            swap(nums, i, --more);
        }
    }
}

快速排序

随机选一个数放在数组最后作划分,把前面的数组划分为小于等于大于划分值的三部分,再对大于和小于部分作上述操作,递归

时间复杂度O(N*logN)   

注:最坏状况复杂度O(N2),但这种情况并不常见

划分数有N种可能,对应数组中N个元素

N种情况等概率,都为1/N

N种情况下时间复杂度不同

对这N种情况下时间复杂度取数学期望:O(N*logN)

快速排序通常比其他O(N*logN)算法更有效率

void swap(vector<int>& nums, int i, int j){
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}
pair<int, int> partition(vector<int>& nums, int l, int r){
    // less:小于区域最右一个元素 more:大于区域最左一个元素
    int less = l - 1;
    int more = r;
    int i = l;
    while(i < more){
        if(nums[i] < nums[r]){
            swap(nums, i++, ++less);
        }
        else if(nums[i] == nums[r]){
            i++;
        }
        else{
            swap(nums, i, --more);
        }
    }
    swap(nums, more, r);
    return pair<int, int>{less + 1, more};
}
void quickSort(vector<int>& nums, int l, int r){
    if(l < r){
        swap(nums, r, l + (rand() % (r - l + 1)));
        // #include <utility>
        pair<int, int> p = partition(nums, l, r);
        quickSort(nums, l, p.first - 1);
        quickSort(nums, p.second + 1, r);
    }
}

标签:return,nums,int,复杂度,快排,vector,master,数组,逆序
From: https://blog.51cto.com/u_15724083/7253738

相关文章

  • 文件逆序2
    场景:图片的十六进制编码顺序与期望相反,需要进行逆序原十六进制:87353B逆序后:B35378importbinasciifromPILimportImageimportpytesseracta=open("文件路径","rb+")#使用open函数以二进制形式打开文件a=a.read()#read函数读取文件hex=binascii.b2a_hex(a)#......
  • 剑指 Offer 51. 数组中的逆序对(困难)
    题目:classSolution{//这道题利用了归并排序(分而治之)的思想,就是在每一次排序中统计逆序对的个数public:intmergesort(intl,intr,vector<int>&nums,vector<int>&tmp){//tmp用于记录合并之前的两个子数组if(l>=r)return0;//递归......
  • ios 开发之--逆序输出字符串
    //字符串反转NSString*str=@"abcedfghijklmnopqrstuvwxyz";NSMutableString*string=[NSMutableStringstringWithCapacity:str.length];intj=(int)str.length;for(inti=j-1;i>=0;i--){[stringappendFormat:@"%c......
  • k8s重置master节点
    1.删除所有node节点2.清空原先设置,所有节点执行kubeadmreset3.获取默认配置文件kubeadmconfigprintinit-defaults>kubeadm-config.yaml修改初始化配置文件1)advertiseAddress:192.168.2.34#本机IP2)imageRepository:registry.aliyuncs.com/google_container......
  • 以二进制文件安装K8S之部署Master高可用集群
    如下以二进制文件方式部署安全的KubernetesMaster高可用集群,具体步骤如下:1.下载Kubernetes服务的二进制文件2.部署kube-apiserver服务3.创建客户端CA证书4.创建客户端连接kube-apiserver服务所需的kubeconfig配置文件5.部署kube-controller-manager服务6.部署kube-schedule......
  • 8-17|2023-08-16 12:33:55,972 [salt.master :1643][ERROR ][20321] Received
    该日志条目显示了来自于Saltminion(在这里标识为`[master]`,这可能是minion的名称或者是由于其他原因导致的日志格式)的错误,表示minion在执行一个函数时发生了异常。日志内容“`Theminionfunctioncausedanexception`”表示在minion端执行的特定Salt函数引发了一个错误或异常。......
  • 5708: 逆序对 归并排序
    描述  给定 一个序列,求其逆序对的总数。所谓逆序对是指:序列a中存在两个元素a[i]和a[j],满足 i<j 且a[i]>a[j],则a[i]和a[j]为一个逆序对。  输入  第一行为正整数n(n<=100000)。第二行有n个正整数,最大不超过1000000。  输出  输出逆序对的总数。......
  • 某公司笔试题 - 句子逆序(附python代码)
    #将一个英文语句以单词为单位逆序排放。例如“Iamaboy”,逆序排放后为“boyaamI”,所有单子之间用一个空格隔开,语句中除了英文字母外,不再包含其他字符#数据范围:输入的字符串长度满足1<=n<=1000importrestr1=input("请输入一个英语句子:")#通过正则匹配输入英......
  • solr的master-slave和Multiple Cores
    Solrmulticore配置April21st,2011绚丽也尘埃LeaveacommentGotocommentsSolr继续学习中,感觉Solr的multicore主要用途有两个:1、充分利用服务器资源。在一台服务器上部署不用的搜索应用。2、提高一个应用服务能力,在服务器上同时部署同一个应用的多个core,这些core共用一份索......
  • 7-3 逆序的三位数 (10分)
    7-3 逆序的三位数 (10分)程序每次读入一个正3位数,然后输出按位逆序的数字。注意:当输入的数字含有结尾的0时,输出不应带有前导的0。比如输入700,输出应该是7。输入格式:每个测试是一个3位的正整数。输出格式:输出按位逆序的数。输入样例:123输出样例:321鸣谢安阳师范学院软件学院李康康......