You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
6.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package class22;
import java.util.HashSet;
import java.util.Stack;
public class Code04_VisibleMountains {
// 栈中放的记录,
// value就是指times是收集的个数
public static class Record {
public int value;
public int times;
public Record(int value) {
this.value = value;
this.times = 1;
}
}
public static int getVisibleNum(int[] arr) {
if (arr == null || arr.length < 2) {
return 0;
}
int N = arr.length;
int maxIndex = 0;
// 先在环中找到其中一个最大值的位置,哪一个都行
for (int i = 0; i < N; i++) {
maxIndex = arr[maxIndex] < arr[i] ? i : maxIndex;
}
Stack<Record> stack = new Stack<Record>();
// 先把(最大值,1)这个记录放入stack中
stack.push(new Record(arr[maxIndex]));
// 从最大值位置的下一个位置开始沿next方向遍历
int index = nextIndex(maxIndex, N);
// 用“小找大”的方式统计所有可见山峰对
int res = 0;
// 遍历阶段开始当index再次回到maxIndex的时候说明转了一圈遍历阶段就结束
while (index != maxIndex) {
// 当前数要进入栈,判断会不会破坏第一维的数字从顶到底依次变大
// 如果破坏了,就依次弹出栈顶记录,并计算山峰对数量
while (stack.peek().value < arr[index]) {
int k = stack.pop().times;
// 弹出记录为(X,K)如果K==1产生2对; 如果K>1产生2*K + C(2,K)对。
res += getInternalSum(k) + 2 * k;
}
// 当前数字arr[index]要进入栈了,如果和当前栈顶数字一样就合并
// 不一样就把记录(arr[index],1)放入栈中
if (stack.peek().value == arr[index]) {
stack.peek().times++;
} else { // >
stack.push(new Record(arr[index]));
}
index = nextIndex(index, N);
}
// 清算阶段开始了
// 清算阶段的第1小阶段
while (stack.size() > 2) {
int times = stack.pop().times;
res += getInternalSum(times) + 2 * times;
}
// 清算阶段的第2小阶段
if (stack.size() == 2) {
int times = stack.pop().times;
res += getInternalSum(times)
+ (stack.peek().times == 1 ? times : 2 * times);
}
// 清算阶段的第3小阶段
res += getInternalSum(stack.pop().times);
return res;
}
// 如果k==1返回0如果k>1返回C(2,k)
public static int getInternalSum(int k) {
return k == 1 ? 0 : (k * (k - 1) / 2);
}
// 环形数组中当前位置为i数组长度为size返回i的下一个位置
public static int nextIndex(int i, int size) {
return i < (size - 1) ? (i + 1) : 0;
}
// 环形数组中当前位置为i数组长度为size返回i的上一个位置
public static int lastIndex(int i, int size) {
return i > 0 ? (i - 1) : (size - 1);
}
// for test, O(N^2)的解法,绝对正确
public static int rightWay(int[] arr) {
if (arr == null || arr.length < 2) {
return 0;
}
int res = 0;
HashSet<String> equalCounted = new HashSet<>();
for (int i = 0; i < arr.length; i++) {
// 枚举从每一个位置出发,根据“小找大”原则能找到多少对儿,并且保证不重复找
res += getVisibleNumFromIndex(arr, i, equalCounted);
}
return res;
}
// for test
// 根据“小找大”的原则返回从index出发能找到多少对
// 相等情况下比如arr[1]==3arr[5]==3
// 之前如果从位置1找过位置5那么等到从位置5出发时就不再找位置1去重
// 之前找过的、所有相等情况的山峰对都保存在了equalCounted中
public static int getVisibleNumFromIndex(int[] arr, int index,
HashSet<String> equalCounted) {
int res = 0;
for (int i = 0; i < arr.length; i++) {
if (i != index) { // 不找自己
if (arr[i] == arr[index]) {
String key = Math.min(index, i) + "_" + Math.max(index, i);
// 相等情况下,确保之前没找过这一对
if (equalCounted.add(key) && isVisible(arr, index, i)) {
res++;
}
} else if (isVisible(arr, index, i)) { // 不相等的情况下直接找
res++;
}
}
}
return res;
}
// for test
// 调用该函数的前提是lowIndex和highIndex一定不是同一个位置
// 在“小找大”的策略下从lowIndex位置能不能看到highIndex位置
// next方向或者last方向有一个能走通就返回true否则返回false
public static boolean isVisible(int[] arr, int lowIndex, int highIndex) {
if (arr[lowIndex] > arr[highIndex]) { // “大找小”的情况直接返回false
return false;
}
int size = arr.length;
boolean walkNext = true;
int mid = nextIndex(lowIndex, size);
// lowIndex通过next方向走到highIndex沿途不能出现比arr[lowIndex]大的数
while (mid != highIndex) {
if (arr[mid] > arr[lowIndex]) {
walkNext = false;// next方向失败
break;
}
mid = nextIndex(mid, size);
}
boolean walkLast = true;
mid = lastIndex(lowIndex, size);
// lowIndex通过last方向走到highIndex沿途不能出现比arr[lowIndex]大的数
while (mid != highIndex) {
if (arr[mid] > arr[lowIndex]) {
walkLast = false; // last方向失败
break;
}
mid = lastIndex(mid, size);
}
return walkNext || walkLast; // 有一个成功就是能相互看见
}
// for test
public static int[] getRandomArray(int size, int max) {
int[] arr = new int[(int) (Math.random() * size)];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * max);
}
return arr;
}
// for test
public static void printArray(int[] arr) {
if (arr == null || arr.length == 0) {
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int size = 10;
int max = 10;
int testTimes = 3000000;
System.out.println("test begin!");
for (int i = 0; i < testTimes; i++) {
int[] arr = getRandomArray(size, max);
if (rightWay(arr) != getVisibleNum(arr)) {
printArray(arr);
System.out.println(rightWay(arr));
System.out.println(getVisibleNum(arr));
break;
}
}
System.out.println("test end!");
}
}