【问题标题】:Merge sort implementation in c++ using vectors使用向量在 C++ 中合并排序实现
【发布时间】:2020-10-07 04:20:08
【问题描述】:

我正在尝试使用 C++ 中的向量实现归并排序,这是我正在执行的以下代码:

#include <iostream>
#include <vector>

using namespace std;

void merge(vector<int> &a, int l, int m, int r) {
    int i, j, k;
    int n1 = m - l + 1;
    int n2 = r - m;
    
    int L[n1], R[n2];
    for (i = 0; i < n1; i++) {
        L[i] = a[l + i];
    }
    for (j = 0; j < n2; j++) {
        L[j] = a[m + 1 + j];
    }
    i = 0;
    j = 0;
    k = l;     //merged array
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            a[k] = L[i];
            i++;
        } else {
            a[k] = R[j];
            j++;
        }
        k++;
    }
    while (i < n1) {
        a[k] = L[i];
        i++;
        k++;
    }
    while (j < n2) {
        a[k] = R[j];
        j++;
        k++;
    }
}

void mergeSort(vector<int> &a, int l, int r) {
    if (l < r) {
        int m = l + (r - l) / 2;
        mergeSort(a, l, m);
        mergeSort(a, m + 1, r);
        merge(a, l, m, r);
    }
}

int main() {
    int n;
    std::cin >> n;
    vector<int> a(n);
    for (int i = 0; i < a.size(); i++) {
        cin >> a[i];
    }
    mergeSort(a, 0, a.size() - 1);
    for (int i = 0; i < a.size(); i++) {
        cout << a[i];
    }
}

当我执行此操作并输入任何值时,我会在数组中返回垃圾值a[i] = 7405024,由于我无法就地更改向量的值或者是还有其他的。

【问题讨论】:

  • 使用调试器逐行检查程序并在每一步检查变量的内容。准确找出变量内容开始与预期不同的位置和时间。
  • 无论哪本 C++ 教科书教你写“int L[n1],R[n2];”,都请扔掉,换一本更好的 C++ 教科书。这不是标准的 C++。除此之外,是的,这就是调试器的用途。使用调试器遍历程序,一次一行,检查所有变量的值,并观察它们的变化。这是您可以证明您的假设的唯一方法,即您的“主要问题”是什么。
  • 不,@Yasir,至少使用size() 的部分是正确的。为什么你认为应该在这里使用capacity()
  • 大概L[j]=a[m+1+j];应该是R[j]=a[m+1+j];
  • @Yasir std::vector&lt;int&gt; myVector(5); 声明了一个包含 5 个元素(又名 size()==5)和未指定容量的向量。元素已经存在,OP 只是在改变它们。使用capacity() 仅在您将push_back 其中一些并且您不想或不能创建默认对象时才有意义。

标签: c++ sorting stl mergesort


【解决方案1】:

问题出在merge函数中

for(j=0;j<n2;j++)
{
    R[j]=a[m+1+j]; // not L[j]
}

当您使用向量实现时,建议将LR 也更改为向量。默认情况下,C++ 不支持 VLA。一些编译器可能会接受,但应该完全避免使用。

std::vector<int> L(n1);
std::vector<int> R(n2);

