如何排序(使用链排序算法)整数数组? C#

时间:2014-06-25 16:52:39

标签: c# sorting

关于我的问题:我在学校有一个作业,但我失败了,因为我的代码运行得太慢了。现在我必须修理和研究它,因为我将不得不解释它如何修复它以及它是如何工作的。

我的问题是:我可以修复我的代码的哪些部分来归档平均性能O(n ^ 2)?我怎样才能让它跑得更快?

需要什么:

  • 按升序对整数数组进行排序,
  • 算法的平均性能必须为O(n ^ 2),
  • 不得使用Sort() array方法,
  • System.Collections.Generic或其他库不允许(仅允许使用系统库),
  • 必须使用链排序算法,

这是我到目前为止所尝试的,但它的工作方式太慢......我必须排序为200000个数字:

编辑:此代码无法正常运行。我在下面发布了更新版本。

static public int[] StrandSortAscending(int[] p1)
{
    int[] p2,
        p3;
    int p1s1,
        p2s1,
        p2s2,
        p3s1,
        p3s2,
        n;

    p2 = new int[p1.Length];
    p3 = new int[p1.Length];
    Reset(ref p2);
    Reset(ref p3);
    p1s1 = p1.Length;
    p3s1 = 0;

    while (p1s1 != 0)
    {
        n = int.MinValue;
        p2s1 = 0;

        for (int i8 = 0; i8 < p1.Length; i8++)
        {
            if (p1[i8] != int.MaxValue)
            {
                if (p1[i8] >= n)
                {
                    p2[p2s1] = p1[i8];
                    n = p1[i8];
                    p1[i8] = int.MaxValue;
                    p1s1--;
                    p2s1++;
                }
            }
        }

        int p3p,
            zs;
        bool pn = false;

        for (int i5 = 0; i5 < p2.Length; i5++)
        {
            p3p = int.MinValue;
            for (int i6 = 0; i6 < p3.Length; i6++)
            {
                if (pn)
                {
                    zs = p3[i6];
                    p3[i6] = p3p;
                    p3p = zs;
                }
                else
                {
                    if (p2[i5] >= p3p && p2[i5] <= p3[i6])
                    {
                        p3p = p3[i6];
                        p3[i6] = p2[i5];
                    }
                }
            }
        }

        Reset(ref p2);
    }

    return p3;
}

static void Reset(ref int[] a)
{
    for (int i = 0; i < a.Length; i++)
        a[i] = int.MaxValue;
}

维基百科链接到链排序算法:http://en.wikipedia.org/wiki/Strand_sort

注意:这个算法最大的错误是,我忘了数组是参考数据类型。并且2个数组在内存中指向同一个数组。我花了大部分时间来解决这个问题。我就像......这里到底出了什么问题......然后它终于点击了。 :)

更新(我做了一些升级):

我在这里得到了合并算法的想法:How to merge two sorted arrays into a sorted array?

static public int[] StrandSortAscending2(int[] np)
  {
    int[] up = new int[np.Length]; // sorted array
    int[] zp = new int[np.Length]; // temporary array
    int[] nnp = new int[np.Length]; // new unsorted array
    int[] sp = new int[np.Length]; // merged sorted array

    int dvup = 0; // the length of non-empty elements of the sorted array

    int dvnp = np.Length; // the length of non-empty elements of the unsorted array

    //0. repeat until the unsorted array isn't empty
    while (dvnp > 0)
    {
        //NOTE: reference data type. 2 arrays point to the same array/table in memoty (RAM),
        //from the previous cycle, that's why the values in unsorted array (and temp and merged array) were wrong...
        zp = new int[np.Length];
        nnp = new int[np.Length];
        sp = new int[np.Length];

        //these counters are needed for knowing till which index of an array are the elements not-empty
        //so that i don't need to always increase and decrease the size of an array, but just overwrite the old values
        //the algorithm must not be slow, the higher memory usage is not a problem

        int dvzp = 0; // the length of non-empty elements of the temporary array
        int dvnnp = 0; // the length of non-empty elements of the new unsorted array

        //1.1 fill the temporary and the new unsorted array with values
        //1.2 the unsorted array should point then to the new unsorted array
        int nszp = int.MinValue; // biggest number of the temporary array

        for (int inp = 0; inp < dvnp; inp++) // index of unsorted array
        {
          if (inp == 0 || np[inp] > nszp)
          {
            nszp = np[inp];
            zp[dvzp++] = np[inp];
          }
          else
          {
            nnp[dvnnp++] = np[inp];
          }
        }

        np = nnp;
        dvnp = dvnnp;

        //2. merge temporary and sorted arrays 
        int izp = 0; // index/counter of temporary array
        int iup = 0; // index/counter of sorted array
        int isp = 0; // index/counter of merged array

        if (dvup > 0)
        {
          while (izp < dvzp && iup < dvup)
          {
            if (zp[izp] < up[iup])
                sp[isp++] = zp[izp++];
            else
                sp[isp++] = up[iup++];
          }

          //if there are still numbers left in the temporary array
          //then add then all to the merged array
          //they are all bigger then the ones already in the merged array
          while (izp < dvzp)
            sp[isp++] = zp[izp++];

          //do the same for the sorted array
          while (iup < dvup)
            sp[isp++] = up[iup++];

          // dfdfgdgd
          up = sp;
          dvup = isp;
        }
        else
        {
          up = zp;
          dvup = dvzp;
        }

    }

    return up;
  }

