簡體   English   中英

用C ++中的合並排序計數反轉

[英]Count Inversions with merge sort in C++

我正在開發我的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);


}

好消息:從這里開始計算反轉非常容易。

考慮一下您的“合並”方法。 每次將左側向量中的元素放入輸出中時,都不會更改其相對於右側元素的位置。 另一方面,每當您從右向量中添加一個元素時,您就將其放置在左向量中之前仍要處理的所有元素的“之前”,即先將它們放置在“之后”,即創建(left.size- i)“倒置”。

如果需要,您可以通過歸納輕松證明這一點。

因此答案很簡單:將int *傳遞給您的merge方法,並在每次從右側向量中推送元素時將其遞增(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 ++之前先閱讀有關指針的知識。

還要注意,此代碼絕不是最佳選擇:創建了太多中間向量,並且向量在這里沒有用,數組就足夠了。 但是,我將再離開一次。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM