赞
踩
#include <iostream> #include <vector> using namespace std; // 节点结构 struct ListNode { int val; ListNode* next; ListNode(int x) :val(x), next(NULL) {} }; // 创建链表 ListNode* Creat(vector<int>& arr) { ListNode* p, * dummy, * pre; // 注意这种连续初始化的方式! dummy = new ListNode(0); // 给一个初始值0(虚拟头节点) pre = dummy; for (int i = 0; i < arr.size(); ++i) { p = new ListNode(arr[i]); pre->next = p; pre = p; } return dummy->next; } int main() { vector<int> arr = { 5,4,3,2,1,0 }; ListNode* L = Creat(arr); // 打印链表 while (L != NULL) { cout << L->val; L = L->next; } return 0; }
struct ListNode { int val; ListNode* prev; ListNode* next; ListNode(int v) :val(v), prev(nullptr), next(nullptr) {} }; class DoubleList { public: DoubleList() { head = new ListNode(0); tail = new ListNode(0); head->next = tail; tail->prev = head; } // 在链表尾部添加节点x,时间 O(1) void AddList(ListNode* x) { x->prev = tail->prev; x->next = tail; tail->prev->next = x; tail->prev = x; } // 删除链表中的 x 节点 void Remove(ListNode* x) { x->next->prev = x->prev; x->prev->next = x->next; } private: ListNode* head; ListNode* tail; };
// 创建节点 struct Node { int key, val; Node* prev; // 前驱节点 Node* next; // 后驱节点 Node(int k, int v) :key(k), val(v), prev(nullptr), next(nullptr) {} }; // 创建双向链表类 class DoubleList { public: DoubleList() { head = new Node(0, 0); tail = new Node(0, 0); head->next = tail; tail->prev = head; size = 0; } void addList(Node* x) { // 在链表尾部添加节点x,时间 O(1) x->prev = tail->prev; x->next = tail; tail->prev->next = x; tail->prev = x; size++; } void remove(Node* x) { // 由于是双链表且给的是目标 Node 节点,时间 O(1) x->next->prev = x->prev; x->prev->next = x->next; size--; } Node* removeFirst() { // 自定义函数:删除链表中第一个节点,并返回该节点,时间 O(1) if (head->next == tail) // 若无插入,则默认不移除,返回空指针 return nullptr; Node* first = head->next; remove(first); return first; } int sizeOfList() { // 返回链表长度,时间 O(1) return size; } private: Node* head; // 双向链表头节点 Node* tail; // 双向链表尾节点 int size; }; class LRUCache { public: LRUCache(int capacity) :cap(capacity) {} int get(int key) { if (!hashMap.count(key)) { return -1; } makeRecently(key); // 将该数据提升为最近使用的 return hashMap[key]->val; } void put(int key, int value) { if (hashMap.count(key)) { //makeRecently(key); //不能这样写!提升key操作不会更改hashmap中键key对应的值val,而此处put需要更新val(即使遇到已经存在的索引key) deleteKey(key); // 删除旧的数据 addRecently(key, value); // 新插入的数据为最近使用的数据 return; } if (cache.sizeOfList() == cap) { // 若容量已满,需要删除最久未使用的key removeLeastRecently(); } addRecently(key, value); } private: /*--------私有方法:哈希链表的抽象API--------*/ void makeRecently(int key) { // 将某个 key 提升为最近使用 ——> 在 get 方法中,使用(获取)需要将 key 键值对提升为最近使用 Node* x = hashMap[key]; cache.remove(x); // 先从链表中删除这个节点 cache.addList(x); // 重新插到队尾 } void addRecently(int key, int val) { // 添加最近使用的元素 ——> 在 put 方法中,修改 or 插入时都需要该添加操作 Node* x = new Node(key, val); cache.addList(x); // 链表尾部就是最近使用的元素 hashMap.insert(make_pair(key, x)); // 注意:记得在哈希表中添加映射!!! } void deleteKey(int key) { // 删除某个key ——> 在 put 方法中,修改时需要先删除 Node* x = hashMap[key]; cache.remove(x); // 从链表中删除 hashMap.erase(key); // 从 map 中删除 } // 删除最久未使用的元素 ——> 在 put 方法中,插入时需要先验证容量是否满,若满,则需要移除最久没使用的 void removeLeastRecently() { Node* x = cache.removeFirst(); // 链表头部的第一个元素就是最久未使用的 int deleteKey = x->key; hashMap.erase(deleteKey); // 注意:别忘了从 map 中删除它的 key!!! } private: unordered_map<int, Node*> hashMap; DoubleList cache; int cap; };
#include <iostream> #include <vector> #include <queue> using namespace std; // 节点结构 struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; // 根据数组构造二叉树 TreeNode* ConstructBinaryTree(const vector<int>& vec) { vector<TreeNode*> vecTree(vec.size(), NULL); // 注意:初始化指针置空 TreeNode* root = NULL; for (int i = 0; i < vec.size(); i++) { TreeNode* node = NULL; // 初始化为空节点 if (vec[i] != -1) { node = new TreeNode(vec[i]); // 非空节点 } vecTree[i] = node; // 赋值节点 if (i == 0) { // 找到根节点 root = node; } } for (int i = 0; i * 2 + 2 < vec.size(); i++) { if (vecTree[i] != NULL) { vecTree[i]->left = vecTree[i * 2 + 1]; vecTree[i]->right = vecTree[i * 2 + 2]; } } return root; } int main() { // 用 -1 来表示null,注意:本代码没有考虑输入异常数据的情况 vector<int> vec = { 4,1,6,0,2,5,7,-1,-1,-1,3,-1,-1,-1,8 }; // 代表的其实是:二叉树的层序遍历 TreeNode* root = ConstructBinaryTree(vec); }
/* ----【深度优先遍历-DFS】的统一迭代法(标志位法)框架---- */ vector<int> postorderTraversal(TreeNode* root) { vector<int> result; stack<TreeNode*> st; //利用:栈---先进后出 if (root != NULL) st.push(root); while (!st.empty()) { TreeNode* node = st.top(); if (node != NULL) { st.pop(); //将该访问的中间节点弹出,避免重复操作,下面会重新再将该中间节点添加到栈中 //前序遍历(中左右)(遵循:入栈顺序和遍历顺序相反!) if (node->right) st.push(node->right); // 右 if (node->left) st.push(node->left); // 左 st.push(node); // 中 st.push(NULL); //中序遍历(左中右) if (node->right) st.push(node->right); // 右 st.push(node); // 中 st.push(NULL); if (node->left) st.push(node->left); // 左 //后续遍历(左右中) st.push(node); // 中 st.push(NULL); if (node->right) st.push(node->right); // 右 if (node->left) st.push(node->left); // 左 } else { st.pop(); // 将空节点弹出 node = st.top(); // 取出访问的中间元素 st.pop(); // 节点处理逻辑语句{.....}!!!根据题意修改! result.push_back(node->val); } } return result; } /* ----【广度优先遍历-BFS(层级遍历)】的迭代法框架(关键!!!)---- */ vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> result; queue<TreeNode*> que; //利用:队列---先进先出 if (root != NULL) que.push(root); // while 循环控制一层一层往下走,for 循环利用 size 变量控制从[左到右]遍历每一层二叉树节点。 while (!que.empty()) { int size = que.size(); //注意! vector<int> vec; // 这里一定要使用先固定大小size,不要使用que.size(),因为que.size在for中是不断变化的! for (int i = 0; i < size; i++) { TreeNode* node = que.front(); que.pop(); vec.push_back(node->val); // 划重点:根据题意修改!eg:这里是遍历,存储当前节点值 if (node->left) que.push(node->left); if (node->right) que.push(node->right); } result.push_back(vec); // 划重点:更新整棵树的遍历结果在这里 } return result; }
class Quick { public: void sort(vector<int>& nums) { shuffle(nums); // 洗牌算法,将输入的数组随机打乱,避免出现耗时的极端情况 sort(nums, 0, nums.size() - 1); // 排序整个数组(原地修改) } private: void sort(vector<int>& nums, int low, int high) { if (low >= high) return; // 注意:根据partition函数,p的范围:[low,high],故递归sort的判断应该是>=而非== int p = partition(nums, low, high); // 对 nums[lo..hi] 进行切分,使得 nums[lo..p-1] <= nums[p] < nums[p+1..hi] sort(nums, low, p - 1); sort(nums, p + 1, high); } // 对 nums[lo..hi] 进行切分(关键函数!!!必背!) int partition(vector<int>& nums, int low, int high) { int pivot = nums[low]; int i = low + 1, j = high; // 尤其注意:把 i, j 定义为开区间,同时定义:[lo, i) <= pivot;(j, hi] > pivot // 当 i > j 时结束循环,以保证区间 [lo, hi] 都被覆盖 while (i <= j) { while (i < high && nums[i] <= pivot) { // 此 while 结束时恰好 nums[i] > pivot i++; } while (j > low && nums[j] > pivot) { // 此 while 结束时恰好 nums[j] <= pivot j--; } if (i >= j) break; // 如果走到这里,一定有:nums[i] > pivot && nums[j] < pivot // 所以需要交换 nums[i] 和 nums[j],保证 nums[low..i] < pivot < nums[j..high] swap(nums[i], nums[j]); } swap(nums[low], nums[j]); // 将 pivot(即:nums[low]) 放到合适的位置,即 pivot 左边元素较小,右边元素较大 return j; } void shuffle(vector<int>& nums) { // 洗牌算法实现 int n = nums.size(); for (int i = 0; i < n; i++) { int randIndex = rand() % (n - i) + i; // 生成 [i, n - 1] 的随机数 swap(nums[i], nums[randIndex]); } } };
class Merge { public: // 留出调用接口 void sort(vector<int>& nums) { temp.resize(nums.size()); // 先给辅助数组开辟内存空间 sort(nums, 0, nums.size() - 1); // 排序整个数组(原地修改) } private: vector<int> temp; // 用于辅助合并有序数组 // 定义:将子数组 nums[lo..hi] 进行排序 void sort(vector<int>& nums, int low, int high) { if (low == high) return; // 单个元素不用排序 int mid = low + (high - low) / 2; // 这样写是为了防止溢出,效果等同于 (hi + lo) / 2 sort(nums, low, mid); // 先对【左半部分】数组 nums[lo..mid] 排序 sort(nums, mid + 1, high); // 再对【右半部分】数组 nums[mid+1..hi] 排序 merge(nums, low, mid, high); // 将两部分有序数组【合并】成一个【有序】数组 } // 将 nums[lo..mid] 和 nums[mid+1..hi] 这两个有序数组【合并】成一个【有序】数组(关键函数!!!必背!) void merge(vector<int>& nums, int low, int mid, int high) { for (int i = low; i <= high; i++) { temp[i] = nums[i]; // 先把 nums[lo..hi] 复制到辅助数组中,以便合并后结果能直接存入 nums } // 数组双指针技巧,合并两个有序数组 int i = low, j = mid + 1; for (int p = low; p <= high; p++) { // 注意:次序不能搞错!先判断左or右半边数组是否已经被合并,然后再判断元素大小 if (i == mid + 1) { nums[p] = temp[j++]; // 左半边数组已全部被合并 } else if (j == high + 1) { nums[p] = temp[i++]; // 右半边数组已全部被合并 } else if (temp[i] > temp[j]) { nums[p] = temp[j++]; } else { // temp[i] <= temp[j] nums[p] = temp[i++]; } } } };
// 原始版本(如果 n == a.size(),则是对整个数组a排序) void bubbleSort(vector<int>& a, int n) { for (int i = 0; i < n - 1 ; ++i) { // 对于数组a的前n个元素,排序n - 1轮 for (int j = 0; j < n - 1 - i; ++j) { // 每一轮分别进行n - 1、n - 2...1(= n - 1 - i)次循环 if (a[j] < a[j + 1]) // 降序!若改为升序,则:a[j] > a[j + 1] swap(a[j], a[j + 1]); } } } // 优化版本 void bubbleSort(vector<int>& a) { int n = a.size(); bool flag = false; for (int i = 0; i < n - 1; ++i) { // 对于数组a的共n个元素,排序n - 1轮 flag = false; for (int j = 0; j < n - 1 - i; ++j) { if (a[j] < a[j + 1]) { // 降序!若改为升序,则:a[j] > a[j + 1] // 某一趟排序中,只要发生一次元素交换,flag就从false变为了true // 也即表示这一趟排序还不能确定所剩待排序列是否已经有序,应继续下一趟循环 flag = true; swap(a[j], a[j + 1]); } } // 但若某一趟中一次元素交换都没有,即依然为flag = false,那么表明所剩待排序列已经有序 // 之后就不必再进行趟数比较,外层循环应该结束,即此时if (!flag) break; 跳出循环 if (!flag) { break; } } }
vector<int> vecRaw = { 0,5,7,9,6,3,4,5,2, }; vector<int> vecObj(vecRaw.size(), 0); void CountSort(vector<int>& vecRaw, vector<int>& vecObj) { if (vecRaw.size() == 0) // 确保待排序容器非空 return; int vecCountLength = (*max_element(begin(vecRaw), end(vecRaw))) + 1; // 使用 vecRaw 的最大值 + 1 作为计数容器 countVec 的大小 vector<int> vecCount(vecCountLength, 0); for (int i = 0; i < vecRaw.size(); i++) // 统计每个键值出现的次数 vecCount[vecRaw[i]]++; for (int i = 1; i < vecCountLength; i++) // 后面的键值出现的位置为前面所有键值出现的次数之和 vecCount[i] += vecCount[i - 1]; for (int i = vecRaw.size(); i > 0; i--) // 将键值放到目标位置,此处逆序是为了保持相同键值的稳定性 vecObj[--vecCount[vecRaw[i - 1]]] = vecRaw[i - 1]; }
class MaxHeap { public: MaxHeap() { max_heap.resize(1); // 新建一个初始空位,使得:父节点索引i对应子节点索引为2*i、2*i+1 } // 构建最大堆则从 size/2 开始进行下沉 void Sink(int sink_node, int size) { // 下沉操作 int parent = sink_node; while (parent * 2 <= size) { // 二叉树性质:左孩子为 son=2*parent int son = 2 * parent; if (son + 1 <= size && max_heap[son] < max_heap[son + 1]) { // son+1<=size 表:有右孩子 且 右孩子更大,索引=son+1=2*parent+1 son++; // 指向最大孩子 } if (max_heap[parent] >= max_heap[son]) { // 下沉结点比左右孩子节点最大值还大,表:下沉结束,直接退出 return; } else { // 下沉结点不是最大,交换 swap(max_heap[parent], max_heap[son]); } parent = son; // 继续下沉 } } void Rise(int size) { // 一直上浮直到满足最大堆或者到达根 while (size > 1 && max_heap[size] > max_heap[size / 2]) { swap(max_heap[size], max_heap[size / 2]); size /= 2; } } void Pop() { // 首尾交换 -> 弹出尾元素 -> 调整最大堆(下沉) max_heap[1] = max_heap[max_heap.size() - 1]; max_heap.pop_back(); Sink(1, max_heap.size() - 1); // 转发,下沉结点, 大小 } void Push(int val) { max_heap.push_back(val); Rise(max_heap.size() - 1); // 最后一个结点进行上浮 } bool Empty() { return max_heap.size() == 1; } int Top() { return max_heap[1]; } private: vector<int> max_heap; }; // 测试代码如下: int main() { // 操作次数和存放指令 int count; string op; cin >> count; cin.get(); // 读取缓冲的换行符 MaxHeap maxheap; // 大根堆的数据结构 while (--count >= 0) { getline(cin, op); // 读取空格! if (op.substr(0, 4) == "push") { int val = stoi(op.substr(5)); maxheap.Push(val); } if (maxheap.Empty()) { // 空直接跳过本次循环(一位多余) cout << "empty" << endl; continue; } if (op.substr(0, 3) == "top") { cout << maxheap.Top() << endl; } if (op.substr(0, 3) == "pop") { maxheap.Pop(); } } return 0; }
void HeapSortSolution() { // 1: 先将待排序的数视作完全二叉树(按层次遍历顺序进行编号, 从0开始) vector<int> arr = { 3,4,2,1,5,8,7,6 }; HeapSort(arr, arr.size()); } void HeapSort(vector<int>& arr, int len) { // 将得到的无序序列转化为一个【无序大顶堆】,i从最后一个父节点开始调整。无序大顶堆:满足大顶堆性质,但堆数组不是有序的! for (int i = len / 2 - 1; i >= 0; i--) { Adjust(arr, i, len - 1); } // 在输出堆顶元素之后(完全二叉树的树根结点),如何调整剩余元素构建一个新的堆?结论:构建大顶堆--排序结果升序,构建小顶堆--排序结果降序 for (int i = len - 1; i > 0; i--) { swap(arr[0], arr[i]); // 新无序区arr[0...i-1]和新有序区arr[i...len-1] ——> 该有序区【升序】!因为每次取一个【无序大顶堆】的堆顶元素! Adjust(arr, 0, i - 1); // 将无序区arr[0...i-1]构建成一个新的【无序大顶堆】 } } void Adjust(vector<int>& arr, int start, int end) { // 依据之前构建【大顶堆】的方法,进行相应的下沉操作 --- 修改为:(1)、(2)就是【小顶堆】 int parent = start; while (parent * 2 + 1 <= end) { int son = parent * 2 + 1; if (son + 1 <= end && arr[son] < arr[son + 1]) { // 改为:arr[son] > arr[son + 1] --- (1) son++; } if (arr[parent] >= arr[son]) { // 改为:arr[parent] < arr[son] --- (2) return; } else { swap(arr[parent], arr[son]); } parent = son; } }
class Trie { private: vector<Trie*> children; bool isEnd; public: Trie() : children(26), isEnd(false) {} void insert(string word) { // 插入:向 Trie 中插入一个单词 word Trie* node = this; for (char ch : word) { if (node->children[ch - 'a'] == nullptr) { node->children[ch - 'a'] = new Trie(); // 一直匹配到前缀链上没有对应的字符,这时开始不断开辟新的结点 } node = node->children[ch - 'a']; } node->isEnd = true; // 直到插入完 word 的最后一个字符,并将最后一个结点isEnd = true; } Trie* searchPrefix(string prefix) { // 查找前缀的方法 Trie* node = this; for (char ch : prefix) { if (node->children[ch - 'a'] == nullptr) { return nullptr; } node = node->children[ch - 'a']; } return node; // 若是前缀就返回最后一个字母所在的node,否则返回nullptr } bool search(string word) { // 查找 Trie 中是否存在单词 word Trie* node = this->searchPrefix(word); return node != nullptr && node->isEnd; // 如果匹配到了最后一个字符,只需判断 node->isEnd 即可。 } bool startsWith(string prefix) { // 判断 Trie 中是或有以 prefix 为前缀的单词 Trie* node = this->searchPrefix(word); return node != nullptr; // 不需要判断最后一个字符结点的isEnd } };
// 以 T-130. 被围绕的区域 为例:
vector<vector<int>> dirs = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
vector<vector<int>> visited;
void DFS(vector<vector<char>>& board, int i, int j) {
int m = board.size(), n = board[0].size();
if (i < 0 || j < 0 || i >= m || j >= n || visited[i][j]) {
return;
}
visited[i][j] = true; // !!!
for (vector<int> d : dirs) {
int next_i = i + d[0];
int next_j = j + d[1];
dfs(board, next_i, next_j);
}
}
// 以 T-130. 被围绕的区域 为例: vector<vector<int>> dirs = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} }; vector<vector<int>> visited; void BFS(vector<vector<char>>& board, int i, int j) { int m = board.size(), n = board[0].size(); queue<pair<int, int>> que; que.push(make_pair(i, j)); // 加入起点! visited[i][j] = true; // 注意!不要遗漏! while (!que.empty()) { int size = que.size(); for (int j = 0; j < size; ++j) { pair<int, int> cur = que.front(); que.pop(); for (vector<int> d : dirs) { int x = i + d[0]; int y = j + d[1]; if (x < 0 || x >= m || y < 0 || y >= n || visited[x][y]) { continue; } visited[x][y] = true; // !!! que.push(make_pair(x, y)); } } } // 对于步数的题目,这里可以更新! }
vector<vector<int>> graph; // graph[s] 是一个列表,存储着节点 s 所指向的节点。
vector<vector<int>> buildGraph(int numCourses, vector<vector<int>>& prerequisites){ //【有向图】建图函数
vector<vector<int>> graph(numCourses, vector<int>(0,0)); // 图中共有 numCourses 个节点
for(vector<int> edge : prerequisites){
// 添加一条从 from 指向 to 的有向边,边的方向是「被依赖」关系,即:先完成from才能完成to
int from = edge[1];
int to = edge[0];
graph[from].push_back(to); // 对于【无向图】!需要后面再添加一句:graph[to].push_back(from); 即可!
}
return graph;
}
/* ------- DFS ------- */ vector<int> postorder; vector<int> path; vector<int> visited; bool hasCycle = false; vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) { vector<vector<int>> graph = buildGraph(numCourses, prerequisites); // 建图函数 path.resize(numCourses, 0); visited.resize(numCourses, 0); for (int i = 0; i < numCourses; ++i) { dfs(graph, i); } if (hasCycle) { // 存在环则无法进行拓扑排序! return vector<int>{}; } reverse(postorder.begin(), postorder.end()); // 将后序位置结果进行反转!!!就是拓扑排序的结果。 return vector<int>(postorder.begin(), postorder.end()); } void dfs(vector<vector<int>>& graph, int from) { if (path[from]) { hasCycle = true; } if (visited[from] || hasCycle) { return; } visited[from] = true; path[from] = true; for (auto& to : graph[from]) { dfs(graph, to); } path[from] = false; postorder.emplace_back(from); // 后序位置处理!!! } /* ------- BFS ------- */ vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) { vector<vector<int>> graph = buildGraph(numCourses, prerequisites); // 建图函数 vector<int> indegree(numCourses, 0); // 入度数组 for (auto& edge : prerequisites) { int to = edge[0]; indegree[to]++; // 节点to的入度+1 } queue<int> q; for (int i = 0; i < numCourses; i++) { if (indegree[i] == 0) { q.push(i); } } vector<int> ans; int count = 0; while (!q.empty()) { int cur = q.front(); q.pop(); count++; ans.emplace_back(cur); for (auto& adj : graph[cur]) { indegree[adj]--; if (indegree[adj] == 0) { q.push(adj); } } } return count == numCourses ? ans : vector<int>{}; // 存在环则无法进行拓扑排序! }
/* ------- DFS ------- */ bool isOk = true; vector<int> color, visited; bool isBipartite(vector<vector<int>>& graph) { int size = graph.size(); color.resize(size); visited.resize(size); for (int i = 0; i < size; ++i) { if (!isOk) { return false; } if (!visited[i]) { dfs(graph, i); } } return true; } void dfs(vector<vector<int>>& graph, int i) { visited[i] = true; // !!! for (int j : graph[i]) { if (!visited[j]) { color[j] = !color[i]; // 进行染色(只有两种颜色,所以直接取非) dfs(graph, j); } else { if (color[j] == color[i]) { isOk = false; return; } } } } /* ------- BFS ------- */ bool isOk = true; vector<int> visited, color; bool isBipartite(vector<vector<int>>& graph) { int size = graph.size(); visited.resize(size); color.resize(size); for (int i = 0; i < size; i++) { if (!isOk) { return false; } if (!visited[i]) { bfs(graph, i); } } return true; } void bfs(vector<vector<int>>& graph, int start) { queue<int> q; visited[start] = true; // !!! q.push(start); while (!q.empty()) { int cur = q.front(); q.pop(); for (int j : graph[cur]) { if (!visited[j]) { visited[j] = true; // !!! color[j] = !color[cur]; // 进行染色(只有两种颜色,所以直接取非) q.push(j); } else { if (color[j] == color[cur]) { isOk = false; return; } } } } }
class UF { private: int m_count; // 连通分量个数 vector<int> parent; // 存储一棵树(以一维数组形式!) public: UF(int n) { m_count = n; // n 为图中节点的个数 parent.resize(n, 0); for (int i = 0; i < n; i++) { parent[i] = i; } } void Union(int p, int q) { // 将节点 p 和节点 q 连通 int rootp = Find(p); // ! int rootq = Find(q); // ! if (rootp == rootq) { return; } parent[rootq] = rootp; // parent[rootp] = rootq; 也可以! m_count--; // 两个连通分量合并成一个连通分量,连通分量个数-1 } bool Connected(int p, int q) { // 判断节点 p 和节点 q 是否连通 int rootp = Find(p); // ! int rootq = Find(q); // ! return rootp == rootq; } int Find(int x) { // 返回节点 x 的连通分量根节点 if (parent[x] != x) { parent[x] = Find(parent[x]); } return parent[x]; } int Count() { // 返回图中的连通分量个数 return m_count; } };
int miniTree(int n, vector<vector<int>>& connections) { UF uf(n + 1); // 注意:初始化了n + 1个节点(不是n个),而节点编号为1 ~ n,不包括 0!故之后0这个节点是没有进行连通操作(仅仅进行了初始化),故答案需独立算上验证:uf.Count() 是否 = 2 sort(connections.begin(), connections.end(), [](vector<int>& a, vector<int>& b) { return a[2] < b[2]; }); // 贪心思想 int mst = 0; for (vector<int> edge : connections) { int u = edge[0], v = edge[1], weight = edge[2]; if (uf.Connected(u, v)) { continue; } mst += weight; uf.Union(u, v); } return uf.Count() == 2 ? mst : -1; } class UF { // ... };
// 创建辅助状态类 struct State { State(int id, int distFromStart) :m_id(id), m_distFromStart(distFromStart) {} int m_id; // 图节点的 id int m_distFromStart; // 从 start 节点到当前节点的距离 }; // 输入一幅图和一个起点 start,计算 start 到其他节点的最短距离 vector<int> dijkstra(int start, vector<vector<pair<int, int>>>& graph) { int V = graph.length; // 图中节点的个数 vector<int> distTo(V, INT_MAX); // distTo[i]的定义(牢记!):节点start到达节点i的【最短】路径权重。因为求最小值,所以 dp table 初始化为正无穷! distTo[start] = 0; // base case,start到start的最短距离就是:0 auto cmp = [](const State& a, const State& b) { return a.m_distFromStart > b.m_distFromStart; }; priority_queue<State, vector<State>, decltype(cmp)> pq(cmp); // 优先级队列,distFromStart 较小的排在前面 pq.push(State(start, 0)); // 将起点start装入,从起点start开始进行BFS while (!pq.empty()) { State curState = pq.top(); pq.pop(); int curNodeId = curState.m_id; int curDistFromStart = curState.m_distFromStart; // 若只想计算到 end 的最短路径,在这里加一个判断就行了,其他代码不用改 if (curNodeID == end) { return curDistFromStart; } if (curDistFromStart > distTo[curNodeID]) { // 已经有一条更短的路径到达 curNode 节点了 continue; } for (auto& neighbor : graph[curNodeId]) { // 将 curNode 的相邻节点装入队列 int nextNodeId = neighbor.first; int distToNextNode = distTo[curNodeId] + neighbor.second; // 关键!邻节点的指标计算(根据具体题意!这里是权重和!) if (distTo[nextNodeId] > distToNextNode) { // 看看从 curNode 达到 nextNode 的距离是否会【更】短 distTo[nextNodeId] = distToNextNode; // 更短则更新 dp table pq.push(State(nextNodeId, distToNextNode)); // 将这个节点以及距离放入队列继续之后的迭代遍历 } } } return distTo; }
vector<int> nextGreaterElement(vector<int>& nums) { vector<int> res(nums.size()); // 存放【单调递增】答案的数组 stack<int> s; // 【倒着】往栈里放 for (int i = nums.size() - 1; i >= 0; i--) { while (!s.empty() && s.top() <= nums[i]) { // 判定个子高矮:若求【单调递减】, 这里改为: s.top() >= nums[i] s.pop(); // 矮个起开,反正也被挡着了。。。 } res[i] = s.empty() ? -1 : s.top(); // nums[i] 身后的 next great number s.push(nums[i]); //将相对高个子入栈 } return res; } // 「循环数组」处理策略 for (int i = 2 * n - 1; i >= 0; --i) { // 将数组长度翻倍,通过 % 运算符求模(余数),来获得环形特效! while (!stk.empty() && nums[stk.top()] <= nums[i % n]) { stk.pop(); } ans[i % n] = (stk.empty() ? -1 : nums[stk.top()]); stk.push(i % n); }
vector<int> maxSlidingWindow(vector<int>& nums, int k) { // T-239. 滑动窗口最大值,k表:窗口大小 int n = nums.size(); deque<int> q; // 双端队列 for (int i = 0; i < k; ++i) { while (!q.empty() && nums[i] >= nums[q.back()]) { // 保证单调递减 q.pop_back(); } q.push_back(i); } vector<int> ans = {nums[q.front()]}; for (int i = k; i < n; ++i) { while (!q.empty() && nums[i] >= nums[q.back()]) { // 保证单调递减 q.pop_back(); } q.push_back(i); if (q.front() <= i - k) { // 达到队容量时,需要弹出队首元素(重要结论:该队列中的数组下标一定是递增的!) q.pop_front(); } ans.push_back(nums[q.front()]); // 记录每个滑动窗口中的最大值 } return ans; }
class MyCycQueue { private: int front; // 队尾指针 int rear; // 队头指针 int capacity; // 循环队列可使用的总大小 vector<int> elements; public: MyCycQueue(int k) { this->capacity = k + 1; this->elements = vector<int>(capacity); rear = front = 0; } bool Push(int value) { // 模拟入队的函数 if (isFull()) { return false; } elements[rear] = value; rear = (rear + 1) % capacity; // 更新!!! return true; } bool Pop() { // 模拟出队的函数 if (isEmpty()) { return false; } front = (front + 1) % capacity; // 更新!!! return true; } int Front() { if (isEmpty()) { return -1; } return elements[front]; } int Rear() { if (isEmpty()) { return -1; } return elements[(rear - 1 + capacity) % capacity]; } bool isEmpty() { return rear == front; } bool isFull() { return ((rear + 1) % capacity) == front; } };
class MyQueue { public: stack<int> s1; stack<int> s2; MyQueue() { } void Push(int x) { s1.push(x); } int Pop() { int delNum = Peek(); s2.pop(); return delNum; } int Peek() { if (s2.empty()) { while (!s1.empty()) { s2.push(s1.top()); s1.pop(); } } return s2.top(); } bool Empty() { return s1.empty() && s2.empty(); } };
class MyStack { public: queue<int> q; int topVal; MyStack() { topVal = 0; } void Push(int x) { q.push(x); } int Pop() { int topSt = Top(); while (q.front() != topSt) { // 将队尾前的所有元素依次弹出并插入到队尾 q.push(q.front()); q.pop(); } q.pop(); // 将原队尾元素弹出 return topSt; // 返回原队尾元素 } int Top() { topVal = q.back(); return topVal; } bool Empty() { return q.empty(); } };
/*----------------------统一写为:左闭右闭型----------------------*/ int binary_search(int[] nums, int target) { // 普通二分 int left = 0, right = nums.length - 1; while(left <= right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else if (nums[mid] > target) { right = mid - 1; } else if(nums[mid] == target) { return mid; // 直接返回!!! } } return -1; // 直接返回!!! } int left_bound(int[] nums, int target) { // 左边界二分 int left = 0, right = nums.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else if (nums[mid] > target) { right = mid - 1; } else if (nums[mid] == target) { right = mid - 1; } } if (left > nums.length - 1 || nums[left] != target) // 最后要检查 left 越界的情况!!! return -1; return left; } int right_bound(int[] nums, int target) { // 右边界二分 int left = 0, right = nums.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else if (nums[mid] > target) { right = mid - 1; } else if (nums[mid] == target) { left = mid + 1; } } if (right < 0 || nums[right] != target) // 最后要检查 right 越界的情况!!! return -1; return right; }
void slidingWindow(string s, string t) {
unordered_map<char, int> window;
int left = 0, right = 0, valid = 0;
while (right < s.size()) {
char c = s[right]; // c 是将移入窗口的字符
right++; // 右移窗口
// ...... // 进行窗口内数据的一系列更新!!!——> (1)
while (window needs shrink) { // 判断左侧窗口是否要收缩 ——> (2)
char d = s[left]; // d 是将移出窗口的字符
left++; // 左移窗口
// ...... // 进行窗口内数据的一系列更新!!!——> (3)
}
}
}
class KMP { private: vector<vector<int>> dp; string m_pat; public: KMP(string pat) { m_pat = pat; int M = m_pat.size(); int X = 0; // 影子状态 X 初始为 0 dp.resize(M, vector<int>(256)); // dp[当前状态][字符] = 下个状态 // base case dp[0][m_pat[0]] = 1; // 开始状态转移 for (int j = 1; j < M; j++) { // 注意:当前状态 j 从 1 开始 for (int c = 0; c < 256; c++) { // 当前是状态 j,遇到字符 c,pat 应该转移到哪个状态? if (c == m_pat[j]) { dp[j][c] = j + 1; // 更新目标状态dp[j][c](= dp[j][pat[j]])(对目标状态进行状态推进) } else { dp[j][c] = dp[X][c]; // 其余状态dp[j][...]全用退回影子状态(状态重启) } } X = dp[X][m_pat[j]]; // 更新影子状态:当前是状态 X,遇到字符 pat[j],pat 应该转移到哪个状态? } } int search(string txt) { int M = m_pat.size(); int N = txt.size(); int j = 0; // 模式串 pat 的初始态为 0 for (int i = 0; i < N; i++) { j = dp[j][txt[i]]; // 计算 pat 的下一个状态 if (j == M) { return i + 1 - M; // 到达终止态,返回结果 } } return -1; // 没到达终止态,匹配失败 } };
int quickMulti(int A, int B) {
int ans = 0;
while (B > 0) {
if ((B & 1) == 1) {
ans += A; // !
}
A <<= 1; // !
B >>= 1;
}
return ans;
}
// 快速幂 的递归形式 double myPow(double x, int n) { long long N = n; return N >= 0 ? quickMul(x, N) : 1.0 / quickMul(x, -N); } double quickMul(double x, long long N) { if (N == 0) { return 1.0; } double y = quickMul(x, N / 2); return N % 2 == 0 ? y * y : y * y * x; } // 快速幂 的迭代形式 double myPow(double x, int n) { long long N = n; double ans = 1.0; double x_contribute = x; // 贡献的初始值为 x if (N < 0) { x_contribute = 1 / x_contribute; N = -N; } while (N > 0) { if ((N & 1) == 1) { ans *= x_contribute; // ! } x_contribute *= x_contribute; // ! N >>= 1; } return ans; }
C++
笔试常见输入输出练习 ——> 重要!!!C++
)C++
输入输出汇总// 输入任意组字符串,每组字符串包含一个匹配串s,一个文本串t // 输入: xuda hjj // xxuuda hujj // xdaasdf jjxus // ..... ——> 需要自定义设置退出输入的标志位 // 输出: x1 // x2 // x3 // ..... string input; vector<vector<string>> store; while (getline(cin, input)) { // 注意:getline遇到回车结束一行输入,但不会保留回车符('\n')在流中。故:只输入一个回车符时,input为空。 if (input.size() == 0) break; // 设置两次回车后退出 string str1, str2; istringstream iss(input); while (iss >> str1 >> str2) { store.push_back({ str1,str2 }); } } for (int i = 0; i < store.size(); ++i) { string str1 = store[i][0], str2 = store[i][1]; cout << fx(str1, str2) << endl; // 处理函数 } // 注意与如下形式的区别: string in_1, in_2; while (cin >> in_1 >> in_2) { // ..... }
注意:除了 getline()
全局函数外,C++ 还提供了一个同名的 getline()
成员函数,定义在 istream
类中,它也可以读取一行数据。这两个名称、功能都相同的函数。这里讲的 getline()
指的是全局函数!
此函数可读取整行,包括前导和嵌入的空格,并将其存储在字符串对象中。
getline(cin, inputLine); // 其中 cin 是正在读取的输入流,而 inputLine 是接收输入字符串的 string 变量的名称。
int main() {
string name;
string city;
cout << "Please enter your name: ";
getline(cin, name);
cout << "Enter the city you live in: ";
getline(cin, city);
cout << "Hello, " << name << endl;
cout << "You live in " << city << endl;
return 0;
}
class NumArray {
private:
vector<int> preSum;
public:
NumArray(vector<int>& nums) {
preSum.resize(nums.size() + 1);
for (int i = 1; i < preSum.size(); ++i) {
preSum[i] = preSum[i - 1] + nums[i - 1];
}
}
int SumRange(int left, int right) {
return preSum[right + 1] - preSum[left];
}
};
class Difference { private: vector<int> diff; public: Difference(vector<int>& nums) { diff.resize(nums.size()); diff[0] = nums[0]; for (int i = 1; i < nums.size(); ++i) { diff[i] = nums[i] - nums[i - 1]; } } void Increment(int i, int j, int val) { diff[i] += val; if (j + 1 < diff.size()) { diff[j + 1] -= val; } } vector<int> Result() { vector<int> res(diff.size()); res[0] = diff[0]; for (int i = 1; i < diff.size(); ++i) { res[i] = res[i - 1] + diff[i]; } return res; } };
sort(nums.begin(), nums.end()); // 一定要先排序!!! vector<vector<int>> nSumTarget(vector<int>& nums, int n, int start, int target) { // 求:目标和为target的n个数之和 int sz = nums.size(); vector<vector<int>> res; if (n < 2 || sz < n) // 至少是 2Sum,且数组大小不应该小于 n return res; if (n == 2) { // 2Sum 是 base case int lo = start, hi = sz - 1; while (lo < hi) { // 双指针操作 int sum = nums[lo] + nums[hi]; int left = nums[lo], right = nums[hi]; if (sum < target) { while (lo < hi && nums[lo] == left) lo++; // !!! } else if (sum > target) { while (lo < hi && nums[hi] == right) hi--; // !!! } else { res.push_back({ left, right }); while (lo < hi && nums[lo] == left) lo++; // !!! while (lo < hi && nums[hi] == right) hi--; // !!! } } } else { for (int i = start; i < sz; i++) { // n > 2 时,递归计算 (n-1)Sum 的结果 vector<vector<int>> sub = nSumTarget(nums, n - 1, i + 1, target - nums[i]); for (vector<int>& arr : sub) { arr.push_back(nums[i]); // (n-1)Sum 加上 nums[i] 就是 nSum res.push_back(arr); } while (i < sz - 1 && nums[i] == nums[i + 1]) i++; // !注:i < sz - 1 类似 left < right 一样 } } return res; }
未知多组 + 结束条件 + 0结束
输入描述:
a,b(1 <= a, b <= 10^9)
输出描述:
a+b
的结果思考:
未知多组 + 两个输入均为 + 0结束的条件:while (cin >> a >> b && (a || b))
int main() {
int a, b;
while (cin >> a >> b && (a || b)) {
cout << a + b << endl;
}
return 0;
}
其余详见:https://blog.csdn.net/weixin_44671418/article/details/125352042?spm=1001.2014.3001.5502
template<typename T> class SharedPtr { public: SharedPtr(T* ptr = NULL) :_ptr(ptr), _pcount(new int(1)) {} // 注:初始化将引用计数计为:1 SharedPtr(const SharedPtr& s) :_ptr(s._ptr), _pcount(s._pcount) { // 拷贝构造 (*_pcount)++; } SharedPtr<T>& operator=(const SharedPtr& s) { // 赋值运算符 if (this != &s) { _ptr = s._ptr; _pcount = s._pcount; *(_pcount)++; } return *this; } T& operator*() { // 解引用 return *(this->_ptr); } T* operator->() { // 指向符 return this->_ptr; } ~SharedPtr() { // 析构函数 --(*(this->_pcount)); if (*(this->_pcount) == 0) { // 模拟自动垃圾回收机制。一旦T类型对象的引用计数为0,就释放该对象。 delete _ptr; _ptr = NULL; delete _pcount; _pcount = NULL; } } private: T* _ptr; // 裸指针 int* _pcount; // 指向引用计数的指针,多个SharedPtr之间可以同步修改 };
class A { public: shared_ptr<B> bptr; A() { cout << "A constructed!" << endl; } ~A() { cout << "A destructed!" << endl; } }; class B { public: shared_ptr<A> aptr; // 改为:weak_ptr<A> aptr; 即可解决循环引用问题 B() { cout << "B constructed!" << endl; } ~B() { cout << "B destructed!" << endl; } }; auto ap = make_shared<A>(); auto bp = make_shared<B>(); ap->bptr = bp; bp->aptr = ap; cout << "A: " << ap.use_count() << endl; cout << "B: " << bp.use_count() << endl;
pthread_mutex_t mutex_A = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex_B = PTHREAD_MUTEX_INITIALIZER; //线程函数 A void* threadA_proc(void* data) { // 锁序:A B B A cout << "thread A waiting get ResourceA " << endl; pthread_mutex_lock(&mutex_A); cout << "thread A got ResourceA " << endl; sleep(1); cout << "thread A waiting get ResourceB " << endl; pthread_mutex_lock(&mutex_B); cout << "thread A got ResourceB " << endl; pthread_mutex_unlock(&mutex_B); pthread_mutex_unlock(&mutex_A); return (void*)0; } //线程函数 B void* threadB_proc(void* data) { // 锁序:B A A B ——> 改为:A B B A 即可解决死锁! cout << "thread B waiting get ResourceB " << endl; pthread_mutex_lock(&mutex_B); cout << "thread B got ResourceB " << endl; sleep(1); cout << "thread B waiting get ResourceA " << endl; pthread_mutex_lock(&mutex_A); cout << "thread B got ResourceA " << endl; pthread_mutex_unlock(&mutex_A); pthread_mutex_unlock(&mutex_B); return (void*)0; } int main() { pthread_t tidA, tidB; //创建两个线程 pthread_create(&tidA, NULL, threadA_proc, NULL); pthread_create(&tidB, NULL, threadB_proc, NULL); pthread_join(tidA, NULL); // 主线程阻塞,等待线程A结束 pthread_join(tidB, NULL); // 主线程阻塞,等待线程B结束 cout << "exit" << endl; return 0; } // C++实现: #include <thread> #include <mutex> #include <stdlib.h> mutex A, B; //线程函数 A void threadA_proc() { cout << "thread A waiting get ResourceA " << endl; A.lock(); cout << "thread A got ResourceA " << endl; Sleep(1); cout << "thread A waiting get ResourceB " << endl; B.lock(); cout << "thread A got ResourceB " << endl; B.unlock(); A.unlock(); } //线程函数 B void threadB_proc() { cout << "thread B waiting get ResourceB " << endl; B.lock(); cout << "thread B got ResourceB " << endl; Sleep(1); cout << "thread B waiting get ResourceA " << endl; A.lock(); cout << "thread B got ResourceA " << endl; A.unlock(); B.unlock(); } int main() { thread A(threadA_proc); thread B(threadB_proc); A.join(); // 暂停,直到第一个函数执行完成 B.join(); // 暂停,直到第二个函数执行完成 cout << "exit" << endl; return 0; }
// 读写锁要求: // 多个读者同时读 // 一个写者孤单(互斥其他写者、读者) // 写者优先于读者(一旦写时阻塞读、优先唤醒写) class RWLock { public: RWLock() :rd_cnt(0), wr_cnt(0) { pthread_mutex_init(&mxt, NULL); pthread_cond_init(&cond, NULL); } void ReadLock() { // 读加锁 pthread_mutex_lock(&mxt); ++rd_cnt; while (wr_cnt > 0) pthread_cond_wait(&cond, &mxt); pthread_mutex_unlock(&mxt); } void ReadUnlock() { // 读解锁 pthread_mutex_lock(&mxt); --rd_cnt; if (rd_cnt == 0) pthread_cond_signal(&cond); pthread_mutex_unlock(&mxt); } void WriteLock() { // 写加锁 pthread_mutex_lock(&mxt); ++wr_cnt; while (wr_cnt + rd_cnt >= 2) pthread_cond_wait(&cond, &mxt); pthread_mutex_unlock(&mxt); } void WriterUnlock() { // 写解锁 pthread_mutex_lock(&mxt); --wr_cnt; if (wr_cnt == 0) pthread_cond_signal(&cond); pthread_mutex_unlock(&mxt); } private: pthread_mutex_t mxt; // 互斥锁 pthread_cond_t cond; // 条件变量 int rd_cnt; // 等待读的数量 int wr_cnt; // 等待写的数量 };
// 读者-写者的问题描述: // 「读-读」允许:同一时刻,允许多个读者同时读 // 「读-写」互斥:没有写者时读者才能读,没有读者时写者才能写 // 「写-写」互斥:没有其他写者时,写者才能写 // 读者优先(若想实现:公平竞争,则添加注释代码即可!) typedef int semaphore; semaphore count_mutex = 1; // 控制对读操作数量rCount的互斥修改,初始值为 1 semaphore data_mutex = 1; // 控制对写操作数据的互斥修改,初始值为 1 // semaphore flag; // 用于实现公平竞争,初始值为 0 int rCount = 0; // 正在读操作的读者个数,初始值为 0 void Reader() { while (TRUE) { // P(&flag); P(&count_mutex); rCount++; if (rCount == 1) P(&data_mutex); // 第一个读者需要对数据进行加锁,防止写进程访问 V(&count_mutex); // V(&flag); read(); // 读数据!!! P(&count_mutex); rCount--; if (rCount == 0) V(&data_mutex); // 最后一个读者要对数据进行解锁,防止写进程无法访问 V(&count_mutex); } } void Writer() { while (TRUE) { // P(&flag); P(&data_mutex); write(); // 写数据!!! V(&data_mutex); // V(&flag); } }
#define N 100 semaphore mutex = 1; // 互斥信号量,用于临界区的互斥访问 semaphore emptyBuffers = N; // 表示缓冲区「空槽」的个数 semaphore fullBuffers = 0; // 表示缓冲区「满槽」的个数 // 生产者线程函数 void Producer() { while (TRUE) { P(emptyBuffers); // 将空槽的个数 -1 P(mutex); // 进入临界区 bufffer_add(); // 将生成的数据放到缓冲区中 V(mutex); // 离开临界区 V(fullBuffers); // 将满槽的个数 +1 } } // 消费者线程函数 void Consumer() { while (TRUE) { P(fullBuffers); //将满槽的个数 -1 P(mutex); //进入临界区 buffer_remove(); // 从缓冲区里读取数据 V(mutex); //离开临界区 V(emptyBuffers); // 将空槽的个数 +1 } }
template <class T, class Sequence = deque<T>> // class Sequence = list<T> class myStack { public: typedef typename Sequence::value_type value_type; // typename用来声明Sequence::value_type是一个类型参数 typedef typename Sequence::size_type size_type; typedef typename Sequence::reference reference; typedef typename Sequence::const_reference const_reference; protected: Sequence c; public: bool empty() { return c.empty(); } size_type size() const { return c.size(); } reference top() { return c.back(); } // !!! const_reference top() const { return c.back(); } // !!! void push(const value_type& x) { c.push_back(x); } void pop() { c.pop_back(); } };
template <class T, class Sequence = deque<T>> // class Sequence = list<T> class myQueue { public: typedef typename Sequence::value_type value_type; typedef typename Sequence::size_type size_type; typedef typename Sequence::reference reference; typedef typename Sequence::const_reference const_reference; protected: Sequence c; public: bool empty() { return c.empty(); } size_type size() const { return c.size(); } reference front() { return c.front(); } // !!! const_reference front() const { return c.front(); } // !!! void push(const value_type& x) { c.push_back(x); } void pop() { c.pop_front(); } };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。