算法打卡Ques20201009

1. 问题描述

在Internet上的搜索引擎经常需要对信息进行比较,比如可以通过某个人对一些事物的排名来估计他对各种不同信息的兴趣。对于不同的排名结果可以用逆序来评价他们之间的差异。考虑1, 2, …, n的排列i1, i2, …, in,如果其中存在ij,ik,使得j<k但ij>ik,那么就称(ij, ik)是这个排列的一个逆序。一个排列含有逆序的个数称为这个排列的逆序数。例如:排列2 6 3 4 5 1含有8个逆序:(2, 1),(6, 3),(6, 4),(6, 5),(6, 1),(3, 1),(4, 1),(5, 1),它的逆序数就是8。一个由1, 2, …, n组成的所有n!个排列中,最小的逆序数是0,对应的排列是1 2 3 4…n,最大的逆序数是n(n - 1) / 2,对应的排列是n n - 1 … 2 1。逆序数越大的排列与原始排列的差异度越大。

简单来说:求逆序数

2. 思路

借鉴归并排序过程中,贴一张归并排序步骤如图1

图1 归并排序基础

 归并排序过程中,会比较两个子数组,此时会传入low mid high三个参数,mid是划分的位置。设置两个j和h指针,会比较arr[h]和arr[j],把小的那个扔到temp数组里面去。代码如下:

//利用归并排序
	// 关键词:递归 分治 归并 复杂度O(nlogn)      
    void merge(vector<int>& arr, int low, int mid, int high) {
        //merge函数将两部分,(分别排好序的子数组),合并成一个,再存于原数组arr
        int h, i, j, k;
        h = low; i = low; j = mid + 1; // h,j作为游标,i指向temp存放数组元素的游标
        int length = arr.size();
        vector<int> temp(length); // 这个temp数组是local的,长度为数组arr长度
        while (h <= mid && j <= high) { // 当两个集合都没有取尽时          
            if (arr[h] <= arr[j]) {
                temp[i] = arr[h];
                h++;
                // 如果S1的最小元素小于S2的最小元素,此时将S1[h]放入temp
           // 逆序数不增加

            }
            else {
                // 如果S1的最小元素大于S2的最小元素x,此时将S2[j]放入temp
            // 此时S1中的每个元素都和x构成逆序
                count = count + (mid - h) + 1;
                temp[i] = arr[j];
                j++;
            }
            i++;         
        }
        if (h > mid) { // 当第一子组元素被 取尽,而第二组元素未被取尽时
            for (int k = j; k <= high; k++) {
                temp[i] = arr[k];
                i++;
            }
        }
        else { // 当第2组取尽,第一组未被取尽时
            for (int k = h; k <= mid; k++) {
                temp[i] = arr[k];
                i++;
            }
        }
        for (int k = low; k <= high; k++) {//将临时数组temp中的元素再赋值给arr
            arr[k] = temp[k];
        }
    }
    void mergeSort(vector<int>& arr, int low, int high) {
        // arr[low..high]是一个全程数组,含有high-low+1个待排序元素
        if (low < high) {
            int mid = low + (high - low) / 2;
            mergeSort(arr, low, mid);
            mergeSort(arr, mid + 1, high);
            merge(arr, low, mid, high);
        }
    }	

3. 求逆序数的关键点

分析归并排序过程,在合并两个子数组时会设置两个指针j和h,若数组S1的最小元素小于S2的最小元素,此时将S1[h]放入临时数组temp,逆序数不增加。

如果S1的最小元素大于S2的最小元素x,此时将S2[j]放入临时数组temp,此时S1中的每个元素都和x构成逆序,因此增加的逆序数就是现有的左边子数组剩余元素的个数,在代码中表示为(mid-h)。所以在代码中else判断中增加这一行count:

扫描二维码关注公众号,回复: 12046092 查看本文章

4. 运行效果

//Ques20201008
int main() {
    Ques20201008 qus=Ques20201008();
    qus.test();
	return 0;
}

int count = 0;//记录逆序数
	void test() {
        vector<int> arrs = { 2, 6, 3 ,4 ,5, 1 };
        mergeSort(arrs,0,5);
        cout << count;
	}

排列2 6 3 4 5 1含有8个逆序:(2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1),它的逆序数就是8。

猜你喜欢

转载自blog.csdn.net/Toky_min/article/details/108968421