3.9 两道关于逆序对的题目

2019-03-23  本文已影响0人  Aurochsy

Chapter3: 更好的查找与排序算法

9. 用归并排序思想解两道关于逆序对的题目

合并有序数组

题目

给定两个排序后的数组A和B,其中A的末端有足够的缓冲空间容纳B。编写一个方法,将B合并入A并排序

算法

基本思想

其实就是归并排序,只不过不是用额外的空间,空间已经给你了

遍历数组B,一个指针遍历数组A,选择大的数往后放

逆序对个数

题目

一个数列,如果左边的数大,右边的数小,则称这两个数为一个逆序对(这两个数的位置不一定连续)。求出一个数列中有多少个逆序对。

比如数列{4,3,5,2},有{4,3},{4,2},{3,2},{5,2}四对逆序对。

算法

解法1:暴力解法

用两层for循环判断分别每个元素 i 与其之后的每个元素的大小关系,时间复杂度为O(n^2);

解法2:用归并排序的思想
基本思想
代码

实际就是在归并排序的代码中增加了一个全局变量 resPair, 在merge() 函数比较左右两个数组的元素大小部分的代码中,在左边元素大于右边元素的情况下添加 resPair+=(mid-keftP+1); 以统计逆序对数量,可以在主函数中打印resPair

while((leftP<=mid)&&(rightP<=end)){
    if(arr[leftP]<=arr[rightP]){
        tmpArr[i]=arr[leftP];
        i++;
        leftP++;
        //或直接写为 tmpArr[i++]=arr[leftP++];
    }
    else{//左边元素大于右边元素
        tmpArr[i]=arr[rightP];
        i++;
        rightP++; 
        resPair+=(mid-leftP+1);//统计逆序对数量
    }
}

完整代码如下:

#include<iostream>
#include<cstdlib>
using namespace std;

void mergeSort(int* arr,int begin,int end); 
void merge(int* arr,int begin,int mid,int end);

int resPair;

int main(){
    int arr[6]={1,3,2,5,4,7};
    int arrLen=sizeof(arr)/sizeof(arr[0]);
    
    mergeSort(arr,0,arrLen-1);
    
    for(int i=0;i<arrLen;i++){
        printf("%d ",arr[i]);
    }
    return 0;
} 

void mergeSort(int* arr,int begin,int end){
    if(begin<end){//注意这个出口条件,不然会陷入死循环 
        int mid=begin+((end-begin)>>1);
        mergeSort(arr,begin,mid);//两次调用mergeSort分别传入两个区间的参数,相当于不断划分了
        mergeSort(arr,mid+1,end);
        merge(arr,begin,mid,end);//合并
    }
}
/*合并的函数*/
void merge(int* arr,int begin,int mid,int end){
    int len=end-begin+1;//当前划分状态下的数组长度 
    int tmpArr[len];//辅助数组
     
    int leftP=begin;//左指针 
    int rightP=mid+1;//右指针 
    int i=0;//辅助数组当前存取的下标 
    //printf("initial. len=%d\n",len);
    
    while((leftP<=mid)&&(rightP<=end)){
        if(arr[leftP]<=arr[rightP]){
            tmpArr[i]=arr[leftP];
            i++;
            leftP++;
            //或直接写为 tmpArr[i++]=arr[leftP++];注意i++和++i的区别
        }
        else{//左边元素大于右边元素
            tmpArr[i]=arr[rightP];
            i++;
            rightP++; 
            resPair+=(mid-leftP+1);//统计逆序对数量
        }
    }
    while(leftP<=mid){//如果左数组有剩余元素 
        tmpArr[i]=arr[leftP];
        i++; 
        leftP++;
    }
    while(rightP<=end){//如果右数组有剩余元素 
        tmpArr[i]=arr[rightP];
        i++;
        rightP++;
    }
    /*将排序好的辅助数组复制到原数组中,
    因为递归要用到上一层排好序的原数组,并所以这一步必不可少*/ 
    i=0;  
    while(begin<=end){
        arr[begin++]=tmpArr[i++];
    }
    //注意每次递归数组的开始位置不一样,为begin,不一定是0,所以不能用下面的赋值方法 
//  for(i=0;i<len;i++){
//      arr[i]=tmpArr[i];
//  }       

}
上一篇 下一篇

猜你喜欢

热点阅读