代码之家  ›  专栏  ›  技术社区  ›  Kaiser Advisor

在C中,外壳排序(梳状/递减增量排序)最优雅的方式是什么?

  •  3
  • Kaiser Advisor  · 技术社区  · 15 年前

    有没有更好的方法来使用c_进行外壳排序?

    // array of integers to hold values
    private int[] a = new int[100];
    
    // number of elements in array
    private int count;
    
    // Shell Sort Algorithm
    public void sortArray()
    {
      int i, j, increment, temp;
    
      increment = 3;
    
      while( increment > 0 )
      {
        for( i=0; i < count; i++ )
        {
          j = i;
          temp = a[i];
    
          while( (j >= increment) && (a[j-increment] > temp) )
          {
            a[j] = a[j - increment];
            j = j - increment;
          }
    
          a[j] = temp;
        }
    
        if( increment/2 != 0 )
        {
          increment = increment/2;
        }
        else if( increment == 1 )
        {
          increment = 0;
        }
        else
        {
          increment = 1;
        }
      }
    }
    

    顺便说一下,我想知道的是,我有一些不同语言的“优雅”排序示例(比如 C# F# ,我正在比较它们。在现实生活中,我可能会在C中使用以下大部分时间:

    Array.Sort( object[] )
    

    我不在乎这些是不是“学术的”和非实际的模式。如果你愿意的话,你可以把我变成遗忘的人。

    灵魂

    2 回复  |  直到 9 年前
        1
  •  2
  •   Jon Skeet    15 年前

    您可以很容易地进行以下改进:

    • 不要在对象中保持状态。只需使用局部变量就可以轻松做到这一点。
    • 使用传统的C名称,例如 ShellSort 而不是 shellSort
    • 使用有意义的名称,如 count 而不是 x
    • 使用条件运算符,例如

      // This replaces your last 12 lines
      int halfIncrement = increment / 2;
      increment = halfIncrement != 0 ? halfIncrement : 1 - increment;
      
    • 使代码成为通用的-为什么要将自己限制为整数?

    • 使您的方法获取相关数据,并使其成为 IList<T>
    • 通过 IComparer<T> ,提供使用默认值的重载。
    • 尽可能晚地声明变量,以减少其范围并提高可读性

    这实际上很少与排序相关-我还没有验证您的代码是否是合法的外壳排序…

        2
  •  1
  •   Gary    15 年前

    在我的测试中,这比使用相同System.Func比较器的数组排序快75%到90%。我使用它来排序自定义结构。您可以轻松地将其修改为排序类。

    public class DualQuickSort<T> where T : struct
        {
            private readonly System.Func<T, T, int> comparer;
            public DualQuickSort(System.Func<T, T, int> comparer)
            {
                this.comparer = comparer;
            }
    
        public DualQuickSort(IComparer<T> comparer)
            : this(comparer.Compare)
        {
    
        }
    
        public  void Sort(T[] a)
        {
            Sort(a, 0, a.Length);
        }
        public  void Sort(T[] a, int fromIndex, int toIndex)
        {
            RangeCheck(a.Length, fromIndex, toIndex);
    
            DualPivotQuicksort(a, fromIndex, toIndex - 1, 3);
        }
        private static void RangeCheck(int length, int fromIndex, int toIndex)
        {
            if (fromIndex > toIndex)
            {
                throw new ArgumentException("fromIndex > toIndex");
            }
            if (fromIndex < 0)
            {
                throw new IndexOutOfRangeException(fromIndex + " is less than 0");
            }
            if (toIndex > length)
            {
                throw new IndexOutOfRangeException(toIndex + " is greater than " + fromIndex);
            }
        }
    
        private static void Swap(T[] a, int i, int j)
        {
            var temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
        private  void DualPivotQuicksort(T[] a, int left, int right, int div)
        {
            var len = right - left;
    
            if (len < 27)
            { // insertion sort for tiny array
                for (var i = left + 1; i <= right; i++)
                {
                    for (var j = i; j > left && comparer(a[j] , a[j - 1])==-1; j--)
    
                    {
                        Swap(a, j, j - 1);
                    }
                }
                return;
            }
            var third = len / div;
            // "medians"
            var m1 = left + third;
            var m2 = right - third;
            if (m1 <= left)
            {
                m1 = left + 1;
            }
            if (m2 >= right)
            {
                m2 = right - 1;
            }
            if (comparer(a[m1] , a[m2])==-1)
            {
                Swap(a, m1, left);
                Swap(a, m2, right);
            }
            else
            {
                Swap(a, m1, right);
                Swap(a, m2, left);
            }
            // pivots
            var pivot1 = a[left];
            var pivot2 = a[right];
            // pointers
            var less = left + 1;
            var great = right - 1;
            // sorting
            for (var k = less; k <= great; k++)
            {
                if (comparer(a[k] , pivot1)==-1)
                {
                    Swap(a, k, less++);
                }
                else if (comparer(a[k], pivot2) == 1)
                {
                    while (k < great && comparer(a[great] , pivot2)==1)
                    {
                        great--;
                    }
                    Swap(a, k, great--);
                    if (comparer(a[k], pivot1) == -1)
                    {
                        Swap(a, k, less++);
                    }
                }
            }
            // Swaps
            var dist = great - less;
            if (dist < 13)
            {
                div++;
            }
            Swap(a, less - 1, left);
            Swap(a, great + 1, right);
            // subarrays
            DualPivotQuicksort(a, left, less - 2, div);
            DualPivotQuicksort(a, great + 2, right, div);
    
            // equal elements
            if (dist > len - 13 && comparer(pivot1,pivot2)!=0)
            {
                for (int k = less; k <= great; k++)
                {
                    if (comparer(a[k] , pivot1)==0)
                    {
                        Swap(a, k, less++);
                    }
                    else if (comparer(a[k], pivot2) == 0)
                    {
                        Swap(a, k, great--);
                        if (comparer(a[k], pivot1) == 0)
                        {
                            Swap(a, k, less++);
                        }
                    }
                }
            }
            // subarray
            if (comparer(pivot1 , pivot2)==-1)
            {
                DualPivotQuicksort(a, less, great, div);
            }
        }
    }