【问题标题】:How do you do this in C# without using List?在不使用 List 的情况下如何在 C# 中执行此操作?
【发布时间】:2020-03-13 07:21:25
【问题描述】:

我是 C# 新手。以下代码是我为解决挑战而提出的解决方案。我不确定如何在不使用 List 的情况下执行此操作,因为我的理解是您无法推送到 C# 中的数组,因为它们的大小是固定的。

到目前为止,我对我所说的话的理解是否正确?

有没有一种方法可以在每次需要添加到数组时不涉及创建新数组?如果没有其他方法,当我的循环开始之前数组的大小未知时,我将如何创建一个新数组?

返回所有小于给定 n 且可被 3 和 4 整除的非负数的排序数组。对于 n = 30,输出应为

threeAndFour(n) = [0, 12, 24]。

int[] threeAndFour(int n) {
  List<int> l = new List<int>(){ 0 };

  for (int i = 12; i < n; ++i)
    if (i % 12 == 0)
      l.Add(i);

  return l.ToArray();
}

编辑:我已经将此代码重构为..

int[] threeAndFour(int n) {
  List<int> l = new List<int>(){ 0 };

  for (int i = 12; i < n; i += 12)
      l.Add(i);

  return l.ToArray();
}

【问题讨论】:

  • (n / 12) + 1 是数组所需的大小。
  • 数组确实是固定大小的。列表只不过是数组的包装器,让它们自动增长。 List 也可以通过 ToArray() 转换为数组。制作一个可以增长的集合的唯一另一种方法是链接列表。但在非常特殊的情况下,这些都是无用的。
  • 有趣的事实。 'List` 只不过是一个数组的管理包装器(如果数组已满,它们会将大小加倍并复制所有元素)。但是,如果您想要不同的数据结构来处理添加未知数量的元素而不使用数组,您可以查看链表(基本上每个项目都包装在指向下一个项目的包装器中)

标签: c# arrays list loops


【解决方案1】:

List 通常工作得很好,因为我了解您的问题,您已经挑战自己在不使用 List 类的情况下解决问题。数组(或列表)使用连续的内存块来存储元素。数组是固定大小的。 List 将动态扩展以接受新元素,但仍将所有内容保存在单个内存块中。

您可以使用链表https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.linkedlist-1?view=netframework-4.8 来生成数组的模拟。链表为每个元素(节点)分配额外的内存,用于指向下一个(也可能是前一个)。这允许您在没有大块分配的情况下添加元素,但您为每个添加的元素支付空间成本(增加内存使用)。链表的另一个问题是您不能快速访问随机元素。要到达元素 5,您必须遍历元素 0 到 4。数组和类似数组的结构在许多任务中受到青睐是有原因的,但尝试以不同的方式做常见的事情总是很有趣。

【讨论】:

  • 直到现在我才知道 LinkedList。谢谢!
【解决方案2】:

A.列表没问题

如果您想使用for 找出数字,那么List 是在您发现数字时收集数字的合适数据结构。

B.使用更多数学

static int[] threeAndFour(int n) {
  var a = new int[(n / 12) + 1];
  for (int i = 12; i < n; i += 12) a[i/12] = i;
  return a;
}

C.带有IEnumerable&lt;int&gt; 的生成器模式

我知道这不会返回一个数组,但它确实避免了一个列表。

static IEnumerable<int>  threeAndFour(int n) {
  yield return 0;

  for (int i = 12; i < n; i += 12)
      yield return i;
}

D.转身避开列表

代码可以for 两次。首先计算大小或数组,然后填充它。

int[] threeAndFour(int n) {
  // Version: A list is really undesirable, arrays are great.
  int size = 1;
  for (int i = 12; i < n; i += 12)
      size++;
  var a = new int[size];
  a[0] = 0;
  int counter = 1;
  for (int i = 12; i < n; i += 12) a[counter++] = i;
}

【讨论】:

  • 感谢您回答我的问题,如果有办法只使用数组来做到这一点。您还提供了替代方案,这很棒。答案 D 是您如何仅使用数组来执行此操作。你唯一遗漏的是return a;
【解决方案3】:

我不确定如何在不使用 List 的情况下执行此操作,因为我的理解是您无法推送到 C# 中的数组,因为它们的大小是固定的。

数组不能增长是正确的。 List 是作为数组的包装器而发明的,该数组会在需要时自动增长。请注意,您可以给 List 一个integer via the Constructor,它会告诉它它应该期望的最小大小。它会在第一次分配至少那么多。这可以限制与增长相关的开销。

