2021-07-17:一个不含有负数的数组可以代表一圈环形山,每个位置的值代表山的高度。比如, {3,1,2,4,5}、{4,5,3,1,2}或{1,2,4,5,3}都代表同样结构的环形山。山峰A和山峰B能够相互看见的条件为: 1.如果A和B是同一座山,认为不能相互看见,2.如果A和B是不同的山,并且在环中相邻,认为可以相互看见,3.如果A和B是不同的山,并且在环中不相邻,假设两座山高度的最小值为min,1)如果A通过顺时针方向到B的途中没有高度比min大的山峰,认为A和B可以相互看见,2)如果A通过逆时针方向到B的途中没有高度比min大的山峰,认为A和B可以相互看见。两个方向只要有一个能看见,就算A和B可以相互看见。给定一个不含有负数且没有重复值的数组 arr,请返回有多少对山峰能够相互看见。进阶问题:给定一个不含有负数但可能含有重复值的数组arr,返回有多少对山峰能够相互看见。
福大大 答案2021-07-17:
时间紧,见代码。
代码用golang编写。代码如下:
package main import ( "container/list" "fmt" ) func main() { arr := []int{3, 1, 2, 4, 5, 3, 1, 2, 4, 5} ret := getVisibleNum(arr) fmt.Println(ret) } // 栈中放的记录, // value就是指,times是收集的个数 type Record struct { value int times int } func NewRecord(value int) *Record { ret := &Record{} ret.value = value ret.times = 1 return ret } func twoSelectOne(c bool, a int, b int) int { if c { return a } else { return b } } func getVisibleNum(arr []int) int { if len(arr) < 2 { return 0 } N := len(arr) maxIndex := 0 // 先在环中找到其中一个最大值的位置,哪一个都行 for i := 0; i < N; i++ { maxIndex = twoSelectOne(arr[maxIndex] < arr[i], i, maxIndex) } stack := list.New().Init() // 先把(最大值,1)这个记录放入stack中 stack.PushBack(NewRecord(arr[maxIndex])) // 从最大值位置的下一个位置开始沿next方向遍历 index := nextIndex(maxIndex, N) // 用“小找大”的方式统计所有可见山峰对 res := 0 // 遍历阶段开始,当index再次回到maxIndex的时候,说明转了一圈,遍历阶段就结束 for index != maxIndex { // 当前数要进入栈,判断会不会破坏第一维的数字从顶到底依次变大 // 如果破坏了,就依次弹出栈顶记录,并计算山峰对数量 for stack.Back().Value.(*Record).value < arr[index] { k := stack.Back().Value.(*Record).times stack.Remove(stack.Back()) // 弹出记录为(X,K),如果K==1,产生2对; 如果K>1,产生2*K + C(2,K)对。 res += getInternalSum(k) + 2*k } // 当前数字arr[index]要进入栈了,如果和当前栈顶数字一样就合并 // 不一样就把记录(arr[index],1)放入栈中 if stack.Back().Value.(*Record).value == arr[index] { stack.Back().Value.(*Record).times++ } else { // > stack.PushBack(NewRecord(arr[index])) } index = nextIndex(index, N) } // 清算阶段开始了 // 清算阶段的第1小阶段 for stack.Len() > 2 { times := stack.Back().Value.(*Record).times stack.Remove(stack.Back()) res += getInternalSum(times) + 2*times } // 清算阶段的第2小阶段 if stack.Len() == 2 { times := stack.Back().Value.(*Record).times stack.Remove(stack.Back()) res += getInternalSum(times) + twoSelectOne(stack.Back().Value.(*Record).times == 1, times, 2*times) } // 清算阶段的第3小阶段 res += getInternalSum(stack.Back().Value.(*Record).times) stack.Remove(stack.Back()) return res } // 如果k==1返回0,如果k>1返回C(2,k) func getInternalSum(k int) int { return twoSelectOne(k == 1, 0, k*(k-1)/2) } // 环形数组中当前位置为i,数组长度为size,返回i的下一个位置 func nextIndex(i int, size int) int { return twoSelectOne(i < (size-1), i+1, 0) }
执行结果如下: