diff --git a/README.md b/README.md index 11165f7..1c47296 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,12 @@ LeetCode 与 LintCode 解题记录。此为个人练习仓库,代码中对重 - LeetCode 136. 只出现一次的数字 https://leetcode-cn.com/problems/single-number/ +- [山脉数组中查找目标值](src/array/find-in-mountain-array.js) + + - LeetCode 1095. 山脉数组中查找目标值 https://leetcode-cn.com/problems/find-in-mountain-array/ + - LeetCode 852. 山脉数组的峰顶索引 https://leetcode-cn.com/problems/peak-index-in-a-mountain-array/ + - LintCode 585. 山脉序列中的最大值 https://www.lintcode.com/problem/maximum-number-in-mountain-sequence/description + ## 栈 - [最大矩阵](src/stack/maximal-rectangle.js) @@ -260,4 +266,3 @@ LeetCode 与 LintCode 解题记录。此为个人练习仓库,代码中对重 - LeetCode 23. 合并K个排序链表 https://leetcode-cn.com/problems/merge-k-sorted-lists/ - LintCode 104. 合并k个排序链表 https://www.lintcode.com/problem/merge-k-sorted-lists/description - diff --git a/src/array/find-in-mountain-array.js b/src/array/find-in-mountain-array.js new file mode 100644 index 0000000..9c7c127 --- /dev/null +++ b/src/array/find-in-mountain-array.js @@ -0,0 +1,111 @@ +/** + * LeetCode 852. 山脉数组的峰顶索引 + * @param {number[]} A + * @return {number} + */ +export const peakIndexInMountainArray = function (A) { + let left = 0 + let right = A.length - 1 + + while (left < right) { + const mid = left + ((right - left) >> 1) + if (A[mid] > A[mid - 1] && A[mid] > A[mid + 1]) return mid + if (A[mid] < A[mid + 1] && A[mid] > A[mid - 1]) { + left = mid + } else { + right = mid + } + } + + return 0 +} + +/** + * LintCode 585. 山脉序列中的最大值 + * @param nums: a mountain sequence which increase firstly and then decrease + * @return: then mountain top + */ +export const mountainSequence = function (nums) { + let left = 0 + let right = nums.length - 1 + + while (left < right) { + const mid = left + ((right - left) >> 1) + if (nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1]) return nums[mid] + if (nums[mid] < nums[mid + 1] && nums[mid] > nums[mid - 1]) { + left = mid + } else { + right = mid + } + } + + return nums[0] +} + +/** + * // This is the MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * function MountainArray() { + * @param {number} index + * @return {number} + * this.get = function(index) { + * ... + * }; + * + * @return {number} + * this.length = function() { + * ... + * }; + * }; + */ + +/** + * 1095. 山脉数组中查找目标值 + * @param {number} target + * @param {MountainArray} mountainArr + * @return {number} + */ +export const findInMountainArray = function (target, mountainArr) { + let left = 0 + let right = mountainArr.length() - 1 + + // 山峰索引 + let peak = 0 + + while (left < right) { + const mid = left + ((right - left) >> 1) + if (mountainArr.get(mid) > mountainArr.get(mid - 1) && mountainArr.get(mid) > mountainArr.get(mid + 1)) { + peak = mid + break + } + if (mountainArr.get(mid) < mountainArr.get(mid + 1) && mountainArr.get(mid) > mountainArr.get(mid - 1)) { + left = mid + } else { + right = mid + } + } + + // 在山峰左边查找 + const index = binarySearch(mountainArr, target, 0, peak, v => v) + + // 若存在,则直接返回下标,否则在山峰右边查找 + return index !== -1 ? index : binarySearch(mountainArr, target, peak + 1, mountainArr.length() - 1, v => -v) + + // 二分法查找 + // 其中 fn 是用来对升序还是降序的特殊处理 + function binarySearch (mountainArr, target, l, r, fn) { + target = fn(target) + while (l <= r) { + const mid = (l + (r - l) / 2) | 0 + const cur = fn(mountainArr.get(mid)) + if (cur === target) { + return mid + } else if (cur < target) { + l = mid + 1 + } else { + r = mid - 1 + } + } + return -1 + } +} diff --git a/test/array/find-in-mountain-array.test.js b/test/array/find-in-mountain-array.test.js new file mode 100644 index 0000000..062a183 --- /dev/null +++ b/test/array/find-in-mountain-array.test.js @@ -0,0 +1,30 @@ +import { peakIndexInMountainArray, mountainSequence, findInMountainArray } from '../../src/array/find-in-mountain-array' + +test('山脉数组的峰顶索引', () => { + expect(peakIndexInMountainArray([0, 1, 0])).toBe(1) + expect(peakIndexInMountainArray([0, 2, 1, 0])).toBe(1) +}) + +test('山脉序列中的最大值', () => { + expect(mountainSequence([1, 2, 4, 8, 6, 3])).toBe(8) + expect(mountainSequence([10, 9, 8, 7])).toBe(10) +}) + +function MountainArray (arr) { + this.arr = arr + // @param {number} index + // @return {number} + this.get = function (index) { + return this.arr[index] + } + + // @return {number} + this.length = function () { + return this.arr.length + } +}; + +test('山脉数组中查找目标值', () => { + expect(findInMountainArray(3, new MountainArray([1, 2, 3, 4, 5, 3, 1]))).toBe(2) + expect(findInMountainArray(3, new MountainArray([0, 1, 2, 4, 2, 1]))).toBe(-1) +})