我了解,由于分区的工作方式/远距离交换,快速排序的确是不稳定的(对于基元而言)。我试图了解如果使用quicksort对具有相等键的复杂对象进行排序会发生什么。从本质上讲,为什么Java Collections.sort
不使用Quicksort。
这是我创建的一个演示应用程序,以帮助我理解。按照此应用程序,具有相等键的对象似乎保留其输入顺序。我知道我在这里有一些理解上的空白。我确实在网上搜索,但大多数示例都是基于整数排序的。
请帮助我了解快速排序稳定性问题。
演示
import java.util.*;
public class QuickSortStabilityDemo {
static class Node implements Comparable<Node> {
String name;
int rank;
public Node(String name, int rank) {
this.name = name;
this.rank = rank;
}
@Override
public int compareTo(Node o) {
int result = this.name.compareTo(o.name);
if(result == 0) {
return this.rank == o.rank ? 0 : this.rank < o.rank ? -1: 1;
}
else {
return result;
}
}
@Override
public String toString() {
return "{" + this.name + "," + this.rank + "," + this.hashCode() + "}" ;
}
}
//Fisher-Yates
public void shuffleArray(Node[] arr) {
Random random = new Random();
int n = arr.length;
for(int i=n-1; i>=0; i--) {
int j = random.nextInt(i+1);
Node temp = arr[i];
arr[i]= arr[j];
arr[j]=temp;
}
}
private void swap(Node[] arr, int i, int j) {
Node temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public void sort(Node[] arr, int start, int end) {
if(start >= end) {
return;
}
Node pivot = arr[start];
int lt = start;
int gt = end;
for(int current=start+1;current <= gt; ) {
if(arr[current].compareTo(pivot) < 0) {
swap(arr,current,lt);
current++;
lt++;
}
else if(arr[current].compareTo(pivot) > 0) {
swap(arr,current,gt);
gt--;
}
else {
current++;
}
}
sort(arr,start,lt-1);
sort(arr,gt+1,end);
}
public static void main(String[] args) {
QuickSortStabilityDemo sort = new QuickSortStabilityDemo();
String[] cities = {"New York","Jersey City","Pittsburgh"};
List<Node> list = new ArrayList<>();
for(int i=0;i <3;i++) {
for(int j=1; j <=3; j++) {
list.add(new Node(cities[i],i));
}
}
Node[] arr = list.toArray(new Node[list.size()]);
System.out.println("Before sorting...");
System.out.println(Arrays.toString(arr));
sort.sort(arr,0,arr.length-1);
System.out.println("After sorting...");
System.out.println(Arrays.toString(arr));
}
}
答案 0 :(得分:3)
如果要查看不稳定的结果,请勿比较rank
。
如果同时比较名称和等级,则项目之间的顺序很严格,那么结果是稳定的。
不稳定的结果仅在两个项目彼此相等时发生。
这是我的版本:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class QuickSortStabilityDemo {
static class Node implements Comparable<Node> {
String name;
int rank;
public Node(String name, int rank) {
this.name = name;
this.rank = rank;
}
@Override
public int compareTo(Node o) {
return this.name.compareTo(o.name);
}
@Override
public String toString() {
return "{" + this.name + "," + this.rank + "}";
}
}
private void swap(Node[] arr, int i, int j) {
Node temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public void sort(Node[] arr, int start, int end) {
if (start >= end) {
return;
}
Node pivot = arr[start];
int lt = start;
int gt = end;
for (int current = start + 1; current <= gt; ) {
if (arr[current].compareTo(pivot) < 0) {
swap(arr, current, lt);
current++;
lt++;
} else if (arr[current].compareTo(pivot) > 0) {
swap(arr, current, gt);
gt--;
} else {
current++;
}
}
sort(arr, start, lt - 1);
sort(arr, gt + 1, end);
}
public static void main(String[] args) {
QuickSortStabilityDemo sort = new QuickSortStabilityDemo();
String[] cities = {"New York", "Jersey City", "Pittsburgh"};
List<Node> list = new ArrayList<>();
for (int i = 1; i <= 3; i++) {
for (int j = 0; j < 3; j++) {
list.add(new Node(cities[j], i));
}
}
Node[] arr = list.toArray(new Node[list.size()]);
System.out.println("Before sorting...");
System.out.println(Arrays.toString(arr));
sort.sort(arr, 0, arr.length - 1);
System.out.println("After sorting...");
System.out.println(Arrays.toString(arr));
}
}
输出:
Before sorting...
[{New York,1}, {Jersey City,1}, {Pittsburgh,1}, {New York,2}, {Jersey City,2}, {Pittsburgh,2}, {New York,3}, {Jersey City,3}, {Pittsburgh,3}]
After sorting...
[{Jersey City,1}, {Jersey City,3}, {Jersey City,2}, {New York,2}, {New York,1}, {New York,3}, {Pittsburgh,2}, {Pittsburgh,3}, {Pittsburgh,1}]
在排序之前,您可以看到{Jersey City,2}
在{Jersey City,3}
之前。
但是排序之后,{Jersey City,2}
之后是{Jersey City,3}
。
这是不稳定的结果。
PS:如果使用其他稳定算法,则结果必须为{J,1},{J,2},{J,3},{N,1},{N,2},{N,3},{P,1},{P,2},{P,3}
。
答案 1 :(得分:0)
我找到了答案:
在我发布的原始演示中,数据有些虚构。每个集合中对象的属性是相同的,并且是有目的地完成的。我没有对数组进行洗牌;将枢轴设置为要排序的数组部分的开始元素。
当我进一步调试演示时,尽管 NY 和 JC 对象保留了其原始顺序,但 Pgh 对象确实改变了其原始插入顺序。因此,我确实看到了算法的不稳定性。
我使用这些元素的哈希码来跟踪其原始插入顺序。
以下是运行的输出:
[{New York,0,1163157884}
, {New York,0,1956725890}
, {New York,0,356573597}
, {Jersey City,1,1735600054}
, {Jersey City,1,21685669}
, {Jersey City,1,2133927002}
, {Pittsburgh,2,1836019240}
, {Pittsburgh,2,325040804}
, {Pittsburgh,2,1173230247}
]
After sorting
[{Jersey City,1,1735600054}
, {Jersey City,1,21685669}
, {Jersey City,1,2133927002}
, {New York,0,1163157884}
, {New York,0,1956725890}
, {New York,0,356573597}
, {Pittsburgh,2,325040804}
, {Pittsburgh,2,1173230247}
, {Pittsburgh,2,1836019240}
]
如果我对输入数组进行混洗,则算法的不稳定性更加明显。
这是一次运行的输出(具有混洗的输入):
Original order
[{New York,0,1163157884}
, {New York,0,1956725890}
, {New York,0,356573597}
, {Jersey City,1,1735600054}
, {Jersey City,1,21685669}
, {Jersey City,1,2133927002}
, {Pittsburgh,2,1836019240}
, {Pittsburgh,2,325040804}
, {Pittsburgh,2,1173230247}
]
After shuffling
[{New York,0,1163157884}
, {New York,0,1956725890}
, {Pittsburgh,2,325040804}
, {Jersey City,1,2133927002}
, {New York,0,356573597}
, {Jersey City,1,1735600054}
, {Pittsburgh,2,1836019240}
, {Pittsburgh,2,1173230247}
, {Jersey City,1,21685669}
]
After sorting
[{Jersey City,1,21685669}
, {Jersey City,1,2133927002}
, {Jersey City,1,1735600054}
, {New York,0,1956725890}
, {New York,0,356573597}
, {New York,0,1163157884}
, {Pittsburgh,2,1173230247}
, {Pittsburgh,2,1836019240}
, {Pittsburgh,2,325040804}
]
请告诉我有关此答案的任何建议。