排序练习2

来源:互联网 发布:网络异常360修复不了 编辑:程序博客网 时间:2024/06/06 07:41

1、三色排序

有一个只由0,1,2三种元素构成的整数数组,请使用交换、原地排序而不是使用计数进行排序。

给定一个只含0,1,2的整数数组A及它的大小,请返回排序后的数组。保证数组大小小于等于500。

测试样例:
[0,1,1,0,2,2],6
返回:[0,0,1,1,2,2]

思想:来自于快速排序。在数组左边设置一个为0的空域,表示放置0的地方(代码中用indexZ指示)。在数组右边设置一个为0的空域,表示放置为2的地方。(代码中用indexT指示)。使用一个current索引从左到右遍历数组,如果遇到0则与0域的下一个位置进行交换,如果遇到2,则和2域的前一个位置进行交换。如果遇到1则跳过。

代码中可能产生疑问的地方:为什么A[current] == 0时,current都要加1,而A[current] == 2时,current不需加1

由于indexZ从左到右移动,如果当前数为0,交换完后有可能还是0,此时如果current不变,但是indexZ是随着交换递增的,indexZ后面的是没有处理的,那么很有可能会将已处理的当前数换成没有处理的数,即有可能0换成1或2了。而0本来就应该放在最前面。 indexT从右向左移动,如果当前数为2,交换完的数只会小于等于2,每次indexT会随着交换递减,也就是保证了indexT后面的都是2了,所以就算current和--indexT交换,也不会打乱原来的排列。如果交换后的数不为2(肯定是1,0的),就又可以继续排列了。

代码如下:

public class ThreeColor {
    public void swap(int[] tmp, int index1, int index2) {
int temp = tmp[index1];
tmp[index1] = tmp[index2];
tmp[index2] = temp;
}


public int[] sortThreeColor(int[] A, int n) {
if (A == null || A.length < 2) {
return A;
}
// 记录当前1的范围,初始时为空
int indexZ = -1;
// 记录当前2的范围,初始时为空
int indexT = n;
int current = 0;
while (current < indexT) {
if(A[current] == 0){
swap(A,current++,++indexZ);
}else if(A[current] == 2){
swap(A,current,--indexT);
}else{
current++;
}

}
return A;
}
}


2、有序矩阵查找

现在有一个行和列都排好序的矩阵,请设计一个高效算法,快速查找矩阵中是否含有值x。

给定一个int矩阵mat,同时给定矩阵大小nxm及待查找的数x,请返回一个bool值,代表矩阵中是否存在x。所有矩阵中数字及x均为int范围内整数。保证nm均小于等于1000。

测试样例:
[[1,2,3],[4,5,6],[7,8,9]],3,3,10
返回:false
思想:从矩形的右上角开始寻找,因为该矩阵行和列都是有序的,所以右上角是该行的最大值,该列的最小值,判断这个数与要查找的数的大小,如果比要查的数大,那么该行左边的已经不用考虑了,直接往下挪一行,如果比要查的小,则往左挪一列。

以上面测试样例为例,画图分析如下:

(1)从右上角开始找,3<10       (2)6<10                    (3)9<10             (4)再往下一行,发现越界,返回false

                      

代码如下:

public class Finder {
   public boolean findX(int[][] mat, int n, int m, int x) {
int a = 0;
int b = m-1;
while(a<n&&b>=0){
if(mat[a][b]>x){
b--;
}else if(mat[a][b]<x){
a++;
}else if(mat[a][b]==x){
return true;
}
}
return false;
    }
}


3、最短子数组

对于一个数组,请设计一个高效算法计算需要排序的最短子数组的长度。

给定一个int数组A和数组的大小n,请返回一个二元组,代表所求序列的长度。(原序列位置从0开始标号,若原序列有序,返回0)。保证A中元素均为正整数。

测试样例:
[1,4,6,5,9,10],6
返回:2
思路:从左到右扫描,边扫描边找最大元素,一开始设置max为A[0],如果扫描到的数比max大,则将max重新赋值,若比max小,则将maxIndex赋值为该元素数组下标。然后再从右到左扫描,边扫描边找最小元素,一开始设置min为A[n-1],如果扫描到的数比min小,则将min重新赋值,否则,将minIndex赋值为该元素下标。结果就是maxIndex-minIndex+1。

代码如下:

public int shortestSubsequence(int[] A, int n) {
if(n <= 1){
return 0;
}
int max = A[0];
int maxIndex = 0;
int min = A[n-1];
int minIndex =0;
    for(int i = 0;i<n;i++){
    if(max<=A[i]){
    max = A[i];     
    }else{
                  maxIndex = i;
             }    
    }
for(int j = n-1;j>=0;j--){
if(min >= A[j]){
min = A[j];
}else{
                  minIndex = j;
             }
}
return maxIndex - minIndex > 0 ? maxIndex - minIndex +1:0;
}


4、相邻两数最大差值

有一个整形数组A,请设计一个复杂度为O(n)的算法,算出排序后相邻两数的最大差值。

给定一个int数组AA的大小n,请返回最大的差值。保证数组元素多于1个。

测试样例:
[1,2,5,4,6],5
返回:2
思路:找出数组中的最大值和最小值,比如在该样例中为6和1。将区间[1~6]分成n个桶,找出当前桶的最小值min和上一个桶的最大值max的差的最大值就是需要的结果。为了保证桶里面是有数据的,需要用一个boolean数组来标识。
ps:为什么是当前的最小值减去上一个的最大值呢?因为要求的是排序后相邻两数的最大差值,当前数的最小值理所当然是前一个数最大值的后一个数。
代码如下:
 public int maxGap(int[] A, int n) {
if (A == null || n < 2) {
return 0;
}
int min = A[0];
int max = A[0];
// 找出数组中最大值和最小值
for (int i = 0; i < n; i++) {
if (A[i] < min) {
min = A[i];
} else if (A[i] > max) {
max = A[i];
}
}
if (min == max) {
return 0;
}
boolean[] hasNum = new boolean[n + 1];
int[] mins = new int[n + 1];
int[] maxs = new int[n + 1];
int index = 0;
for (int i = 0; i < n; i++) {
// 算出放入几号桶(从0开始)
index = bucket(A[i], n, min, max);
mins[index] = hasNum[index]?Math.min(mins[index], A[i]):A[i];
maxs[index] = hasNum[index]?Math.max(maxs[index], A[i]):A[i];
hasNum[index] = true;
}
int k = 0;
int lastMax = 0;
while(k<=n){
if(hasNum[k++]){
lastMax = maxs[k-1];
break;
}
}
int res = 0;
for(;k<n+1;k++){
if(hasNum[k]){
res = Math.max(res,mins[k]-lastMax);
lastMax = maxs[k];
}

}
return res;
}

// 计算桶号,使用long类型是为了防止相乘时溢出
public int bucket(long num, long len, long min, long max) {
return (int) ((num - min) * len / (max - min));
    }

原创粉丝点击