前言
此篇博客是学习笔记,根据Hello算法学习。GitHub链接:https://github.com/krahets/hello-algo
数组arr
初始化
int arr[5];
// 存储在栈上
int nums[5] { 1, 2,3, 4,5 }
// 存储在堆上
int* arr1 = new int[5];
int* num1 = new int[5] {1, 2, 3, 4, 5}
访问元素
// 随机访问元素
int randomAccess(int *nums, int size){
int randomIndex = rand() % size;
int randomNum = nums[randomIndex];
return randomNum;
}
插入元素
void insert(int *nums, int size, int num, int index) {
for (int i=size-1; i > index; i--) {
nums[i] = nums[i-1];
}
nums[index] = num;
}
删除元素
void remove(int *nums, int size, int index){
for(int i=index; i < size-1; i++){
nums[i] = nums[i+1];
}
}
查找元素
int find(int *num, int size, int target){
for (int i=0; i<size; i++) {
if(nums[i] == target)
return i;
}
return -1;
}
扩容数组
int *extend(int *nums, int size, int enlarge) {
// 初始化一个扩展长度后的数组
int *res = new int[size + enlarge];
for (int i=0; i<size; i++) {
res[i] = nums[i];
}
delete[] nums; // 释放内存
return res;
}
链表
struct ListNode {
int val; // 节点值
ListNode *next; // 指向下一节点
ListNode(int x) : val(x), next(nullptr) {} // 构造函数
};
初始化
/* 初始化链表 1 -> 3 -> 2 -> 5 -> 4 */
// 初始化各个节点
ListNode* n0 = new ListNode(1);
ListNode* n1 = new ListNode(3);
ListNode* n2 = new ListNode(2);
ListNode* n3 = new ListNode(5);
ListNode* n4 = new ListNode(4);
// 构建引用指向
n0->next = n1;
n1->next = n2;
n2->next = n3;
n3->next = n4;
插入节点
void insert(ListNode *n0, ListNode *p) {
ListNode *n1 = n0->next;
P->next = n1;
n0->next = P;
}
删除节点
void remove(ListNode *n0) {
if (n0->next == nullptr)
return;
// n0 -> P -> n1
ListNode *P = n0 -> next;
ListNode *n1 = P->next;
n0->next = n1;
delete P; // 释放内存
}
访问节点
ListNode *access(ListNode *head, int index) {
for (int i=0; i<index; i++) {
if (head == nullptr)
return nullptr;
head = head->next;
}
return head;
}
查找节点
int find(ListNode *head, int target) {
int index = 0;
while (head != nullptr) {
if (head->val == target)
return index;
head = head->next;
index++;
}
return -1;
}
常见链表类型
- 单向链表
- 环形链表
- 双向链表
struct ListNode {
int val;
ListNode *next;
ListNode *prev;
ListNode(int x) : val(x), next(nullptr), prev(nullptr) {} // 构造函数
};
列表
初始化
/*初始化列表*/
vector<int> list1;
vector<int> list = { 1, 3, 2, 5, 4 };
访问元素
list num = list[1];
list[1] = 0;
插入与删除元素
相较于数组,列表可以自由地添加与删除元素。在列表尾部添加元素的时间复杂度O(1),但插入和删除元素的效率仍与数组相同,为O(n)。
list.clear(); // 清空列表
list.push_back(1);
list.push_back(3);
list.push_back(2);
list.push_back(5);
list.push_back(4);
list.insert(list.begin() + 3, 6); // 在索引 3 处插入数字 6
list.erase(list.begin() + 3); // 删除索引 3 处的元素
遍历列表
// 通过索引遍历
int count = 0;
for (int i=0; i<list.size(); i++) {
count++;
}
// 直接遍历
count = 0;
for (int n : list) {
count++;
}
拼接列表
vector<int> list1 = { 6, 8, 7, 10, 9 };
// 将list1 拼接到list之后
list.insert(list.end(), list1.begin(), list1.end());
排序列表
sort(list.begin(), list.end()); // 排序后,列表元素由小到大排列
列表实现
class MyList {
private:
int *nums; // 数组(存储列表元素)
int numsCapacity = 10; // 列表容量
int numsSize = 0; // 列表长度(当前元素数量)
int extendRatio = 2; // 每次列表扩容的倍数
public:
// 构造方法
MyList() {
nums = new int[numsCapacity];
}
// 析构方法
~MyList() {
delete[] nums;
}
// 获取列表长度
int size() {
return numsSize;
}
// 获取列表容量
int capacity() {
return numsCapacity;
}
};
栈
// 初始化栈
stack<int> stack;
stack.push(1);
stack.push(3);
stack.push(2);
stack.push(5);
stack.push(4);
int top = stack.top(); // 访问栈顶元素
stack.pop(); // 出栈(无返回值)
int size = stack.size(); // 栈的长度
bool empty = stack.empty(); // 判断是否为空
基于链表的实现栈
class LinkedListStack {
private:
ListNode *stackTop;
int stkSize;
public:
LinkedListStack(){
stackTop = nullptr;
stkSize = 0;
}
~LinkedListStack() {
freeMemoryLinkedList(stackTop);
}
int size(){
return stkSize;
}
bool isEmpty() {
return size() == 0;
}
void push(int num) {
ListNode *node = new ListNode(num);
node->next = stackTop;
stackTop = node;
stkSize++;
}
void pop(){
int num = top();
ListNode *tmp = stackTop;
stackTop = stackTop->next;
delete tmp;
stkSize--;
}
int top() {
if (isEmpty())
throw out_of_range("栈为空");
return stackTop->val;
}
vector<int> toVector() {
ListNode *node = stackTop;
vector<int> res(size());
for (int i=res.size() - 1; i>=0; i--) {
res[i] = node->val;
node = node->next;
}
return res;
}
};
基于数组的实现
class ArrayStack {
private:
vector<int> stack;
public:
int size(){
return stack.size();
}
bool isEmpty() {
return stack.size() == 0;
}
void push(int num) {
stack.push_back(num);
}
void pop(){
int oldTop = top();
stack.pop_back():
}
int top() {
if(isEmpty())
throw out_of_range("栈为空");
return stack.back();
}
vector<int> toVector(){
return stack;
}
};
队列
queue<int> queue; // 初始化队列
queue.push(1);
queue.push(3);
queue.push(2);
queue.push(5);
queue.push(4);
int front = queue.front();
queue.pop():
int size = queue.size();
bool empty = queue.empty();
基于链表实现队列
/* 基于链表实现的队列 */
class LinkedListQueue {
private:
ListNode *front, *rear; // 头节点 front ,尾节点 rear
int queSize;
public:
LinkedListQueue() {
front = nullptr;
rear = nullptr;
queSize = 0;
}
~LinkedListQueue() {
// 遍历链表删除节点,释放内存
freeMemoryLinkedList(front);
}
/* 获取队列的长度 */
int size() {
return queSize;
}
/* 判断队列是否为空 */
bool isEmpty() {
return queSize == 0;
}
/* 入队 */
void push(int num) {
// 尾节点后添加 num
ListNode *node = new ListNode(num);
// 如果队列为空,则令头、尾节点都指向该节点
if (front == nullptr) {
front = node;
rear = node;
}
// 如果队列不为空,则将该节点添加到尾节点后
else {
rear->next = node;
rear = node;
}
queSize++;
}
/* 出队 */
void pop() {
int num = peek();
// 删除头节点
ListNode *tmp = front;
front = front->next;
// 释放内存
delete tmp;
queSize--;
}
/* 访问队首元素 */
int peek() {
if (size() == 0)
throw out_of_range("队列为空");
return front->val;
}
/* 将链表转化为 Vector 并返回 */
vector<int> toVector() {
ListNode *node = front;
vector<int> res(size());
for (int i = 0; i < res.size(); i++) {
res[i] = node->val;
node = node->next;
}
return res;
}
};
基于数组的实现队列
/* 基于环形数组实现的队列 */
class ArrayQueue {
private:
int *nums; // 用于存储队列元素的数组
int front; // 队首指针,指向队首元素
int queSize; // 队列长度
int queCapacity; // 队列容量
public:
ArrayQueue(int capacity) {
// 初始化数组
nums = new int[capacity];
queCapacity = capacity;
front = queSize = 0;
}
~ArrayQueue() {
delete[] nums;
}
/* 获取队列的容量 */
int capacity() {
return queCapacity;
}
/* 获取队列的长度 */
int size() {
return queSize;
}
/* 判断队列是否为空 */
bool isEmpty() {
return size() == 0;
}
/* 入队 */
void push(int num) {
if (queSize == queCapacity) {
cout << "队列已满" << endl;
return;
}
// 计算队尾指针,指向队尾索引 + 1
// 通过取余操作,实现 rear 越过数组尾部后回到头部
int rear = (front + queSize) % queCapacity;
// 将 num 添加至队尾
nums[rear] = num;
queSize++;
}
/* 出队 */
void pop() {
int num = peek();
// 队首指针向后移动一位,若越过尾部则返回到数组头部
front = (front + 1) % queCapacity;
queSize--;
}
/* 访问队首元素 */
int peek() {
if (isEmpty())
throw out_of_range("队列为空");
return nums[front];
}
/* 将数组转化为 Vector 并返回 */
vector<int> toVector() {
// 仅转换有效长度范围内的列表元素
vector<int> arr(queSize);
for (int i = 0, j = front; i < queSize; i++, j++) {
arr[i] = nums[j % queCapacity];
}
return arr;
}
};
双向队列
deque<int> deque;
deque.push_back(2); // 添加到队尾
deque.push_back(5);
deque.push_back(4);
deque.push_front(3); // 添加到队首
deque.push_front(1);
int front = deque.front();
int back = deque.back();
int size = deque.size();
bool empty = deque.empty();
哈希表
unordered_map<int, string> map; // 初始化
// 添加操作
map[5] = "哈";
map[8] = "希";
map[46] = "表";
string name = map[8]; // 输入键key,得到值value
map.erase(8); // 删除哈希表中的键值对
哈希表的三种遍历方式
/* 遍历哈希表 */
// 遍历键值对 key->value
for (auto kv: map) {
cout << kv.first << " -> " << kv.second << endl;
}
// 单独遍历键 key
for (auto key: map) {
cout << key.first << endl;
}
// 单独遍历值 value
for (auto val: map) {
cout << val.second << endl;
}
哈希表的简单实现
/* 键值对 */
struct Pair {
public:
int key;
string val;
Pair(int key, string val) {
this->key = key;
this->val = val;
}
};
/* 基于数组简易实现的哈希表 */
class ArrayHashMap {
private:
vector<Pair *> buckets;
public:
ArrayHashMap() {
// 初始化数组,包含 100 个桶
buckets = vector<Pair *>(100);
}
~ArrayHashMap() {
// 释放内存
for (const auto &bucket : buckets) {
delete bucket;
}
buckets.clear();
}
/* 哈希函数 */
int hashFunc(int key) {
int index = key % 100;
return index;
}
/* 查询操作 */
string get(int key) {
int index = hashFunc(key);
Pair *pair = buckets[index];
if (pair == nullptr)
return nullptr;
return pair->val;
}
/* 添加操作 */
void put(int key, string val) {
Pair *pair = new Pair(key, val);
int index = hashFunc(key);
buckets[index] = pair;
}
/* 删除操作 */
void remove(int key) {
int index = hashFunc(key);
// 释放内存并置为 nullptr
delete buckets[index];
buckets[index] = nullptr;
}
/* 获取所有键值对 */
vector<Pair *> pairSet() {
vector<Pair *> pairSet;
for (Pair *pair : buckets) {
if (pair != nullptr) {
pairSet.push_back(pair);
}
}
return pairSet;
}
/* 获取所有键 */
vector<int> keySet() {
vector<int> keySet;
for (Pair *pair : buckets) {
if (pair != nullptr) {
keySet.push_back(pair->key);
}
}
return keySet;
}
/* 获取所有值 */
vector<string> valueSet() {
vector<string> valueSet;
for (Pair *pair : buckets) {
if (pair != nullptr) {
valueSet.push_back(pair->val);
}
}
return valueSet;
}
/* 打印哈希表 */
void print() {
for (Pair *kv : pairSet()) {
cout << kv->key << " -> " << kv->val << endl;
}
}
};
二叉树
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
初始化二叉树
TreeNode* n1 = new TreeNode(1);
TreeNode* n2 = new TreeNode(2);
TreeNode* n3 = new TreeNode(3);
TreeNode* n4 = new TreeNode(4);
TreeNode* n5 = new TreeNode(5);
n1->left = n2;
n1->right = n3;
n2->left = n4;
n2->right = n5;
插入和删除节点
TreeNode* P = new TreeNode(0);
// 在n1->n2中间插入P节点
n1->left = P;
P->left = n2;
// 删除P节点
n1->left = n2;
二叉树的遍历
层序遍历
使用队列来实现,队列“先进先出”,层序遍历“逐层推进”。
vector<int> levelOrder(TreeNode *root){
queue<TreeNode *> queue;
queue.push(root);
vector<int> vec;
while(!queue.empty()){
TreeNode *node = queue.front();
queue.pop();
vec.push_back(node_val);
if(node->left != nullptr)
queue.push(node->left);
if(node->right != nullptr)
queue.push(node->right);
}
return vec;
}
前序/中序/后序
void preOrder(TreeNode *root) {
if (root != nullptr)
return ;
// 根左右
vec.push_back(root->val);
preOrder(root->left);
preOrder(root->right);
}
void inOrder(TreeNode *root) {
if (root != nullptr)
return ;
// 左根右
inOrder(root->left);
vec.push_back(root->val);
inOrder(root->right);
}
void postOrder(TreeNode *root) {
if (root != nullptr)
return ;
// 左右根
postOrder(root->left);
postOrder(root->right);
vec.push_back(root->val);
}
二叉树数组表示
/* 二叉树的数组表示 */
// 使用int 最大值 INT_MAX 标记空位
vector<int> tree = {1, 2, 3, 4, INT_MAX, 6, 7, 8, 9, INT_MAX, INT_MAX, 12, INT_MAX, INT_MAX, 15};
class ArrayBinaryTree {
public:
ArrayBinaryTree(vector<int> arr) {
tree = arr;
}
int size(){
return tree.size();
}
int val(int i){
if(i < 0 || i >= size())
return INT_MAX;
return tree[i];
}
int left(int i) {
return 2 * i + 1;
}
int right(int i) {
return 2 * i + 2;
}
int parent(int i) {
return (i - 1) / 2;
}
vector<int> levelOrder() {
vector<int> res;
for(int i=0; i<size(); i++) {
if (val(i) != INT_MAX)
res.push_back(val(i));
}
return res;
}
// 前序
vector<int> preOrder() {
vector<int> res;
dfs(0, "pre", res);
return res;
}
// 中序
vector<int> inOrder(){
vector<int> res;
dfs(0, "in", res);
return res;
}
// 后序
vector<int> postOrder(){
vector<int> res;
dfs(0, "post", res);
return res;
}
private:
vector<int> tree;
void dfs(int i, string order, vector<int> &res) {
// 若为空位,则返回
if (val(i) == INT_MAX)
return;
// 前序遍历
if (order == "pre")
res.push_back(val(i));
dfs(left(i), order, res);
// 中序遍历
if (order == "in")
res.push_back(val(i));
dfs(right(i), order, res);
// 后序遍历
if (order == "post")
res.push_back(val(i));
}
};
二叉搜索树
【左子树的所有节点的值<根节点的值<右子树的所有节点的值】
查找节点
TreeNode *search(int num) {
TreeNode *cur = root;
while (cur != nullptr) {
if(cur->val < num)
cur = cur->right;
else if (cur->val > num)
cur = cur->left;
else
break;
}
return cur;
}
插入节点
void insert(int num) {
if (root == nullptr){
root = new TreeNode(num);
return;
}
TreeNode *cur = root, *pre = nullptr;
while (cur != nullptr) {
// 找到重复节点,直接返回
if (cur->val == num)
return;
pre = cur;
// 插入位置在 cur 的右子树中
if (cur->val < num)
cur = cur->right;
// 插入位置在 cur 的左子树中
else
cur = cur->left;
}
// 插入节点
TreeNode *node = new TreeNode(num);
if (pre->val < num)
pre->right = node;
else
pre->left = node;
}
删除节点
/* 删除节点 */
void remove(int num) {
// 若树为空,直接提前返回
if (root == nullptr)
return;
TreeNode *cur = root, *pre = nullptr;
// 循环查找,越过叶节点后跳出
while (cur != nullptr) {
// 找到待删除节点,跳出循环
if (cur->val == num)
break;
pre = cur;
// 待删除节点在 cur 的右子树中
if (cur->val < num)
cur = cur->right;
// 待删除节点在 cur 的左子树中
else
cur = cur->left;
}
// 若无待删除节点,则直接返回
if (cur == nullptr)
return;
// 子节点数量 = 0 or 1
if (cur->left == nullptr || cur->right == nullptr) {
// 当子节点数量 = 0 / 1 时, child = nullptr / 该子节点
TreeNode *child = cur->left != nullptr ? cur->left : cur->right;
// 删除节点 cur
if (cur != root) {
if (pre->left == cur)
pre->left = child;
else
pre->right = child;
} else {
// 若删除节点为根节点,则重新指定根节点
root = child;
}
// 释放内存
delete cur;
}
// 子节点数量 = 2
else {
// 获取中序遍历中 cur 的下一个节点
TreeNode *tmp = cur->right;
while (tmp->left != nullptr) {
tmp = tmp->left;
}
int tmpVal = tmp->val;
// 递归删除节点 tmp
remove(tmp->val);
// 用 tmp 覆盖 cur
cur->val = tmpVal;
}
}
堆
堆是一种满足特定条件的完全二叉树,主要可分为小顶堆和大顶堆。
堆常用操作
//初始化堆
// 初始化小顶堆
priority_queue<int, vector<int>, greater<int>> minHeap;
// 初始化大顶堆
priority_queue<int, vector<int>, less<int>> maxHeap;
// 元素入堆
maxHeap.push(1);
maxHeap.push(3);
maxHeap.push(2);
maxHeap.push(5);
maxHeap.push(4);
//获取堆顶元素
int peek = maxHeap.top();
// 堆顶元素出堆【出堆元素会形成一个从大到小的序列】
maxHeap.pop(); // 5
maxHeap.pop(); // 4
maxHeap.pop(); // 3
maxHeap.pop(); // 2
maxHeap.pop(); // 1
// 获取堆的大小
int size = maxHeap.size();
// 判断堆是否为空
bool isEmpty = maxHeap.empty();
// 输入列表并建堆
vector<int> input{1, 3, 2, 5, 4};
priority_queue<int, vector<int>, greater<int>> minHeap(input.begin(), input.end());
堆的实现
堆的存储和表示
// 获取左子节点索引
int left(int i) {
return 2 * i + 1;
}
// 获取右子节点索引
int right(int i) {
return 2 * i + 2;
}
// 获取父节点索引
int parent(int i) {
return (i - 1) / 2; // 向下取整
}
访问堆顶元素
int peek() {
return maxHeap[0];
}
元素入堆
void push(int val) {
// 添加节点
maxHeap.push_back(val);
siftUp(size() - 1);
}
void siftUp( int i ) {
while (true) {
int p = parent(i);
if (p < 0 || maxHeap[i] <= maxHeap[p])
break;
swap(maxHeap[i], maxHeap[p]);
i = p;
}
}
堆顶元素出堆
// 元素出堆
void pop(){
if ( isEmpty() ) {
throw out_of_range("堆位空");
}
swap(maxHeap[0], maxHeap[size() - 1]);
// 删除节点
maxHeap.pop_back();
siftDown(0);
}
void siftDown(int i) {
while(true) {
int l = left(i), r = right(i), ma = i;
if (l<size() && maxHeap[l] > maxHeap[ma])
ma = l;
if (r<size() && maxHeap[r] > maxHeap[ma])
ma = r;
if (ma == i)
break;
swap(maxHeap[i], maxHeap[ma]);
i = ma;
}
}
建堆操作
MaxHeap(vector<int> nums) {
maxHeap = nums;
for ( int i = parent(size() - 1); i >= 0; i--) {
siftDown(i);
}
}
图
图基础操作
基于邻接矩阵的实现
// 基于邻接矩阵实现的无向图类
/* 基于邻接矩阵实现的无向图类 */
class GraphAdjMat {
vector<int> vertices; // 顶点列表,元素代表“顶点值”,索引代表“顶点索引”
vector<vector<int>> adjMat; // 邻接矩阵,行列索引对应“顶点索引”
public:
/* 构造方法 */
GraphAdjMat(const vector<int> &vertices, const vector<vector<int>> &edges) {
// 添加顶点
for (int val : vertices) {
addVertex(val);
}
// 添加边
// 请注意,edges 元素代表顶点索引,即对应 vertices 元素索引
for (const vector<int> &edge : edges) {
addEdge(edge[0], edge[1]);
}
}
/* 获取顶点数量 */
int size() const {
return vertices.size();
}
/* 添加顶点 */
void addVertex(int val) {
int n = size();
// 向顶点列表中添加新顶点的值
vertices.push_back(val);
// 在邻接矩阵中添加一行
adjMat.emplace_back(vector<int>(n, 0));
// 在邻接矩阵中添加一列
for (vector<int> &row : adjMat) {
row.push_back(0);
}
}
/* 删除顶点 */
void removeVertex(int index) {
if (index >= size()) {
throw out_of_range("顶点不存在");
}
// 在顶点列表中移除索引 index 的顶点
vertices.erase(vertices.begin() + index);
// 在邻接矩阵中删除索引 index 的行
adjMat.erase(adjMat.begin() + index);
// 在邻接矩阵中删除索引 index 的列
for (vector<int> &row : adjMat) {
row.erase(row.begin() + index);
}
}
/* 添加边 */
// 参数 i, j 对应 vertices 元素索引
void addEdge(int i, int j) {
// 索引越界与相等处理
if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) {
throw out_of_range("顶点不存在");
}
// 在无向图中,邻接矩阵沿主对角线对称,即满足 (i, j) == (j, i)
adjMat[i][j] = 1;
adjMat[j][i] = 1;
}
/* 删除边 */
// 参数 i, j 对应 vertices 元素索引
void removeEdge(int i, int j) {
// 索引越界与相等处理
if (i < 0 || j < 0 || i >= size() || j >= size() || i == j) {
throw out_of_range("顶点不存在");
}
adjMat[i][j] = 0;
adjMat[j][i] = 0;
}
/* 打印邻接矩阵 */
void print() {
cout << "顶点列表 = ";
printVector(vertices);
cout << "邻接矩阵 =" << endl;
printVectorMatrix(adjMat);
}
};
基于领接表的实现
/* 基于邻接表实现的无向图类 */
class GraphAdjList {
public:
// 邻接表,key: 顶点,value:该顶点的所有邻接顶点
unordered_map<Vertex *, vector<Vertex *>> adjList;
/* 在 vector 中删除指定节点 */
void remove(vector<Vertex *> &vec, Vertex *vet) {
for (int i = 0; i < vec.size(); i++) {
if (vec[i] == vet) {
vec.erase(vec.begin() + i);
break;
}
}
}
/* 构造方法 */
GraphAdjList(const vector<vector<Vertex *>> &edges) {
// 添加所有顶点和边
for (const vector<Vertex *> &edge : edges) {
addVertex(edge[0]);
addVertex(edge[1]);
addEdge(edge[0], edge[1]);
}
}
/* 获取顶点数量 */
int size() {
return adjList.size();
}
/* 添加边 */
void addEdge(Vertex *vet1, Vertex *vet2) {
if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2)
throw invalid_argument("不存在顶点");
// 添加边 vet1 - vet2
adjList[vet1].push_back(vet2);
adjList[vet2].push_back(vet1);
}
/* 删除边 */
void removeEdge(Vertex *vet1, Vertex *vet2) {
if (!adjList.count(vet1) || !adjList.count(vet2) || vet1 == vet2)
throw invalid_argument("不存在顶点");
// 删除边 vet1 - vet2
remove(adjList[vet1], vet2);
remove(adjList[vet2], vet1);
}
/* 添加顶点 */
void addVertex(Vertex *vet) {
if (adjList.count(vet))
return;
// 在邻接表中添加一个新链表
adjList[vet] = vector<Vertex *>();
}
/* 删除顶点 */
void removeVertex(Vertex *vet) {
if (!adjList.count(vet))
throw invalid_argument("不存在顶点");
// 在邻接表中删除顶点 vet 对应的链表
adjList.erase(vet);
// 遍历其他顶点的链表,删除所有包含 vet 的边
for (auto &adj : adjList) {
remove(adj.second, vet);
}
}
/* 打印邻接表 */
void print() {
cout << "邻接表 =" << endl;
for (auto &adj : adjList) {
const auto &key = adj.first;
const auto &vec = adj.second;
cout << key->val << ": ";
printVector(vetsToVals(vec));
}
}
};
图的遍历
广度优先搜索
/* 广度优先遍历 BFS */
// 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点
vector<Vertex *> graphBFS(GraphAdjList &graph, Vertex *startVet) {
// 顶点遍历序列
vector<Vertex *> res;
// 哈希表,用于记录已被访问过的顶点
unordered_set<Vertex *> visited = {startVet};
// 队列用于实现 BFS
queue<Vertex *> que;
que.push(startVet);
// 以顶点 vet 为起点,循环直至访问完所有顶点
while (!que.empty()) {
Vertex *vet = que.front();
que.pop(); // 队首顶点出队
res.push_back(vet); // 记录访问顶点
// 遍历该顶点的所有邻接顶点
for (auto adjVet : graph.adjList[vet]) {
if (visited.count(adjVet))
continue; // 跳过已被访问过的顶点
que.push(adjVet); // 只入队未访问的顶点
visited.emplace(adjVet); // 标记该顶点已被访问
}
}
// 返回顶点遍历序列
return res;
}
深度优先搜索
/* 深度优先遍历 DFS 辅助函数 */
void dfs(GraphAdjList &graph, unordered_set<Vertex *> &visited, vector<Vertex *> &res, Vertex *vet) {
res.push_back(vet); // 记录访问顶点
visited.emplace(vet); // 标记该顶点已被访问
// 遍历该顶点的所有邻接顶点
for (Vertex *adjVet : graph.adjList[vet]) {
if (visited.count(adjVet))
continue; // 跳过已被访问过的顶点
// 递归访问邻接顶点
dfs(graph, visited, res, adjVet);
}
}
/* 深度优先遍历 DFS */
// 使用邻接表来表示图,以便获取指定顶点的所有邻接顶点
vector<Vertex *> graphDFS(GraphAdjList &graph, Vertex *startVet) {
// 顶点遍历序列
vector<Vertex *> res;
// 哈希表,用于记录已被访问过的顶点
unordered_set<Vertex *> visited;
dfs(graph, visited, res, startVet);
return res;
}