这个在C#中使用QuickSort算法有什么问题?

时间:2012-10-08 16:39:55

标签: c# algorithm quicksort

可悲的是,我也遇到了Quicksort的问题。我和其他学生讨论了这个问题并尝试了标准的故障排除方法。但是我找不到我做错了什么......

static int partition(int[] A)
    {
        int pivot, i, j;
        i = 0;
        j = A.Length;
        pivot = A[i];

        do
        {
            do
            {
                i++;
            }
            while (A[i] < pivot || i < j);
            do
            {
                j--;
            }
            while (A[j] > pivot);

            swap(ref A[i], ref A[j]);
        }
        while (i < j);

        if (i <= j)
        {
            swap(ref A[i], ref A[j]);
            swap(ref A[0], ref A[j]);
        }

        return j;
    }

    static void swap(ref int a, ref int b)
    {
        int aCopy = a;
        a = b;
        b = aCopy;
    }

    static int[] QuickSort(int[] A)
    {
        int s;
        int left = 0;
        int right = A.Length;
        int[] B, C;

        if (A.Length == 0 || A.Length == 1)
            return A;
        else
        {
            s = partition(A);
            B = new int[s];
            C = new int[right - s];

            for (int i = left; i < s; i++)
            {
                B[i - left] = A[i];
            }
            for (int i = s; i < right; i++)
            {
                C[i - s] = A[i];
            }

            QuickSort(B);
            QuickSort(C);
            return A;
        }
    }

调试器经常给我一个带有j变量的'IndexOutOfRangeException'。我试过调整

while (A[i] < pivot || i < j);

一部分。但是这没有做任何事情(好吧,它导致了一次StackOverflowException)。

4 个答案:

答案 0 :(得分:3)

您有j = A.Length,但它应该是j = A.Length - 1(基于您正在使用的循环)。

答案 1 :(得分:1)

我注意到即使片段长度为2或3个元素,您仍在尝试使用Quicksort。当您将输入数组的大小减少到少于4个元素时,我认为您需要使用替代排序技术。请记住,快速排序要求您将输入数组拆分为“pivot”元素和2个单独的数组,其中一个数组的所有元素都小于数据透视图,另一个数组的所有元素都大于数据透视表。要做到这一点,输入数组必须至少有3个元素。这可能是您问题的根源。

这是一个使用这种技术的通用快速排序......

public delegate int CompareDlg<T>(T thisOne, T otherOne);

public class QuickSort<T> 
{
    #region private variable to sort inplace
    readonly private IList<T> itms;
    private CompareDlg<T> cmp;
    #endregion private variable to sort inplace

    #region properties
    public CompareDlg<T> Comparer
    {
        set { cmp = value; }
        get { return cmp; }
    }
    #endregion properties

    #region ctor
    private QuickSort() { } //     Hide parameterless constructor
    /// <summary>
    /// Constructor, requires generic List of Ts 
    /// where T must implement CompareTo() method...
    /// </summary>
    /// <param name="list">List of T elements to sort</param>
    public QuickSort(IList<T> list) : this(list, null) { }
    /// <summary>
    /// Constructor, requires generic List of Ts 
    /// where T must implement CompareTo() method,
    /// And Compare method to use when sorting,
    /// (Overrides default CompareTo() implemented by T) ...
    /// </summary>
    /// <param name="list">List of T elements to sort</param>
    /// <param name="compareDelegate">Method to use to compare elements</param>
    public QuickSort(IList<T> list, CompareDlg<T> compareDelegate)
        : this()
    {
        if (list.Count == 0) throw new InvalidOperationException(
            "Empty List passed to QuickSort.");
        var first = default(T);
        if (typeof(T).IsClass)
        {
            foreach (var t in list)
                if (!((first = t).Equals(default(T))))
                    break;

            if (first.Equals(default(T)))
                throw new InvalidOperationException(
                    "List passed to QuickSort contains all nulls.");
        }
        if (compareDelegate == null && !(first is IComparable<T>))
            throw new InvalidOperationException(string.Format(
                "Type {0} does not implement IComparable<{0}>. " +
                "Generic Type T must either implement IComparable " +
                "or a comparison delegate must be provided.", typeof(T)));
        itms = list;
        cmp += compareDelegate ?? CompareDefault;
    }
    #endregion ctor

