需求描述
已知一个列表是先增后减的半有序列表,现在需要找出列表中的最大值,列表长度可能很大,考虑时间复杂度,该算法该如何实现?
实现思路
在不考虑时间复杂的的情况下,可以直接遍历,用max标识来记录最大值,知道找到一个比最大值小的记录,说明列表开始降序,最大值就找到了。
如果考虑到时间复杂度,且是半有序的列表,收到二分查找的启发,可以使用二分查找,每次比较中间项与其相邻元素的大小,判断最大值所处的范围。例如:如果mid>mid-1,说明是增,最大值就在【mid,last】区间里面,否则就是在【first,mid】区间。如果只剩下2-3个元素,就找最大值,如果在当前区间里面first>first+1说明开始降序,最大值就是first。
Java实现代码
public static Integer findMax(List<Integer> list, int first, int last) {
//只剩下两个元素,或者只剩下三个元素
if(last==first+1||last==first+2){
return Collections.max(list.subList(first, last+1));
}
//3个以上的元素,并且开始降序了,则直接返回第一个值
if (list.get(first) > list.get(first + 1)) {
return list.get(first);
}
//否则,折半确定最大值的范围
int middle = (first + last) / 2;
System.out.println("middle:"+middle);
if (list.get(middle) > list.get(middle - 1)) {
// 升序
return findMax(list, middle, last);
}
return findMax(list, first, middle);
}tring[] args) {
Integer [] array1 = {15, 16, 17, 18, 19, 20 };
Integer [] array2 = { 21, 22,23,24,25, 20, 19, 18, 17, 16,11,10,9 };
List<Integer> asc = new ArrayList<Integer>();
asc.addAll(Arrays.asList(array1));
asc.addAll(Arrays.asList(array2));
System.out.println(asc);
System.out.println(findMax(asc,0,asc.size()-1));
}
}
复杂度分析
每次折半,算法时间复杂度和二分查找时间复杂度一样,是logN,取决于列表的长度。如果在列表特别长的情况下,应该会比直接遍历的N的复杂度低一些。