/** * 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 #include #include #include /* Find an element in a vector */ // 在一个 vector 中查找元素 template int vecFind(const vector &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 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 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 string getVectorString(vector &list) // list 是 vector { return "[" + strJoin(", ", list) + "]"; // 使用 strJoin 函数将 vector 用逗号连接成字符串,并在字符串两端添加 '[' 和 ']' } /* Print a vector */ // 打印一个 vector template void printVector(vector list) { cout << getVectorString(list) << '\n'; // 使用 getVectorString 函数获取 vector 的字符串表示,并打印 } /* Print a vector matrix */ // 打印一个 vector 矩阵 template void printVectorMatrix(vector> &matrix) // matrix 是一个二维 vector, 其中每个元素都是一个 vector, 用于表示矩阵 (数组的数组) { cout << "[" << '\n'; for (vector &list : matrix) // 遍历 matrix 中的每一个 vector 元素 cout << " " + getVectorString(list) + "," << '\n'; // 使用 getVectorString 函数获取 vector 的字符串表示,并打印 cout << "]" << '\n'; } /* Print a linked list */ // 打印一个链表 void printLinkedList(ListNode *head) { vector 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 void printStack(stack stk) { // Reverse the input stack // 反转输入的栈 stack 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 void printQueue(queue 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 void printDeque(deque 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 HashMap */ // 打印一个哈希表 // 定义模板参数 TKey 和 TValue ,用于指定键值对的类型 template void printHashMap(unordered_map 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 S &Container(priority_queue &pq) // pq 是优先队列 { struct HackedQueue : private priority_queue // 创建一个私有继承的优先队列 { static S &Container(priority_queue &pq) // 创建一个静态成员函数 Container { return pq.*&HackedQueue::c; // 返回优先队列的底层存储 } }; return HackedQueue::Container(pq); // 调用静态成员函数 Container } /* Print a Heap (PriorityQueue) */ // 打印一个堆(优先队列) template void printHeap(priority_queue &heap) // heap 是优先队列 { vector vec = Container(heap); // 获取优先队列的底层存储 cout << "堆的数组表示:"; printVector(vec); cout << "堆的树状表示:" << endl; TreeNode *root = vectorToTree(vec); // 新建一个二叉树 root,将优先队列的底层存储转换为二叉树 printTree(root); freeMemoryTree(root); }