在Java

时间:2017-04-14 21:36:54

标签: java recursion quicksort

教授先生给我们编写了一个编写自定义qucksort算法的任务,我们必须使用他的大纲来实现(我不能从头开始写我自己的,我必须使用他的)。他称之为smartQuickSort,是什么让这个算法"定制"是我们必须计算枢轴点每侧的平均值,然后用于对数组进行排序。该算法使用名为SmartQuickSortPivot的类,其具有int值leftright以分别保持左/右侧的平均值。

我已经用多种语言编写了许多快速排序算法,但在我的生活中,我不能正确地排序。我花了3天的时间重写和调试这个东西没有成功,所以我真的希望有人可以帮助我,因为我要把所有的头发拉出来。从"骨架代码开始#34;他给了我们(包括评论说明),这是我最近的尝试:

/**
     * split4SmartQuickSort splits the array (from first to last) into two subarrays, left and right, using the
     * provided splitVal. It needs to calculate on the fly the average of all the elements of the left subarray
     * and average of all elements of the right subarray, and store the two averages in the @pivot object.
     * The following implementation is only copy of the code from
     * the split function (from line 247) and you should enhance the function to implement what we need to calculate the averages
     * as the pivot for the left and right subarray.
     *
     * Please be noted that splitVal may not even exist in the array since we choose the average.
     * But this should not impact the correctness algorithm of splitting and sorting.
     * @param first
     * @param last
     * @param splitVal
     * @param leftRightAverages
     * @return
     */
    static int split4SmartQuickSort(int first, int last, int splitVal, SmartQuickSortPivot leftRightAverages)
    {
      int saveF = first;
      int leftAvg = 0;
      int leftCount = 0;
      int rightAvg = 0;
      int rightCount = 0;
      boolean onCorrectSide;

      first++;
      do
      {
        onCorrectSide = true;
        while (onCorrectSide)             // move first toward last
          if (values[first] > splitVal)
            onCorrectSide = false;
          else
          {
            //I think my average calculations here are wrong,
            //but nothing I have tried works correctly 
            leftAvg += first;
            leftCount++;
            first++;
            leftRightAverages.left = leftAvg / leftCount;
            onCorrectSide = (first <= last);
          }

        onCorrectSide = (first <= last);
        while (onCorrectSide)             // move last toward first
          if (values[last] <= splitVal)
            onCorrectSide = false;
          else
          {
            //I think my average calculations here are wrong,
            //but nothing I have tried works correctly 
            rightAvg += last;
            rightCount++;
            last--;
            leftRightAverages.right = rightAvg / rightCount;
            onCorrectSide = (first <= last);
          }

        if (first < last)
        {
          swap(first, last);
          first++;
          last--;
        }
      } while (first <= last);

      swap(saveF, last);
      //I think this is one of my problems. Not sure
      //what I should be returning here
      return last;    
    }

  /**
   * Smart quick sort allows the use of a better splitting value (the pivot value) when to split the array
   * into two. In this algorithm, we will use the average of the array (subarray) of all elements as the pivot.
   *
   * Each call to split (split4SmartQuickSort method), the splitValue will be passed and also the split4SmartQuickSort
   * will return the averages of left subarray and right subarray. The two averages, each will be used for the
   * following calls to smartQuickSort.
   *
   * @param first the first element
   * @param last the last element
   * @param splitVal the pivot value for splitting the array
   */
  static void smartQuickSort(int first, int last, int splitVal)
  {
    if (first < last)
    {
      int splitPoint;
      SmartQuickSortPivot leftRightAverages = new SmartQuickSortPivot();

      splitPoint = split4SmartQuickSort(first, last, splitVal, leftRightAverages);
      if (first <= splitPoint)
      {
        smartQuickSort(first, splitPoint - 1, leftRightAverages.left);
      }
      if (last >= splitPoint)
      {
        smartQuickSort(splitPoint + 1, last, leftRightAverages.right);
      }
    }
  }

以下是用于存储枢轴点左/右平均值的类:

public class SmartQuickSortPivot {
    public int left;
    public int right;
}

最后用于测试的主要方法:

public static void main(String[] args)
  {
    //initValues();
    printValues();
    System.out.println("values is sorted: " + isSorted());
    System.out.println();

    //quickSort(0, values.length - 1);


    /** you can either compute the average first as the first pivot or simplify choose the first one as the pivot */
    smartQuickSort(0, values.length - 1, values[4]);

    printValues();
    System.out.println("values is sorted: " + isSorted());
    System.out.println();
  }
}

