我想我找到了解决办法。首先,我们需要某种找到断点的方法,我的意思是:假设我们有一个数组 {1,2,3,4,9,8,7,6} 注意数字 9在索引 4 处,这是一个断点,因为现在更改了排序方向,重要的部分是在 log(n) 中找到它。为此,我创建了以下类。为了让这个类工作,他需要获取一个数组以下类型 {sorted,reverseSorted}
public class BreakPointSearcher {
private int array[];
private BiFunction<Integer, Integer, Boolean> compare;
private Map<Boolean, Direction> directions;
public BreakPointSearcher(int[] array, BiFunction<Integer, Integer, Boolean> compare) {
this.array = array;
this.compare = compare;
}
public int findTheBreackingPoint(int start, int end) {
if (start >= end || end >= array.length)
return -1;
loadDirectionMap(start, end);
int mid = (start + end) / 2;
if (isBreackingPoint(mid))
return mid;
Direction direction = getDirection(mid);
//Using recursive call by that making the algorithm run in O(log(n))
if (GoRight == direction) {
return findTheBreackingPoint(mid + 1, end);
} else if (GoLeft == direction) {
return findTheBreackingPoint(start, mid - 1);
}
return -1;
}
/**
* I assume that two sides of the array have opposite sorting
* that is if at the start we have ascending at the end we have descending
*/
private void loadDirectionMap(int start, int end) {
directions = new HashMap<>();
directions.put(compare.apply(array[start], array[start + 1]), GoRight);
directions.put(compare.apply(array[end - 1], array[end]), GoLeft);
}
private Direction getDirection(int mid) {
return directions.get(compare.apply(array[mid], array[mid + 1]));
}
private boolean isBreackingPoint(int mid) {
boolean inBetween = compare.apply(array[mid - 1], array[mid]) && compare.apply(array[mid + 1], array[mid]); // a > b < c
boolean inBetweenTypeTwo = compare.apply(array[mid], array[mid - 1]) && compare.apply(array[mid], array[mid + 1]); // a < b > c
return inBetween || inBetweenTypeTwo;
}
enum Direction {
GoLeft,
GoRight
}
public static void main(String... args) {
int[] arrayNums = {1, 2, 3, 4, 5, 6, 7, 20, 9, 8};
BiFunction<Integer, Integer, Boolean> compareAsc = (a, b) -> a > b;
BreakPointSearcher sbs = new BreakPointSearcher(arrayNums, compareAsc);
System.out.println(sbs.findTheBreackingPoint(0, arrayNums.length - 1)); //output is 7
arrayNums = new int[]{252, 48, 22, 10, 12, 13, 16};
sbs = new BreakPointSearcher(arrayNums, compareAsc);
System.out.println(sbs.findTheBreackingPoint(0, arrayNums.length - 1)); //output 3
arrayNums = new int[]{22, 56, 13};
sbs = new BreakPointSearcher(arrayNums, compareAsc);
System.out.println(sbs.findTheBreackingPoint(0, arrayNums.length - 1)); //output 1
arrayNums = new int[]{22, 56, 78, 33};
sbs = new BreakPointSearcher(arrayNums, compareAsc);
System.out.println(sbs.findTheBreackingPoint(0, arrayNums.length - 1)); //output 2
arrayNums = new int[]{1, 2, 3, 4};
sbs = new BreakPointSearcher(arrayNums, compareAsc);
System.out.println(sbs.findTheBreackingPoint(0, arrayNums.length - 1)); //output -1
arrayNums = new int[]{4, 3, 2, 1};
sbs = new BreakPointSearcher(arrayNums, compareAsc);
System.out.println(sbs.findTheBreackingPoint(0, arrayNums.length - 1)); //output -1
}
}
现在我们有了这样的类,我们可以开始二进制搜索我要做的是搜索键,如果找到它,则函数完成如果在某个时候算法注意到排序被颠倒它会去在该点的左侧和右侧,然后将搜索反转数组的边界,然后我们在每个数组中都有 3 个数组,我们执行二进制搜索(其中一个是反向的),就是这样。这不是一个完美的解决方案并且可能会在某些特殊情况下,但作为一般 idia,我认为这是正确的
public class SpecialBinarySearch {
private static BiFunction<Integer, Integer, Boolean> ascComp = (a, b) -> a > b;
private static BiFunction<Integer, Integer, Boolean> dscComp = (a, b) -> a < b;
public static int search(int[] array, int key, int start, int end, BiFunction<Integer, Integer, Boolean> compare) {
int mid = (start + end) / 2;
if (array[mid] == key)
return mid;
if (start == end)
return -1;
boolean opositeSorted = compare.apply(array[mid], array[mid + 1]);
if (opositeSorted) {
BreakPointSearcher breakPointSearcher = new BreakPointSearcher(array, compare);
int leftBound = breakPointSearcher.findTheBreackingPoint(0, mid);
int rightBound = breakPointSearcher.findTheBreackingPoint(mid, array.length - 1);
leftBound = leftBound == -1 ? 0 : leftBound; //
rightBound = rightBound == -1 ? array.length - 1 : rightBound;
int opt1 = search(array, key, leftBound, rightBound, getOpositeCompare(compare));
int opt2 = search(array, key, start, leftBound, compare);
int opt3 = search(array, key, rightBound, end, compare);
return Math.max(Math.max(opt1, opt2), opt3);
}
if (compare.apply(key, array[mid])) {
return search(array, key, mid + 1, end, compare);
} else {
return search(array, key, start, mid - 1, compare);
}
}
private static BiFunction<Integer, Integer, Boolean> getOpositeCompare(BiFunction<Integer, Integer, Boolean> compare) {
return compare == ascComp ? dscComp : ascComp;
}
public static void main(String... args) {
int[] array = {1, 2, 3, 20, 25};
System.out.println(search(array, 20, 0, array.length - 1, ascComp)); //output 3
array = new int[]{10, 9, 8, 7, 6, 3};
System.out.println(search(array, 6, 0, array.length - 1, dscComp));//output 4
array = new int[]{1, 2, 3, 4, 5, 20, 19, 18, 17, 16};
System.out.println(search(array, 18, 0, array.length - 1, ascComp));//output 7
array = new int[]{1, 2, 3, 4, 5, 20, 19, 18, 22, 25};
System.out.println(search(array, 22, 0, array.length - 1, ascComp));//output 8
array = new int[]{1, 2, 3, 4, 5, 20, 19, 18, 17, 16};
System.out.println(search(array, 16, 0, array.length - 1, ascComp));//output 9
array = new int[]{1, 2, 3, 4, 5, 20, 19, 18, 17, 16};
System.out.println(search(array, 2, 0, array.length - 1, ascComp));//output 1
}
}