面试题深入思考04-----排序的稳定性与你写的真的稳定吗?
1. 排序的稳定性
各大排序的稳定性(侵删)
1.1 什么是稳定排序?
简单一句概括,对于待排数组的相同数值元素(以值的排序为例),排序后其相对位置不变。
那么稳定的排序有什么用吗?大多数情况下本身每用,但就如大家所说,如果待排数组本身就是某种意义上的有序,再按照其他意义排序时,原先意义的顺序再新顺序下仍有意义。
例如:
对于按照价格高到低排序的数组,再按销量进行重新排序,那么相同销量下的元组,还是拥有一个价格高到低的顺序。
2. 你写的排序真的稳定吗?
以归并排序为例,当你没有注意细节时,可能稳定的算法就会变得不稳定。
我简单截取了归并排序中合并两个有序数组的方法的代码来解释:
//我将待排数组升为两维,标记其原先索引位置,用来追踪稳定性。
private static void merge_two(int[][] nums,int left,int right){
int[][] temp = new int[right-left+1][2];
int cur = 0;
int mid = (left+right)/2;
int i = left;
int j = mid+1;
while (i<=mid&&j<=right){
//重点就在于这句话if的等号是否添加
if(nums[i][0]<=nums[j][0]){
temp[cur][0] = nums[i][0];
temp[cur][1] = nums[i][1];
cur++;
i++;
}else {
temp[cur][0] = nums[j][0];
temp[cur][1] = nums[j][1];
cur++;
j++;
}
}
while (i<=mid){
temp[cur][0] = nums[i][0];
temp[cur][1] = nums[i][1];
cur++;
i++;
}
while (j<=right){
temp[cur][0] = nums[j][0];
temp[cur][1] = nums[j][1];
cur++;
j++;
}
for (int k = 0; k < right-left+1; k++) {
nums[left+k][0] = temp[k][0];
nums[left+k][1] = temp[k][1];
}
}
在这方法中,对于合并两个有序的数组是影响归并排序的关键。代码中注释的那一行,当你取等号时,意义是遇到两个相同元素,优先将前一个放入新数组,这就满足了稳定性,而不取等号则来到else分支,导致相同的两个数,后者被提前排序,排序不稳定。
完整源码
大家可以按这个源码去实践一下会更清楚:
public class MyMergeSort {
public static void main(String[] args) {
//待排数组
int[] nums = new int[]{2,2,2,2,2,2,2};
//加入索引来追踪位置
int[][] test = new int[nums.length][2];
for(int i=0; i< nums.length; i++){
test[i][0] = nums[i];
test[i][1] = i;
}
merge(test,0, nums.length-1);
for (int i = 0; i < test.length; i++) {
System.out.print(Arrays.toString(test[i])+" ");
}
}
private static void merge(int[][] n,int left,int right){
if(left<right){
int mid = (left+right)/2;
merge(n,left,mid);
merge(n,mid+1,right);
merge_two(n,left,right);
}
}
private static void merge_two(int[][] nums,int left,int right){
int[][] temp = new int[right-left+1][2];
int cur = 0;
int mid = (left+right)/2;
int i = left;
int j = mid+1;
while (i<=mid&&j<=right){
if(nums[i][0]<=nums[j][0]){
temp[cur][0] = nums[i][0];
temp[cur][1] = nums[i][1];
cur++;
i++;
}else {
temp[cur][0] = nums[j][0];
temp[cur][1] = nums[j][1];
cur++;
j++;
}
}
while (i<=mid){
temp[cur][0] = nums[i][0];
temp[cur][1] = nums[i][1];
cur++;
i++;
}
while (j<=right){
temp[cur][0] = nums[j][0];
temp[cur][1] = nums[j][1];
cur++;
j++;
}
for (int k = 0; k < right-left+1; k++) {
nums[left+k][0] = temp[k][0];
nums[left+k][1] = temp[k][1];
}
}
}
对于排序有疑问的小伙伴可以参考:排序