字典只是列表机制的一种变体,具有哈希表键搜索速度。

据我所知,只有 1 个其他集合可以增长。然而,除了理论和一些非常具体的案例之外,它很少被提及:

Linked Lists。链表具有无与伦比的增长性能,并且由于 Fragmentation 而遇到 OutOfMemory 异常的问题最低。不幸的是,它们的随机访问时间是最差的。除非您可以从一开始(或有时结束)专门按顺序处理这些集合,否则它们的性能将非常糟糕。只有堆栈和队列可能会使用它们。但是,您仍然可以在 .NET 中使用一个实现:https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.linkedlist-1

你的代码也有一些潜力:

  for (int i = 12; i < n; ++i)
    if (i % 12 == 0)
      l.Add(i);

每次迭代计数 12 会更有效 - 毕竟您只对第 12 个数字感兴趣。您可能必须更改循环,但我认为 do...while 会这样做。数组/最小列表大小也很容易预测:只需将 n 除以 12 并加 1。但我认为这主要是模拟代码,实际上并不是确定性的。

【讨论】:

  • 感谢您对我的问题的澄清。如果这确实是使用 List 解决此问题的最佳方法,那么是的,我同意重构循环中的条件以消除 if 语句是最好的。它会减少很多工作。
  • @Milliorn 有了这些值,计算负载实际上会减少 12。但是我会给它一个 50/50 的机会,即编译器优化和 JiT 编译器已经解决了这个问题。所以可能没有实际的速度增加。
  • 阅读您的评论后,我想出了这个重构。 int[] threeAndFour(int n) { List&lt;int&gt; l = new List&lt;int&gt;(){ 0 }; for (int i = 12; i &lt; n; i += 12) l.Add(i); return l.ToArray(); }
  • @Milliorn 这比我的想法更好。不需要除以 12。如果 12 的值也是确定性的,还有一种更快的方法:在内存中预先计算整个列表。只能从中“打印”到某个索引。但我不确定这是否值得你付出编码努力。特别是把它放到代码中,在启动时生成它或者从文件中读取它可能比它更值得麻烦。
【解决方案4】:

既然您知道这是分 12 步进行的,并且在开始之前就知道有多少步,那么您可以这样做:

Enumerable.Range(0,n/12+1).Select(x => x*12).ToArray();

【讨论】:

  • 之前的两张海报提到了类似的事情。是否有像复杂性这样的原因,您以这种方式编写它而不是其他两个如何编写它? ``` return Enumerable.Range(0,n).Where(x=>x%12==0).ToArray();
  • @Milliorn 效率更高:生成你知道不可整除的数字有点毫无意义。但是同意,在这种操作很简单并且除数很小的情况下,担心性能差异还不够,也许您想要一个更像问题的解决方案。现在,如果它是从数据库中选择记录,您可能需要更多地担心效率......
【解决方案5】:

您可以为此目的使用 Linq 和 Enumerable.Range 方法。例如,

int[] threeAndFour(int n) 
{
    return Enumerable.Range(0,n).Where(x=>x%12==0).ToArray();

}

Enumerable.Range 生成指定范围内的整数序列,然后根据条件(x%12==0) 对其进行过滤以检索所需的结果。

【讨论】:

  • 我喜欢单线解决方案,但这未通过测试。输入:n:30 输出:[12, 24] 预期输出:[0, 12, 24]
  • @Milliorn 请检查更新后的答案。刚刚意识到您也需要 0
  • 是的,您的重构现在通过了测试。
【解决方案6】:

if (i % 12 == 0)

所以你已经发现,3 和 4 的整数正好是 12 的整数。

你能算出在给定的n 之下有多少这样的数字吗? - 你可以在不计算数字的情况下这样做吗 - 如果不需要动态增长的容器,你可以将容器初始化为正确的大小。

一旦你有了你的数组,只需跟踪下一个要填充的索引。

【讨论】:

  • 是的,最小公分母,这里是12。我能想到在给定 n 之下有多少数字的唯一方法是运行一个循环递增一个 int 变量,然后使用它来分配一个新数组的长度。那是对的吗?如果是这样,那么只需创建所有元素为 0 的新数组,然后在以下循环中重新分配值?
  • @Milliorn 这不正确。你应该很容易回答有多少个小于 12000000000000 的非负数除以 12。
猜你喜欢
  • 1970-01-01
  • 2017-12-10
  • 2015-02-12
  • 1970-01-01
  • 1970-01-01
  • 2013-07-11
  • 1970-01-01
  • 2013-10-04
  • 2023-03-31
相关资源
最近更新 更多