旋转数组的最小数字
题目描述:
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
解题思路
如果遇到从排序的数组中查找数字需要尝试二分查找法。
我们利用两个指针分别指向数组的第一个元素和最后一个元素,我们可以先找到数组中间的元素,如果该中间元素大于或等于第一个元素,则位于前面的非递减子数组,此时我们就可以把第一个指针指向该中间元素,这样就可以缩小寻找的范围,移动以后,第一个指针依然指向前面的非递减数组;否则属于后面的非递减子数组,此时我们就可以把第二个指针指向该中间元素,移动以后,第二个指针依然指向后面的非递减数组。然后循环下去,最终第一个指针将指向前面子数组的最后一个元素,而第二个指针将会指向后面子数组的第一个元素,而第二个指针指向的刚好是最小的元素。循环结束。
特例:
- 如果数组整体就是一个排序的数组可以直接返回
- 当出现多个重复的数字时不得不使用顺序查找的方式
参考代码:
class Solution {
public int minArray(int[] numbers) {
int len =numbers.length;
//长度为0直接返回
if(len==0){
return 0;
}
int left=0;
int right=len-1;
//刚开始就是排序的数组,直接返回第一个即可
if(numbers[left]<numbers[right]){
return numbers[left];
}
while(left<right){
//int mid = i+(j-i)/2;
int mid = (left+right)/2;
//最小数位于前面
if(numbers[mid]<numbers[right]){
right=mid;
}else if(numbers[mid]>numbers[right]){
//最小数位于后面,
//且临界条件时需要+1,用来终止循环
left=mid+1;
}else{
//有相同值时需要跳过
right--;
}
}
return numbers[left];
}
}
补充
如果面试题是要求在排序的数组(或者部分排序的数组)中查找一个数字或者统计某个数字出现的次数, 那么我们都可以尝试 二分查找算法。
强烈建议在准备面试的时候,一定要对各种排序算法的特点烂熟于胸,能够从额外空间消耗、平均时间复杂度和最差时间复杂度等方面去比较他们的优缺点。(特别关注手写快排)
快排算法思想如下:
先在数组中选择一个数字,接下来吧数组中的数字分为两部分,比选择的数字小的数字移到数组的左边,比选择的的数字大的数字移到数组的右边 (善用指针的想法)。然后左右两侧分别递归得到最后结果。
import java.util.Random;
/**
* 快速排序
*/
public class QuickSort {
/**
* 交换数组中的两个值
* @param array
* @param a
* @param b
*/
public static void swap (int[] array, int a, int b) {
int temp = array[a];
array[a] = array[b];
array[b] = temp;
}
/**
* 分区函数
* 按随机出的基准值分区,将比基准值小放在基准值左边,大的放在基准值右边
* @param data
* @param length
* @param start
* @param end
* @return
* @throws Exception
*/
public static int partition(int data[], int length, int start, int end) throws Exception {
Random random = new Random();
if (data == null || length <=0 || start <0 || end >= length) {
throw new Exception("Invalid Parameters");
}
// 尽量避免极端情况,每次都是以最后一个数字作为基准值,在start和end之间随机出一个数
int index = start + random.nextInt(end - start + 1);
// 将比较的值放在数组的最后
swap(data, index, end);
// 定义一个指针,指向所有换序之后所有比基准值都小的数据的最右边,最后最指向基准值应该在位置
int small = start - 1;
for (int i = start; i < end; i++) {
if (data[i] < data[end]) {
small++;
// 避免原来就是有序数列的无用交换
if (small != i) {
swap(data, small, i);
}
}
}
// 把基准值放在中间
small++;
swap(data, small, end);
return small;
}
/**
* 利用分区函数实现快速排序
* 递归
* @param data
* @param length
* @param start
* @param end
* @throws Exception
*/
public static void quickSort(int[] data, int length, int start, int end) throws Exception {
// 当只剩一个数的时候就不用递归了
if (start == end) {
return;
}
int index = partition(data, length, start, end);
// 防止异常
if (index > start) {
quickSort(data, length, start, index - 1);
}
if (index < end) {
quickSort(data, length, index + 1, end);
}
}
}