Подтвердить что ты не робот

В С#, как упорядочить элементы в списке, где "наибольшие" значения находятся в середине списка

Я был в тупике по этому поводу некоторое время. Я хочу взять список и заказать список, чтобы продукты с наибольшей ценой оказались в середине списка. И я также хочу сделать обратное, т.е. Убедиться, что элементы с наибольшей ценой попадают на внешние границы списка.

Представьте себе такую ​​структуру данных, как это. 1,2,3,4,5,6,7,8,9,10

В первом сценарии мне нужно вернуться назад 1,3,5,7,9,10,8,6,4,2 Во втором сценарии мне нужно вернуться назад 10,8,6,4,2,1,3,5,7,9

В списке может быть более 250 наименований, цифры не будут равномерно распределены, и они не будут последовательными, и я хотел бы свести к минимуму копирование. Числа будут содержаться в объектах Product, а не простые примитивные целые числа.

Есть ли простое решение, которое я не вижу?

Любые мысли.

Итак, для тех из вас, кто задается вопросом, что я делаю, я заказываю предметы на основе рассчитанного размера шрифта. Вот код, с которым я пошел...

Реализация...

private void Reorder()
{
    var tempList = new LinkedList<DisplayTag>();
    bool even = true;
    foreach (var tag in this) {
        if (even)
            tempList.AddLast(tag);
        else
            tempList.AddFirst(tag);

        even = !even;
    }

    this.Clear();
    this.AddRange(tempList);
}

Тест...

[TestCase(DisplayTagOrder.SmallestToLargest, Result=new[]{10,14,18,22,26,30})]
[TestCase(DisplayTagOrder.LargestToSmallest, Result=new[]{30,26,22,18,14,10})]
[TestCase(DisplayTagOrder.LargestInTheMiddle, Result = new[] { 10, 18, 26, 30, 22, 14 })]
[TestCase(DisplayTagOrder.LargestOnTheEnds, Result = new[] { 30, 22, 14, 10, 18, 26 })]
public int[] CalculateFontSize_Orders_Tags_Appropriately(DisplayTagOrder sortOrder)
{
    list.CloudOrder = sortOrder;
    list.CalculateFontSize();

    var result = (from displayTag in list select displayTag.FontSize).ToArray();
    return result;
}

Использование...

public void CalculateFontSize()
{
    GetMaximumRange();
    GetMinimunRange();
    CalculateDelta();

    this.ForEach((displayTag) => CalculateFontSize(displayTag));

    OrderByFontSize();
}

private void OrderByFontSize()
{
    switch (CloudOrder) {
        case DisplayTagOrder.SmallestToLargest:
            this.Sort((arg1, arg2) => arg1.FontSize.CompareTo(arg2.FontSize));
            break;
        case DisplayTagOrder.LargestToSmallest:
            this.Sort(new LargestFirstComparer());
            break;
        case DisplayTagOrder.LargestInTheMiddle:
            this.Sort(new LargestFirstComparer());
            Reorder();
            break;
        case DisplayTagOrder.LargestOnTheEnds:
            this.Sort();
            Reorder();
            break;
    }
}
4b9b3361

Ответ 1

Соответствующая структура данных - это LinkedList, потому что она позволяет вам эффективно добавлять в любой конец:

        LinkedList<int> result = new LinkedList<int>();

        int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        Array.Sort(array);

        bool odd = true;

        foreach (var x in array)
        {
            if (odd)
                result.AddLast(x);
            else
                result.AddFirst(x);
            odd = !odd;
        }

        foreach (int item in result)
            Console.Write("{0} ", item);

Никаких дополнительных шагов копирования, никаких шагов реверсирования,... только небольшие накладные расходы на node для хранения.

Ответ 2

Версия Iterator С#

(Очень простой код, удовлетворяющий всем условиям.)

Одна функция, чтобы управлять ими всеми! Не использует промежуточную коллекцию хранилищ (см. yield). Заказывает большие числа либо в середине, либо в стороны в зависимости от аргумента. Он реализован как С# -тератор

// Pass forward sorted array for large middle numbers,
//  or reverse sorted array for large side numbers.
//
public static IEnumerable<long> CurveOrder(long[] nums) {

    if (nums == null || nums.Length == 0)
        yield break; // Nothing to do. 

    // Move forward every two.
    for (int i = 0;  i < nums.Length;  i+=2)
        yield return nums[i];

    // Move backward every other two. Note: Length%2 makes sure we're on the correct offset.
    for (int i = nums.Length-1 - nums.Length%2;  i >= 0;  i-=2)
        yield return nums[i];
}

