2.10 寻找数组中的最大值与最小值
2.10 寻找数组中的最大值与最小值
基本问题:对于一个由N个整数组成的数组,需要比较多少次才能将最大的和最小的数找出来呢?
- 解法1 :比较2n次,对数组中的所有元素进行遍历,一次比较,更新最大值与最小值,
- 解法2 : 一般情况下,最大的数和最小的数不会是同一个数。我们先把数据分成两个部分,再从这两部分中分别找出最大的数与最小的数,总共的比较次数0.5n+0.5n+0.5n = 1.5n
- 解法3 : 同解法2,但是不更改元数组,总共的比较次数0.5n+0.5n+0.5n = 1.5n
- 解法4 : 分治 \(f(n) = 2*f(n/2) + 2 = .....= 1.5n-2\)
// 2.10 寻找数组中的最大值与最小值
class Test{
// 基本问题:对于一个由N个整数组成的数组,需要比较多少次才能将最大的和最小的数找出来呢?
public static void main(String[] args) {
int[] arr = new int[]{23,45,6,8,93,31,3,13,1,3,4,5,6,57,6,78,4};
print(arr);
findMaxMin(arr);
}
public static void print(int[] arr){
for(int i:arr) System.out.print(i+" ");
System.out.println();
}
// 时间复杂度$O(n)$,但是经过了2n次的比较,本题的目的是尽量减少比较次数
public static void findMaxMin(int[] arr){
int minIndex = 0;
int maxIndex = 0;
for(int i = 0;i<arr.length;i++){
if(arr[i] > arr[maxIndex]) maxIndex = i;
if(arr[i] < arr[minIndex]) minIndex = i;
}
System.out.println("max value: "+arr[maxIndex]);
System.out.println("min value: "+arr[minIndex]);
}
/**
一般情况下,最大的数和最小的数不会是同一个数。我们先把数据分成两个部分,再从这两部分中分别找出最大的数与最小的数
第一轮排序经过了0.5n次比较
在偶数位置找到最大的值经过了0.5n次比较
在奇数位置找到最小的值经过了0.5n次比较
总共的比较次数0.5n+0.5n+0.5n = 1.5n
*/
public static void findMaxMin2(int[] arr){
// 相邻的比较,大的放偶数位,小的放奇数位
for(int i = 0;i<arr.length-1;i+=2){
if(arr[i] >= arr[i+1]) continue;
int tmp = arr[i+1];
arr[i+1] = arr[i];
arr[i] = tmp;
}
// 从偶数位找最大的值
int max = Integer.MIN_VALUE;
for(int i = 0;i<arr.length;i+=2) max = Math.max(arr[i],max);
// 从奇数位找最小的值
int min = Integer.MAX_VALUE;
for(int i = 1;i<arr.length;i+=2) min = Math.min(arr[i],min);
System.out.println("max value: "+max);
System.out.println("min value: "+min);
}
/**
解法2虽然将数组中元素的比较次数减少到了1.5n次,但是修改了原来的数组,能否在不修改原来数组的情况下进行呢?
虽然没有改变原来的数组,但是比较次数并没有得到相应的降低,还是1.5n次
*/
public static void findMaxMin3(int[] arr){
int min = arr[1];
int max = arr[0];
for(int i = 0;i<arr.length-2;i+=2){
if(arr[i] > arr[i+1]) {
max = Math.max(max,arr[i]);
min = Math.min(min,arr[i+1]);
}
else{
max = Math.max(max,arr[i+1]);
min = Math.min(min,arr[i]);
}
}
System.out.println("max value: "+max);
System.out.println("min value: "+min);
}
/**
分治算法
$$f(n) = 2*f(n/2) + 2 = .....= 1.5n-2$$
所以说采用分治算法,总的比较次数仍然没有减少
*/
public static void findMaxMin4(int[] arr){
int[] res = search(arr,0,arr.length-1);
System.out.println("max value: "+res[0]);
System.out.println("min value: "+res[1]);
}
// return int[max,min]
public static int[] search(int[] arr,int start,int end){
if(end-start<=1){
if(arr[start] < arr[end]) return new int[]{arr[end],arr[start]};
else return new int[]{arr[start],arr[end]};
}
int[] L = search(arr,start,start+(end-start)/2);
int[] R = search(arr,start+(end-start)/2+1,end);
int max = L[0]>R[0]?L[0]:R[0];
int min = L[1]<R[1]?L[1]:R[1];
return new int[]{max,min};
}
}
拓展问题:如果需要找出N个数的数组中的第二大的数呢?需要比较多少次呢?是否可以使用类似的分治思想来降低比较的次数呢?
分治:对于分治而言,最终比较的次数依然是1.5n次
// 2.10 寻找数组中的第二大的值
class Test{
public static void main(String[] args) {
int[] arr = new int[]{23,45,6,8,93,31,3,13,1,3,4,5,6,57,6,78,4};
findSecondMax(arr);
print(arr);
}
/**
拓展问题:如果需要找出N个数的数组中的第二大的数呢?需要比较多少次呢?是否可以使用类似的分治思想来降低比较的次数呢?
answer:基本思路是一样的
对于分治而言,最终比较的次数依然是1.5n次
*/
public static void findSecondMax(int[] arr){
int[] res = search2(arr,0,arr.length-1);
System.out.println("max value: "+res[0]);
System.out.println("second max value: "+res[1]);
}
// return int[max,secondmax]
public static int[] search2(int[] arr,int start,int end){
if(end-start<=1){
if(arr[start] < arr[end]) return new int[]{arr[end],arr[start]};
else return new int[]{arr[start],arr[end]};
}
int[] L = search2(arr,start,start+(end-start)/2);
int[] R = search2(arr,start+(end-start)/2+1,end);
int max = L[0]>R[0]?L[0]:R[0];
int secondMax = L[0] > R[0]?(R[0]>L[1]?R[0]:L[1]):(L[0]>R[1]?L[0]:R[1]);
return new int[]{max,secondMax};
}
}
Saying Less Doing More