一、荷兰国旗问题
题意:
只包含0,1,2三个整数(或者说三种颜色)的数组进行排序,要求使用交换排序,原地排序,而不是利用计数进行排序。
思路:
这道题就是经典的荷兰国旗问题,而解决的方法类似于快速排序的划分。可以做到
- 时间复杂度为O(n)
- 额外空间复杂度为(1)
我们可以在数组的前方设置一个0区间,在数组的后方设置一个2区间,那么在我们遍历数组的过程中,将0放到0区间(和0区间后面的位置互换,增大0区间),继续遍历,将2放到2区间(和2区间前面的元素互换,2区间增大),因为2区间前面的元素还没有遍历,所以引用不变,继续比较当前元素。以此类推,直到引用下标到达2区间,结束。
代码举例:
import java.util.*;
public class ThreeColor {
public int[] sortThreeColor(int[] A, int n) {
// write code here
//创建三个区间的下标表示
int zero = 0;
int now = 0;
int two = n-1;
//当前下标和2区间下标刚好重合之前,遍历数组
while(now<=two){
//如果等于0
if(A[now]==0){
int temp = A[now];
A[now] = A[zero];
A[zero] = temp;
zero++;
now++;
}else if(A[now]==2){ //如果等于2,注意这里当前下标是不变的
int temp = A[now];
A[now] = A[two];
A[two] = temp;
two--;
}else{
now++; //如果等于1
}
}
return A;
}
}
二、在矩阵找元素
题意:
在行和列都排好序的矩阵中招数
例如:0,1,2,5 如果k为7,返回true,如果k为6.返回false
2,3,4,7
4,4,4,8
5,7,7,9
思路:
这种题目,如果是一个m*n大小的矩阵,那么是将复杂度可以优化到O(m+n),空间复杂度为O(1);
找法其实是从二维数组的右上角开始找,如果要找的数比右上角的数下,那么下标向左移,因为二维数组的行和列都是有序的,下方的数一定比上方的大,右边的数一定比左边的数大。
代码举例:
import java.util.*;
public class Finder {
public boolean findX(int[][] mat, int n, int m, int x) {
// write code here
//设置右上角的坐标
int a = n-1;
int b = 0;
while(a>=0 && b<=m-1){
if(mat[a][b] == x){
return true;
}else if(mat[a][b]>x){
//如果右上角的数大于目标数,坐标向左移动
a--;
//直到数组越界
if(a<0){
break;
}
}
if(mat[a][b]<x){
//如果右上角的数小于目标数,坐标向下移动
b++;
}
}
return false;
}
}
三、求需要排序的最短子数组问题
题意:
例如数组【1,5,4,3,2,6,7】,返回值应该是4,因为只有【5,4,3,2】需要排序
思路:
这种题目的最后解时间复杂度为O(n),空间复杂度为O(1);
我们可以这样解决,先从左到右遍历数组,找出遍历过的数组中最大的数,然后记录一种情况:遍历过数组的最大值大于当前数组的情况,记录下这种情况中最靠右的位置,因为遍历过的数组中最大值只会在这个位置或者这个位置后面。同理,接下来从右往左遍历数组,找出遍历过的数组中最小的数,然后记录一种情况:遍历过数组的最小值小于当前数组的情况,记录下这种情况中最靠左的位置,因为遍历过的数组中最小值只会在这个位置或者它的左边 。最后,右边位置减去左边位置的大小就是最小的排序子数组。
代码举例:
import java.util.*;
public class Subsequence {
public int shortestSubsequence(int[] A, int n) {
// write code here
//创建变量表示遍历过的最大值和最小值
int max = A[0];
int min = A[n-1];
//创建坐标表示从左往右和从右往左遍历
int i = 0;
int j = n-1;
//创建变量表示最左位置和最右位置
int left = 0;
int right = 0;
//从左往右遍历数组
while(i<=n-1){
//如果遇到比它大的,赋值,指针后移
if(A[i]>=max){
max=A[i];
i++;
}else{
left = i; //将坐标更新
i++;
}
}
//从右往左遍历数组
while(j>=0){
//如果遇到比它小的,赋值,指针前移
if(A[j]<=min){
min = A[j];
j--;
}else{
right = j;
j--;
}
}
return (left-right)>0?(left-right+1):0;
}
}
四、相邻两数最大差值
题意:
给定一个整形数组arr,返回如果排序之后,相邻两数的最大差值。例如某数组排序之后为1,2,3,4,7,8,9;最大差值来自于4和7,所以返回3
思路:
这道题要使用到桶排序的思想,但是并不是真的实现桶排序,最优时间复杂度为O(n),额外空间复杂度为O(n);
这道题的解法是:首先遍历数组,找出数组中的最大值和最小值,然后将最大数减去最小数,得到的值除以数组的数量,作为桶区间值,一共有n(数组元素个数)个桶,然后遍历将每个元素放入桶中,这样最小的元素一定在第一个桶中,将最大的元素放到第n+1个桶中,那么桶的个数有n+1个,而数组的个数只有n个,那么一定会有空桶。我们可以分析出来,处在同一个桶中的元素一定没有两个不同桶中的元素差值大,所以我们不用在意同一个桶中的元素得到差值,接下来我们需要比较每一个桶的最小值和前一个非空桶的最大值的差值,这样就能得出结论了。
代码举例:
import java.util.*;
public class Gap {
public int maxGap(int[] A, int n) {
// write code here
if (A == null || A.length < 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];
}
if(A[i]>max){
max = A[i];
}
}
if(min==max) return 0;
//寻找每个桶中的最大值和最小者
//首先判断每个桶是不是空桶
boolean[] hasNum = new boolean[n+1];
int[] maxs = new int[n+1];
int[] mins = new int[n+1];
int bid = 0; //表示桶的位置
for(int i=0;i<n;i++){
bid = bucket(A[i],n,min,max);
//将每个区间的最大最小值储存起来
maxs[bid] = hasNum[bid] ? Math.max(maxs[bid], A[i]) : A[i];
mins[bid] = hasNum[bid] ? Math.min(mins[bid], A[i]) : A[i];
hasNum[bid] = true;
}
//寻找第一个非空的桶
int i = 0;
//第一个非空桶的最大值
int lastMax = 0;
//最终结果,最大距离
int maxDistance = 0;
while(i<n){
if(hasNum[i++]){
lastMax = maxs[i-1];
break;
}
}
//一次计算后面的桶中的最小值和这个前面的非空桶的最大值的差值
for (int j = i; j <= A.length; j++) {
if (hasNum[j]) {
//比较差值
maxDistance = Math.max(maxDistance, mins[j] - lastMax);
//更新最大值
lastMax = maxs[j];
}
}
return maxDistance;
}
//定义桶的间距,将元素放入对应的区间
public int bucket(long num, long len, long min, long max) {
return (int) ((num - min) * len / (max - min));
}
}