Пример использования

Например, с массивом long[] nums = { 1,2,3,4,5,6,7,8,9,10,11 };

Начните с прямого порядка сортировки, чтобы поднять высокие числа в середину.

Array.Sort(nums); //forward sort
// Array argument will be:  { 1,2,3,4,5,6,7,8,9,10,11 };
long[] arrLargeMiddle = CurveOrder(nums).ToArray();

Производит: 1 3 5 7 9 11 10 8 6 4 2

Или, Начните с обратного порядка сортировки, чтобы нажимать большие числа на стороны.

Array.Reverse(nums); //reverse sort
// Array argument will be:  { 11,10,9,8,7,6,5,4,3,2,1 };
long[] arrLargeSides = CurveOrder(nums).ToArray();

Производит: 11 9 7 5 3 1 2 4 6 8 10

Значительные пространства имен:

using System;
using System.Collections.Generic;
using System.Linq;

Примечание. Итератор оставляет решение до вызывающего абонента о том, следует ли использовать промежуточное хранилище. Вызывающий может просто генерировать цикл foreach по результатам.


Вариант метода расширения

Необязательно изменить заголовок статического метода, чтобы использовать этот модификатор public static IEnumerable<long> CurveOrder(this long[] nums) { и поместить его в статический класс в вашем пространстве имен;
Затем вызовите метод заказа непосредственно на любом длинном экземпляре массива [] так:

Array.Reverse(nums); //reverse sort
// Array argument will be:  { 11,10,9,8,7,6,5,4,3,2,1 };
long[] arrLargeSides = nums.CurveOrder().ToArray();

Просто какой-то (незаменимый) синтаксический сахар, чтобы немного смешать вещи для удовольствия. Это можно применить к любым ответам на ваш вопрос, которые принимают аргумент массива.

Ответ 3

Что-то вроде этого?

public IEnumerable<int> SortToMiddle(IEnumerable<int> input)
{
    var sorted = new List<int>(input);
    sorted.Sort();
    var firstHalf = new List<int>();
    var secondHalf = new List<int>();

    var sendToFirst = true;
    foreach (var current in sorted)
    {
        if (sendToFirst)
        {
            firstHalf.Add(current);
        }
        else
        {
            secondHalf.Add(current);
        }
        sendToFirst = !sendToFirst;
    }
    //to get the highest values on the outside just reverse 
    //the first list instead of the second
    secondHalf.Reverse();
    return firstHalf.Concat(secondHalf);
}

Для вашего конкретного (общего) случая (при условии уникальных ключей):

public static IEnumerable<T> SortToMiddle<T, TU>(IEnumerable<T> input, Func<T, TU> getSortKey)
{
    var sorted = new List<TU>(input.Select(getSortKey));
    sorted.Sort();
    var firstHalf = new List<TU>();
    var secondHalf = new List<TU>();

    var sendToFirst = true;
    foreach (var current in sorted)
    {
        if (sendToFirst)
        {
            firstHalf.Add(current);
        }
        else
        {
            secondHalf.Add(current);
        }
        sendToFirst = !sendToFirst;
    }
    //to get the highest values on the outside just reverse 
    //the first list instead of the second
    secondHalf.Reverse();
    sorted = new List<TU>(firstHalf.Concat(secondHalf));

    //This assumes the sort keys are unique - if not, the implementation 
    //needs to use a SortedList<TU, T>
    return sorted.Select(s => input.First(t => s.Equals(getSortKey(t))));

}

И принимая не уникальные ключи:

public static IEnumerable<T> SortToMiddle<T, TU>(IEnumerable<T> input, Func<T, TU> getSortKey)
{
    var sendToFirst = true;
    var sorted = new SortedList<TU, T>(input.ToDictionary(getSortKey, t => t));
    var firstHalf = new SortedList<TU, T>();
    var secondHalf = new SortedList<TU, T>();

    foreach (var current in sorted)
    {
        if (sendToFirst)
        {
            firstHalf.Add(current.Key, current.Value);
        }
        else
        {
            secondHalf.Add(current.Key, current.Value);
        }
        sendToFirst = !sendToFirst;
    }
    //to get the highest values on the outside just reverse 
    //the first list instead of the second
    secondHalf.Reverse();
    return(firstHalf.Concat(secondHalf)).Select(kvp => kvp.Value);
}

Ответ 4

Я мог бы пойти на что-то вроде этого

static T[] SortFromMiddleOut<T, U>(IList<T> list, Func<T, U> orderSelector, bool largestInside) where U : IComparable<U>
{
    T[] sortedArray = new T[list.Count];

    bool add = false;
    int index = (list.Count / 2);
    int iterations = 0;

    IOrderedEnumerable<T> orderedList;
    if (largestInside)
        orderedList = list.OrderByDescending(orderSelector);
    else
        orderedList = list.OrderBy(orderSelector);

    foreach (T item in orderedList)
    {
        sortedArray[index] = item;
        if (add)
            index += ++iterations;
        else
            index -= ++iterations;

        add = !add;
    }

    return sortedArray;
}

Обратные вызовы:

int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

int[] sortedArray = SortFromMiddleOut(array, i => i, false);
foreach (int item in sortedArray)
    Console.Write("{0} ", item);

Console.Write("\n");

sortedArray = SortFromMiddleOut(array, i => i, true);
foreach (int item in sortedArray)
    Console.Write("{0} ", item);

Поскольку он является общим, он может быть списком Foo, и селектор заказов может быть f => f.Name или все, что вы хотите бросить на него.

Ответ 5

Самое быстрое (но не самое ясное) решение, вероятно, просто вычислить новый индекс для каждого элемента:

        Array.Sort(array);

        int length = array.Length;
        int middle = length / 2;
        int[] result2 = new int[length];

        for (int i = 0; i < array.Length; i++)
        {
            result2[middle + (1 - 2 * (i % 2)) * ((i + 1) / 2)] = array[i];
        }

Ответ 6

Простейшее решение - закажите список по убыванию, создайте два новых списка, в первую очередь, каждый элемент с нечетным индексом, в другой каждый даже индексированный элемент. Переверните первый список, затем добавьте второй к первому.

Ответ 7

Хорошо, я не собираюсь подвергать сомнению ваше здравомыслие здесь, так как я уверен, что вы не зададите вопрос, если не было веской причины: -)