我注释掉的那一行,//quickSort(0, values.length - 1);是我写的算法,它不包含leftRightAverages对象参数,但基本相同,而且效果很好,所以我非常困惑为什么我无法获得&#34; custom&#34; smartQuickSort工作。为简单起见,我注释掉了initValues()方法,而是使用了一个如下所示的预设数组:

  static int[] values = {2,5,1,66,89,44,32,51,8,6};   // values to be sorted

我尝试过的事情(并且失败了):

1。)将行leftRightAverages.left = leftAvg / leftCount;leftRightAverages.right = rightAvg / rightCount;移到do-while循环之外,由于函数的递归性质(我认为),最终给出了除以零的RTE 。

2.。)将split4SmartQuickSort()的返回值从last更改为rightLeftAverages.leftrightLeftAverages.right的不同组合,这会导致堆栈从递归中溢出。这是我真的很困惑的地方,因为我不确定这个方法应该在这个快速排序的特定实现中返回什么(更重要的是,如何正确计算它)。

我认为我的问题是双重的;我没有正确计算枢轴两侧的平均值(我使用了很多枢轴点,但它们似乎没有任何区别),而且我没有返回正确的计算结果split4SmartQuickSort()方法本身。如果我从方法参数中删除rightLeftAverages对象并使用更传统的方法进行快速排序,则算法可以正常工作。这就是为什么我认为我列出的这两个问题是算法无法正常运行的原因。 split4SmartQuickSort()(我认为)的返回值作为排序的新枢轴点,使用splitVal参数作为原始枢轴点。

是的,这是我的作业,但我已经花了几个小时的真正努力,没有运气。我的教授周末没有回复电子邮件,他的办公时间也在我的其他课程中,所以我无处可去。

2 个答案:

答案 0 :(得分:3)

我认为你有这个问题,因为在这种情况下很难使用一个整数分裂点。原因如下:

想象一下,在某些算法中你得到了44,51,89,66来进行分区,平均值为62.5~62。如果你使用62作为枢轴元素,那么作为一个分裂点会有什么不确定性(因为你可以返回索引1或2(相应值51或89)。

让我们假设您选择2.这将导致无效算法(让我们记住,分裂点(pivot)a_j是将数组划分为两个子数组的点,例如每个i < j a_i < a_j和每个{{1因为k > j a_j < a_k并且不能成为分裂点。

你需要做的是将中间的东西作为分裂点返回。为此,您需要返回89 !< 66对象而不是int,并使用其左/右值作为左/右数组的结束/起始索引。

SmartQuickSortPivot

答案 1 :(得分:1)

感谢下面的好建议,我得到了算法,但它仍然没有正确排序重复(遇到欺骗时无限循环)。在玩完代码之后,我现在有了一个完整的工作算法。更改仅在split4SmartQuickSort(),因此更新了该方法:

static SmartQuickSortPivot split4SmartQuickSort
                    (int first, int last, int splitVal, SmartQuickSortPivot leftRightAverages)
    {
      int f = first;
      int l = last;
      int sumLeft = 0;
      int sumRight = 0;

      while (f < l)
      {
        while (values[f] < splitVal)
        {
          sumLeft += values[f];
          f++;
        }

        while (values[l] > splitVal)
        {
          sumRight += values[l];
          l--;
        }

        if (f <= l)
        {
          swap(f, l);

          //handling duplicates in the list
          if (values[f] == values[l])
          {
            f++;
          }
        }
      }

      if (f - first == 0)
      {
        leftRightAverages.left = values[first];
      }
      else
      {
        leftRightAverages.left = sumLeft / (f - first);
      }

      if (last - l == 0)
      {
        leftRightAverages.right = values[last];
      }
      else
      {
        leftRightAverages.right = sumRight / (last - l);
      }

      //create SmartQuickSortPivot object to be returned. Used in
      //smartQuickSort as the split point for sorting
      SmartQuickSortPivot sqsp = new SmartQuickSortPivot();
      sqsp.left = f;
      sqsp.right = l;
      return sqsp;

    }

最后,smartQuickSort()算法:

static void smartQuickSort(int first, int last, int splitVal)
  {
    if (first < last)
    {
      SmartQuickSortPivot splitPoint;
      SmartQuickSortPivot leftRightAverages = new SmartQuickSortPivot();

      splitPoint = split4SmartQuickSort(first, last, splitVal, leftRightAverages);
      if (first <= splitPoint.left)
      {
        smartQuickSort(first, splitPoint.left - 1, leftRightAverages.left);
      }
      if (last >= splitPoint.right)
      {
        smartQuickSort(splitPoint.right + 1, last, leftRightAverages.right);
      }
    }
  }

再次感谢@ shyyko-serhiy,因为他们应该获得大部分功劳:)