-
Notifications
You must be signed in to change notification settings - Fork 31
/
bisect.ts
151 lines (134 loc) · 4.13 KB
/
bisect.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/* eslint-disable no-inner-declarations */
import assert from 'assert'
interface BisectOptions<E, T> {
/**
* 二分查找的起始索引
*/
left?: number
/**
* 二分查找的结束索引
*/
right?: number
/**
* 将数组中的元素转换为比较的值
*/
key?: (e: E) => T
}
/**
* 返回 `target` 在 `arrayLike` 中最左边的插入位置。
* 存在多个相同的值时,返回最左边的位置。
*
* @param arrayLike 某个参数有序的数组
* @param target 查找的目标值
* @param options {@link BisectOptions}
*/
function bisectLeft<E, T>(
arrayLike: ArrayLike<E>,
target: T,
options?: BisectOptions<E, T>
): number {
const n = arrayLike.length
if (n === 0) return 0
let { left = 0, right = n - 1, key = (e: E) => e } = options ?? {}
while (left <= right) {
const mid = (left + right) >>> 1
const midElement = key(arrayLike[mid])
if (midElement < target) {
left = mid + 1
} else {
right = mid - 1
}
}
return left
}
/**
* 返回 `target` 在 `arrayLike` 中最右边的插入位置。
* 存在多个相同的值时,返回最右边的位置。
*
* @param arrayLike 某个参数有序的数组
* @param target 查找的目标值
* @param options {@link BisectOptions}
*/
function bisectRight<E, T>(
arrayLike: ArrayLike<E>,
target: T,
options?: BisectOptions<E, T>
): number {
const n = arrayLike.length
if (n === 0) return 0
let { left = 0, right = n - 1, key = (e: E) => e } = options ?? {}
while (left <= right) {
const mid = (left + right) >>> 1
const midElement = key(arrayLike[mid])
if (midElement <= target) {
left = mid + 1
} else {
right = mid - 1
}
}
return left
}
/**
* 在 `array` 中插入 `target`,并保持 `array` 有序。
* 如果 `array` 中存在多个相同的值,插入到`最左边`的位置。
*
* @param array 某个参数有序的数组
* @param target 插入的目标值
* @param options {@link BisectOptions}
*
* @deprecated 使用 {@link insortRight} 代替
*/
function insortLeft<E>(array: E[], target: E, options?: BisectOptions<E, E>): void {
const pos = bisectLeft(array, target, options)
array.splice(pos, 0, target)
}
/**
* 在 `array` 中插入 `target`,并保持 `array` 有序。
* 如果 `array` 中存在多个相同的值,插入到`最右边`的位置。
* !适合用于维护长度较小(2000左右)的有序数组。
*
* @param array 某个参数有序的数组
* @param target 插入的目标值
* @param options {@link BisectOptions}
*
* @alias bisectInsort
*/
function insortRight<E>(array: E[], target: E, options?: BisectOptions<E, E>): void {
const pos = bisectRight(array, target, options)
array.splice(pos, 0, target)
}
const bisectInsort = insortRight
if (require.main === module) {
const arr0 = [-3, -1, 1, 3]
assert.strictEqual(bisectLeft(arr0, 1), 2)
assert.strictEqual(bisectRight(arr0, 1), 3)
assert.strictEqual(bisectLeft(arr0, 5), 4)
assert.strictEqual(bisectRight(arr0, 5), 4)
const arr1 = [1, 2, 2, 2, 3, 3, 4, 5, 6, 7]
assert.strictEqual(bisectRight(arr1, 3), 6)
assert.strictEqual(bisectLeft(arr1, 3), 4)
assert.strictEqual(bisectRight(arr1, 2), 4)
assert.strictEqual(bisectLeft(arr1, 2), 1)
const arr3 = [1, 2, 2, 2, 3]
insortLeft(arr3, 2)
assert.deepStrictEqual(arr3, [1, 2, 2, 2, 2, 3])
insortRight(arr3, 2)
assert.deepStrictEqual(arr3, [1, 2, 2, 2, 2, 2, 3])
// 2763. 所有子数组中不平衡数字之和 (n^3 -> 1400ms)
// https://leetcode.cn/problems/sum-of-imbalance-numbers-of-all-subarrays/
function sumImbalanceNumbers(nums: number[]): number {
let res = 0
const n = nums.length
for (let left = 0; left < n; left++) {
const sl: number[] = []
for (let right = left; right < n; right++) {
insortLeft(sl, nums[right])
for (let i = 1; i < sl.length; i++) {
res += +(sl[i] - sl[i - 1] > 1)
}
}
}
return res
}
}
export { bisectLeft, bisectRight, insortLeft, insortRight, bisectInsort }