2023-06-18: Given a one-dimensional array scores of length N, representing the initial scores of employees No. 0~N-1,
scores[i] = a, which means that employee i scored a at the beginning,
Given a two-dimensional array operations of length M,
operations[i] = {a, b, c}。
Indicates that the i-th operation is:
If a==1, it means to change the score of all employees whose current score is <b to b, and the value of c is useless.
If a==2, it means changing the score of the employee numbered b to c,
All operations occur sequentially from 0 to M-1.
Return a one-dimensional array ans of length N, indicating the score of each employee after all operations are completed.
1 <= N <= 10 to the 6th power,
1 <= M <= 10 to the 6th power,
0 <= score <= 10 to the 9th power.
From the TikTok US written test.
Answer 2023-06-18:
Specific steps are as follows:
1. Create a one-dimensional array of length N scores
, representing the initial score of each employee.
2. Create a two-dimensional array of length M operations
representing the sequence of operations.
3. Define a function operateScores2
to handle the sequence of operations.
4. Initialize a node array nodes
to store the node information of each employee.
5. Initialize an empty score and bucket mapping table scoreBucketMap
.
6. Traverse scores
the array, create a bucket for each score value, and add the corresponding employee node to the bucket.
7. Traverse operations
the array, processing each operation.
8. For the operation of type 1, obtain the maximum score value that is less than the current score floorKeyV
, and then merge their buckets into the bucket corresponding to the new score value.
9. For the operation of type 2, obtain the employee node, remove it from the original bucket, and then add it to the bucket corresponding to the new score value.
10. Traverse the mapping table of scores and buckets scoreBucketMap
, take out the employee nodes in the buckets in order, and update them into the result array ans
.
11. Return the final result array ans
.
12. Carry out functional testing and performance testing.
Time complexity analysis:
-
Traverse
scores
the array and create buckets, the time complexity is O(N). -
Traversing
operations
the array, the time complexity of each operation is O(logN) (because the ordered map is used to implement the bucket, the time complexity of the retrieval operation is O(logN)). -
Traversing the mapping table of scores and buckets
scoreBucketMap
, the number of employee nodes in each bucket is O(1), and the time complexity of traversal is O(N). -
The overall time complexity is O(N + KlogN), where K is the length of the sequence of operations.
Space Complexity Analysis:
-
Create an array of length N
scores
with space complexity O(N). -
Create an array of length M
operations
with space complexity O(M). -
Create a node array of length N
nodes
with space complexity O(N). -
Create an ordered mapping table
scoreBucketMap
to store the bucket corresponding to each score value, and the space complexity is O(N). -
The length of the resulting array
ans
is N and the space complexity is O(N). -
The overall space complexity is O(N + M).
The complete code of go is as follows:
package main
import (
"fmt"
"math/rand"
"time"
)
// 桶,得分在有序表里!桶只作为有序表里的value,不作为key
type Bucket struct {
head *Node
tail *Node
}
func NewBucket() *Bucket {
head := &Node{
index: -1}
tail := &Node{
index: -1}
head.next = tail
tail.last = head
return &Bucket{
head: head, tail: tail}
}
func (b *Bucket) add(node *Node) {
node.last = b.tail.last
node.next = b.tail
b.tail.last.next = node
b.tail.last = node
}
func (b *Bucket) merge(join *Bucket) {
if join.head.next != join.tail {
b.tail.last.next = join.head.next
join.head.next.last = b.tail.last
join.tail.last.next = b.tail
b.tail.last = join.tail.last
join.head.next = join.tail
join.tail.last = join.head
}
}
// Node represents a node in the bucket
type Node struct {
index int
last *Node
next *Node
}
func (n *Node) connectLastNext() {
n.last.next = n.next
n.next.last = n.last
}
// 暴力方法
func operateScores1(scores []int, operations [][]int) []int {
n := len(scores)
ans := make([]int, n)
copy(ans, scores)
for _, op := range operations {
if op[0] == 1 {
for i := 0; i < n; i++ {
ans[i] = max(ans[i], op[1])
}
} else {
ans[op[1]] = op[2]
}
}
return ans
}
// 正式方法
func operateScores2(scores []int, operations [][]int) []int {
n := len(scores)
nodes := make([]*Node, n)
scoreBucketMap := make(map[int]*Bucket)
for i := 0; i < n; i++ {
nodes[i] = &Node{
index: i}
if _, ok := scoreBucketMap[scores[i]]; !ok {
scoreBucketMap[scores[i]] = NewBucket()
}
scoreBucketMap[scores[i]].add(nodes[i])
}
for _, op := range operations {
if op[0] == 1 {
floorKeyV := floorKey(scoreBucketMap, op[1]-1)
if floorKeyV != -1 && scoreBucketMap[op[1]] == nil {
scoreBucketMap[op[1]] = NewBucket()
}
for floorKeyV != -1 {
scoreBucketMap[op[1]].merge(scoreBucketMap[floorKeyV])
delete(scoreBucketMap, floorKeyV)
floorKeyV = floorKey(scoreBucketMap, op[1]-1)
}
} else {
cur := nodes[op[1]]
cur.connectLastNext()
if scoreBucketMap[op[2]] == nil {
scoreBucketMap[op[2]] = NewBucket()
}
scoreBucketMap[op[2]].add(cur)
}
}
ans := make([]int, n)
for score, bucket := range scoreBucketMap {
cur := bucket.head.next
for cur != bucket.tail {
ans[cur.index] = score
cur = cur.next
}
}
return ans
}
func floorKey(m map[int]*Bucket, target int) int {
for score := range m {
if score <= target {
return score
}
}
return -1
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
// RandomScores generates an array of random scores
func randomScores(n, v int) []int {
scores := make([]int, n)
rand.Seed(time.Now().UnixNano())
for i := 0; i < n; i++ {
scores[i] = rand.Intn(v)
}
return scores
}
// RandomOperations generates a 2D array of random operations
func randomOperations(n, m, v int) [][]int {
operations := make([][]int, m)
rand.Seed(time.Now().UnixNano())
for i := 0; i < m; i++ {
operations[i] = make([]int, 3)
if rand.Float32() < 0.5 {
operations[i][0] = 1
operations[i][1] = rand.Intn(v)
} else {
operations[i][0] = 2
operations[i][1] = rand.Intn(n)
operations[i][2] = rand.Intn(v)
}
}
return operations
}
// IsEqual checks if two arrays are equal
func isEqual(arr1, arr2 []int) bool {
if len(arr1) != len(arr2) {
return false
}
for i := 0; i < len(arr1); i++ {
if arr1[i] != arr2[i] {
return false
}
}
return true
}
// Main function for testing
func main() {
N := 1000
M := 1000
V := 100000
testTimes := 100
fmt.Println("功能测试开始")
for i := 0; i < testTimes; i++ {
n := rand.Intn(N) + 1
m := rand.Intn(M) + 1
scores := randomScores(n, V)
operations := randomOperations(n, m, V)
ans1 := operateScores1(scores, operations)
ans2 := operateScores2(scores, operations)
if !isEqual(ans1, ans2) {
fmt.Println("出错了!")
}
}
fmt.Println("功能测试结束")
fmt.Println("性能测试开始")
n := 100000
m := 100000
v := 100000000
scores := randomScores(n, v)
operations := randomOperations(n, m, v)
fmt.Println("总人数:", n)
fmt.Println("操作数:", n)
fmt.Println("值范围:", v)
start := time.Now()
operateScores2(scores, operations)
end := time.Now()
fmt.Println("运行时间:", end.Sub(start))
fmt.Println("性能测试结束")
}
The complete c++ code is as follows:
#include <iostream>
#include <vector>
#include <map>
#include <random>
#include <ctime>
using namespace std;
class Bucket;
// Node represents a node in the bucket
class Node {
public:
int index;
Node* last;
Node* next;
void connectLastNext() {
last->next = next;
next->last = last;
}
};
// Bucket, scores stored in a sorted list
class Bucket {
public:
Node* head;
Node* tail;
Bucket() {
head = new Node();
tail = new Node();
head->index = -1;
tail->index = -1;
head->next = tail;
tail->last = head;
}
void add(Node* node) {
node->last = tail->last;
node->next = tail;
tail->last->next = node;
tail->last = node;
}
void merge(Bucket* join) {
if (join->head->next != join->tail) {
tail->last->next = join->head->next;
join->head->next->last = tail->last;
join->tail->last->next = tail;
tail->last = join->tail->last;
join->head->next = join->tail;
join->tail->last = join->head;
}
}
};
vector<int> operateScores1(const vector<int>& scores, const vector<vector<int>>& operations) {
int n = scores.size();
vector<int> ans(scores);
for (const auto& op : operations) {
if (op[0] == 1) {
for (int i = 0; i < n; i++) {
ans[i] = max(ans[i], op[1]);
}
}
else {
ans[op[1]] = op[2];
}
}
return ans;
}
int floorKey(const map<int, Bucket*>& m, int target);
vector<int> operateScores2(const vector<int>& scores, const vector<vector<int>>& operations) {
int n = scores.size();
vector<Node*> nodes(n);
map<int, Bucket*> scoreBucketMap;
for (int i = 0; i < n; i++) {
nodes[i] = new Node();
nodes[i]->index = i;
if (scoreBucketMap.find(scores[i]) == scoreBucketMap.end()) {
scoreBucketMap[scores[i]] = new Bucket();
}
scoreBucketMap[scores[i]]->add(nodes[i]);
}
for (const auto& op : operations) {
if (op[0] == 1) {
int floorKeyV = floorKey(scoreBucketMap, op[1] - 1);
if (floorKeyV != -1 && scoreBucketMap.find(op[1]) == scoreBucketMap.end()) {
scoreBucketMap[op[1]] = new Bucket();
}
while (floorKeyV != -1) {
scoreBucketMap[op[1]]->merge(scoreBucketMap[floorKeyV]);
scoreBucketMap.erase(floorKeyV);
floorKeyV = floorKey(scoreBucketMap, op[1] - 1);
}
}
else {
Node* cur = nodes[op[1]];
cur->connectLastNext();
if (scoreBucketMap.find(op[2]) == scoreBucketMap.end()) {
scoreBucketMap[op[2]] = new Bucket();
}
scoreBucketMap[op[2]]->add(cur);
}
}
vector<int> ans(n);
for (const auto& entry : scoreBucketMap) {
int score = entry.first;
Bucket* bucket = entry.second;
Node* cur = bucket->head->next;
while (cur != bucket->tail) {
ans[cur->index] = score;
cur = cur->next;
}
}
return ans;
}
int floorKey(const map<int, Bucket*>& m, int target) {
for (const auto& entry : m) {
int score = entry.first;
if (score <= target) {
return score;
}
}
return -1;
}
int main() {
int N = 1000;
int M = 1000;
int V = 100000;
int testTimes = 100;
cout << "功能测试开始" << endl;
for (int i = 0; i < testTimes; i++) {
int n = rand() % N + 1;
int m = rand() % M + 1;
vector<int> scores(n);
vector<vector<int>> operations(m, vector<int>(3));
for (int j = 0; j < n; j++) {
scores[j] = rand() % V;
}
for (auto& op : operations) {
if (rand() < 0.5) {
op[0] = 1;
op[1] = rand() % V;
}
else {
op[0] = 2;
op[1] = rand() % n;
op[2] = rand() % V;
}
}
vector<int> ans1 = operateScores1(scores, operations);
vector<int> ans2 = operateScores2(scores, operations);
if (ans1 != ans2) {
cout << "出错了!" << endl;
}
}
cout << "功能测试结束" << endl;
cout << "性能测试开始" << endl;
int n = 1000000;
int m = 1000000;
int v = 1000000000;
vector<int> scores(n);
vector<vector<int>> operations(m, vector<int>(3));
for (int i = 0; i < n; i++) {
scores[i] = rand() % v;
}
for (auto& op : operations) {
op[0] = rand() < 0.5 ? 1 : 2;
op[1] = rand() % n;
op[2] = rand() % v;
}
cout << "总人数: " << n << endl;
cout << "操作数: " << m << endl;
cout << "值范围: " << v << endl;
clock_t start = clock();
operateScores2(scores, operations);
clock_t end = clock();
cout << "运行时间: " << double(end - start) / CLOCKS_PER_SEC << endl;
cout << "性能测试结束" << endl;
return 0;
}