记录高频场景题,部分内容自行搜索

快排存在的问题,如何优化?

3 种快排基准选择方法:

  • 随机(rand 函数)
  • 固定(队首、队尾)
  • 三数取中(队首、队中和队尾的中间数)

4 种优化方式:

  • 优化 1:当待排序序列的长度分割到一定大小后,使用插入排序
  • 优化 2:在一次分割结束后,可以把与 key 相等的元素聚在一起,继续下次分割时,不用再对与 key 相等元素分割
  • 优化 3:优化递归操作
  • 优化 4:使用并行或多线程处理子序列

写三个线程交替打印 ABC

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
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

class AlternatingPrinter {
public:
AlternatingPrinter() : current_turn(0) {}

void print(char ch, int turn) {
for (int i = 0; i < 10; ++i) { // 打印10次循环
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this, turn] { return current_turn % 3 == turn; });
std::cout << ch;
current_turn++;
cv.notify_all(); // 通知所有等待线程
}
}

private:
std::mutex mtx;
std::condition_variable cv;
int current_turn; // 当前轮次,0:A, 1:B, 2:C
};

int main() {
AlternatingPrinter printer;

std::thread t1([&printer] { printer.print('A', 0); });
std::thread t2([&printer] { printer.print('B', 1); });
std::thread t3([&printer] { printer.print('C', 2); });

t1.join();
t2.join();
t3.join();

std::cout << std::endl;
return 0;
}

不使用临时变量实现 swap 函数

1
2
3
4
5
void swap_xor(int a, int b) {
a ^= b;
b ^= a;
a ^= b;
}

✍️ Top K 问题(可以采取的方法有哪些,各自优点)

✍️ 8G 的 int 型数据,计算机的内存只有 2G,怎么对它进行排序?

✍️ 手撕线程安全的单例模式

手撕 shared_ptr(线程安全)

非线程安全的简单实现

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
#include <memory>

template<typename T>
class smartPtr {
private:
T *_ptr;
size_t* _count;

public:
smartPtr(T *ptr = nullptr):_ptr(ptr) {
if (_ptr) {
_count = new size_t(1);
} else {
_count = new size_t(0);
}
}

smartPtr(const smartPtr &ptr) {
if (this != &ptr) {
this->_ptr = ptr._ptr;
this->_count = ptr._count;
++(*this->_count) ;
}
}

smartPtr& operator=(const smartPtr &ptr) {
if (this->_ptr == ptr._ptr)
return *this;

if (this->_ptr) {
--(*this->_count);
if (this->_count == 0) {
delete this->_ptr;
delete this->_count;
}
}

this->_ptr = ptr._ptr;
this->_count = ptr._count;
++(*this->_count);

return *this;
}

~smartPtr() {
--(*this->_count);
if (0 == *this->_count) {
delete this->_ptr;
delete this->_count;
}
}

size_t use_count() {
return *this->_count;
}

T& operator*() {
assert(this->_ptr == nullptr);
return *(this->_ptr);
}

T* operator->() {
assert(this->_ptr == nullptr);
return this->_ptr;
}
};

基于原子操作的线程安全实现

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
#pragma once

#include <atomic> // 引入原子操作

template <typename T>
class shared_ptr {
private:
T* ptr; // 指向管理的对象
std::atomic<std::size_t>* ref_count; // 原子引用计数

// 释放资源
void release() {
// P.S. 这里使用 std::memory_order_acq_rel 内存序,保证释放资源的同步
if (ref_count && ref_count->fetch_sub(1, std::memory_order_acq_rel) == 1) {
delete ptr;
delete ref_count;
}
}

public:
// 默认构造函数
shared_ptr() : ptr(nullptr), ref_count(nullptr) {}

// 构造函数
// P.S. 这里使用 explicit 关键字,防止隐式类型转换
// shared_ptr<int> ptr1 = new int(10); 不允许出现
explicit shared_ptr(T* p) : ptr(p), ref_count(p ? new std::atomic<std::size_t>(1) : nullptr) {}

// 析构函数
~shared_ptr() { release(); }

// 拷贝构造函数
shared_ptr(const shared_ptr<T>& other) : ptr(other.ptr), ref_count(other.ref_count) {
if (ref_count) {
ref_count->fetch_add(1, std::memory_order_relaxed); // 引用计数增加,不需要强内存序
}
}

// 拷贝赋值运算符
shared_ptr<T>& operator=(const shared_ptr<T>& other) {
if (this != &other) {
release(); // 释放当前资源
ptr = other.ptr;
ref_count = other.ref_count;
if (ref_count) {
ref_count->fetch_add(1, std::memory_order_relaxed); // 引用计数增加
}
}
return *this;
}

// 移动构造函数
// P.S. noexcept 关键字表示该函数不会抛出异常。
// 标准库中的某些操作(如 std::swap)要求移动操作是 noexcept 的,以确保异常安全。
// noexcept 可以帮助编译器生成更高效的代码,因为它不需要为异常处理生成额外的代码。
shared_ptr(shared_ptr<T>&& other) noexcept : ptr(other.ptr), ref_count(other.ref_count) {
other.ptr = nullptr;
other.ref_count = nullptr;
}

// 移动赋值运算符
shared_ptr<T>& operator=(shared_ptr<T>&& other) noexcept {
if (this != &other) {
release(); // 释放当前资源
ptr = other.ptr;
ref_count = other.ref_count;
other.ptr = nullptr;
other.ref_count = nullptr;
}
return *this;
}

// 解引用运算符
// P.S. const 关键字表示该函数不会修改对象的状态。
T& operator*() const { return *ptr; }

// 箭头运算符
T* operator->() const { return ptr; }

// 获取引用计数
std::size_t use_count() const { return ref_count ? ref_count->load(std::memory_order_acquire) : 0; }

// 获取原始指针
T* get() const { return ptr; }

// 重置指针
void reset(T* p = nullptr) {
release();
ptr = p;
ref_count = p ? new std::atomic<std::size_t>(1) : nullptr;
}
};

✍️ 手撕线程池

✍️ 手撕 string

✍️ 手撕 ringbuffer

✍️ 实现一个线程安全的带过期时间的 FIFO Cache

✍️ 实现一个线程安全的带过期时间的 LRU

✍️ 一致性哈希

✍️ 海量数据的 bitmap 使用原理

✍️ 布隆过滤器原理与优点


本站总访问量

本站共发表 125 篇文章 · 总计 541.6k 字
载入天数...载入时分秒...