如何在数组中间添加元素?我试过在谷歌搜索,但没有循环就找不到办法。任何人都可以通过提供代码片段来帮助我,但请不要建议使用循环,因为阵列很重,性能是这里的瓶颈。
修改
实际上我希望每个奇数索引元素都被复制到偶数索引。
实施例
myarray[0] = "a";
myarray[1] = "b";
myarray[2] = "c";
预期答案
myarray[0] = "a";
myarray[1] = "a";
myarray[2] = "b";
myarray[3] = "b";
myarray[4] = "c";
myarray[5] = "c";
答案 0 :(得分:7)
如何在数组中间添加元素?
您无法在数组中的任何位置添加元素。创建数组后,其大小是固定的 - 您可以修改元素,但不能添加或删除它们。
您可以改为使用List<T>
, 允许插入 - 但是需要复制插入点之后出现的元素。
您可能会使用LinkedList<T>
代替,但是您需要循环才能获取到正确的节点。
您可能需要考虑使用某种树结构,这样可以更容易找到正确的位置,然后插入数据。
或者,考虑重新设计 - 我们没有太多的上下文,但您可能想要考虑收集所有数据开始,忽略排序,然后将其排序为正确的顺序。
编辑:对于您的示例,我不会使用任何类型的插入 - 我会从旧的列表中创建一个新列表,例如:
// Or use Enumerable.Repeat instead of creating an array for each element.
var newList = oldList.SelectMany(x => new[] { x, x }).ToList();
或者没有LINQ:
var copies = 2; // Or whatever
var newList = new List<string>(oldList.Count * copies);
foreach (var item in oldList)
{
for (int i = 0; i < copies; i++)
{
newList.Add(item);
}
}
从根本上说,你不会比O(N)更便宜,其中N是结果列表中的项目数...
答案 1 :(得分:1)
你可以使用&#34; Buffer.BlockCopy&#34;但你必须为它创建一个单独的数组副本
答案 2 :(得分:1)
您无法在Array中添加元素。但是,如果您使用List,则方法如下:
public void Operation(List<string> list)
{
if (list == null || list.Count == 0) return;
int count = list.Count;
for (int i = 0; i <= count; i++)
if (i % 2 == 0)
list.Insert(i + 1, list[i]);
}
答案 3 :(得分:1)
发表评论:
没有循环可以使用list,因为我的性能因为循环而下降
虽然不可能完全避免循环并保持相同的结构(但见下文),但我们可以大量改进多个插入。
每次执行插入操作时,列表中稍后的所有元素都必须在内部结构中复制。因此,对于n元素列表,将有n-1
个复制操作,其中复制了总共n * (n - 1) / 2
个元素(因为每次复制的元素数量都会减少。这意味着所花费的时间大致为O( n 2 ) - 二次时间。这是不好的。
我们可以做得更好,首先使列表的大小加倍,然后将元素复制到新的位置。由于我们只需执行n * 2
个副本,因此我们现在在O(n) - 线性时间内执行此操作。
要将列表的大小加倍,我们会将其添加到自身。这既方便(我们不需要创建另一个集合,或者反复调用Add()
),并利用List<T>.AddRange()
优化处理添加到自身的列表这一事实:
list.AddRange(list);
现在,说清单包含{"1", "2", "3"}
。它现在包含{"1", "2", "3", "1", "2", "3"}
,我们想要{"1", "1", "2", "2", "3", "3"}
。
所以我们从原始集合的最后一个元素(list[2]
)开始,然后将其复制到最后两个位置,然后在列表中向后移动。因为我们在向前复制时向后移动,所以我们不会最终复制到一个尚未复制的位置(如果我们在开始时我们只是复制相同的{{1 \ n}到处都是。
"1"
现在剩下的就是要意识到没有特别与字符串相关的内容,并对该方法进行通用化,以便我们可以在所有列表中使用它:
public static void DoupleUp(List<string> list)
{
if(list == null || list.Count == 0)
return;
list.AddRange(list);
for(int idx = list.Count / 2 - 1; idx != -1; --idx)
{
T el = list[idx];
list[idx * 2] = el;
list[idx * 2 + 1] = el;
}
}
在修复Ankush的答案后,我做了一个测试运行,我开始使用public static void DoupleUp<T>(List<T> list)
{
if(list == null || list.Count == 0)
return;
list.AddRange(list);
for(int idx = list.Count / 2 - 1; idx != -1; --idx)
{
T el = list[idx];
list[idx * 2] = el;
list[idx * 2 + 1] = el;
}
}
(数字0到9作为字符串),然后在其上调用他的Enumerable.Range(0, 10).Select(i => i.ToString()).ToList()
15时间,然后回到原始列表并调用我的Operation
15次(15不多,但每次都加倍,所以最后的调用是将163840个元素的列表变成一个具有327680个元素的元素)
在我的机器上使用DoubleUp
执行此操作需要大约12.8秒,而使用Operation
执行此操作大约需要0.01秒。
如果你只想重复一次这个列表,那么你可以更好地创建列表:
DoubleUp
然后你可以使用public static IEnumerable<T> DoubleElements<T>(this IEnumerable<T> source)
{
foreach(T item in source)
{
yield return item;
yield return item;
}
}
,甚至不必改变使用数组。
你甚至可以概括一下:
foreach(string str in DoubleElements(myarray))
然后使用public static IEnumerable<T> RepeatElements<T>(this IEnumerable<T> source, int count)
{
foreach(T item in source)
for(int i = 0; i < count; ++i)
yield return item;
}
。
现在,如果您真的需要避免循环,那么您将不得不再次做一些非常不同的事情,它会带走一些可能的进一步使用:
foreach(string str in RepeatElements(myarray, 2))
这将在恒定时间内创建这样的列表:public class RepeatList<T> : IList<T>
{
private readonly IList<T> _backing;
private readonly int _repeats;
public RepeatList(IList<T> backing, int repeats)
{
if(backing == null)
throw new ArgumentNullException("backing");
if(repeats < 1)
throw new ArgumentOutOfRangeException("repeats");
_backing = backing;
_repeats = repeats;
}
public RepeatList(int repeats)
: this(new List<T>(), repeats)
{
}
public T this[int index]
{
get { return _backing[index / _repeats]; }
set { _backing[index / _repeats] = value; }
}
public int Count
{
get { return _backing.Count * _repeats; }
}
public bool IsReadOnly
{
get { return _backing.IsReadOnly; }
}
public int IndexOf(T item)
{
int idx = _backing.IndexOf(item);
return idx >= 0 ? idx * _repeats : -1;
}
public void Insert(int index, T item)
{
_backing.Insert(index / _repeats, item);
}
public void RemoveAt(int index)
{
_backing.RemoveAt(index / _repeats);
}
public void Add(T item)
{
_backing.Add(item);
}
public void Clear()
{
_backing.Clear();
}
public bool Contains(T item)
{
return _backing.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
if(array == null)
throw new ArgumentNullException("array");
if(arrayIndex < 0)
throw new ArgumentOutOfRangeException("arrayIndex");
if(array.Length - arrayIndex < _backing.Count * _repeats)
throw new ArgumentException("array is too small to copy all elements starting from index " + arrayIndex);
foreach(T item in this)
array[arrayIndex++] = item;
}
public bool Remove(T item)
{
return _backing.Remove(item);
}
public IEnumerator<T> GetEnumerator()
{
foreach(T item in _backing)
for(int i = 0; i != _repeats; ++i)
yield return item;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
几乎立即返回。
它变异为var rep = new RepeatList<string>(myArray, 2)
(更改myArray
的元素2并更改myArray
的元素4和5),如果与非只读备份列表一起使用,则双向,包括实际添加2个元素的rep
调用。您可以通过使其成为只读并使所有变异成员抛出Add
来最小化这种奇怪性,但是对于直写也是有用的。