剑指Offer面试题8:寻找旋转数组的最小元素

本文介绍了一种在旋转数组中高效查找最小值的方法,通过二分查找的方式将时间复杂度降低到O(logn),适用于递增排序数组经过旋转的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    题目:求一个旋转数组的最小数字
    把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组中的最小元素。例如, {1,2,3,4,5}的一个旋转数组为{3,4,5,1,2},该数组的最小元素为1.
    旋转数组的特点:
(1)递增排序的数组旋转之后可以划分为两个排序的子数组;
(2)前面的子数组的元素都大于或等于后面子数组的元素;
(3)最小的元素刚好是这两个子数组的分界线;
(4)旋转数组在一定程度上是有序的;
     这道题最直观的解法是从头到尾遍历一遍数组,就能找到最小的元素,这种思路的时间复杂度为O(n).在有序数组中利用二分查找法可以实现时间复杂度为O(logn)的查找。
    算法分析:
1.设置两个指针i和j,初始状态i指向前面子数组的第一个元素,j指向后面子数组的后一个元素;
2.  找到找到i和j中间的元素middle,判断middle位于前面递增的子数组还是后面递增的子数组,若位于前面递增,则将i指向middle元素,此时最小元素应位于i和j之间;若middle位于后一个递增子数组,则将j指向middle元素,此时最小元素仍位于i和j之间;
3.循环步骤(1)(2),直到i和J之间没有中间元素为止;
4.此时j所指向的元素即为最小元素。

注意:当两个指针指向的数字及它们中间的数字三者相等时,无法判断中间数字位于前面的子数组还是后面的子数组,也就无法移动两个指针来缩小查找的范围,此时只能用顺序查找的方法。例如:数组{1,0,1,1,1}和数组{1,1,1,0,1}都可看成是递增数组{0,1,1,1,1}的旋转。第一种情况,中间数字位于后面的子数组,第二种情况,中间数字位于前面的子数组。
java代码实现:
/**************************************************************      
* Copyright (c) 2016, 
* All rights reserved.                   
* 版 本 号:v1.0                   
* 题目描述:求一个旋转数组的最小数字
* 		       把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组中的最小元素。例如,
* 			{1,2,3,4,5}的一个旋转数组为{3,4,5,1,2},该数组的最小元素为1.
* 输入描述:请输入一个旋转数组: 
*           7 9 10 15 19 23 46 4 5
* 程序输出: 输入的旋转数组是:
*			7 9 10 15 19 23 46 4 5
*			最小数字是:4
* 问题分析:无
* 算法描述:旋转之后的数组实际上可以划分为两个排序数组,而且前面的子数组的元素都大于或等于后面子数组的元素。最小的数组刚好是两个子数组的分界线。
* 			(1)设两个指针i和j,分别指向头数组和尾数组
* 			(2)找到i和j中间的元素middle,判断middle位于前面递增的子数组还是后面递增的子数组,若位于前面递增,则将i指向middle元素,此时最小元素应位于i和j之间
* 			(3)若middle位于后一个递增子数组,则将j指向middle元素,此时最小元素仍位于i和j之间
* 			(4)循环步骤(1)(2),直到i和J之间没有中间元素为止
* 			(5)此时j所指向的元素即为最小元素。
* 完成日期:2016-07-24
***************************************************************/  

package org.marsguo.offerproject;

import java.util.Scanner;
class FindMin{	
	/*输出用户输入的旋转数组*/
	public FindMin(int[] s){				
		System.out.println("输入的旋转数组为:");
		for(int i = 0; i < s.length; i++){
			System.out.print(s[i] + " ");
		}
		System.out.println();
	}
	/*寻找最小元素方法*/
	public void searchfun(int[] array){
		int i = 0;								//将i指向数组的头
		int j = array.length-1;					//将j指向数组尾
		int middle = array.length/2;			//找到中间的那个元素
		//System.out.println("中间数字为:" + array[middle]);
		while(i<j){								//当i<j时一直循环寻找
			
			if(array[middle]>array[i]){					//判断中间元素是否大于头元素
				i = middle;								//将i指向中间元素
			}else if(array[middle]<array[j]){			//判断中间元素是否小于尾元素
				j =middle;								//将j指向尾元素
			}
			middle = (j-i)/2 + i;					 	//重新寻找新i和j中间的元素
			//System.out.println("中间数字为" + array[middle]);
			if(middle == i){							//如果此时中间元素即为i,证明已经全部找完
				System.out.println("最小的数字是:" + array[j]);			//输出最小元素,此时最小元素即为j所指向的元素
				break;													//退出整个循环,结束程序。
			}
		}
	}
}
public class SearchMin {
	public static void main(String args[]){				
		Scanner scanner = new Scanner(System.in);						//扫描键盘输入
		System.out.println("请输入一个旋转数组:");
		String str = scanner.nextLine();								//将键盘输入转化为字符串
		String[] temp = str.split(" ");									//将字符串用“ ”分开转化为字符串数组
		int[] array = new int[temp.length];								//定义一个整型数组array
		for(int i = 0; i< temp.length; i++){							//将字符串数组强制转化为整型数组
			array[i] = Integer.parseInt(temp[i]);						//这种方法非常巧妙
		}
		FindMin findmin = new FindMin(array);							//创建对象
		findmin.searchfun(array);										//调用对象寻找最小元素方法
	}
}
程序输出:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值