【问题标题】:How to combine 2 partially sorted arrays?如何组合 2 个部分排序的数组?
【发布时间】:2012-01-17 03:27:53
【问题描述】:

给定 2 个具有 int、A1 和 A2 的数组。 A1长度为L1,A2长度为L1+L2。 A1 和 A2 的前 L2 元素已排序。将第一个 L2 元素组合到 A2 中。 例如

   A1:  1  3  6  0
   A2:  2  5  7  13  10 22 11 
   result: 
   A1 + A2:  1 2 3 5 6 7 13 10 22  11

我的解决方案:

通过将 min { A1[i], A2[i]} 放入数组 B[i], O(2 * L2) 来选择每个元素 将 B 复制到 A2。 O(L1 + L2)。

不应更改未排序的部分。

有更好的解决方案吗?

谢谢

【问题讨论】:

  • 好吧,我尝试了一些想法,但没有一个考虑到您的价值观几乎是有序的。我的直觉告诉我,你应该能够稍微好一点,但不会很多。即使是桶排序也抛弃了预排序的大部分优势。不过还在想……
  • “将第一个 L2 元素合并到 A2 中”,您的意思是“第一个 L1+L2 元素”吗?看起来是这样,因为您将 A2.length 定义为 L1+L2,而 L2 将是一个重要的数字,因为您想保留最后的 L2 数字并附加它们。
  • A2 大小将为 L2 +L2 + L1(已排序 + 未排序)
  • A1 末尾的 0 是什么意思?

标签: c++ algorithm search sorting data-structures


【解决方案1】:
int* semiMerge(int*, int, int*, int);

int main() {
  const int A1[] = {1, 3, 6, 0};
  const int A2[] = {2, 5, 7, 13, 10, 22, 11};

  const int L1 = sizeof(A1)/sizeof(int);
  const int L2 = sizeof(A2)/sizeof(int) - L1;

  int* out = semiMerge(A1, L1, A2, L2);
}

int* semiMerge(A1, L1, A2, L2) {
  int* output = new int[L2 + L2 + L1];

  //merge does a sorted combination of the items--both sets must be sorted up to the endpoints;
  //we want to merge only the first L1 results from each array
  std::merge(A1, A1 + L2,
             A2, A2 + L2,
             output);
  //at this point, we have an array of 2*L1 elements, all sorted properly.


  //we want to start looking at the first element we didn't copy from A2,
  //the -1 is to account for the fact that begin() + L1 is the start of the L1+1th slot
  std::copy(A2 + L2,
            A2 + L2 + L1, 
            (output + L2 + L2 - 1));

return output;
}

我选择将 A1 和 A1 显示为静态数组,但是如果您将它们作为 int*s 获取到堆分配的数组,并且如果将完成的数组放置在 L2 中很重要,那么您可以说 @致电semiMerge() 后拨打987654323@。我选择不在 main 中执行此操作,因为我将 A2 表示为静态数组,而将其切换为 out 的内容将要求它是指向动态数组的指针。

【讨论】:

  • 完成您的方法并确保没有内存泄漏。
  • @AndréCaron 呃,也许我遗漏了什么,但我看到的唯一变化是您将输出更改为 std::vector<int>,因为您没有更改它的变量类型存储在里面,只是破坏代码...直接作用于动态int数组是有效的,只要客户端记得调用delete[]
  • @mathias:恰恰相反。我更改了output 变量的类型,而不是函数的返回类型。您之前的示例无效,因为 (1) 它试图从单个 int* 参数构造一个向量,并且 (2) 它总是泄漏分配的内存,因为指向它的唯一指针位于局部变量中。
  • @mathias:我没有注意到函数前向声明和main() 期望int* 作为输出。不错的收获!
  • 是的,我最初是根据向量来实现它,但决定将其降低到int*,因为 OP 正在谈论处理数组。你抓住了我错过的一部分,并做了与我应该做的相反的事情。此外,您确实返回了指向数组的指针,此时管理它是客户的责任。不过,我确实在底部段落中使用了错误的delete;我已经改变了。
【解决方案2】:
  1. 将 A1 和 A2 复制到一个数组中
  2. std::inplace_merge with begin, begin + L2, end

(你没有指定任何库函数!);)

这听起来像是一个作业问题,在提供作业代码之前我通常会犹豫。但是,这里似乎需要一个示例:

std::vector<int> A1; // initialize to { 1  3  6  0 }
std::vector<int> A2; // initialize to { 2  5  7  13  10 22 11 }

// Now assumption here is that we want L2 items from A1, so let's copy
A2.insert(A2.begin(), A1.begin(), A1.begin() + L2);

// Now A2 contains the sorted range from A1 0 is dropped (?)
// Now call inplace_merge, this leaves the unsorted segment of A2 alone, and only includes
// L2 items from A2.
std::inplace_merge(A2.begin(), A2.begin() + L2, A2.begin() + (2*L2));

【讨论】:

  • 它是 (2 * L2 * lg 2*L2)。当 L2 >> L1 时,效率不高。
  • merge(begin(A1), end(A1), begin(A2), end(A2), back_inserter(dest)); 有什么问题?
  • @mooing,是的,如果 B 可以是一个单独的数组,如果 A2(或 A1)对于两个都足够,我猜就地更好......
  • 在这种情况下 inplace 肯定更好,end 将是两个排序范围的结尾。这将留下未排序的部分。我会仔细检查 NlogN 声明...
  • A2 的一部分已排序,另一部分未排序,应保持此状态。为什么这些部分不保存在不同的数组中?这将简化一切并提供 O(2* L2)。
【解决方案3】:

现在我已经弄清楚 L1L2 是什么:

{
    std::vector<int> B(L2+L2+L1, 0);
    std::merge(A1.begin(), A1.begin()+L2, A2.begin(), A2.begin()+L2, B.begin());
    if (L1 > L2)
        B.insert(B.end(), A2.begin()+L2, A2.end());
    A2.swap(B);
}

B 包含[合并的排序部分][未排序的 A2]。那是正确的格式吗?这是您发布的算法。就地(如 Nim)速度较慢,但​​使用的内存较少,因此需要权衡取舍。

【讨论】:

    猜你喜欢
    • 2015-12-04
    • 2016-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-09
    • 2017-02-20
    • 2021-10-05
    相关资源
    最近更新 更多