《算法导论》中的核心算法整理
以下是根据《算法导论》中的核心算法整理的C++实现示例,涵盖分治、动态规划、图算法等经典内容。示例代码均遵循C++标准,可直接用于学习或项目参考。
分治算法
1. 归并排序
void merge(vector<int>& arr, int l, int m, int r) {
vector<int> temp(r - l + 1);
int i = l, j = m + 1, k = 0;
while (i <= m && j <= r) temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
while (i <= m) temp[k++] = arr[i++];
while (j <= r) temp[k++] = arr[j++];
for (int p = 0; p < k; ++p) arr[l + p] = temp[p];
}
void mergeSort(vector<int>& arr, int l, int r) {
if (l >= r) return;
int m = l + (r - l) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
}
2. 快速排序
int partition(vector<int>& arr, int l, int r) {
int pivot = arr[r], i = l;
for (int j = l; j < r; ++j) {
if (arr[j] <= pivot) swap(arr[i++], arr[j]);
}
swap(arr[i], arr[r]);
return i;
}
void quickSort(vector<int>& arr, int l, int r) {
if (l >= r) return;
int p = partition(arr, l, r);
quickSort(arr, l, p - 1);
quickSort(arr, p + 1, r);
}
3. 最大子数组问题
struct Subarray { int l, r, sum; };
Subarray maxCrossingSubarray(vector<int>& arr, int l, int m, int r) {
int left_sum = INT_MIN, sum = 0, max_l;
for (int i = m; i >= l; --i) {
sum += arr[i];
if (sum > left_sum) { left_sum = sum; max_l = i; }
}
int right_sum = INT_MIN, max_r;
sum = 0;
for (int j = m + 1; j <= r; ++j) {
sum += arr[j];
if (sum > right_sum) { right_sum = sum; max_r = j; }
}
return {max_l, max_r, left_sum + right_sum};
}
Subarray maxSubarray(vector<int>& arr, int l, int r) {
if (l == r) return {l, r, arr[l]};
int m = l + (r - l) / 2;
Subarray left = maxSubarray(arr, l, m);
Subarray right = maxSubarray(arr, m + 1, r);
Subarray cross = maxCrossingSubarray(arr, l, m, r);
if (left.sum >= right.sum && left.sum >= cross.sum) return left;
if (right.sum >= left.sum && right.sum >= cross.sum) return right;
return cross;
}
动态规划
4. 钢条切割
int cutRod(vector<int>& price, int n) {
vector<int> dp(n + 1, 0);
for (int i = 1; i <= n; ++i) {
int max_val = INT_MIN;
for (int j = 0; j < i; ++j)
max_val = max(max_val, price[j] + dp[i - j - 1]);
dp[i] = max_val;
}
return dp[n];
}
5. 矩阵链乘法
int matrixChainOrder(vector<int>& p) {
int n = p.size() - 1;
vector<vector<int>> dp(n, vector<int>(n, 0));
for (int l = 2; l <= n; ++l) {
for (int i = 0; i <= n - l; ++i) {
int j = i + l - 1;
dp[i][j] = INT_MAX;
for (int k = i; k < j; ++k) {
int cost = dp[i][k] + dp[k + 1][j] + p[i] * p[k + 1] * p[j + 1];
dp[i][j] = min(dp[i][j], cost);
}
}
}
return dp[0][n - 1];
}
6. 最长公共子序列
int lcs(string& X, string& Y) {
int m = X.size(), n = Y.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (X[i - 1] == Y[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[m][n];
}
贪心算法
7. 活动选择问题
int activitySelection(vector<pair<int, int>>& activities) {
sort(activities.begin(), activities.end(),
[](auto& a, auto& b) { return a.second < b.second; });
int count = 1, last_end = activities[0].second;
for (int i = 1; i < activities.size(); ++i) {
if (activities[i].first >= last_end) {
++count;
last_end = activities[i].second;
}
}
return count;
}
8. 霍夫曼编码
struct Node {
char ch;
int freq;
Node *left, *right;
Node(char c, int f) : ch(c), freq(f), left(nullptr), right(nullptr) {}
};
struct Compare {
bool operator()(Node* a, Node* b) { return a->freq > b->freq; }
};
void encode(Node* root, string code, unordered_map<char, string>& huffmanCode) {
if (!root) return;
if (!root->left && !root->right) huffmanCode[root->ch] = code;
encode(root->left, code + "0", huffmanCode);
encode(root->right, code + "1", huffmanCode);
}
unordered_map<char, string> buildHuffmanTree(string text) {
unordered_map<char, int> freq;
for (char ch : text) freq[ch]++;
priority_queue<Node*, vector<Node*>, Compare> pq;
for (auto& pair : freq) pq.push(new Node(pair.first, pair.second));
while (pq.size() > 1) {
Node* left = pq.top(); pq.pop();
Node* right = pq.top(); pq.pop();
Node* newNode = new Node('\0', left->freq + right->freq);
newNode->left = left;
newNode->right = right;
pq.push(newNode);
}
unordered_map<char, string> huffmanCode;
encode(pq.top(), "", huffmanCode);
return huffmanCode;
}
图算法
9. BFS遍历
void bfs(vector<vector<int>>& graph, int start) {
queue<int> q;
vector<bool> visited(graph.size(), false);
q.push(start);
visited[start] = true;
while (!q.empty()) {
int u = q.front(); q.pop();
cout << u << " ";
for (int v : graph[u]) {
if (!visited[v]) {
visited[v] = true;
q.push(v);
}
}
}
}
10. DFS遍历
void dfsUtil(vector<vector<int>>& graph, int u, vector<bool>& visited) {
visited[u] = true;
cout << u << " ";
for (int v : graph[u]) {
if (!visited[v]) dfsUtil(graph, v, visited);
}
}
void dfs(vector<vector<int>>& graph, int start) {
vector<bool> visited(graph.size(), false);
dfsUtil(graph, start, visited);
}
11. Dijkstra最短路径
void dijkstra(vector<vector<pair<int, int>>>& graph, int src) {
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
vector<int> dist(graph.size(), INT_MAX);
pq.push({0, src});
dist[src] = 0;
while (!pq.empty()) {
int u = pq.top().second; pq.pop();
for (auto& edge : graph[u]) {
int v = edge.first, weight = edge.second;
if (dist[v] > dist[u] + weight) {
dist[v] = dist[u] + weight;
pq.push({dist[v], v});
}
}
}
}
其他重要算法
12. 红黑树插入
enum Color { RED, BLACK };
struct Node {
int data;
Color color;
Node *left, *right, *parent;
Node(int d) : data(d), color(RED), left(nullptr),
right(nullptr), parent(nullptr) {}
};
void insertFixup(Node*& root, Node* pt) {
while (pt != root && pt->parent->color == RED) {
Node* uncle = (pt->parent == pt->parent->parent->left) ?
pt->parent->parent->right : pt->parent->parent->left;
if (uncle && uncle->color == RED) {
pt->parent->color = BLACK;
uncle->color = BLACK;
pt->parent->parent->color = RED;
pt = pt->parent->parent;
} else {
if (pt->parent == pt->parent->parent->left &&
pt == pt->parent->right) {
pt = pt->parent;
leftRotate(root, pt);
}
pt->parent->color = BLACK;
pt->parent->parent->color = RED;
rightRotate(root, pt->parent->parent);
}
}
root->color = BLACK;
}
(因篇幅限制,剩余示例可扩展实现:KMP字符串匹配、FFT快速傅里叶变换、NP完全问题验证算法等。)
KMP(Knuth-Morris-Pratt)算法
KMP算法简介
KMP(Knuth-Morris-Pratt)算法是一种高效的字符串匹配算法,通过预处理模式串构建部分匹配表(Partial Match Table),避免主串指针回溯,时间复杂度为O(n+m)。
C++实现代码模板
#include <vector>
#include <string>
using namespace std;
vector<int> computeLPS(const string& pattern) {
vector<int> lps(pattern.size(), 0);
int len = 0;
for (int i = 1; i < pattern.size(); ) {
if (pattern[i] == pattern[len]) {
lps[i++] = ++len;
} else {
if (len != 0) len = lps[len-1];
else lps[i++] = 0;
}
}
return lps;
}
vector<int> KMPSearch(const string& text, const string& pattern) {
vector<int> lps = computeLPS(pattern);
vector<int> matches;
int i = 0, j = 0;
while (i < text.size()) {
if (text[i] == pattern[j]) {
i++; j++;
if (j == pattern.size()) {
matches.push_back(i - j);
j = lps[j-1];
}
} else {
if (j != 0) j = lps[j-1];
else i++;
}
}
return matches;
}
实例集合
每个示例包含测试字符串和模式串:
示例1
文本: "ABABDABACDABABCABAB"
模式: "ABABCABAB"
输出: [10]
示例2
文本: "AABAACAADAABAABA"
模式: "AABA"
输出: [0, 9, 12]
示例3
文本: "THIS IS A TEST TEXT"
模式: "TEST"
输出: [10]
示例4
文本: "AAAAABAAABA"
模式: "AAAA"
输出: [0, 1]
示例5
文本: "ABCDABCDABDE"
模式: "BCD"
输出: [1, 5]
示例6
文本: "ABCABCABCABC"
模式: "ABCABC"
输出: [0, 3, 6]
示例7
文本: "GEEKS FOR GEEKS"
模式: "GEEK"
输出: [0, 10]
示例8
文本: "ABABABABABABAB"
模式: "ABABA"
输出: [0, 2, 4, 6, 8]
示例9
文本: "1111111111"
模式: "111"
输出: [0,1,2,3,4,5,6,7]
示例10
文本: "ABC#DEF#GHI#"
模式: "#"
输出: [3,7,11]
边界测试用例
示例11
文本: ""
模式: "ABC"
输出: []
示例12
文本: "ABCD"
模式: ""
输出: []
示例13
文本: "A"
模式: "A"
输出: [0]
示例14
文本: "ABCDEF"
模式: "XYZ"
输出: []
示例15
文本: "重复重复重复"
模式: "重复"
输出: [0, 2, 4]
特殊字符测试
示例16
文本: "abc$123*^xx"
模式: "$123*"
输出: [3]
示例17
文本: "你好世界你好"
模式: "你好"
输出: [0, 4]
示例18
文本: "a\nb\tc\0d"
模式: "b\tc"
输出: [2]
性能测试用例
示例19
文本: string(1000000, 'A') + "B"
模式: string(1000, 'A') + "B"
输出: [999000]
示例20
文本: "ABCD"*10000
模式: "CDAB"
输出: [2,6,10,...,39998]
综合应用场景
示例21 - DNA序列匹配
文本: "AGCTAGCTAGCT"
模式: "AGCT"
输出: [0,4,8]
示例22 - 日志分析
文本: "ERROR:404;ERROR:500;WARN:301"
模式: "ERROR:"
输出: [0, 12]
示例23 - 二进制模式
文本: "\x01\x00\x01\x01\x00"
模式: "\x01\x00"
输出: [0, 3]
示例24 - URL路由匹配
文本: "/user/profile/account/settings"
模式: "/account/"
输出: [13]
示例25 - 版本号检测
文本: "v1.2.3 v1.2.4 v2.0.1"
模式: "v1.2."
输出: [0, 6]
复杂模式示例
示例26 - 重叠匹配
文本: "ababababa"
模式: "aba"
输出: [0,2,4,6]
示例27 - 前后缀测试
文本: "mississippi"
模式: "issi"
输出: [1,4]
示例28 - 全匹配
文本: "aaaaa"
模式: "aa"
输出: [0,1,2,3]
示例29 - Unicode字符
文本: "😊🐱🐶🍎😊"
模式: "😊"
输出: [0,4]
示例30 - 混合编码
文本: "中文ABC123!@#"
模式: "ABC"
输出: [2]
使用说明
- 将computeLPS和KMPSearch函数复制到C++项目中
- 使用g++编译时需要添加-std=c++11标准
- 结果返回所有匹配的起始位置索引(从0开始)
- 空模式或空文本时返回空向量
复杂度分析
- 预处理阶段:O(m) 其中m为模式串长度
- 匹配阶段:O(n) 其中n为文本长度
- 总时间复杂度:O(n+m)
- 空间复杂度:O(m) 用于存储LPS数组
场景下的C++插入排序
以下是不同场景下的C++插入排序实例,涵盖基础实现、变体及优化版本。每个示例独立且可直接运行,通过注释说明核心逻辑。
基础插入排序(升序)
void insertionSort(int arr[], int n) {
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
降序排列
void insertionSortDesc(int arr[], int n) {
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] < key) { // 比较符号反向
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
字符串数组排序
void insertionSortStrings(string arr[], int n) {
for (int i = 1; i < n; i++) {
string key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
结构体排序(按年龄)
struct Person {
string name;
int age;
};
void sortByAge(Person people[], int n) {
for (int i = 1; i < n; i++) {
Person key = people[i];
int j = i - 1;
while (j >= 0 && people[j].age > key.age) {
people[j + 1] = people[j];
j--;
}
people[j + 1] = key;
}
}
双向插入排序
void bidirectionalInsertionSort(int arr[], int n) {
int left = 0, right = n - 1;
while (left < right) {
for (int i = left + 1; i <= right; i++) {
int key = arr[i];
int j = i - 1;
while (j >= left && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
right--;
}
}
递归实现
void recursiveInsertionSort(int arr[], int n) {
if (n <= 1) return;
recursiveInsertionSort(arr, n - 1);
int key = arr[n - 1];
int j = n - 2;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
使用STL的插入排序
void stlInsertionSort(vector<int>& v) {
for (auto it = v.begin(); it != v.end(); ++it) {
auto const insertion_point = upper_bound(v.begin(), it, *it);
rotate(insertion_point, it, it + 1);
}
}
链表排序
struct Node {
int data;
Node* next;
};
void insertionSortLinkedList(Node** head) {
Node* sorted = nullptr;
Node* current = *head;
while (current != nullptr) {
Node* next = current->next;
if (sorted == nullptr || sorted->data >= current->data) {
current->next = sorted;
sorted = current;
} else {
Node* temp = sorted;
while (temp->next != nullptr && temp->next->data < current->data) {
temp = temp->next;
}
current->next = temp->next;
temp->next = current;
}
current = next;
}
*head = sorted;
}
浮点数排序
void insertionSortFloat(float arr[], int n) {
for (int i = 1; i < n; i++) {
float key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
带比较函数的泛型版本
template <typename T>
void insertionSortGeneric(T arr[], int n, bool (*compare)(T, T)) {
for (int i = 1; i < n; i++) {
T key = arr[i];
int j = i - 1;
while (j >= 0 && compare(arr[j], key)) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
优化版(二分查找插入位置)
void binaryInsertionSort(int arr[], int n) {
for (int i = 1; i < n; i++) {
int key = arr[i];
int left = 0, right = i;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] <= key) left = mid + 1;
else right = mid;
}
for (int j = i; j > left; j--) {
arr[j] = arr[j - 1];
}
arr[left] = key;
}
}
完整代码示例可通过C++编译器直接测试,根据实际需求调整数据类型和比较逻辑。
C++模拟动态表
以下是C++模拟动态表(如std::vector
)插入操作的示例,涵盖不同场景和技巧。所有示例均基于动态数组的实现逻辑,可直接运行测试。
示例1:基础动态表尾部插入
#include <iostream>
using namespace std;
class DynamicArray {
int* arr;
int capacity;
int size;
public:
DynamicArray() : capacity(1), size(0) {
arr = new int[capacity];
}
void push_back(int val) {
if (size == capacity) {
capacity *= 2;
int* temp = new int[capacity];
for (int i = 0; i < size; i++) temp[i] = arr[i];
delete[] arr;
arr = temp;
}
arr[size++] = val;
}
void print() {
for (int i = 0; i < size; i++) cout << arr[i] << " ";
}
};
int main() {
DynamicArray da;
da.push_back(10); // 插入10
da.print(); // 输出: 10
}
示例2:指定位置插入元素
void insert(int index, int val) {
if (index < 0 || index > size) throw "Index out of bounds";
if (size == capacity) {
capacity *= 2;
int* temp = new int[capacity];
for (int i = 0; i < index; i++) temp[i] = arr[i];
temp[index] = val;
for (int i = index; i < size; i++) temp[i+1] = arr[i];
delete[] arr;
arr = temp;
} else {
for (int i = size; i > index; i--) arr[i] = arr[i-1];
arr[index] = val;
}
size++;
}
示例3:插入时缩小容量检查
void push_back(int val) {
if (size == capacity) {
capacity = (capacity == 0) ? 1 : capacity * 2;
int* temp = new int[capacity];
for (int i = 0; i < size; i++) temp[i] = arr[i];
delete[] arr;
arr = temp;
}
arr[size++] = val;
}
示例4:批量插入元素
void insertRange(int index, int* vals, int count) {
while (size + count > capacity) capacity *= 2;
int* temp = new int[capacity];
for (int i = 0; i < index; i++) temp[i] = arr[i];
for (int i = 0; i < count; i++) temp[index + i] = vals[i];
for (int i = index; i < size; i++) temp[i + count] = arr[i];
delete[] arr;
arr = temp;
size += count;
}
示例5:插入排序式插入
void sortedInsert(int val) {
push_back(val); // 先插入尾部
for (int i = size - 1; i > 0 && arr[i] < arr[i-1]; i--) {
swap(arr[i], arr[i-1]);
}
}
可根据实际需求组合这些示例中的技术点,构建更复杂的动态表结构。
C++中实现链表
在C++中实现链表反转是一个常见的数据结构和算法问题。以下是多种实现方法和示例,涵盖递归、迭代以及不同场景的优化方案。
基础迭代法
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
};
ListNode* reverseList(ListNode* head) {
ListNode* prev = nullptr;
ListNode* curr = head;
while (curr) {
ListNode* next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
return prev;
}
递归实现
ListNode* reverseListRecursive(ListNode* head) {
if (!head || !head->next) return head;
ListNode* newHead = reverseListRecursive(head->next);
head->next->next = head;
head->next = nullptr;
return newHead;
}
头插法反转
ListNode* reverseListHeadInsert(ListNode* head) {
ListNode dummy(0);
while (head) {
ListNode* next = head->next;
head->next = dummy.next;
dummy.next = head;
head = next;
}
return dummy.next;
}
反转部分链表(指定区间)
ListNode* reverseBetween(ListNode* head, int m, int n) {
ListNode dummy(0);
dummy.next = head;
ListNode* pre = &dummy;
for (int i = 0; i < m - 1; ++i) pre = pre->next;
ListNode* curr = pre->next;
for (int i = 0; i < n - m; ++i) {
ListNode* next = curr->next;
curr->next = next->next;
next->next = pre->next;
pre->next = next;
}
return dummy.next;
}
每K个节点一组反转
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* curr = head;
int count = 0;
while (curr && count < k) {
curr = curr->next;
count++;
}
if (count == k) {
curr = reverseKGroup(curr, k);
while (count-- > 0) {
ListNode* tmp = head->next;
head->next = curr;
curr = head;
head = tmp;
}
head = curr;
}
return head;
}
双指针优化
ListNode* reverseListTwoPointer(ListNode* head) {
ListNode* slow = nullptr;
ListNode* fast = head;
while (fast) {
ListNode* next = fast->next;
fast->next = slow;
slow = fast;
fast = next;
}
return slow;
}
反转链表并返回新头尾
pair<ListNode*, ListNode*> reverseListReturnHeadTail(ListNode* head) {
if (!head || !head->next) return {head, head};
auto [newHead, newTail] = reverseListReturnHeadTail(head->next);
newTail->next = head;
head->next = nullptr;
return {newHead, head};
}
使用栈反转
ListNode* reverseListUsingStack(ListNode* head) {
stack<ListNode*> s;
while (head) {
s.push(head);
head = head->next;
}
ListNode dummy(0);
ListNode* curr = &dummy;
while (!s.empty()) {
curr->next = s.top();
s.pop();
curr = curr->next;
}
curr->next = nullptr;
return dummy.next;
}
反转交替K个节点
ListNode* reverseAlternateK(ListNode* head, int k) {
ListNode* curr = head;
ListNode* prev = nullptr;
ListNode* next = nullptr;
int count = 0;
while (curr && count < k) {
next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
count++;
}
if (head) head->next = curr;
count = 0;
while (curr && count < k - 1) {
curr = curr->next;
count++;
}
if (curr) curr->next = reverseAlternateK(curr->next, k);
return prev;
}
上示例盖了链表反转的多种场景,包括基础反转、部分反转、分组反转等。实际应用中可根据需求选择合适方法,注意处理边界条件(如空链表或单节点链表)。
C++的二叉搜索树
以下是一些基于C++的二叉搜索树(BST)实例,涵盖基础操作、常见算法和实际应用场景。每个例子均附简要说明,可直接运行或扩展。
基础操作
插入节点
struct Node {
int data;
Node* left;
Node* right;
Node(int val) : data(val), left(nullptr), right(nullptr) {}
};
void insert(Node* &root, int val) {
if (!root) root = new Node(val);
else if (val < root->data) insert(root->left, val);
else insert(root->right, val);
}
查找节点
bool search(Node* root, int val) {
if (!root) return false;
if (root->data == val) return true;
return val < root->data ? search(root->left, val) : search(root->right, val);
}
删除节点
Node* deleteNode(Node* root, int val) {
if (!root) return root;
if (val < root->data) root->left = deleteNode(root->left, val);
else if (val > root->data) root->right = deleteNode(root->right, val);
else {
if (!root->left) return root->right;
if (!root->right) return root->left;
Node* temp = root->right;
while (temp->left) temp = temp->left;
root->data = temp->data;
root->right = deleteNode(root->right, temp->data);
}
return root;
}
遍历与输出
中序遍历(升序输出)
void inorder(Node* root) {
if (!root) return;
inorder(root->left);
std::cout << root->data << " ";
inorder(root->right);
}
层序遍历(广度优先)
#include <queue>
void levelOrder(Node* root) {
if (!root) return;
std::queue<Node*> q;
q.push(root);
while (!q.empty()) {
Node* curr = q.front();
q.pop();
std::cout << curr->data << " ";
if (curr->left) q.push(curr->left);
if (curr->right) q.push(curr->right);
}
}
实用功能
计算树的高度
int height(Node* root) {
if (!root) return 0;
return 1 + std::max(height(root->left), height(root->right));
}
检查是否为BST
bool isBST(Node* root, Node* min = nullptr, Node* max = nullptr) {
if (!root) return true;
if (min && root->data <= min->data) return false;
if (max && root->data >= max->data) return false;
return isBST(root->left, min, root) && isBST(root->right, root, max);
}
寻找第k小的元素
int kthSmallest(Node* root, int& k) {
if (!root) return -1;
int left = kthSmallest(root->left, k);
if (left != -1) return left;
if (--k == 0) return root->data;
return kthSmallest(root->right, k);
}
高级应用
BST转双向链表
void treeToDoublyList(Node* root, Node* &prev, Node* &head) {
if (!root) return;
treeToDoublyList(root->left, prev, head);
if (!prev) head = root;
else {
prev->right = root;
root->left = prev;
}
prev = root;
treeToDoublyList(root->right, prev, head);
}
从有序数组构造BST
Node* sortedArrayToBST(std::vector<int>& nums, int start, int end) {
if (start > end) return nullptr;
int mid = start + (end - start) / 2;
Node* root = new Node(nums[mid]);
root->left = sortedArrayToBST(nums, start, mid - 1);
root->right = sortedArrayToBST(nums, mid + 1, end);
return root;
}
范围查询
void rangeQuery(Node* root, int L, int R, std::vector<int>& res) {
if (!root) return;
if (root->data > L) rangeQuery(root->left, L, R, res);
if (root->data >= L && root->data <= R) res.push_back(root->data);
if (root->data < R) rangeQuery(root->right, L, R, res);
}
以上代码片段可直接组合使用或独立扩展。
基于C++的BFS
以下是一些基于C++的BFS(广度优先搜索)遍历实例,涵盖不同应用场景和数据结构。每个例子包含关键代码片段和简要说明,可直接用于实际项目或学习。
基础BFS遍历(无向图)
#include <queue>
#include <vector>
using namespace std;
void bfs(vector<vector<int>>& graph, int start) {
queue<int> q;
vector<bool> visited(graph.size(), false);
q.push(start);
visited[start] = true;
while (!q.empty()) {
int node = q.front();
q.pop();
cout << node << " ";
for (int neighbor : graph[node]) {
if (!visited[neighbor]) {
visited[neighbor] = true;
q.push(neighbor);
}
}
}
}
迷宫最短路径(网格BFS)
struct Point { int x, y; };
int dirs[4][2] = {
{-1,0}, {1,0}, {0,-1}, {0,1}};
int bfsMaze(vector<vector<int>>& maze, Point start, Point end) {
queue<Point> q;
vector<vector<int>> dist(maze.size(), vector<int>(maze[0].size(), -1));
q.push(start);
dist[start.x][start.y] = 0;
while (!q.empty()) {
Point p = q.front();
q.pop();
if (p.x == end.x && p.y == end.y) return dist[p.x][p.y];
for (auto& dir : dirs) {
int nx = p.x + dir[0], ny = p.y + dir[1];
if (nx >= 0 && nx < maze.size() && ny >= 0 && ny < maze[0].size()
&& maze[nx][ny] == 0 && dist[nx][ny] == -1) {
dist[nx][ny] = dist[p.x][p.y] + 1;
q.push({nx, ny});
}
}
}
return -1;
}
层级遍历二叉树
struct TreeNode {
int val;
TreeNode *left, *right;
};
void bfsTree(TreeNode* root) {
if (!root) return;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
int levelSize = q.size();
for (int i = 0; i < levelSize; ++i) {
TreeNode* node = q.front();
q.pop();
cout << node->val << " ";
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
cout << endl; // 换行表示不同层级
}
}
拓扑排序(有向无环图)
vector<int> topologicalSort(vector<vector<int>>& graph, vector<int>& inDegree) {
queue<int> q;
vector<int> result;
for (int i = 0; i < inDegree.size(); ++i) {
if (inDegree[i] == 0) q.push(i);
}
while (!q.empty()) {
int u = q.front();
q.pop();
result.push_back(u);
for (int v : graph[u]) {
if (--inDegree[v] == 0) {
q.push(v);
}
}
}
return result.size() == graph.size() ? result : vector<int>();
}
多源BFS(多个起点)
vector<vector<int>> multiSourceBfs(vector<vector<int>>& grid, vector<Point>& sources) {
queue<Point> q;
vector<vector<int>> dist(grid.size(), vector<int>(grid[0].size(), INT_MAX));
for (auto& p : sources) {
dist[p.x][p.y] = 0;
q.push(p);
}
while (!q.empty()) {
Point p = q.front();
q.pop();
for (auto& dir : dirs) {
int nx = p.x + dir[0], ny = p.y + dir[1];
if (nx >= 0 && nx < grid.size() && ny >= 0 && ny < grid[0].size()
&& grid[nx][ny] == 1 && dist[nx][ny] > dist[p.x][p.y] + 1) {
dist[nx][ny] = dist[p.x][p.y] + 1;
q.push({nx, ny});
}
}
}
return dist;
}
双向BFS(单词接龙问题)
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
unordered_set<string> dict(wordList.begin(), wordList.end());
if (!dict.count(endWord)) return 0;
unordered_set<string> beginSet{beginWord}, endSet{endWord};
int steps = 1;
while (!beginSet.empty() && !endSet.empty()) {
if (beginSet.size() > endSet.size()) swap(beginSet, endSet);
unordered_set<string> tempSet;
for (string word : beginSet) {
for (int i = 0; i < word.size(); ++i) {
char original = word[i];
for (char c = 'a'; c <= 'z'; ++c) {
word[i] = c;
if (endSet.count(word)) return steps + 1;
if (dict.count(word)) {
tempSet.insert(word);
dict.erase(word);
}
}
word[i] = original;
}
}
beginSet = tempSet;
steps++;
}
return 0;
}
0-1权图BFS(双端队列)
int bfs01(vector<vector<pair<int, int>>>& graph, int start, int end) {
deque<int> dq;
vector<int> dist(graph.size(), INT_MAX);
dist[start] = 0;
dq.push_front(start);
while (!dq.empty()) {
int u = dq.front();
dq.pop_front();
for (auto& [v, w] : graph[u]) {
if (dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
w == 0 ? dq.push_front(v) : dq.push_back(v);
}
}
}
return dist[end];
}
状态压缩BFS(八数码问题)
int slidingPuzzle(vector<vector<int>>& board) {
string target = "123450";
string start;
for (auto& row : board) {
for (int num : row) {
start += to_string(num);
}
}
vector<vector<int>> dirs = {
{1,3}, {0,2,4}, {1,5}, {0,4}, {1,3,5}, {2,4}};
queue<string> q{
{start}};
unordered_set<string> visited{start};
int steps = 0;
while (!q.empty()) {
int size = q.size();
while (size--) {
string curr = q.front();
q.pop();
if (curr == target) return steps;
int zero = curr.find('0');
for (int next : dirs[zero]) {
string neighbor = curr;
swap(neighbor[zero], neighbor[next]);
if (!visited.count(neighbor)) {
visited.insert(neighbor);
q.push(neighbor);
}
}
}
steps++;
}
return -1;
}
- 网页爬虫:基于URL的层级抓取
- 连通区域检测:图像处理中的像素连通性分析
- 游戏AI:寻路算法(A*的基础)
- 电路布线:VLSI设计中的网格布线
每个场景均可通过调整上述基础模板实现。BFS的核心在于队列管理和状态记录,实际应用中需根据问题特点设计节点表达方式和转移规则。
基于C++的社交网络好友推荐
以下是一些基于C++的社交网络好友推荐实现思路和示例代码片段,涵盖不同算法和场景的应用:
基于共同好友的推荐
void recommendByCommonFriends(const User& targetUser, const SocialGraph& graph) {
unordered_map<UserId, int> commonCounts;
for (const auto& friendId : targetUser.friends) {
for (const auto& potentialFriend : graph.getUser(friendId).friends) {
if (!targetUser.isFriend(potentialFriend)) {
commonCounts[potentialFriend]++;
}
}
}
// 按共同好友数排序输出推荐
}
Jaccard相似度推荐
vector<UserId> recommendByJaccard(const User& u1, const User& u2) {
unordered_set<UserId> unionSet(u1.friends.begin(), u1.friends.end());
unionSet.insert(u2.friends.begin(), u2.friends.end());
unordered_set<UserId> intersection;
for (auto& f : u1.friends) {
if (u2.friends.count(f)) intersection.insert(f);
}
double similarity = intersection.size() / (double)unionSet.size();
return similarity > 0.2 ? getTopRecommendations() : vector<UserId>();
}
基于图的BFS推荐
void bfsRecommendation(UserId start, const SocialGraph& graph, int depth) {
queue<pair<UserId, int>> q;
unordered_set<UserId> visited;
q.push({start, 0});
while (!q.empty()) {
auto [current, dist] = q.front();
q.pop();
if (dist == depth) {
recommendUser(current);
continue;
}
for (UserId neighbor : graph.getFriends(current)) {
if (!visited.count(neighbor)) {
visited.insert(neighbor);
q.push({neighbor, dist+1});
}
}
}
}
矩阵分解推荐
void matrixFactorization(const MatrixXd& R, int k, double alpha, double lambda) {
MatrixXd P = MatrixXd::Random(R.rows(), k);
MatrixXd Q = MatrixXd::Random(k, R.cols());
for (int iter = 0; iter < 50; ++iter) {
for (int i = 0; i < R.rows(); ++i) {
for (int j = 0; j < R.cols(); ++j) {
if (R(i,j) > 0) {
double error = R(i,j) - P.row(i)*Q.col(j);
P.row(i) += alpha * (2 * error * Q.col(j).transpose() - lambda * P.row(i));
Q.col(j) += alpha * (2 * error * P.row(i).transpose() - lambda * Q.col(j));
}
}
}
}
}
基于兴趣标签的推荐
vector<UserId> recommendByTags(const User& target, const vector<User>& users) {
vector<pair<UserId, double>> scores;
for (const auto& user : users) {
if (user.id == target.id) continue;
double score = cosineSimilarity(target.tags, user.tags);
scores.emplace_back(user.id, score);
}
sort(scores.begin(), scores.end(), [](auto& a, auto& b) {
return a.second > b.second;
});
vector<UserId> recommendations;
for (int i = 0; i < min(5, (int)scores.size()); ++i) {
recommendations.push_back(scores[i].first);
}
return recommendations;
}
其他实现方向(需完整代码可扩展)
1. 基于PageRank的推荐
void calculatePageRank(const SocialGraph& graph) {
// 实现PageRank算法计算用户影响力
}
2. 社团发现推荐
vector<Community> detectCommunities(Graph& g) {
// 使用Louvain等方法发现社团
}
3. 基于深度学习的嵌入
class GraphSAGE : public torch::nn::Module {
// 实现图神经网络嵌入
};
4. 实时推荐系统
class RealTimeRecommender {
// 使用消息队列处理实时交互
};
5. 地理位置推荐
vector<UserId> recommendByLocation(UserId userId, double radiusKm) {
// 基于地理位置的推荐逻辑
}
6. 混合推荐系统
void hybridRecommendation(const User& user) {
// 组合多种推荐策略
}
7. 基于活动的推荐
void eventBasedRecommendation(UserId userId) {
// 根据用户参与的活动推荐
}
8. 社交影响力推荐
void influenceBasedRecommendation() {
// 基于用户影响力评分
}
9. 时间衰减推荐
double timeDecayScore(Interaction interaction) {
// 实现时间衰减因子
}
10. 社交图谱嵌入
void graphEmbedding() {
// 使用node2vec等算法
}
完整系统架构示例
class FriendRecommender {
private:
SocialGraph graph;
RecommenderAlgorithm* algorithm;
public:
void setAlgorithm(RecommenderAlgorithm* algo) {
algorithm = algo;
}
vector<Recommendation> generateRecommendations(UserId userId) {
return algorithm->recommend(userId, graph);
}
};
// 策略模式实现不同算法
class RecommenderAlgorithm {
public:
virtual vector<Recommendation> recommend(UserId, SocialGraph&) = 0;
};
// 具体算法实现
class CommonFriendsAlgorithm : public RecommenderAlgorithm {
// 实现具体逻辑
};
性能优化技巧
// 使用位集加速共同好友计算
bitset<MAX_USERS> getFriendBitset(const User& user) {
bitset<MAX_USERS> bs;
for (auto fid : user.friends) bs.set(fid);
return bs;
}
// 并行计算推荐
void parallelRecommend() {
#pragma omp parallel for
for (int i = 0; i < users.size(); ++i) {
// 并行处理每个用户的推荐
}
}
以上示例涵盖了从基础到进阶的各种好友推荐实现方法。完整实现需要结合具体社交网络的数据结构和业务需求进行扩展。每个示例都可以作为独立模块或组合成混合推荐系统。
DFS(深度优先搜索)
DFS拓扑排序简介
DFS(深度优先搜索)拓扑排序是一种用于有向无环图(DAG)的排序算法,通过递归或栈实现节点的线性排序,确保每个节点在其前驱节点之后出现。
基本实现代码
以下是C++中DFS拓扑排序的基础代码框架:
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
void topologicalSortUtil(int v, vector<bool>& visited, stack<int>& Stack, const vector<vector<int>>& adj) {
visited[v] = true;
for (int i : adj[v]) {
if (!visited[i]) {
topologicalSortUtil(i, visited, Stack, adj);
}
}
Stack.push(v);
}
void topologicalSort(int V, const vector<vector<int>>& adj) {
stack<int> Stack;
vector<bool> visited(V, false);
for (int i = 0; i < V; ++i) {
if (!visited[i]) {
topologicalSortUtil(i, visited, Stack, adj);
}
}
while (!Stack.empty()) {
cout << Stack.top() << " ";
Stack.pop();
}
}
应用实例分类
以下是DFS拓扑排序的应用实例,涵盖不同场景:
课程安排问题
给定课程间的先修关系,输出可行的学习顺序。
vector<vector<int>> adj = {
{1, 2}, {3}, {3}, {}};
topologicalSort(4, adj); // 输出: 0 2 1 3
任务调度
根据任务依赖关系生成执行顺序。
vecto