65
65
66
66
<!-- solution:start -->
67
67
68
- ### 方法一:树状数组或线段树
68
+ ### 方法一:树状数组
69
69
70
70
对于本题,我们先用 pos 记录每个数在 nums2 中的位置,然后依次对 nums1 中的每个元素进行处理。
71
71
@@ -79,27 +79,14 @@ tags:
79
79
1 . ...
80
80
1 . 最后是 2,此时 nums2 中出现情况为 ` [4,1,0,2,3] ` ,2 之前有值的个数是 4,2 之后没有值的个数是 0。因此以 2 为中间数字能形成 0 个好三元组。
81
81
82
- 我们可以用** 树状数组** 或** 线段树** 这两种数据结构来更新 nums2 中各个位置数字的出现情况,快速算出每个数字左侧 1 的个数,以及右侧 0 的个数。
83
-
84
- ** 数据结构 1:树状数组**
82
+ 我们可以用** 树状数组** 来更新 nums2 中各个位置数字的出现情况,快速算出每个数字左侧 1 的个数,以及右侧 0 的个数。
85
83
86
84
树状数组,也称作“二叉索引树”(Binary Indexed Tree)或 Fenwick 树。 它可以高效地实现如下两个操作:
87
85
88
86
1 . ** 单点更新** ` update(x, delta) ` : 把序列 x 位置的数加上一个值 delta;
89
87
1 . ** 前缀和查询** ` query(x) ` :查询序列 ` [1,...x] ` 区间的区间和,即位置 x 的前缀和。
90
88
91
- 这两个操作的时间复杂度均为 $O(\log n)$。
92
-
93
- ** 数据结构 2:线段树**
94
-
95
- 线段树将整个区间分割为多个不连续的子区间,子区间的数量不超过 ` log(width) ` 。更新某个元素的值,只需要更新 ` log(width) ` 个区间,并且这些区间都包含在一个包含该元素的大区间内。
96
-
97
- - 线段树的每个节点代表一个区间;
98
- - 线段树具有唯一的根节点,代表的区间是整个统计范围,如 ` [1, N] ` ;
99
- - 线段树的每个叶子节点代表一个长度为 1 的元区间 ` [x, x] ` ;
100
- - 对于每个内部节点 ` [l, r] ` ,它的左儿子是 ` [l, mid] ` ,右儿子是 ` [mid + 1, r] ` , 其中 ` mid = ⌊(l + r) / 2⌋ ` (即向下取整)。
101
-
102
- > 本题 Python3 线段树代码 TLE。
89
+ 这两个操作的时间复杂度均为 $O(\log n)$。因此,整体的时间复杂度为 $O(n \log n)$,其中 $n$ 为数组 $\textit{nums1}$ 的长度。空间复杂度 $O(n)$。
103
90
104
91
<!-- tabs:start -->
105
92
@@ -302,20 +289,87 @@ func goodTriplets(nums1 []int, nums2 []int) int64 {
302
289
}
303
290
```
304
291
292
+ #### TypeScript
293
+
294
+ ``` ts
295
+ class BinaryIndexedTree {
296
+ private c: number [];
297
+ private n: number ;
298
+
299
+ constructor (n : number ) {
300
+ this .n = n ;
301
+ this .c = Array (n + 1 ).fill (0 );
302
+ }
303
+
304
+ private static lowbit(x : number ): number {
305
+ return x & - x ;
306
+ }
307
+
308
+ update(x : number , delta : number ): void {
309
+ while (x <= this .n ) {
310
+ this .c [x ] += delta ;
311
+ x += BinaryIndexedTree .lowbit (x );
312
+ }
313
+ }
314
+
315
+ query(x : number ): number {
316
+ let s = 0 ;
317
+ while (x > 0 ) {
318
+ s += this .c [x ];
319
+ x -= BinaryIndexedTree .lowbit (x );
320
+ }
321
+ return s ;
322
+ }
323
+ }
324
+
325
+ function goodTriplets(nums1 : number [], nums2 : number []): number {
326
+ const n = nums1 .length ;
327
+ const pos = new Map <number , number >();
328
+ nums2 .forEach ((v , i ) => pos .set (v , i + 1 ));
329
+
330
+ const tree = new BinaryIndexedTree (n );
331
+ let ans = 0 ;
332
+
333
+ for (const num of nums1 ) {
334
+ const p = pos .get (num )! ;
335
+ const left = tree .query (p );
336
+ const total = tree .query (n );
337
+ const right = n - p - (total - left );
338
+ ans += left * right ;
339
+ tree .update (p , 1 );
340
+ }
341
+
342
+ return ans ;
343
+ }
344
+ ```
345
+
305
346
<!-- tabs:end -->
306
347
307
348
<!-- solution:end -->
308
349
309
350
<!-- solution:start -->
310
351
311
- ### 方法二
352
+ ### 方法二:线段树
353
+
354
+ 我们也可以用线段树来实现。线段树是一种数据结构,能够高效地进行区间查询和更新操作。它的基本思想是将一个区间划分为多个子区间,并且每个子区间都可以用一个节点来表示。
355
+
356
+ 线段树将整个区间分割为多个不连续的子区间,子区间的数量不超过 ` log(width) ` 。更新某个元素的值,只需要更新 ` log(width) ` 个区间,并且这些区间都包含在一个包含该元素的大区间内。
357
+
358
+ - 线段树的每个节点代表一个区间;
359
+ - 线段树具有唯一的根节点,代表的区间是整个统计范围,如 ` [1, N] ` ;
360
+ - 线段树的每个叶子节点代表一个长度为 1 的元区间 ` [x, x] ` ;
361
+ - 对于每个内部节点 ` [l, r] ` ,它的左儿子是 ` [l, mid] ` ,右儿子是 ` [mid + 1, r] ` , 其中 ` mid = ⌊(l + r) / 2⌋ ` (即向下取整)。
362
+
363
+ 时间复杂度 $O(n \log n)$,其中 $n$ 为数组 $\textit{nums1}$ 的长度。空间复杂度 $O(n)$。
312
364
313
365
<!-- tabs:start -->
314
366
315
367
#### Python3
316
368
317
369
``` python
318
370
class Node :
371
+ __slots__ = (" l" , " r" , " v" )
372
+
319
373
def __init__ (self ):
320
374
self .l = 0
321
375
self .r = 0
@@ -539,6 +593,174 @@ public:
539
593
};
540
594
```
541
595
596
+ #### Go
597
+
598
+ ```go
599
+ type Node struct {
600
+ l, r, v int
601
+ }
602
+
603
+ type SegmentTree struct {
604
+ tr []Node
605
+ }
606
+
607
+ func NewSegmentTree(n int) *SegmentTree {
608
+ tr := make([]Node, 4*n)
609
+ st := &SegmentTree{tr: tr}
610
+ st.build(1, 1, n)
611
+ return st
612
+ }
613
+
614
+ func (st *SegmentTree) build(u, l, r int) {
615
+ st.tr[u].l = l
616
+ st.tr[u].r = r
617
+ if l == r {
618
+ return
619
+ }
620
+ mid := (l + r) >> 1
621
+ st.build(u<<1, l, mid)
622
+ st.build(u<<1|1, mid+1, r)
623
+ }
624
+
625
+ func (st *SegmentTree) modify(u, x, v int) {
626
+ if st.tr[u].l == x && st.tr[u].r == x {
627
+ st.tr[u].v += v
628
+ return
629
+ }
630
+ mid := (st.tr[u].l + st.tr[u].r) >> 1
631
+ if x <= mid {
632
+ st.modify(u<<1, x, v)
633
+ } else {
634
+ st.modify(u<<1|1, x, v)
635
+ }
636
+ st.pushup(u)
637
+ }
638
+
639
+ func (st *SegmentTree) pushup(u int) {
640
+ st.tr[u].v = st.tr[u<<1].v + st.tr[u<<1|1].v
641
+ }
642
+
643
+ func (st *SegmentTree) query(u, l, r int) int {
644
+ if st.tr[u].l >= l && st.tr[u].r <= r {
645
+ return st.tr[u].v
646
+ }
647
+ mid := (st.tr[u].l + st.tr[u].r) >> 1
648
+ res := 0
649
+ if l <= mid {
650
+ res += st.query(u<<1, l, r)
651
+ }
652
+ if r > mid {
653
+ res += st.query(u<<1|1, l, r)
654
+ }
655
+ return res
656
+ }
657
+
658
+ func goodTriplets(nums1 []int, nums2 []int) int64 {
659
+ n := len(nums1)
660
+ pos := make(map[int]int)
661
+ for i, v := range nums2 {
662
+ pos[v] = i + 1
663
+ }
664
+
665
+ tree := NewSegmentTree(n)
666
+ var ans int64
667
+
668
+ for _, num := range nums1 {
669
+ p := pos[num]
670
+ left := tree.query(1, 1, p)
671
+ right := n - p - (tree.query(1, 1, n) - tree.query(1, 1, p))
672
+ ans += int64(left * right)
673
+ tree.modify(1, p, 1)
674
+ }
675
+
676
+ return ans
677
+ }
678
+ ```
679
+
680
+ #### TypeScript
681
+
682
+ ``` ts
683
+ class Node {
684
+ l: number = 0 ;
685
+ r: number = 0 ;
686
+ v: number = 0 ;
687
+ }
688
+
689
+ class SegmentTree {
690
+ private tr: Node [];
691
+
692
+ constructor (n : number ) {
693
+ this .tr = Array (4 * n );
694
+ for (let i = 0 ; i < 4 * n ; i ++ ) {
695
+ this .tr [i ] = new Node ();
696
+ }
697
+ this .build (1 , 1 , n );
698
+ }
699
+
700
+ private build(u : number , l : number , r : number ): void {
701
+ this .tr [u ].l = l ;
702
+ this .tr [u ].r = r ;
703
+ if (l === r ) return ;
704
+ const mid = (l + r ) >> 1 ;
705
+ this .build (u << 1 , l , mid );
706
+ this .build ((u << 1 ) | 1 , mid + 1 , r );
707
+ }
708
+
709
+ modify(u : number , x : number , v : number ): void {
710
+ if (this .tr [u ].l === x && this .tr [u ].r === x ) {
711
+ this .tr [u ].v += v ;
712
+ return ;
713
+ }
714
+ const mid = (this .tr [u ].l + this .tr [u ].r ) >> 1 ;
715
+ if (x <= mid ) {
716
+ this .modify (u << 1 , x , v );
717
+ } else {
718
+ this .modify ((u << 1 ) | 1 , x , v );
719
+ }
720
+ this .pushup (u );
721
+ }
722
+
723
+ private pushup(u : number ): void {
724
+ this .tr [u ].v = this .tr [u << 1 ].v + this .tr [(u << 1 ) | 1 ].v ;
725
+ }
726
+
727
+ query(u : number , l : number , r : number ): number {
728
+ if (this .tr [u ].l >= l && this .tr [u ].r <= r ) {
729
+ return this .tr [u ].v ;
730
+ }
731
+ const mid = (this .tr [u ].l + this .tr [u ].r ) >> 1 ;
732
+ let res = 0 ;
733
+ if (l <= mid ) {
734
+ res += this .query (u << 1 , l , r );
735
+ }
736
+ if (r > mid ) {
737
+ res += this .query ((u << 1 ) | 1 , l , r );
738
+ }
739
+ return res ;
740
+ }
741
+ }
742
+
743
+ function goodTriplets(nums1 : number [], nums2 : number []): number {
744
+ const n = nums1 .length ;
745
+ const pos = new Map <number , number >();
746
+ nums2 .forEach ((v , i ) => pos .set (v , i + 1 ));
747
+
748
+ const tree = new SegmentTree (n );
749
+ let ans = 0 ;
750
+
751
+ for (const num of nums1 ) {
752
+ const p = pos .get (num )! ;
753
+ const left = tree .query (1 , 1 , p );
754
+ const total = tree .query (1 , 1 , n );
755
+ const right = n - p - (total - left );
756
+ ans += left * right ;
757
+ tree .modify (1 , p , 1 );
758
+ }
759
+
760
+ return ans ;
761
+ }
762
+ ```
763
+
542
764
<!-- tabs:end -->
543
765
544
766
<!-- solution:end -->
0 commit comments