我可以进一步改善表现吗?这样它会跑得更快吗?

2 个答案:

答案 0 :(得分:2)

好吧,我感到无聊并且想要帮助(同样,如果没有答案,就不能留下大量评论的问题:D),所以拿了Wikipedia PHP implementation并将其转换为C#,但稍后用户添加他不能使用泛型...

所以,我发布了一个完整的实现,一个使用泛型,另一个使用它自己的LinkedList,它有点长但是它有效(采用维基百科示例,一步一步,它的行为与示例表完全相同)和它也被评论,所以它很容易理解(链表代码没有被评论)。

它来了!

public class StrandSort
{
    public static int[] SortWithGenerics(int[] Values)
    {

        List<int> Original = new List<int>();
        List<int> Result = new List<int>();
        List<int> Sublist = new List<int>();

        Original.AddRange(Values);

        //While we still have numbers to sort
        while (Original.Count > 0)
        {

            //Clear sublist and take first available number from original to the new sublist
            Sublist.Clear();
            Sublist.Add(Original[0]);
            Original.RemoveAt(0);

            //Iterate through original numbers
            for (int x = 0; x < Original.Count; x++)
            {
                //If the number is bigger than the last item in the sublist
                if (Original[x] > Sublist[Sublist.Count - 1])
                {
                    //Add it to the sublist and remove it from original
                    Sublist.Add(Original[x]);
                    Original.RemoveAt(x);
                    //Roll back one position to compensate the removed item
                    x--;

                }

            }

            //If this is the first sublist
            if (Result.Count == 0)
                Result.AddRange(Sublist); //Add all the numbers to the result
            else
            {
                //Iterate through the sublist
                for (int x = 0; x < Sublist.Count; x++)
                {

                    bool inserted = false;

                    //Iterate through the current result
                    for (int y = 0; y < Result.Count; y++)
                    {
                        //Is the sublist number lower than the current item from result?
                        if (Sublist[x] < Result[y])
                        {
                            //Yes, insert it at the current Result position
                            Result.Insert(y, Sublist[x]);
                            inserted = true;
                            break;

                        }

                    }

                    //Did we inserted the item because found it was lower than one of the result's number?
                    if (!inserted)
                        Result.Add(Sublist[x]);//No, we add it to the end of the results

                }

            }

        }

        //Return the results
        return Result.ToArray();
    }

    public static int[] SortWithoutGenerics(int[] Values)
    {

        IntLinkedList Original = new IntLinkedList();
        IntLinkedList Result = new IntLinkedList();
        IntLinkedList Sublist = new IntLinkedList();

        Original.AddRange(Values);

        //While we still have numbers to sort
        while (Original.Count > 0)
        {

            //Clear sublist and take first available number from original to the new sublist
            Sublist.Clear();
            Sublist.Add(Original.FirstItem.Value);
            Original.Remove(Original.FirstItem);

            IntLinkedItem currentOriginalItem = Original.FirstItem;

            //Iterate through original numbers
            while (currentOriginalItem != null)
            {
                //If the number is bigger than the last item in the sublist
                if (currentOriginalItem.Value > Sublist.LastItem.Value)
                {
                    //Add it to the sublist and remove it from original
                    Sublist.Add(currentOriginalItem.Value);
                    //Store the next item
                    IntLinkedItem nextItem = currentOriginalItem.NextItem;
                    //Remove current item from original
                    Original.Remove(currentOriginalItem);
                    //Set next item as current item
                    currentOriginalItem = nextItem;


                }
                else
                    currentOriginalItem = currentOriginalItem.NextItem;

            }

            //If this is the first sublist
            if (Result.Count == 0)
                Result.AddRange(Sublist); //Add all the numbers to the result
            else
            {

                IntLinkedItem currentSublistItem = Sublist.FirstItem;

                //Iterate through the sublist
                while (currentSublistItem != null)
                {

                    bool inserted = false;

                    IntLinkedItem currentResultItem = Result.FirstItem;

                    //Iterate through the current result
                    while (currentResultItem != null)
                    {
                        //Is the sublist number lower than the current item from result?
                        if (currentSublistItem.Value < currentResultItem.Value)
                        {
                            //Yes, insert it at the current Result position
                            Result.InsertBefore(currentResultItem, currentSublistItem.Value);
                            inserted = true;
                            break;

                        }

                        currentResultItem = currentResultItem.NextItem;

                    }

                    //Did we inserted the item because found it was lower than one of the result's number?
                    if (!inserted)
                        Result.Add(currentSublistItem.Value);//No, we add it to the end of the results

                    currentSublistItem = currentSublistItem.NextItem;

                }

            }

        }

        //Return the results
        return Result.ToArray();
    }

