【问题标题】:Is there any way to make this sorting algorithm have a linear time complexity?有没有办法让这个排序算法具有线性时间复杂度?
【发布时间】:2020-06-24 02:39:32
【问题描述】:

我正在我的数据结构和算法课程中做这个家庭作业,要求我制作一个随机数数组、一个计数器数组,并使用 C++ 中的这两个数组制作我们自己的排序算法。

计数器数组基本上包含对数字数组进行排序时每个元素应该去的索引,这样 counters[i] 的值将是 array[i] 之后的索引排序。它使用了这个代码,它是由我们的教授给出的:

for (int i = 1; i < arraySize; i++)
        for (int j = 0; j < i; j++)
        {
            if (array[i] < array[j])
                counters[j]++;

            else
                counters[i]++;
        }

我认为使用这两个数组,我们将能够设计一个具有 O(n) 时间复杂度的排序算法,所以我试图想办法做到这一点,我想出了这个:

for (int i = 0; i < arraySize; i++)
    {
       int index = counters[i];
       swap(array[i], array[counters[i]]);
       swap(counters[i], counters[index]);
    }

对于每次迭代,我都将数组 [i] 与数组 [计数器 [i]] 交换,因为计数器 [i] 确定数组 [i] 的索引,然后我还交换计数器中的值以确保计数器与其对应的值保持在同一索引中。

由于某种原因,这没有正确实现排序,但我不明白为什么。

我正在考虑使用另一种排序算法,但我想先尝试这个,因为它会是 O(n)。

有人可以帮忙吗?

谢谢。

【问题讨论】:

  • 你知道计算这些“计数器”的循环是 O(N^2),对吧?
  • 正如@paddy 所指出的,这与O(n) 并不接近。您是否应该实施counting sort?在输入范围有限的情况下,可以下降到O(n + k)(在时间和空间上),其中n 是输入大小,k 是值的范围,因此相对有限的范围将有效地@ 987654328@.
  • 是的,但我关注的是排序算法本身,因为另一个循环是我们的教授给出的。 @paddy
  • @ShadowRanger 对,但我说的是排序算法本身,另一个循环是我们教授给出的
  • @JaMiT 应该是 {0, 1, 2, 3} 对吧?如果我没记错的话,这就是它应该做的。

标签: c++ algorithm sorting data-structures big-o


【解决方案1】:

既然教授给了你计数(顺便说一句,它甚至可以正确处理重复),我同意你应该用它来完成额外的 O(n) 排序。

为此,请继续将array[i] 中的内容交换到它所属的位置,直到array[i] 包含属于那里的内容。只有然后转到i+1。这样你就知道array[0..i] 都包含属于那里的东西(因此在 所有东西 中都是它所属的地方)。

  for (int i = 0; i < arraySize; i++) {
    int belongs_at = counters[i];
    while (belongs_at != i) {
      swap(array[i], array[belongs_at]);
      swap(counters[i], counters[belongs_at]);
      belongs_at = counters[i];
    }
  }

这是 O(n),因为内部循环的每次迭代都会将一个(或两个)更多的值放在它所属的位置,总的来说,你不能在它们所属的位置放置超过 n 个值,所以总的来说你可以'内部循环迭代次数不超过 n 次。

我们以{20, 50, 60, 70, 10, 40, 30} 为例,看看for-loop 每次迭代结束时数组的样子:

10 20 60 70 50 40 30   # The while-loop fixed the cycle 20->50->10
10 20 60 70 50 40 30   # 20 was already where it belongs, so nothing happened
10 20 30 40 50 60 70   # The while-loop fixed the cycle 60->40->70->30
10 20 30 40 50 60 70   # 40 was already where it belongs, so nothing happened
10 20 30 40 50 60 70   # 50 was already where it belongs, so nothing happened
10 20 30 40 50 60 70   # 60 was already where it belongs, so nothing happened
10 20 30 40 50 60 70   # 70 was already where it belongs, so nothing happened

让我们看一个你出错的例子:{1, 2, 3, 0}。您将首先将1 交换到它所属的位置:{2, 1, 3, 0}。这仍然将2 留在它所属的地方!你依靠它希望以后得到修复。但这永远不会发生,因为您随后将1 与自身交换,然后将30 交换,然后将3 与自身交换。但是,如果在i=0 继续前进,直到array[i] 包含属于那里的内容,那么您不要将2 放在那里危险地不合适,而是立即修复它。

完整的代码,也是at repl.it(这产生了上面的输出):

#include <iostream>
using namespace std;

int main() {

  // Sample array
  int arraySize = 7;
  int array[7] = {20, 50, 60, 70, 10, 40, 30};

  // Count
  int counters[7] = {};
  for (int i = 1; i < arraySize; i++)
    for (int j = 0; j < i; j++)
      if (array[i] < array[j])
        counters[j]++;
      else
        counters[i]++;

  // Sort
  for (int i = 0; i < arraySize; i++) {
    int belongs_at = counters[i];
    while (belongs_at != i) {
      swap(array[i], array[belongs_at]);
      swap(counters[i], counters[belongs_at]);
      belongs_at = counters[i];
    }

    // Show
    for (int i = 0; i < arraySize; i++)
      cout << array[i] << ' ';
    cout << endl;
  }
}

【讨论】:

  • 谢谢。我现在明白这个问题了。
【解决方案2】:

如果要实现线性时间,输入数组必须带有某种假设,即输入数组是整数并且在计数排序和基数排序的某种范围内。

在底层解决方案中,counterSort假设输入数组是0-9,并将数字插入到一对中,

vector< pair<vector<int>,int> >

该对将继续跟踪向量及其计数器。

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

using namespace std;
// counterSort 
void counterSort(int sze, int* arrp, vector<pair< vector<int>,int> >& v)
{
    // pair will store a map between list and counter, 
    for(int i = 0; i < sze; i++)
    {
        int numb = arrp[i];
        v[numb].first.push_back(numb);
        v[numb].second++;
    }
}

// method for print out
void printOut(vector< pair<vector<int>,int> >& v)
{
    for(auto& item: v)
    {
        cout << "counter:" << item.second << "| ";
        for(auto& i:item.first)
        {
            cout << i << " ";
        }
        cout << endl;
    }
}
int main()
{
    int* arrp = new int[50];
    for(int i = 0; i < 50; i++)
    {
        int r = rand() % 10;
        arrp[i] = r;
        cout << arrp[i] << " ";
    }
    cout << endl;
    int sze = 50;
    vector<pair<vector<int>,int> > v( sze,make_pair(vector<int>(0),0) );
    counterSort(sze,arrp, v);
    printOut(v);
    delete [] arrp; 
    return 0;
}

【讨论】:

    猜你喜欢
    • 2020-12-13
    • 1970-01-01
    • 2021-02-01
    • 1970-01-01
    • 2019-07-08
    • 1970-01-01
    • 2015-01-05
    • 1970-01-01
    相关资源
    最近更新 更多