如何获得快速排序的分区以产生正确和预期的输出?
How to get partition of quicksort to produce the correct and expected output?
这涉及 https://whosebug.com/help/on-topic 中的 "a software algorithm",在本例中是快速排序算法
这是来自https://www.hackerrank.com/challenges/quicksort1
的练习编码问题(非竞赛)
基本上你应该接受一个列表,比如
4 5 3 7 2
并围绕第一个元素进行分区,在本例中为 4。预期输出为
3 2 4 5 7
但是我得到的输出是
2 3 4 7 5
这是我进行分区的代码(基于 https://courses.cs.washington.edu/courses/cse373/13wi/lectures/02-27/20-sorting3-merge-quick.pdf 幻灯片 16 中的学习友好版本)
static void partition(int[] ar) {
int toPartitionAround = ar[0];
swap(ar, 0, ar.length - 1);
int index = partition(ar, 0, ar.length - 2, toPartitionAround);
swap(ar, index, ar.length - 1);
printArray(ar);
}
static int partition(int[] ar, int left, int right, int toAround) {
while(left <= right) {
while(ar[left] < toAround) {
left ++;
}
while(ar[right] > toAround) {
right --;
}
if(left <= right) {
swap(ar, left, right);
left ++;
right --;
}
}
return left;
}
static void swap(int[] arrayNums, int index1, int index2) {
int temp = arrayNums[index1];
arrayNums[index1] = arrayNums[index2];
arrayNums[index2] = temp;
}
我可以对此进行修改以匹配预期的输出吗?我在技术上是否仍然得到可能的正确输出,因为我的输出确实遵循 属性 枢轴左侧的元素小于枢轴并且枢轴右侧的元素大于枢轴?
我所做的是分配主元,将其移动到数组的后面,然后围绕主元划分数组的其余部分(从索引 0 到长度 - 2)。使用此输入,在遍历数组(3 和 5)时发生一次交换。然后,当我从分区结果中获取索引时,我将主元与该结果交换,这意味着我交换了 4 和 5。
我是否遵循类似的过程来获得预期的输出?我看不到要替换什么。
阅读@greybeard 和@Smac89 的评论后,我意识到我需要一种稳定形式的快速排序,稳定如 "preserving the original order of the input set, where the comparison algorithm does not distinguish between two or more items"(https://softwareengineering.stackexchange.com/questions/247440/what-does-it-mean-for-a-sorting-algorithm-to-be-stable)
对我来说,稳定排序对这个 运行 没有真正意义,因为比较算法确实区分 3 和 2,但是哦,好吧。有人可以澄清一下吗?
我想出的维护相对的解决方案是创建两个 ArrayList,一个用于保存小于或等于主元的值,一个用于保存大于主元的值。这样就保留了相对定位。(2 将在 3 之后,就像在原始列表中一样)。然后,我将两个 ArrayList 中的值连同枢轴一起移回原始枢轴。
如果有人遇到问题,这是我的代码解决方案
static void partition(int[] ar) {
List<Integer> smallerValues = new ArrayList<Integer>();
List<Integer> biggerValues = new ArrayList<Integer>();
int toPartitionAround = ar[0];
for(int c=1;c<ar.length;c++) {
if(ar[c] <= toPartitionAround) {
smallerValues.add(ar[c]);
} else {
biggerValues.add(ar[c]);
}
}
for(int c=0;c<smallerValues.size();c++) {
ar[c] = smallerValues.get(c);
}
ar[smallerValues.size()] = toPartitionAround;
for(int m=0;m<biggerValues.size();m++) {
ar[m + smallerValues.size() + 1] = biggerValues.get(m);
}
printArray(ar);
}
这有点令人困惑,不是吗?我为解决该问题所做的一切就是创建 2 个单独的列表,一个用于存储低于枢轴的元素,另一个用于存储高于枢轴的元素。然后,您只需打印出这些值。这是保留元素顺序并进行 "in-place" 排序的方式。但它也消耗内存。
这涉及 https://whosebug.com/help/on-topic 中的 "a software algorithm",在本例中是快速排序算法
这是来自https://www.hackerrank.com/challenges/quicksort1
的练习编码问题(非竞赛)基本上你应该接受一个列表,比如
4 5 3 7 2
并围绕第一个元素进行分区,在本例中为 4。预期输出为
3 2 4 5 7
但是我得到的输出是
2 3 4 7 5
这是我进行分区的代码(基于 https://courses.cs.washington.edu/courses/cse373/13wi/lectures/02-27/20-sorting3-merge-quick.pdf 幻灯片 16 中的学习友好版本)
static void partition(int[] ar) {
int toPartitionAround = ar[0];
swap(ar, 0, ar.length - 1);
int index = partition(ar, 0, ar.length - 2, toPartitionAround);
swap(ar, index, ar.length - 1);
printArray(ar);
}
static int partition(int[] ar, int left, int right, int toAround) {
while(left <= right) {
while(ar[left] < toAround) {
left ++;
}
while(ar[right] > toAround) {
right --;
}
if(left <= right) {
swap(ar, left, right);
left ++;
right --;
}
}
return left;
}
static void swap(int[] arrayNums, int index1, int index2) {
int temp = arrayNums[index1];
arrayNums[index1] = arrayNums[index2];
arrayNums[index2] = temp;
}
我可以对此进行修改以匹配预期的输出吗?我在技术上是否仍然得到可能的正确输出,因为我的输出确实遵循 属性 枢轴左侧的元素小于枢轴并且枢轴右侧的元素大于枢轴?
我所做的是分配主元,将其移动到数组的后面,然后围绕主元划分数组的其余部分(从索引 0 到长度 - 2)。使用此输入,在遍历数组(3 和 5)时发生一次交换。然后,当我从分区结果中获取索引时,我将主元与该结果交换,这意味着我交换了 4 和 5。
我是否遵循类似的过程来获得预期的输出?我看不到要替换什么。
阅读@greybeard 和@Smac89 的评论后,我意识到我需要一种稳定形式的快速排序,稳定如 "preserving the original order of the input set, where the comparison algorithm does not distinguish between two or more items"(https://softwareengineering.stackexchange.com/questions/247440/what-does-it-mean-for-a-sorting-algorithm-to-be-stable)
对我来说,稳定排序对这个 运行 没有真正意义,因为比较算法确实区分 3 和 2,但是哦,好吧。有人可以澄清一下吗?
我想出的维护相对的解决方案是创建两个 ArrayList,一个用于保存小于或等于主元的值,一个用于保存大于主元的值。这样就保留了相对定位。(2 将在 3 之后,就像在原始列表中一样)。然后,我将两个 ArrayList 中的值连同枢轴一起移回原始枢轴。
如果有人遇到问题,这是我的代码解决方案
static void partition(int[] ar) {
List<Integer> smallerValues = new ArrayList<Integer>();
List<Integer> biggerValues = new ArrayList<Integer>();
int toPartitionAround = ar[0];
for(int c=1;c<ar.length;c++) {
if(ar[c] <= toPartitionAround) {
smallerValues.add(ar[c]);
} else {
biggerValues.add(ar[c]);
}
}
for(int c=0;c<smallerValues.size();c++) {
ar[c] = smallerValues.get(c);
}
ar[smallerValues.size()] = toPartitionAround;
for(int m=0;m<biggerValues.size();m++) {
ar[m + smallerValues.size() + 1] = biggerValues.get(m);
}
printArray(ar);
}
这有点令人困惑,不是吗?我为解决该问题所做的一切就是创建 2 个单独的列表,一个用于存储低于枢轴的元素,另一个用于存储高于枢轴的元素。然后,您只需打印出这些值。这是保留元素顺序并进行 "in-place" 排序的方式。但它也消耗内存。