【讨论】:

    【解决方案2】:

    我可以看到的一个明显变化是

    IN merge() function
    
    for(j=0;j<n2;j++)
    {
        R[j]=a[m+1+j];
    }
    

    因为这些值必须存储在RIGHT 向量中。

    【讨论】:

      【解决方案3】:

      问题是您对R 的初始化。无需编写循环复制到LR,您可以直接初始化它们。

      std::vector<int> L(a.begin() + l, a.begin() + m + 1);
      std::vector<int> R(a.begin() + m + 1, a.begin() + r + 1);
      

      请注意,在任何地方都使用迭代器会更容易。

      using iter = std::vector<int>::iterator;
      
      void merge(iter l, iter m, iter r) {
          std::vector<int> L(l, m);
          std::vector<int> R(m, r);
          
          iter i = L.begin();
          iter j = R.begin();
          while (i != L.end() && j != R.end()) {
              if (*i < *j) {
                  *l = *i;
                  i++;
              } else {
                  *l = *j;
                  j++;
              }
              l++;
          }
      
          std::copy(i, L.end(), l);
      }
      
      void mergeSort(iter l, iter r) {
          std::size_t d = std::distance(l, r);
          if (d > 1) {
              iter m = l + (d / 2);
              mergeSort(l, m);
              mergeSort(m, r);
              merge(l, m, r);
          }
      }
      
      int main() {
          std::size_t n;
          std::cin >> n;
          std::vector<int> a(n);
          for (int & i : a) {
              std::cin >> i;
          }
          mergeSort(a.begin(), a.end());
          for (int i : a) {
              std::cout << i;
          }
      }
      

      请注意,在初始化L之前,您可以前进l,直到找到在*m之后排序的元素,这样可以节省一些复制。

      【讨论】:

        【解决方案4】:

        您可以使用以下代码实现合并排序:

        vector<int> merge(vector<int> l,vector<int> r)
                {
            
               vector<int> res;
                    
                    int i=0;
                    int j=0;
                    while(i!=l.size() && j!=r.size())
                    {
                        if(l[i]<=r[j])
                        {
                            re.push_back(l[i++]);
                        }
                        else
                        {
                            re.push_back(r[j++]);
                        }
                    }
                    
                    while(i!=l.size())
                        re.push_back(l[i++]);
                    
                    while(j!=r.size())
                        re.push_back(r[j++]);
                    
                    return res;
                }
                
                
                vector<int> merge_d(vector<int>&A, int s,int e)
                {
                    if(s-e==0)
                    {
                        vector<int> t;
                        t.push_back(A[s]);
                        return t;
                    }
                
                    int m=(s+e)/2;
                    
                    vector<int> l;
                    vector<int> r;
                    l=merge_d(A,s,m);
                    r=merge_d(A,m+1,e);
                    
                    return merge(l,r);
                }
        

        【讨论】:

          【解决方案5】:

          您的代码中的主要问题是您根本没有初始化R。初始化循环中有一个简单的错字,可能是剪切粘贴错误:L[j] = a[m + 1 + j];应该是

          R[j] = a[m + 1 + j];
          

          还要注意,在 C++ 中,指定一个包含第一个元素的索引和排除最后一个元素的索引的切片是惯用的。此约定用于 C、Python 和许多其他语言,允许使用更简单和更通用的代码,您可以使用 l == h 指定一个空切片。它也不太容易出错,因为不需要调整 +1/-1 并且可以安全地使用诸如 size_t 之类的无符号索引类型。

          最后,int L[n1], R[n2]; 是一个 C99 声明,它是 C++ 的扩展。即使在支持 VLA 的环境中,使用自动存储分配它们也会导致 堆栈溢出 用于足够大的集合。您应该为这些临时数组使用vector

          这些向量可以直接从向量切片初始化,避免了容易出错的复制循环:

          vector<int> L(a.begin() + l, a.begin() + m);
          vector<int> R(a.begin() + m, a.begin() + r);
          

          这是修改后的版本:

          #include <iostream>
          #include <vector>
          
          using namespace std;
          
          void merge(vector<int> &a, size_t l, size_t m, size_t r) {
              vector<int> L(a.begin() + l, a.begin() + m);
              vector<int> R(a.begin() + m, a.begin() + r);
          
              size_t i = 0;
              size_t j = 0;
              size_t k = l;     // index into the merged vector slice
              size_t n1 = m - l;
              size_t n2 = r - m;
          
              while (i < n1 && j < n2) {
                  if (L[i] <= R[j]) {
                      a[k++] = L[i++];
                  } else {
                      a[k++] = R[j++];
                  }
              }
              while (i < n1) {
                  a[k++] = L[i++];
              }
              // the last loop in redundant as the remaining elements from R are already at
              // the end of a
          }
          
          void mergeSort(vector<int> &a, size_t l, size_t r) {
              if (r - l >= 2) {
                  size_t m = l + (r - l) / 2;
                  mergeSort(a, l, m);
                  mergeSort(a, m, r);
                  merge(a, l, m, r);
              }
          }
          
          int main() {
              size_t n;
              std::cin >> n;
              vector<int> a(n);
              for (size_t i = 0; i < a.size(); i++) {
                  cin >> a[i];
              }
              mergeSort(a, 0, a.size());
              for (size_t i = 0; i < a.size(); i++) {
                  cout << a[i];
              }
              return 0;
          }
          

          【讨论】:

            猜你喜欢
            • 2016-07-22
            • 1970-01-01
            • 2022-01-03
            • 1970-01-01
            • 1970-01-01
            • 2016-01-29
            • 1970-01-01
            • 1970-01-01
            • 2013-08-11
            相关资源
            最近更新 更多