Самый простой способ повернуть список в c#
списки говорят, что у меня есть список List<int> {1,2,3,4,5}
поворот означает:
=> {2,3,4,5,1} => {3,4,5,1,2} => {4,5,1,2,3}
может быть, поворот не лучшее слово для этого, но надеюсь, вы понимаете, что я имею в виду
мой вопрос, что самый простой способ (в коротком коде, C# 4 Linq ready), и не будет поражен производительностью (разумной производительностью)
спасибо.
16 ответов:
вы можете реализовать его в виде очереди. Удалять и ставить одно и то же значение.
**Я не был уверен в производительности при преобразовании списка в очередь, но люди поддержали мой комментарий, поэтому я публикую это в качестве ответа.
The простой способ (для
List<T>), чтобы использовать:int first = list[0]; list.RemoveAt(0); list.Add(first);производительность неприятная, хотя-O (n).
массив
это в основном эквивалентно
List<T>версия, но более ручной:int first = array[0]; Array.Copy(array, 1, array, 0, array.Length - 1); array[array.Length - 1] = first;если бы вы могли использовать
LinkedList<T>вместо этого, это было бы много проще:int first = linkedList.First; linkedList.RemoveFirst(); linkedList.AddLast(first);Это O (1), так как каждая операция является постоянным временем.
решение cadrell0 об использовании очереди-это один оператор, как
Dequeueудаляет элемент и возвращает это:queue.Enqueue(queue.Dequeue());хотя я не могу найти никакой документации о характеристиках производительности этого, я бы ожидал
Queue<T>для реализации с использованием массива и индекса в качестве "виртуальная начальная точка" - в этом случае это еще одно решение O(1).обратите внимание, что во всех этих случаях вы хотели бы проверить, что список пуст в первую очередь. (Вы можете считать, что это ошибка, или нет-op.)
Я использую этот:
public static List<T> Rotate<T>(this List<T> list, int offset) { return list.Skip(offset).Concat(list.Take(offset)).ToList(); }
похоже, что некоторые ответчики рассматривали это как возможность исследовать структуры данных. Хотя эти ответы информативны и полезны, они не очень Linq'ish.
подход Linq'ish: вы получаете метод расширения, который возвращает ленивый IEnumerable, который знает, как построить то, что вы хотите. Этот метод не изменяет источник и должен выделять только копию источника, если это необходимо.
public static IEnumerable<IEnumerable<T>> Rotate<T>(this List<T> source) { for(int i = 0; i < source.Length; i++) { yield return source.TakeFrom(i).Concat(source.TakeUntil(i)); } } //similar to list.Skip(i-1), but using list's indexer access to reduce iterations public static IEnumerable<T> TakeFrom<T>(this List<T> source, int index) { for(int i = index; i < source.Length; i++) { yield return source[i]; } } //similar to list.Take(i), but using list's indexer access to reduce iterations public static IEnumerable<T> TakeUntil<T>(this List<T> source, int index) { for(int i = 0; i < index; i++) { yield return source[i]; } }Как использовать:
List<int> myList = new List<int>(){1, 2, 3, 4, 5}; foreach(IEnumerable<int> rotation in myList.Rotate()) { //do something with that rotation }
как насчет этого:
var output = input.Skip(rot) .Take(input.Count - rot) .Concat(input.Take(rot)) .ToList();здесь
rot- это количество точек для поворота , которое должно быть меньше, чем количество элементов вinputсписок.как показывает ответ @cadrell0, если это все, что вы делаете со своим списком, вы должны использовать очередь вместо списка.
мое решение может быть слишком простым (я не хотел бы сказать, что это хромой...) и не LINQ иш.
Тем не менее, он имеет довольно хорошую производительность.int max = 5; //the fixed size of your array. int[] inArray = new int[5] {0,0,0,0,0}; //initial values only. void putValueToArray(int thisData) { //let's do the magic here... Array.Copy(inArray, 1, inArray, 0, max-1); inArray[max-1] = thisData; }
попробовать
List<int> nums = new List<int> {1,2,3,4,5}; var newNums = nums.Skip(1).Take(nums.Count() - 1).ToList(); newNums.Add(nums[0]);хотя, мне больше нравится ответ Джона Скита.
мое решение для массивов:
public static void ArrayRotate(Array data, int index) { if (index > data.Length) throw new ArgumentException("Invalid index"); else if (index == data.Length || index == 0) return; var copy = (Array)data.Clone(); int part1Length = data.Length - index; //Part1 Array.Copy(copy, 0, data, index, part1Length); //Part2 Array.Copy(copy, part1Length, data, 0, index); }
вы можете использовать ниже код для левого вращения.
List<int> backUpArray = array.ToList(); for (int i = 0; i < array.Length; i++) { int newLocation = (i + (array.Length - rotationNumber)) % n; array[newLocation] = backUpArray[i]; }
вы можете играть хорошо в .net framework.
Я понимаю, что то, что вы хотите сделать, больше похоже на поведение итерации, чем на новый тип коллекции; поэтому я бы предложил вам попробовать этот метод расширения на основе IEnumerable, который будет работать с коллекциями, списками и т. д...
class Program { static void Main(string[] args) { int[] numbers = { 1, 2, 3, 4, 5, 6, 7 }; IEnumerable<int> circularNumbers = numbers.AsCircular(); IEnumerable<int> firstFourNumbers = circularNumbers.Take(4); // 1 2 3 4 IEnumerable<int> nextSevenNumbersfromfourth = circularNumbers .Skip(4).Take(7); // 4 5 6 7 1 2 3 } } public static class CircularEnumerable { public static IEnumerable<T> AsCircular<T>(this IEnumerable<T> source) { if (source == null) yield break; // be a gentleman IEnumerator<T> enumerator = source.GetEnumerator(); iterateAllAndBackToStart: while (enumerator.MoveNext()) yield return enumerator.Current; enumerator.Reset(); if(!enumerator.MoveNext()) yield break; else yield return enumerator.Current; goto iterateAllAndBackToStart; } }
- разумные показатели
- гибкий
Если вы хотите пойти дальше, сделать
CircularListи удерживайте тот же перечислитель, чтобы пропуститьSkip()при вращении, как в вашем образце.
я использовал следующие расширения для этого:
static class Extensions { public static IEnumerable<T> RotateLeft<T>(this IEnumerable<T> e, int n) => n >= 0 ? e.Skip(n).Concat(e.Take(n)) : e.RotateRight(-n); public static IEnumerable<T> RotateRight<T>(this IEnumerable<T> e, int n) => e.Reverse().RotateLeft(n).Reverse(); }Они, конечно, легко (OP title request), и у них есть разумная производительность (op write-up request). Вот небольшая демонстрация, которую я запустил в LINQPad 5 на ноутбуке с питанием выше среднего:
void Main() { const int n = 1000000; const int r = n / 10; var a = Enumerable.Range(0, n); var t = Stopwatch.StartNew(); Console.WriteLine(a.RotateLeft(r).ToArray().First()); Console.WriteLine(a.RotateLeft(-r).ToArray().First()); Console.WriteLine(a.RotateRight(r).ToArray().First()); Console.WriteLine(a.RotateRight(-r).ToArray().First()); Console.WriteLine(t.ElapsedMilliseconds); // e.g. 236 }
ниже мой подход. Спасибо
public static int[] RotationOfArray(int[] A, int k) { if (A == null || A.Length==0) return null; int[] result =new int[A.Length]; int arrayLength=A.Length; int moveBy = k % arrayLength; for (int i = 0; i < arrayLength; i++) { int tmp = i + moveBy; if (tmp > arrayLength-1) { tmp = + (tmp - arrayLength); } result[tmp] = A[i]; } return result; }
public static int[] RightShiftRotation(int[] a, int times) { int[] demo = new int[a.Length]; int d = times,i=0; while(d>0) { demo[d-1] = a[a.Length - 1 - i]; d = d - 1; i = i + 1; } for(int j=a.Length-1-times;j>=0;j--) { demo[j + times] = a[j]; } return demo; }
Используя Linq,
List<int> temp = new List<int>(); public int[] solution(int[] array, int range) { int tempLength = array.Length - range; temp = array.Skip(tempLength).ToList(); temp.AddRange(array.Take(array.Length - range).ToList()); return temp.ToArray(); }
меня попросили отменить массив символов с минимальным использованием памяти.
char[] charArray = new char[]{'C','o','w','b','o','y'};способ:
static void Reverse(ref char[] s) { for (int i=0; i < (s.Length-i); i++) { char leftMost = s[i]; char rightMost = s[s.Length - i - 1]; s[i] = rightMost; s[s.Length - i - 1] = leftMost; } }
Как насчет использования модулярной арифметики :
public void UsingModularArithmetic() { string[] tokens_n = Console.ReadLine().Split(' '); int n = Convert.ToInt32(tokens_n[0]); int k = Convert.ToInt32(tokens_n[1]); int[] a = new int[n]; for(int i = 0; i < n; i++) { int newLocation = (i + (n - k)) % n; a[newLocation] = Convert.ToInt32(Console.ReadLine()); } foreach (int i in a) Console.Write("{0} ", i); }Так что в принципе добавление значений в массив, когда я читаю из консоли.
Comments