2023-06-18: Given a one-dimensional array scores of length N, which represents the initial score of employees No. 0~N-1, scores[i] = a, means that employee No. i scored a at the beginning, given a Two-dimensional array operatio of length M

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 operationsrepresenting the sequence of operations.

3. Define a function operateScores2to handle the sequence of operations.

4. Initialize a node array nodesto store the node information of each employee.

5. Initialize an empty score and bucket mapping table scoreBucketMap.

6. Traverse scoresthe array, create a bucket for each score value, and add the corresponding employee node to the bucket.

7. Traverse operationsthe 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 scoresthe array and create buckets, the time complexity is O(N).

  • Traversing operationsthe 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 scoreswith space complexity O(N).

  • Create an array of length M operationswith space complexity O(M).

  • Create a node array of length N nodeswith space complexity O(N).

  • Create an ordered mapping table scoreBucketMapto store the bucket corresponding to each score value, and the space complexity is O(N).

  • The length of the resulting array ansis 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("性能测试结束")
}

insert image description here

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;
}

insert image description here

Guess you like

Origin blog.csdn.net/weixin_48502062/article/details/131274237