Вот как я подхожу к нему. Создайте отсортированный список, затем просто создайте другой список, обработав ключи в порядке, поочередно вставляя перед и добавляя, например:

sortedlist = list.sort (descending)
biginmiddle = new list()
state = append
foreach item in sortedlist:
    if state == append:
        biginmiddle.append (item)
        state = prepend
    else:
        biginmiddle.insert (0, item)
        state = append

Это даст вам список, где большие предметы находятся посередине. Другие предметы будут раздуваться из середины (в чередующихся направлениях) по мере необходимости:

1, 3, 5, 7, 9, 10, 8, 6, 4, 2

Чтобы получить список, в котором большие элементы находятся на концах, просто замените начальную сортировку на восходящую.

Сортированные и окончательные списки могут быть просто указателями на фактические элементы (поскольку вы заявляете, что они не простые целые числа) - это минимизирует как дополнительные требования к хранению, так и копирование.

Ответ 8

Может быть, это не лучшее решение, но здесь отличный способ...

Пусть Product[] parr будет вашим массивом.

Отказ от ответственности Это java, мой С# ржавый. Неподтвержденный код, но вы получаете идею.

int plen = parr.length
int [] indices = new int[plen];
for(int i = 0; i < (plen/2); i ++)
    indices[i] = 2*i + 1;   // Line1
for(int i = (plen/2); i < plen; i++)
    indices[i] = 2*(plen-i); // Line2

for(int i = 0; i < plen; i++)
{
    if(i != indices[i])
        swap(parr[i], parr[indices[i]]);
}

Второй случай, Что-то вроде этого?

int plen = parr.length
int [] indices = new int[plen];
    for(int i = 0; i <= (plen/2); i ++)
       indices[i] = (plen^1) - 2*i;
    for(int i = 0; i < (plen/2); i++)
       indices[i+(plen/2)+1] = 2*i + 1;


for(int i = 0; i < plen; i++)
{
    if(i != indices[i])
        swap(parr[i], parr[indices[i]]);
}