    public class IntLinkedList
    {

        public int count = 0;
        IntLinkedItem firstItem = null;
        IntLinkedItem lastItem = null;

        public IntLinkedItem FirstItem { get { return firstItem; } }
        public IntLinkedItem LastItem { get { return lastItem; } }
        public int Count { get { return count; } }

        public void Add(int Value)
        {

            if (firstItem == null)
                firstItem = lastItem = new IntLinkedItem { Value = Value };
            else
            { 

                IntLinkedItem item = new IntLinkedItem{  PreviousItem = lastItem, Value = Value };
                lastItem.NextItem = item;
                lastItem = item;

            }

            count++;

        }

        public void AddRange(int[] Values)
        {

            for (int buc = 0; buc < Values.Length; buc++)
                Add(Values[buc]);

        }

        public void AddRange(IntLinkedList Values)
        {

            IntLinkedItem item = Values.firstItem;

            while (item != null)
            {

                Add(item.Value);
                item = item.NextItem;

            }

        }

        public void Remove(IntLinkedItem Item)
        {
            if (Item == firstItem)
                firstItem = Item.NextItem;

            if (Item == lastItem)
                lastItem = Item.PreviousItem;

            if(Item.PreviousItem != null)
                Item.PreviousItem.NextItem = Item.NextItem;

            if (Item.NextItem != null)
                Item.NextItem.PreviousItem = Item.PreviousItem;

            count--;

        }

        public void InsertBefore(IntLinkedItem Item, int Value)
        {

            IntLinkedItem newItem = new IntLinkedItem { PreviousItem = Item.PreviousItem, NextItem = Item, Value = Value };

            if (Item.PreviousItem != null)
                Item.PreviousItem.NextItem = newItem;

            Item.PreviousItem = newItem;

            if (Item == firstItem)
                firstItem = newItem;

            count++;
        }

        public void Clear()
        {

            count = 0;
            firstItem = lastItem = null;

        }

        public int[] ToArray()
        {

            int[] results = new int[Count];
            int pos = 0;

            IntLinkedItem item = firstItem;

            while (item != null)
            {
                results[pos++] = item.Value;
                item = item.NextItem;
            }

            return results;

        }
    }

    public class IntLinkedItem
    { 

        public int Value;
        internal IntLinkedItem PreviousItem;
        internal IntLinkedItem NextItem;

    }
}

编辑:它实际上不是链接列表而是双链表;)

编辑:更正了非通用实现中缺少的“其他”

编辑:我真的无聊,并创建了用户代码的重构和更正版本,注释了更改,以便他可以从中学习;)

