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};
	}
}
posted @ 2020-11-23 11:11  BOTAK  阅读(336)  评论(0编辑  收藏  举报