简介
STL分为哪几类
STL (Standard Template Library) 主要由以下几类组件构成:
容器(Containers):
- 序列式容器(Sequence Containers):
vector
, deque
, list
, array
- 关联式容器(Associative Containers):
set
, map
, multiset
, multimap
- 无序容器(Unordered Containers):
unordered_set
, unordered_map
, unordered_multiset
, unordered_multimap
迭代器(Iterators):
- 输入迭代器(Input Iterator)
- 输出迭代器(Output Iterator)
- 前向迭代器(Forward Iterator)
- 双向迭代器(Bidirectional Iterator)
- 随机访问迭代器(Random Access Iterator)
算法(Algorithms):
- 非修改算法(Non-modifying algorithms):
find
, count
, for_each
, 等
- 修改算法(Modifying algorithms):
copy
, swap
, transform
, 等
- 排序算法(Sorting algorithms):
sort
, partial_sort
, nth_element
, 等
- 其他算法:
search
, merge
, includes
, 等
函数对象(Function Objects):如 less
, greater
, plus
, minus
, 等
适配器(Adapters):
- 容器适配器(Container Adapters):
stack
, queue
, priority_queue
- 迭代器适配器(Iterator Adapters):
reverse_iterator
, back_insert_iterator
, front_insert_iterator
, 等
- 函数适配器(Function Adapters):
bind
, function
, mem_fn
, 等
这些组件共同构成了STL,使其能够提供高效且灵活的操作来处理各种数据结构和算法。
STL常用的几个容器有哪些?用时间复杂度,空间复杂度对比一下
STL中常用的容器主要包括:vector
, deque
, list
, set
, map
, unordered_set
, unordered_map
。下面是这些容器的时间复杂度和空间复杂度对比:
1. vector
- 时间复杂度:
- 访问元素:O(1)
- 插入/删除(末尾):O(1) 均摊
- 插入/删除(中间或开头):O(n)
- 空间复杂度:
- 动态数组,空间效率较高,但可能有一些额外的空间用于容器增长。
2. deque
- 时间复杂度:
- 访问元素:O(1)
- 插入/删除(两端):O(1)
- 插入/删除(中间):O(n)
- 空间复杂度:
- 双端队列,通常比
vector
多一些空间开销,但仍然是较为紧凑的。
3. list
- 时间复杂度:
- 访问元素:O(n)
- 插入/删除(任意位置):O(1)
- 空间复杂度:
- 链表,每个元素需要额外的指针空间,相较于
vector
和 deque
更高。
4. set
5. map
6. unordered_set
7. unordered_map
- 时间复杂度:
- 空间复杂度:
- 哈希表,与
unordered_set
类似,空间开销较高。
对比总结
- 访问元素:
vector
和 deque
的随机访问效率最高,list
最低。
- 插入/删除:
list
在任意位置插入/删除效率最高,vector
和 deque
末尾插入/删除效率高,但在中间位置操作效率低。
- 查找:有序容器(如
set
和 map
)和无序容器(如 unordered_set
和 unordered_map
)均提供高效的查找,但有序容器的查找是O(log n),无序容器为O(1)均摊。
- 空间效率:
vector
和 deque
空间效率较高,list
和基于树结构的 set
/map
空间开销较高,无序容器(哈希表)则需要额外空间用于哈希桶。
单例模式怎么实现
单例模式(Singleton Pattern)是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。实现单例模式的方式有很多种,以下是几种常见的实现方法:
1. 懒汉式(线程不安全)
懒汉式单例模式在第一次使用时创建实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Singleton { private: static Singleton* instance; Singleton() {}
public: static Singleton* getInstance() { if (instance == nullptr) { instance = new Singleton(); } return instance; } };
Singleton* Singleton::instance = nullptr;
|
2. 懒汉式(线程安全,使用互斥锁)
使用互斥锁确保线程安全,但会影响性能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <mutex>
class Singleton { private: static Singleton* instance; static std::mutex mtx; Singleton() {}
public: static Singleton* getInstance() { std::lock_guard<std::mutex> lock(mtx); if (instance == nullptr) { instance = new Singleton(); } return instance; } };
Singleton* Singleton::instance = nullptr; std::mutex Singleton::mtx;
|
3. 饿汉式
饿汉式单例模式在类加载时就创建实例。
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Singleton { private: static Singleton* instance; Singleton() {}
public: static Singleton* getInstance() { return instance; } };
Singleton* Singleton::instance = new Singleton();
|
4. 双重检查锁(Double-Checked Locking)
双重检查锁在提高性能的同时确保线程安全。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <mutex>
class Singleton { private: static Singleton* instance; static std::mutex mtx; Singleton() {}
public: static Singleton* getInstance() { if (instance == nullptr) { std::lock_guard<std::mutex> lock(mtx); if (instance == nullptr) { instance = new Singleton(); } } return instance; } };
Singleton* Singleton::instance = nullptr; std::mutex Singleton::mtx;
|
5. 使用局部静态变量(C++11及以上)
C++11标准保证了静态局部变量的线程安全性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Singleton { private: Singleton() {}
public: static Singleton& getInstance() { static Singleton instance; return instance; }
Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; };
|
6. 使用 call_once
(C++11及以上)
std::call_once
保证了一次性初始化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <mutex>
class Singleton { private: Singleton() {} static Singleton* instance; static std::once_flag initFlag;
public: static Singleton* getInstance() { std::call_once(initFlag, []() { instance = new Singleton(); }); return instance; }
Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; };
Singleton* Singleton::instance = nullptr; std::once_flag Singleton::initFlag;
|
这几种方法各有优缺点,选择哪种实现方式取决于具体的需求和场景。
手动实现一个栈或队列
下面是手动实现一个栈(Stack)和一个队列(Queue)的示例代码,分别使用C++语言:
栈的实现
一个简单的栈可以使用数组或链表来实现。这里我们使用链表来实现一个栈。
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
| #include <iostream>
struct Node { int data; Node* next; Node(int value) : data(value), next(nullptr) {} };
class Stack { private: Node* top;
public: Stack() : top(nullptr) {}
void push(int value) { Node* newNode = new Node(value); newNode->next = top; top = newNode; }
void pop() { if (top == nullptr) { std::cerr << "Stack is empty." << std::endl; return; } Node* temp = top; top = top->next; delete temp; }
int peek() { if (top == nullptr) { std::cerr << "Stack is empty." << std::endl; return -1; } return top->data; }
bool isEmpty() { return top == nullptr; }
~Stack() { while (top != nullptr) { pop(); } } };
int main() { Stack stack; stack.push(10); stack.push(20); stack.push(30);
std::cout << "Top element is " << stack.peek() << std::endl;
stack.pop(); std::cout << "Top element is " << stack.peek() << std::endl;
return 0; }
|
队列的实现
一个简单的队列可以使用数组或链表来实现。这里我们使用链表来实现一个队列。
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
| #include <iostream>
struct Node { int data; Node* next; Node(int value) : data(value), next(nullptr) {} };
class Queue { private: Node* front; Node* rear;
public: Queue() : front(nullptr), rear(nullptr) {}
void enqueue(int value) { Node* newNode = new Node(value); if (rear == nullptr) { front = rear = newNode; return; } rear->next = newNode; rear = newNode; }
void dequeue() { if (front == nullptr) { std::cerr << "Queue is empty." << std::endl; return; } Node* temp = front; front = front->next; if (front == nullptr) { rear = nullptr; } delete temp; }
int peek() { if (front == nullptr) { std::cerr << "Queue is empty." << std::endl; return -1; } return front->data; }
bool isEmpty() { return front == nullptr; }
~Queue() { while (front != nullptr) { dequeue(); } } };
int main() { Queue queue; queue.enqueue(10); queue.enqueue(20); queue.enqueue(30);
std::cout << "Front element is " << queue.peek() << std::endl;
queue.dequeue(); std::cout << "Front element is " << queue.peek() << std::endl;
return 0; }
|
这两个示例展示了如何使用链表来实现栈和队列的基本操作,包括入栈/入队、出栈/出队、获取栈顶/队首元素,以及检查栈/队列是否为空。