cpp-algo-cases/utils/print_utils.hpp

311 lines
9.5 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* File: print_utils.hpp
* Created Time: 2021-12-19
* Author: krahets (krahets@163.com), msk397 (machangxinq@gmail.com), LoneRanger(836253168@qq.com)
*/
#pragma once
#include "list_node.hpp"
#include "tree_node.hpp"
#include <climits>
#include <iostream>
#include <sstream>
#include <string>
/* Find an element in a vector */ // 在一个 vector 中查找元素
template <typename T>
int vecFind(const vector<T> &vec, T ele)
{
int j = INT_MAX;
for (int i = 0; i < vec.size(); i++)
{
if (vec[i] == ele) // 当找到元素时,返回元素的下标
{
j = i;
}
}
return j;
}
/* Concatenate a vector with a delim */ // 将一个 vector 用指定的分隔符连接成字符串
template <typename T>
string strJoin(const string &delim, const T &vec) // delim 是分隔符vec 是 vector(使用&vec表示传递的是引用, 而不是拷贝, 以提高效率)
{
ostringstream s; // 创建一个输出流对象
for (const auto &i : vec)
{
if (&i != &vec[0])
{
s << delim; // 将分隔符添加到输出流对象中
}
s << i; // 将元素添加到输出流对象中
}
return s.str(); // .str() 方法是输出流对象的成员函数,用于返回输出流对象的字符串表示
}
/* Repeat a string for n times */ // 将一个字符串重复 n 次
string strRepeat(string str, int n)
{
ostringstream os; // 创建一个输出流对象
for (int i = 0; i < n; i++)
os << str; // 将字符串重复 n 次
return os.str(); // 返回输出流对象的字符串表示
}
/* Print an Array */ // 打印一个数组
template <typename T>
void printArray(T *arr, int n) // arr 是数组的首地址n 是数组的长度
{
cout << "[";
for (int i = 0; i < n - 1; i++)
{
cout << arr[i] << ", "; // 逐个打印数组元素
}
if (n >= 1) // 当数组长度大于等于 1 时
cout << arr[n - 1] << "]" << endl; // 打印数组最后一个元素 并 附加一个 ']' 符号
else
cout << "]" << endl; // 当数组长度为 0 时,直接打印一个 ']' 符号
}
/* Get the Vector String object */ // 获取一个 vector 的字符串表示
template <typename T>
string getVectorString(vector<T> &list) // list 是 vector
{
return "[" + strJoin(", ", list) + "]"; // 使用 strJoin 函数将 vector 用逗号连接成字符串,并在字符串两端添加 '[' 和 ']'
}
/* Print a vector */ // 打印一个 vector
template <typename T>
void printVector(vector<T> list)
{
cout << getVectorString(list) << '\n'; // 使用 getVectorString 函数获取 vector 的字符串表示,并打印
}
/* Print a vector matrix */ // 打印一个 vector 矩阵
template <typename T>
void printVectorMatrix(vector<vector<T>> &matrix) // matrix 是一个二维 vector, 其中每个元素都是一个 vector, 用于表示矩阵 (数组的数组)
{
cout << "[" << '\n';
for (vector<T> &list : matrix) // 遍历 matrix 中的每一个 vector 元素
cout << " " + getVectorString(list) + "," << '\n'; // 使用 getVectorString 函数获取 vector 的字符串表示,并打印
cout << "]" << '\n';
}
/* Print a linked list */ // 打印一个链表
void printLinkedList(ListNode *head)
{
vector<int> list; // 创建一个 vector 用于存储链表的元素
while (head != nullptr) // 遍历链表: 当链表的指针不为空时, 继续遍历
{
list.push_back(head->val); // 将链表的元素添加到 vector 中
head = head->next; // 将指针指向链表的下一个元素
}
cout << strJoin(" -> ", list) << '\n'; // 使用 strJoin 函数将 vector 用箭头连接成字符串,并打印
}
/**
* This tree printer is borrowed from TECHIE DELIGHT // 该树打印器是从 TECHIE DELIGHT 借用的
* https://www.techiedelight.com/c-program-print-binary-tree/
*/
struct Trunk // 树干结构体
{
Trunk *prev; // 指向上一个树干
string str; // 树干的字符串表示
Trunk(Trunk *prev, string str) // 构造函数 cpp 中的 struct 也可以有构造函数)
{
this->prev = prev;
this->str = str;
}
};
/* Helper function to print branches of the binary tree */ // 用于打印二叉树的辅助函数
void showTrunks(Trunk *p)
{
if (p == nullptr) // 当树干为空时,直接返回
{
return;
}
// 递归调用的顺序与打印的顺序相反
showTrunks(p->prev); // 递归调用 showTrunks 函数
cout << p->str; // 打印树干的字符串表示
}
/* Print a binary tree */ // 打印一个二叉树
void printTree(TreeNode *root, Trunk *prev, bool isRight)
{
if (root == nullptr)
{
return;
}
string prev_str = " "; // 初始化 prev_str 为空格
Trunk trunk(prev, prev_str); // 创建一个树干对象
printTree(root->right, &trunk, true); // 递归调用 printTree 函数,打印右子树
if (!prev) // 当树干为空时
{
trunk.str = "———"; // 树干的字符串表示为 "———"
}
else if (isRight) // 当树干不为空且是右子树时
{
trunk.str = "/———"; // 树干的字符串表示为 "/———"
prev_str = " |"; // prev_str 为 " |"
}
else // 当树干不为空且不是右子树时
{
trunk.str = "\\———"; // 树干的字符串表示为 "\———"
prev->str = prev_str; // prev_str 为 prev->str
}
showTrunks(&trunk); // 调用 showTrunks 函数,打印树干
cout << " " << root->val << endl; // 打印节点的值
if (prev) // 当树干不为空时
{
prev->str = prev_str; // prev->str 为 prev_str
}
trunk.str = " |"; // 树干的字符串表示为 " |"
printTree(root->left, &trunk, false); // 递归调用 printTree 函数,打印左子树
}
/* The interface of the tree printer */ // 二叉树打印器的接口 (函数重载: 目的是为了方便调用)
void printTree(TreeNode *root)
{
printTree(root, nullptr, false); // 调用 printTree 函数 (重载版本)
}
/* Print a stack */ // 打印一个栈
template <typename T>
void printStack(stack<T> stk)
{
// Reverse the input stack // 反转输入的栈
stack<T> tmp; // 创建一个临时栈
while (!stk.empty()) // 当输入的栈不为空时 (遍历输入的栈)
{
tmp.push(stk.top()); // 将输入的栈的元素逐个添加到临时栈中
stk.pop(); // 弹出输入的栈的元素
}
// Generate the string to print // 生成要打印的字符串
ostringstream s; // 创建一个输出流对象
bool flag = true; // 创建一个标志位 (默认为 true), 用来标记是否是第一个元素
while (!tmp.empty())
{
if (flag)
{
s << tmp.top(); // 当标志位为 true 时,将栈顶元素添加到输出流对象中
flag = false; // 将标志位设置为 false
}
else // 当标志位为 false 时
s << ", " << tmp.top(); // 将栈顶元素添加到输出流对象中
tmp.pop(); // 弹出栈顶元素
}
cout << "[" + s.str() + "]" << '\n'; // 使用输出流对象的字符串表示,并打印
}
/* Print a queue */ // 打印一个队列
template <typename T>
void printQueue(queue<T> queue) // queue 是队列
{
// Generate the string to print // 生成要打印的字符串
ostringstream s;
bool flag = true;
while (!queue.empty())
{
if (flag)
{
s << queue.front(); // 当标志位为 true 时,将队列的队首元素添加到输出流对象中 (true 表示是第一个元素)
flag = false;
}
else
s << ", " << queue.front(); // 当标志位为 false 时,将队列的队首元素前面添加一个逗号,并添加到输出流对象中
queue.pop(); // 弹出队首元素
}
cout << "[" + s.str() + "]" << '\n'; // 使用输出流对象的字符串表示,并打印
}
/* Print a deque */ // 打印一个双端队列
template <typename T>
void printDeque(deque<T> deque) // deque 是双端队列(念做 deck)
{
// Generate the string to print // 生成要打印的字符串
ostringstream s;
bool flag = true;
while (!deque.empty()) // 当双端队列不为空时
{
if (flag)
{
s << deque.front();
flag = false;
}
else
s << ", " << deque.front();
deque.pop_front(); // 弹出队首元素
}
cout << "[" + s.str() + "]" << '\n';
}
/* Print a deque from back*/ // 从后往前打印一个双端队列
template <typename T>
void printDequeFromBack(deque<T> deque) // deque 是双端队列(念做 deck)
{
// Generate the string to print // 生成要打印的字符串
ostringstream s;
bool flag = true;
while (!deque.empty()) // 当双端队列不为空时
{
if (flag)
{
s << deque.back();
flag = false;
}
else
s << ", " << deque.back();
deque.pop_back(); // 弹出队首元素
}
cout << "[" + s.str() + "]" << '\n';
}
/* Print a HashMap */ // 打印一个哈希表
// 定义模板参数 TKey 和 TValue ,用于指定键值对的类型
template <typename TKey, typename TValue>
void printHashMap(unordered_map<TKey, TValue> map) // map 是哈希表
{
for (auto kv : map) // auto 关键字用于自动推导变量的类型
{
cout << kv.first << " -> " << kv.second << '\n'; // .first 表示键,.second 表示值
}
}
/* Expose the underlying storage of the priority_queue container */ // 暴露优先队列容器的底层存储
template <typename T, typename S, typename C>
S &Container(priority_queue<T, S, C> &pq) // pq 是优先队列
{
struct HackedQueue : private priority_queue<T, S, C> // 创建一个私有继承的优先队列
{
static S &Container(priority_queue<T, S, C> &pq) // 创建一个静态成员函数 Container
{
return pq.*&HackedQueue::c; // 返回优先队列的底层存储
}
};
return HackedQueue::Container(pq); // 调用静态成员函数 Container
}
/* Print a Heap (PriorityQueue) */ // 打印一个堆(优先队列)
template <typename T, typename S, typename C>
void printHeap(priority_queue<T, S, C> &heap) // heap 是优先队列
{
vector<T> vec = Container(heap); // 获取优先队列的底层存储
cout << "堆的数组表示:";
printVector(vec);
cout << "堆的树状表示:" << endl;
TreeNode *root = vectorToTree(vec); // 新建一个二叉树 root将优先队列的底层存储转换为二叉树
printTree(root);
freeMemoryTree(root);
}