【问题标题】:Count Inversions with merge sort in C++在 C++ 中使用合并排序计算反转
【发布时间】:2018-03-04 03:35:40
【问题描述】:

我正在研究我最初的几个算法来培养我的 C++ 技能,目前正在编写一种使用归并排序计算反转的方法。我已经设法一起进行了有效的合并排序,但是在跟踪反转次数时遇到了一些麻烦。关于从这里去哪里的任何想法?如何跟踪这样的递归算法的反转次数?此外,我在互联网旅行中看到了一些不同的实现,并且发现大多数人都偏离了 std::vector 方法,知道为什么吗?感谢您的帮助,我的代码如下!

#include <iostream>
#include <math.h>
#include <vector>

using namespace std;

vector<int> print(vector<int> input){

    for(int i=0; i<input.size(); i++){
        cout<<input[i]<<",";
    }
    cout<<endl;
    return input;
}


vector<int> merge(vector<int> left,vector<int> right){

    //set up some varibles
    vector<int> output;
    int i=0;
    int j=0;

    //loop through the lists and merge
    while(i<left.size() && j<right.size()){

        //push the smallest of the two to the vector output
        if(left[i]<=right[j]){
            output.push_back(left[i]);
            i+=1;
        }
        if(left[i]>right[i]){
            output.push_back(right[j]);
            j+=1;
        }
    }

    //push the remnants of the vectors to output
    for(i; i<left.size(); i++){
        output.push_back(left[i]);
    }

    for(j; j<right.size(); j++){
        output.push_back(right[j]);
    }

    return output;
}//end merge

vector<int> merge_sort(vector<int> input){
    //check the size of the vector
    if(input.size()<2){
        return input;
    }

    else{

    //int new vectors
    vector<int> left;
    vector<int> right;
    vector<int> output;

    //find the middle of the input vector
    int middle=(input.size())/2;

    //build the left vector
    for(int i=0; i<middle; i++){
        left.push_back(input[i]);
    }

    //build the right vector
    for(int i=middle; i<input.size(); i++){
        right.push_back(input[i]);
    }

    //make recursive calls
    left=merge_sort(left);
    right=merge_sort(right);

    //call merge
    output=merge(left,right);


    return output;
    }
}


int main()
{
    vector<int> output;
    vector<int> input;

    input.push_back(2);
    input.push_back(1);
    input.push_back(10);
    input.push_back(4);

    output=merge_sort(input);

    print(output);


}

【问题讨论】:

  • 虽然这个问题很老了,但我有一些笔记:1你一次问了太多问题。如果您有多个问题,请提出多个问题。 (也就是说,(1)算法是什么(已经描述了here),(2)如何实现它,以及(3)为什么人们不喜欢std::vector,注意(3)似乎意见-基于并因此偏离主题)2 拥有一段代码不是很有用,您应该描述(1)您的代码正在做什么(包括错误消息,如果有的话),以及(2)您期望它做什么做。

标签: c++ algorithm c++14 mergesort


【解决方案1】:

好消息:从这里开始计算反转很容易。

想想你的“合并”方法。每次将左侧向量中的元素放入输出时,都不会更改其相对于右侧元素的位置。另一方面,每次你从右向量添加一个元素时,你把它放在“之前”所有仍然要在左向量中处理的元素,而之前它是在它们“之后”,即创建 (left.size - i) “倒置”。

如果需要,您可以通过归纳轻松证明这一点。

所以答案很简单:将一个 int* 传递给您的合并方法,并在每次从右向量推送一个元素时将其递增 (left.size - i)。


编辑:工作代码示例

#include <iostream>
#include <vector>
// removed useless dependency math.h

using namespace std;

// void type -> does not return anything
void print (vector<int> input) {
    // range-based for loop (since C++ 11)
    // no brackets -> only one instruction in for loop
    for(int i : input)
        cout << i << ",";
}

vector<int> merge (vector<int> left, vector<int> right, int * inv_count) {
    vector<int> output;
    // multiple variable definition of the same type
    int i=0, j=0;

    // spaces around "<", after "while", before "{" for readability
    while (i < left.size() && j < right.size()) {

        // one-instruction trick again
        if (left[i] <= right[j])
            // i++ is evaluated to <previous value of i> and then increments i
            // this is strictly equivalent to your code, but shorter
            // check the difference with ++i
            output.push_back(left[i++]);
        // else because the two conditions were complementary
        else {
            output.push_back(right[j++]);
            // pointer incrementation
            *inv_count += (left.size() - i);
        }
    }

    // first field of for ommited because there is no need to initialize i
    for(; i < left.size(); i++)
        output.push_back(left[i]);

    for(; j < right.size(); j++)
        output.push_back(right[j]);

    return output;
}

vector<int> merge_sort (vector<int> input, int * inv_count) {
    // no-braces-idiom again
    // spaces around "<" and after "if" for readability
    if (input.size() < 2)
        return input;

    // no need for else keyword because of the return

    // multiple variable definition
    vector<int> left, right;

    int middle = input.size() / 2;

    // one-instruction for loop
    for(int i=0; i < middle; i++)
        left.push_back(input[i]);

    for(int i=middle; i < input.size(); i++)
        right.push_back(input[i]);

    // no need for intermediate variable
    return merge( merge_sort(left, inv_count),
                  merge_sort(right, inv_count),
                  inv_count);
}

// consistent convention : brace on the same line as function name with a space
int main () {
    // vector initialization (valid only since C++ 11)
    vector<int> input = {2, 1, 10, 4, 42, 3, 21, 7};

    int inv_count = 0;

    // No need for intermediate variables again, you can chain functions
    print( merge_sort(input, &inv_count) );

    // The value inv_count was modified although not returned
    cout << "-> " << inv_count << " inversions" << endl;
}

我修改了您的代码以包含一些常用的 C++ 习语。因为您使用了 C++14 标记,所以我还使用了自 C++11 以来才可用的技巧。我不建议在任何地方都使用所有这些技巧,它们都包含在此处,因为这是一种很好的学习体验。

我建议您在深入了解 C++ 之前阅读指针。

还要注意,这段代码绝不是最优的:创建了太多的中间向量,而向量在这里没有用,数组就足够了。但我会留到下一次。

【讨论】:

  • 感谢您回复我!我绝对觉得我理解这里的想法,但我正在努力将其转化为代码。正如你在上面看到的,我使用了一个函数“vector ”。我怎样才能返回这个 int* 来跟踪它?或者该功能是否有可能对此进行更改?我绝对是 C++ 新手,非常感谢您的帮助!
  • 不客气,这就是 SO 的用途!请查看我对代码示例的编辑
  • 谢谢!我感谢详细的代码评论!我仍然难以理解的一件事是 inv_count 变量。每次调用合并函数时是否只是在原地修改?我认为我的困惑源于函数从不返回 inv_count 变量。
  • inv_count 是一个变量,它存储在内存中的特定位置(称为 inv_count 的地址,通过 &inv_count 访问)。该地址被传递给合并函数,并且合并递增该地址处的任何内容。因此,所有合并实例都会修改完全相同的地址:inv_count 变量的地址。这就是 C 中指针的用途。不需要返回整数,因为可以通过它的地址访问它。
猜你喜欢
  • 1970-01-01
  • 2013-07-12
  • 2016-07-22
  • 1970-01-01
  • 1970-01-01
  • 2012-12-27
  • 2018-11-20
  • 2015-07-08
相关资源
最近更新 更多