用户注意:我建议您为变量使用更有意义的名称,在阅读时更容易理解代码。

    static public int[] SortCorrectedUserCode(int[] Source)
    {
        int[] Sublist,
            Results;
        int ItemsLeft,
            SublistPos,
            ResultPos;//new variable to store current pos in results
        //n; n was useless

        Sublist = new int[Source.Length];
        Results = new int[Source.Length];
        //Avoid resets just using an integer to track array lengths
        //Reset(ref Sublist);
        //Reset(ref Results);
        ItemsLeft = Source.Length;

        ResultPos = 0;

        while (ItemsLeft != 0)
        {
            //n = int.MinValue;
            SublistPos = 0;

            for (int currentSourcePos = 0; currentSourcePos < Source.Length; currentSourcePos++)
            {
                if (Source[currentSourcePos] != int.MaxValue)
                {
                    //Added special treatment for first item in sublist (copy it yes or yes ;D)
                    if (SublistPos == 0 || Source[currentSourcePos] > Sublist[SublistPos])
                    {

                        Sublist[SublistPos] = Source[currentSourcePos];
                        //n = Source[currentSourcePos]; useless
                        Source[currentSourcePos] = int.MaxValue;
                        ItemsLeft--;
                        SublistPos++;
                    }
                }
            }

            //int p3p, zs;

            //pn is never true...
            //bool pn = false;

            //Sublist was being iterated for all it's length, not only for the current items
            //for (int currentSublistPos = 0; currentSublistPos < Sublist.Length; currentSublistPos++)

            for (int currentSublistPos = 0; currentSublistPos < SublistPos; currentSublistPos++)
            {
                //p3p = int.MinValue;

                bool inserted = false;

                //Results was being iterated for all it's length, not only for current items
                //for (int currentResultPos = 0; currentResultPos < Results.Length; currentResultPos++)

                for (int currentResultPos = 0; currentResultPos < ResultPos; currentResultPos++)
                {

                    //This part was never used...
                    //if (pn)
                    //{
                    //    zs = Results[currentResultPos];
                    //    Results[currentResultPos] = p3p;
                    //    p3p = zs;
                    //}
                    //else
                    //{

                    //This IF was wrong, when the code entered this piece of code it started
                    //for subsequent iterations in the current loop to copy data from sublist to list, which is not correct ... I think, not sure 
                    //because it's really confusing
                    //if (Sublist[currentSublistPos] >= p3p && Sublist[currentSublistPos] <= Results[currentResultPos])
                    //{
                    //p3p = Results[currentResultPos];
                    //Results[currentResultPos] = Sublist[currentSublistPos];
                    //}
                    //}

                    //New code, if the item at sublist is lower than the one at results then insert item in current position
                    if (Sublist[currentSublistPos] < Results[currentResultPos])
                    {

                        InsertInArray(currentResultPos, Sublist[currentSublistPos], Results);
                        inserted = true;
                        break;

                    }
                }
                //Did we inserted the item?
                if (!inserted)
                    Results[ResultPos] = Sublist[currentSublistPos]; //If no then just add it to the end of the results

                ResultPos++;
            }

            //Reset(ref Sublist);
        }

        return Results;
    }

    //Helper function to insert a value in an array and displace values to the right
    static void InsertInArray(int Index, int Value, int[] Target)
    {
        //Create temp array of right items
        int[] tempArray = new int[(Target.Length - Index) - 1];

        //Copy items to temp array
        Array.Copy(Target, Index, tempArray, 0, tempArray.Length);

        //Set value at index
        Target[Index] = Value;

        //Copy values from temp array to correct position
        Array.Copy(tempArray, 0, Target, Index + 1, tempArray.Length);

    }

还对功能进行了一些基准测试(因为用户关心速度),这些是我在调试模式下运行的结果(对于5000个项目,没有耐心测试更长的数组,因为旧代码非常慢):

  • 泛型:36ms
  • 非仿制药:21毫秒
  • 用户原始代码23248ms
  • 更正代码:34毫秒

答案 1 :(得分:2)

可能最重要的是,the Wikipedia article说关于链排序:

  

使用其他数据结构(如数组)会因漫长的插入和删除而大大增加算法的运行时间和复杂性。

快速浏览一下代码表明它可能确实是O(n ^ 3),因为每次通过主p2循环都会清除while数组。

我必须遗漏一些东西,因为我不知道你的算法是如何终止的。你在主循环的开头有这个:

while (p1s1 != 0)
{
    n = int.MinValue;
    p2s1 = 0;

    for (int i8 = 0; i8 < p1.Length; i8++)
    {
        if (p1[i8] != int.MaxValue)
        {
            if (p1[i8] >= n)
            {
                p2[p2s1] = p1[i8];
                n = p1[i8];
                p1[i8] = int.MaxValue;
                p1s1--;
                p2s1++;
            }
        }
    }

让我们看一下这两个if语句:

        if (p1[i8] != int.MaxValue)
        {
            if (p1[i8] >= n)

您在循环开始时将n设置为int.MaxValue。这里的第二个条件是检查p1[i8]是否大于或等于n。但它不能超过int.MaxValue,并且它不能等于int.MaxValue,因为如果它超过它将不会超过第一个条件。

我没有看到n分配了值的其他任何地方。因为p1s1唯一被改变的地方是,如果第二个条件评估为true(并且我们知道它不能),那么while循环将永远存在,因为p1s1永远不等于0。