    #region public sort method
    public static void Sort(IList<T> itms) { (new QuickSort<T>(itms)).Sort(); }
    public void Sort(bool asc) { Sort(0, itms.Count - 1, asc); }
    public static void Sort(IList<T> itms, CompareDlg<T> compareDelegate)
    { (new QuickSort<T>(itms, compareDelegate)).Sort(); }
    public void Sort() { Sort(0, itms.Count - 1, true); }
    /// <summary>
    /// Executes QuickSort algorithm
    /// </summary>
    /// <param name="L">Index of left-hand boundary of partition to sort</param>
    /// <param name="R">Index of right-hand boundary of partition to sort</param>
    /// <param name="asc"></param>
    private void Sort(int L, int R, bool asc)
    {
        // Call iSort (insertion-sort) 
        if (R - L < 4) iSort(L, R);
        //for partitions smaller than 4 elements
        else
        {
            int i = (L + R) / 2, j = R - 1;
            // Next three lines to set upper and lower bounds
            if (Comparer(itms[L], itms[i]) > 0 == asc) Swap(L, i);
            if (Comparer(itms[L], itms[R]) > 0 == asc) Swap(L, R);
            if (Comparer(itms[i], itms[R]) > 0 == asc) Swap(i, R);
            Swap(i, j);
            // --------------------------------------------
            var p = itms[j]; // p = itms[j] is pivot element
            i = L;
            while (true)
            {
                while (Comparer(itms[++i], p) < 0 == asc) { }
                while (Comparer(itms[--j], p) > 0 == asc) { }
                if (j < i) break;
                Swap(i, j);
            }
            Swap(i, R - 1);
            Sort(L, i, asc);     // Sort  Left partition
            Sort(i + 1, R, asc); // Sort Right partition
        }
    }
    private static int CompareDefault(T thisOne, T otherOne)
    {
        if(!(thisOne is IComparable<T>)) 
            throw new InvalidCastException(
                "Type must implement IComparable<T>");
        return (thisOne as IComparable<T>).CompareTo(otherOne);
    }
    #endregion public sort method

    #region private Helper methods
    private void Swap(int L, int R)
    {
        var t = itms[L];
        itms[L] = itms[R];
        itms[R] = t;
    }
    private void iSort(int L, int R)
    {
        for (var i = L; i <= R; i++)
        {
            var t = itms[i];
            var j = i;
            while ((j > L) && Comparer(itms[j - 1], t) > 0)
            {
                itms[j] = itms[j - 1];
                j--;
            }
            itms[j] = t;
        }
    }
    #endregion private Helper methods
}

答案 2 :(得分:1)

您始终将第一个项目作为一个项目包含在枢轴下,而不检查值,然后您将包括所有其他值,即使它们位于枢轴之上,只是因为i < j,因为您使用'或条件之间的'运算符(||)。

此:

do
{
  i++;
}
while (A[i] < pivot || i < j);

应该是:

while (i < j && A[i] < pivot);
{
  i++;
}

答案 3 :(得分:1)

嗯,有太多的错误,比如你要交换已经交换过的元素等等。这是您的解决方案,您可以毫无错误地执行此操作,其中还包括之前的答案和评论中所述的内容。不错的编码!

    static int partition(int[] A)
    {
        int pivot, i, j;
        i = 0;
        j = A.Length-1;
        pivot = A[0];

        while (i < j)
        {
            while (A[i] < pivot && i < j)
            {
                i++;
            }

            while (A[j] > pivot && j > i)
            {
                j--;
            }
            swap(ref A[i], ref A[j]);
        }

        return j;
    }

    static void swap(ref int a, ref int b)
    {
        int aCopy = a;
        a = b;
        b = aCopy;
    }

    static int[] QuickSort(int[] A)
    {
        int s;
        int left = 0;
        int right = A.Length;
        int[] B, C;

        if (A.Length == 0 || A.Length == 1)
            return A;
        else
        {
            s = partition(A);
            B = new int[s];
            C = new int[right - s - 1];

            for (int i = left; i < s; i++)
            {
                B[i - left] = A[i];
            }
            for (int i = s + 1; i < right; i++)
            {
                C[i - s - 1] = A[i];
            }

            B = QuickSort(B);
            C = QuickSort(C);
            for(int i = 0; i < s;i++)
                A[i] = B[i - left];
            for (int i = s + 1; i < right; i++)
            {
                A[i] = C[i - s - 1];
            }
            return A;
        }
    }