书籍封面
![](https://img-blog.csdnimg.cn/5abe17ac8401471da366938717b94424.png#pic_center)
第一章 前言
1. 本人衷心建议
~~~~~~ 如果你是一位初学者,我指的是你只会基本的 C/C++ 编程,即使编的很烂,这本书对于你算法和数据结构的提升非常有帮助,所涉及的每一个算法案例都非常经典,且有非常细致的讲解,本人强烈推荐你理解好每一道题的细节。
~~~~~~ 如果你具备一定的算法与数据结构能力,本书的内容依然很适合你,你可以有针对性的补充相应章节的内容,一定会有意想不到的收获。
~~~~~~ 当你看到如此多的章节,每个章节还有如此多的内容时,一定不要被吓到,当本人对算法与数据结构一头雾水时,也是一步步算法过来的,这本书的学习大概花费了我近一个月的时间,所以你不必想着马上就可以看完很多,知识需要慢慢消化,Good luck !
注:算法涉及的题目如何在网上搜索见下文,特意强调,无需翻墙。
2. 本书涉及的所有题目以及对应的编号
3. AOJ 使用方法《挑战程序设计竞赛》
网址
https://onlinejudge.u-aizu.ac.jp/home
注册
点右上角 Guest ==> Sign Up
![](https://img-blog.csdnimg.cn/4c7a0d7fab68496497da49b2f3ed950a.png#pic_center)
注册完成后点击 submit
![](https://img-blog.csdnimg.cn/9211510a1fb34f559448e3025de351de.png#pic_center)
查找题目
1.点击网站上方 Challenge ==> Search
![](https://img-blog.csdnimg.cn/3e40193d2ed14f918c400bce82708d98.png#pic_center)
2.直接搜索题目
3. 或者根据上面 2 中题目对应的编号进行查找
点击 Volumes ,因为题号是 0202,前两位是 02,所以选 Volume2,然后找到对应编号即可
做题
进去之后选择语言,编写代码,并提交即可。当你看到全是日语的时候,不要惊慌,你完全可以看懂题,毕竟每道题的中文翻译都在本文中了…
提交后往下翻查看状态
用 virtual judge 做 AOJ 的题目
一个非常便捷的网站:https://vjudge.net/problem#OJId=Aizu&probNum=&title=&source=&category=all
首先进行账号注册
![](https://img-blog.csdnimg.cn/6bee1b6a0804494a80994790a3b6aaa7.png#pic_center)
点进去做题就可以了
![](https://img-blog.csdnimg.cn/0cd8f5f3688f4568ada1329776a8c75f.png#pic_center)
第二章 算法与复杂度
2.4 算法的效率
2.4.1 复杂度的评估
2.4.2 大 O 表示法
2.4.3 复杂度的比较
第三章 初等排序
3.1 挑战问题之前——排序
3.2 插入排序法
题解
代码实现
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
// 打印数列A
void print(int A[],int n) {
for (int i = 0; i < n; i++)
printf("%d%c", A[i], i == n - 1 ? '\n' : ' ');
}
// 插入排序法
void insertsort(int A[], int n) {
// 从第二个开始,往前找比当前的小的值,
// 比当前值大的就往后移,知道找到小于等于A[i]的
for (int i = 1; i < n; i++) {
int j = i - 1;
int v = A[i];
while (j >= 0 && A[j] > v) {
A[j + 1] = A[j];
j--;
}
A[j + 1] = v;
print(A, n);
}
}
int main() {
int n, A[110];
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &A[i]);
print(A, n);
insertsort(A, n);
return 0;
}
3.3 冒泡排序法
题解
代码实现
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
// 冒泡排序法
int bubbleSort(int A[], int n) {
int cnt = 0;
// 标记是否需要继续冒泡
int flag = 1;
for (int i = 0; flag; i++) {
flag = 0;
for (int j = n - 1; j >= i + 1; j--) {
// 交换两个相邻的元素
if (A[j] < A[j - 1]) {
swap(A[j], A[j - 1]);
flag = 1;
cnt++;
}
}
}
return cnt;
}
int main() {
int n, A[110];
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &A[i]);
int cnt = bubbleSort(A, n);
for (int i = 0; i < n; i++)
printf("%d%c", A[i], i == n - 1 ? '\n' : ' ');
printf("%d\n", cnt);
return 0;
}
3.4 选择排序法
题解
代码实现
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
// 选择排序法
int selectionSort(int A[], int n) {
int cnt = 0;
for (int i = 0; i < n; i++) {
int minj = i;
for (int j = i; j < n; j++) {
if (A[j] < A[minj])
minj = j;
}
if (i != minj)
swap(A[i], A[minj]), cnt++;
}
return cnt;
}
int main() {
int n, A[110];
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &A[i]);
int cnt = selectionSort(A, n);
for (int i = 0; i < n; i++)
printf("%d%c", A[i], i == n - 1 ? '\n' : ' ');
printf("%d\n", cnt);
return 0;
}
3.6 希尔排序法
题解
代码实现
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
//g数组
vector<int>G;
long long cnt;
int A[1000000 + 10];
// 间隔为g的插入排序
void insertionSort(int A[], int n, int g) {
for (int i = g; i < n; i++) {
int v = A[i];
int j = i - g;
while (j >= 0 && A[j] > v) {
A[j + g] = A[j];
j -= g;
cnt++;
}
A[j + g] = v;
}
}
//希尔排序
void shellsort(int A[], int n) {
for (int i = G.size() - 1; i >= 0; i--) {
insertionSort(A, n, G[i]);
}
}
// 生成g数组
void getArray(int n) {
for (int h = 1; h <= n;) {
G.push_back(h);
h = 3 * h + 1;
}
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &A[i]);
getArray(n);
shellsort(A,n);
int len = G.size();
printf("%d\n", len);
for (int i = len - 1; i >= 0; i--)
printf("%d%c", G[i], i == 0 ? '\n' : ' ');
printf("%lld\n", cnt);
for (int i = 0; i < n; i++)
printf("%d\n", A[i]);
return 0;
}
第四章 数据结构
4.1 挑战问题之前一一什么是数据结构
4.2 栈
题解
代码实现
stack1
/*实现栈的功能*/
#include<iostream>
#include<cstdio>
using namespace std;
// 实现栈的功能
// top是指向栈顶的指针, s是实现栈结构的数组
int top, S[1000];
// 将x压入栈的操作
void push(int x) {
// top加1之后将元素插入到top所指的位置
S[++top] = x;
}
// 将栈顶元素返回并删除
int pop() {
top--;
return S[top + 1];
}
// 字符转数字
int CharToInt(char s[]) {
int ans = 0, i = 0;
while (s[i] != '\0') {
ans = ans * 10 + s[i] - '0';
i++;
}
return ans;
}
int main() {
char s[1000];
int a, b;
// 清空栈
top = 0;
while(scanf("%s",s)!=EOF){
if (s[0] == '+') {
b = pop();a = pop();
push(a + b);
}else if (s[0] == '-') {
b = pop(); a = pop();
push(a - b);
}else if (s[0] == '*') {
b = pop(); a = pop();
push(a * b);
}else {
push(CharToInt(s));
}
}
printf("%d\n",pop());
return 0;
}
stack2
/*利用STL的stack类*/
#include<iostream>
#include<cstdio>
#include<stack>
using namespace std;
// 字符转数字
int CharToInt(char s[]) {
int ans = 0, i = 0;
while (s[i] != '\0') {
ans = ans * 10 + s[i] - '0';
i++;
}
return ans;
}
int main()
{
char s[1000];
int a, b;
stack<int> S;
while(scanf("%s",s)!=EOF){
if (s[0] == '+') {
b = S.top(); S.pop();
a = S.top(); S.pop();
S.push(a + b);
}else if (s[0] == '-') {
b = S.top(); S.pop();
a = S.top(); S.pop();
S.push(a - b);
}else if (s[0] == '*') {
b = S.top(); S.pop();
a = S.top(); S.pop();
S.push(a * b);
}else {
S.push(CharToInt(s));
}
}
printf("%d\n", S.top());
return 0;
}
4.3 队列
题解
代码实现
queue1
/*实现队列功能*/
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxx = 100000 + 10;
struct task {
// 任务的名字和耗费的时间
char name[100];
int t;
};
task Queue[maxx];
// 指向队头和队尾的指针
int head, tail;
// 判断循环队列是否已满
bool isFull() {
return head == (tail + 1) % maxx;
}
// 判断循环队列是否为空
bool isEmpty() {
return head == tail;
}
// 将元素x添加到队列的末尾
void enqueue(task x) {
Queue[tail] = x;
// 循环队列
tail = (tail + 1) % maxx;
}
// 返回队头元素并删除
task dequeue() {
task x = Queue[head];
// 循环队列
head = (head + 1) % maxx;
return x;
}
int main() {
int n, q;
scanf("%d %d", &n, &q);
for (int i = 0; i < n; i++) {
scanf("%s %d", Queue[i].name, &Queue[i].t);
}
// 当前已经有n个元素
head = 0; tail = n;
// 当前时间
int nowtime = 0;
while (!isEmpty()) {
// 取出队头
task u = dequeue();
// 执行时间片 p 和 所需要时间u.t
int c = min(q, u.t);
// 任务执行了 c ms
u.t -= c;
// 当前时间加上c ms
nowtime += c;
// 任务还没做完,加入到队尾
if (u.t > 0) {
enqueue(u);
}
else { // 任务已经完成,打印出结果
printf("%s %d\n", u.name, nowtime);
}
}
return 0;
}
queue2
/*利用STL的queue类*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int maxx = 100000 + 10;
struct task {
// 任务的名字和耗费的时间
char name[100];
int t;
};
queue<task> Queue;
int main() {
int n, q;
scanf("%d %d", &n, &q);
for (int i = 0; i < n; i++) {
task t;
scanf("%s %d", t.name, &t.t);
Queue.push(t);
}
// 当前时间
int nowtime = 0;
while (!Queue.empty()) {
// 取出队头
task u = Queue.front();
Queue.pop();
// 执行时间片 p 和 所需要时间u.t
int c = min(q, u.t);
// 任务执行了 c ms
u.t -= c;
// 当前时间加上c ms
nowtime += c;
// 任务还没做完,加入到队尾
if (u.t > 0) {
Queue.push(u);
}
else { // 任务已经完成,打印出结果
printf("%s %d\n", u.name, nowtime);
}
}
return 0;
}
4.4 链 表
题解
代码实现
List1
/*实现双向链表功能*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int maxx = 100000 + 10;
struct Node {
int key;
Node *prev, *next;
};
Node *nil;
// 链表初始化
void init() {
nil = (Node *)malloc(sizeof(Node));
//C++ nil = new Node();
nil->next = nil;
nil->prev = nil;
}
// 从链表搜索出值为key的结点 O(n)
Node* listSearch(int key) {
// 从链表头结点开始访问
Node *cur = nil->next;
while (cur != nil && cur->key != key) {
cur = cur->next;
}
return cur;
}
// 打印链表
void printList() {
Node *cur = nil->next;
int flag = 0;
while (cur != nil) {
if (flag++ > 0)
printf(" ");
printf("%d", cur->key);
cur = cur->next;
}
printf("\n");
}
// 删除结点t
void deleteNode(Node *t) {
// t为头结点不做处理
if (t == nil)
return;
t->prev->next = t->next;
t->next->prev = t->prev;
free(t);
//C++ delete(t);
}
// 删除头结点
void deleteFirst() {
deleteNode(nil->next);
}
// 删除尾结点
void deleteLast() {
deleteNode(nil->prev);
}
// 删除指定的key O(n)
void deleteKey(int key) {
deleteNode(listSearch(key));
}
// 在链表头部添加元素key
void insert(int key) {
Node *x = (Node *)malloc(sizeof(Node));
//C++ Node *x = new Node();
x->key = key;
x->next = nil->next;
nil->next->prev = x;
nil->next = x;
x->prev = nil;
}
int main() {
int n, key, size = 0;
char op[20];
scanf("%d", &n);
getchar();
// 初始化链表
init();
for (int i = 0; i < n; i++) {
scanf("%s %d", op, &key);
if (op[0] == 'i') {
insert(key);
size++;
}
else {
if (strlen(op) > 6) {
if (op[6] == 'F') {//删除头结点
deleteFirst();
}
else {//删除尾结点
deleteLast();
}
}
else { //删除结点
deleteKey(key);
}
size--;
}
}
printList();
return 0;
}
List2
/*利用STL的list类*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<list>
using namespace std;
int main() {
int n, key;
char op[20];
list<int> List;
scanf("%d", &n);
getchar();
for (int i = 0; i < n; i++) {
scanf("%s %d", op, &key);
if (op[0] == 'i') {
List.push_front(key);
}
else {
if (strlen(op) > 6) {
if (op[6] == 'F') {//删除头结点
List.pop_front();
}
else {//删除尾结点
List.pop_back();
}
}
else { //删除结点
for (list<int>::iterator it = List.begin(); it != List.end(); it++) {
if (*it == key) {
List.erase(it);
break;
}
}
}
}
}
int flag = 0;
for (list<int>::iterator it = List.begin(); it != List.end(); it++) {
if (flag != 0)
printf(" ");
flag++;
printf("%d", (*it));
}
printf("\n");
return 0;
}
4.5 标准库的数据结构
4.5.2 stack
4.5.3 queue
4.5.4 vector
4.5.5 list
第五章 搜索
5.1 挑战问题之前一搜索
5.2 线性搜索
题解
代码实现
#include<iostream>
#include<cstdio>
using namespace std;
// 线性搜索
bool LinearSearch(int A[], int n, int key) {
// 将key设置为数列最后一项
A[n] = key;
int i = 0;
while (A[i] != key)
i++;
// 如果i不是n,则代表在原来的数列中找到key,
// 否则代表原来数列中没有key,找到的是刚才插入到最后的元素
return i != n;
}
int main() {
int n, q, key;
int sum = 0;
int A[10000 + 10];
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &A[i]);
scanf("%d", &q);
for (int i = 0; i < q; i++) {
scanf("%d", &key);
if (LinearSearch(A, n, key))
sum++;
}
printf("%d\n", sum);
}
5.3 二分搜索
题解
代码实现
方式一
/*实现二分搜索*/
#include<iostream>
#include<cstdio>
using namespace std;
// 二分搜索
bool BinarySearch(int A[], int n, int key) {
int left = 0 , right = n, mid;
while (left < right) {
mid = (left + right) / 2;
if (key == A[mid])
return true;
if(key > A[mid])
left = mid + 1;
if (key < A[mid])
right = mid;
}
return false;
}
int main() {
int n, q, key;
int sum = 0;
int A[100000 + 10];
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &A[i]);
scanf("%d", &q);
for (int i = 0; i < q; i++) {
scanf("%d", &key);
if (BinarySearch(A, n, key))
sum++;
}
printf("%d\n", sum);
}
方式二
/*利用STL的binary_search*/
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int main() {
int n, q, key;
int sum = 0;
int A[100000 + 10];
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &A[i]);
scanf("%d", &q);
for (int i = 0; i < q; i++) {
scanf("%d", &key);
if (binary_search(A, A + n, key))
sum++;
}
printf("%d\n", sum);
}
5.4 散列法
题解
代码实现
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
const LL maxx = 1000000 + 10;
char H[maxx][15];
// 将字符转换为数值
LL CharToInt(char ch) {
if (ch == 'A') return 1;
if (ch == 'C') return 2;
if (ch == 'G') return 3;
if (ch == 'T') return 4;
return 0;
}
// 将字符串转成数值并生成key
long long getKey(char str[]) {
LL sum = 0, p = 1;
LL len = strlen(str);
for (long long int i = 0; i < len; i++) {
sum += p * CharToInt(str[i]);
p *= 5;
}
return sum;
}
LL h1(LL key) {
return key % maxx;
}
LL h2(LL key) {
return 1 + (key % (maxx - 1));
}
// find 操作
bool find(char str[]) {
LL key, h;
key = getKey(str);
for (LL i = 0;; i++) {
h = (h1(key) + i * h2(key)) % maxx;
//找到key
if (strcmp(H[h], str) == 0) return true;
// key不存在
else if (strlen(H[h]) == 0) return false;
}
return false;
}
// insert操作
void insert(char str[]) {
LL key, h;
key = getKey(str);
for (LL i = 0;; i++) {
h = (h1(key) + i * h2(key)) % maxx;
//找到key
if (strcmp(H[h], str) == 0) return ;
// key不存在
else if (strlen(H[h]) == 0) {
// 插入
strcpy(H[h], str);
return ;
}
}
}
int main() {
int n, q, key;
char str[20], op[20];
for (int i = 0; i < maxx; i++)
H[i][0] = '\0';
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%s %s", op, str);
if (op[0] == 'i')
insert(str);
else {
if (find(str))
printf("yes\n");
else
printf("no\n");
}
}
return 0;
}
5.5 借助标准库搜索
5.5.1 迭 代 器
5.5.2 lower bound
5.6 搜索的应用——计算最优解
这是一个稍微有些难度的挑战题。如果各位觉得太难可以暂时跳过,等具备一定实力后再冋过头来挑战。
题解
代码实现
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
LL A[100000 + 10];
int n, k;
//模拟装货过程
bool check(LL P) {
int cnt = k;
for (int i = 0; i < n; ) {
LL t = P;
cnt--;
// 卡车已经用完,证明卡车当前最大装载量不能装完货物
if (cnt < 0)
return false;
while (i < n && t >= 0) {
// 剩余重量可以装上当前货物
if (t >= A[i])
t -= A[i],i++;
//装不下则用下一辆卡车去装
if (t < A[i])
break;
}
}
return true;
}
// 二分找出P的最小值
LL BinarySearch(LL sum) {
LL left = 0, right = sum;
LL mid;
while (left + 1 < right) {
mid = (left + right) / 2;
//可以装好货物
if (check(mid))
right = mid;
else
left = mid;
}
return right;
}
int main() {
LL sum = 0;
scanf("%d %d", &n, &k);
for (int i = 0; i < n; i++) {
scanf("%lld", &A[i]);
sum += A[i];
}
LL P = BinarySearch(sum);
printf("%lld\n", P);
return 0;
}
第六章 递归和分治法
6.1 挑战问题之前一递归与分治
6.2 穷 举 搜 索
题解
代码实现
#include<iostream>
#include<cstdio>
using namespace std;
int n, A[30];
// 递归穷举
bool solve(int i, int m) {
// 已经找到得到m的组合方式
if (m == 0)
return true;
// 已经穷举过A的全部元素了
if (i >= n)
return false;
// 分别穷举 不选A[i] 和 选择A[i]的情况
return solve(i + 1, m) || solve(i + 1, m - A[i]);
}
int main(){
int t, M;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &A[i]);
}
scanf("%d", &t);
for (int i = 0; i < t; i++) {
scanf("%d", &M);
if (solve(0, M))
printf("yes\n");
else
printf("no\n");
}
return 0;
}
6.3 科 赫 曲 线
题解
代码实现
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const double PI = acos(-1.0);
struct Point{
double x,y;
};
void koch(int n, Point a, Point b) {
// 递归结束
if (n == 0)
return;
// 三等分点s,t ,(u 是 s,t 上方的点)
Point s, t, u;
// 角度从度转成弧度
double th = PI * 60.0 / 180.0;
s.x = (2.0 * a.x + 1.0 * b.x) / 3.0;
s.y = (2.0 * a.y + 1.0 * b.y) / 3.0;
t.x = (1.0 * a.x + 2.0 * b.x) / 3.0;
t.y = (1.0 * a.y + 2.0 * b.y) / 3.0;
u.x = (t.x - s.x) * cos(th) - (t.y - s.y) * sin(th) + s.x;
u.y = (t.x - s.x) * sin(th) + (t.y - s.y) * cos(th) + s.y;
koch(n - 1, a, s);
printf("%.8lf %.8lf\n", s.x, s.y);
koch(n - 1, s, u);
printf("%.8lf %.8lf\n", u.x, u.y);
koch(n - 1, u, t);
printf("%.8lf %.8lf\n", t.x, t.y);
koch(n - 1, t, b);
}
int main(){
Point a, b;
int n;
scanf("%d", &n);
// 初始化
a.x = a.y = 0;
b.x = 100, b.y = 0;
printf("%.8lf %.8lf\n", a.x, a.y);
koch(n, a, b);
printf("%.8lf %.8lf\n", b.x, b.y);
return 0;
}
第八章 树
8.2 有根树的表达
题解
代码实现
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAX = 100005;
const int NIL = -1;
struct Node {
int p, l, r;
};
Node Tree[MAX];
int n, Depth[MAX];
void print(int u) {
printf("node %d: ", u);
printf("parent = %d, ", Tree[u].p);
printf("depth = %d, ", Depth[u]);
if (Tree[u].p == NIL)
printf("root, ");
else if (Tree[u].l == NIL)
printf("leaf, ");
else
printf("internal node, ");
printf("[");
// 从子结点的最左边开始遍历
int c = Tree[u].l;
while (c != NIL) {
printf("%d", c);
if (Tree[c].r != NIL)
printf(", ");
// 跳到右侧紧邻的兄弟结点
c = Tree[c].r;
}
printf("]\n");
}
// 递归得求深度
void rec(int u, int depth) {
Depth[u] = depth;
if (Tree[u].r != NIL) // 右侧兄弟设置为相同深度
rec(Tree[u].r, depth);
if (Tree[u].l != NIL) // 最左侧子结点的深度设置为自己的深度 + 1
rec(Tree[u].l, depth + 1);
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
Tree[i].p = Tree[i].l = Tree[i].r = NIL;
}
int v, num, c, l, root;
for (int i = 0; i < n; i++) {
// v是当前结点值 ,num是v结点的子结点数
scanf("%d %d", &v, &num);
for (int j = 0; j < num; j++) {
scanf("%d", &c);
//设置最左侧结点 和 紧邻右侧兄弟结点
// l记录的是上一个结点,将当前的结点设置为l的紧邻右侧结点
if (j == 0)
Tree[v].l = c;
else
Tree[l].r = c;
l = c;
// 设置父结点
Tree[c].p = v;
}
}
// 找出根结点
for (int i = 0; i < n; i++) {
if (Tree[i].p == NIL)
root = i;
}
rec(root, 0);
for (int i = 0; i < n; i++)
print(i);
return 0;
}
8.3 二叉树的表达
题解
代码实现
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAX = 100005;
const int NIL = -1;
struct Node {
int parent, left, right;
};
Node Tree[MAX];
int n, Depth[MAX], Height[MAX];
int getDegree(int u) {
int deg = 0;
if (Tree[u].left != NIL)
deg++;
if (Tree[u].right != NIL)
deg++;
return deg;
}
void setDepth(int u, int depth) {
if (u == NIL)
return;
Depth[u] = depth;
setDepth(Tree[u].left, depth + 1);
setDepth(Tree[u].right, depth + 1);
}
int setHeight(int u) {
int h1 = 0, h2 = 0;
if (Tree[u].left != NIL)
h1 = setHeight(Tree[u].left) + 1;
if (Tree[u].right != NIL)
h2 = setHeight(Tree[u].right) + 1;
return Height[u] = max(h1, h2);
}
int getSibling(int u) {
if (Tree[u].parent == NIL)
return NIL;
//存在左兄弟
if (Tree[Tree[u].parent].left != u && Tree[Tree[u].parent].left != NIL)
return Tree[Tree[u].parent].left;
//存在右兄弟
if (Tree[Tree[u].parent].right != u && Tree[Tree[u].parent].right != NIL)
return Tree[Tree[u].parent].right;
return NIL;
}
void print(int u) {
printf("node %d: parent = %d, sibling = %d, degree = %d, depth = %d, height = %d, "
, u, Tree[u].parent, getSibling(u), getDegree(u), Depth[u], Height[u]);
if (Tree[u].parent == NIL)
printf("root\n");
else if (Tree[u].left != NIL || Tree[u].right != NIL)
printf("internal node\n");
else
printf("leaf\n");
}
int main() {
int n;
int u, l, r, root;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
Tree[i].parent = Tree[i].left = Tree[i].right = NIL;
}
for (int i = 0; i < n; i++) {
scanf("%d %d %d", &u, &l, &r);
Tree[u].left = l;
Tree[u].right = r;
Tree[l].parent = u;
Tree[r].parent = u;
}
for (int i = 0; i < n; i++) {
if (Tree[i].parent == NIL)
root = i;
}
setHeight(root);
setDepth(root, 0);
for (int i = 0; i < n; i++)
print(i);
return 0;
}
8.4 树的遍历
题解
前序遍历:F, B, A, D, C, E, G, I, H
中序遍历:A, B, C, D, E, F, G, H, I
后续遍历:A, C, E, D, B, H, I, G, F
层序遍历:F, B, G, A, D, I, C, E, H
代码实现
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAX = 100005;
const int NIL = -1;
struct Node {
int parent, left, right;
};
Node Tree[MAX];
int n;
//前序排序
void PreorderTreeWalk(int u) {
if (u == NIL)
return;
printf(" %d", u);
PreorderTreeWalk(Tree[u].left);
PreorderTreeWalk(Tree[u].right);
}
//中序排序
void InorderTreeWalk(int u) {
if (u == NIL)
return;
InorderTreeWalk(Tree[u].left);
printf(" %d", u);
InorderTreeWalk(Tree[u].right);
}
//后序排序
void PostorderTreeWalk(int u) {
if (u == NIL)
return;
PostorderTreeWalk(Tree[u].left);
PostorderTreeWalk(Tree[u].right);
printf(" %d", u);
}
int main() {
int n;
int u, l, r, root;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
Tree[i].parent = Tree[i].left = Tree[i].right = NIL;
}
for (int i = 0; i < n; i++) {
scanf("%d %d %d", &u, &l, &r);
Tree[u].left = l;
Tree[u].right = r;
Tree[l].parent = u;
Tree[r].parent = u;
}
for (int i = 0; i < n; i++) {
if (Tree[i].parent == NIL)
root = i;
}
printf("Preorder\n");
PreorderTreeWalk(root);
printf("\nInorder\n");
InorderTreeWalk(root);
printf("\nPostorder\n");
PostorderTreeWalk(root);
printf("\n");
return 0;
}
8.5 树遍历的应用 —— 树的重建
题解
图中后序遍历的结果为:3 5 6 4 2 8 9 7 1
代码实现
1、已知前序遍历和中序遍历,求后续遍历。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
vector<int> pre, in, post;
int pos;
int n, k;
void reconstruction(int left, int right) {
// 遍历到叶结点
if (left >= right)
return;
// 当前子树的根
int root = pre[pos++];
// 找出当前根的值在中序数组里的下标
int m = distance(in.begin(), find(in.begin(), in.end(), root));
reconstruction(left, m);
reconstruction(m + 1, right);
//存入到后序数组
post.push_back(root);
}
void solve() {
pos = 0;
reconstruction(0, pre.size());
for (int i = 0; i < n; i++) {
printf("%d%c", post[i], i == n-1?'\n':' ');
}
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &k);
pre.push_back(k);
}
for (int i = 0; i < n; i++) {
scanf("%d", &k);
in.push_back(k);
}
solve();
return 0;
}
2、已知后续遍历和中序遍历求前序遍历
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
vector<int> pre, in, post;
int pos;
int n, k;
void reconstruction(int left, int right) {
// 遍历到叶结点
if (left >= right)
return;
// 当前子树的根
int root = pre[pos--];
// 找出当前根的值在中序数组里的下标
int m = distance(in.begin(), find(in.begin(), in.end(), root));
reconstruction(m + 1, right); // 注意优先建立左边的树,根节点时从后往前的
reconstruction(left, m);
//存入到后序数组
post.push_back(root);
}
void solve() {
pos = n - 1;
reconstruction(0, pre.size());
for (int i = n - 1; i >= 0; i--) {
printf("%d%c", post[i], i == n-1?'\n':' ');
}
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &k);
pre.push_back(k);
}
for (int i = 0; i < n; i++) {
scanf("%d", &k);
in.push_back(k);
}
solve();
return 0;
}
3、已知前序遍历和后序遍历不可求中序遍历
4、已知前序||中序||后序遍历(仅该树为完全二叉树),求层序遍历
#include<bits/stdc++.h>
using namespace std;
int n,a[100],tree[100];
int idx=1;
void f(int x)
{
if(x > n) return ;
tree[x] = a[idx++]; //已知前序遍历,求层序遍历
f(x << 1);
//tree[x] = a[idx++]; //已知中序遍历,求层序遍历
f((x << 1) + 1);
//tree[x] = a[idx++]; //已知后序遍历,求层序遍历
}
int main(){
scanf("%d",&n);
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
f(1);
printf("层次遍历:");
for(int i = 1; i <= n; i++)
printf("%d ",tree[i]);
return 0;
}
5、已知层序遍历,求前中后遍历(仅满足树为完全二叉树时)
#include<stdio.h>
const int N=100;
int pre[N],in[N],post[N],tree[N],idx;;
int n;
void f(int x)
{
pre[idx++]=tree[x]; //求前序遍历
if(x<<1<=n)
f(x<<1);
//in[idx++]=tree[x]; //求中序遍历
if((x<<1)+1<=n) //记得加括号,不然会先加1后乘2
f((x<<1)+1);
//post[idx++]=tree[x]; //求后序遍历
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) //输入层遍历
scanf("%d",&tree[i]);
f(1);
for(int i=0;i<n;i++) //输出
printf("%d ",pre[i]);
return 0;
}
第九章 二叉搜索树
9.2 二叉搜索树——插入
题解
代码实现
#include<iostream>
#include<cstdio>
using namespace std;
const int MAX = 100005;
struct Node {
int key;
Node *parent, *left, *right;
};
Node *root, *NIL;
//前序排序
void PreorderTreeWalk(Node *u) {
if (u == NIL)
return;
printf(" %d", u->key);
PreorderTreeWalk(u->left);
PreorderTreeWalk(u->right);
}
//中序排序
void InorderTreeWalk(Node *u) {
if (u == NIL)
return;
InorderTreeWalk(u->left);
printf(" %d", u->key);
InorderTreeWalk(u->right);
}
void insert(int value) {
Node *y = NIL, *x = root;
// 初始化 z 节点作为要插入的节点
Node *z = new Node(); // Node *z = (Node *)malloc(sizeof(Node))
z->key = value;
z->left = z->right = NIL;
// 从根结点往下遍历
while (x != NIL) {
y = x; // 记录搜索下去的前一个节点,作为 待插入节点 z 的父节点
// z 比 x 小, 则从x的左侧遍历
// z 比 x 大, 则从x的右侧遍历
if (z->key < x->key)
x = x->left;
else
x = x->right;
}
z->parent = y;
// 没有父结点的是root
if (y == NIL) {
root = z;
}else {
// z 比 y 小, 则在y的左侧
// z 比 y 大, 则在y的右侧
if (z->key < y->key)
y->left = z;
else
y->right = z;
}
}
int main() {
int n, value;
char s[200];
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%s", s);
if (s[0] == 'i') {
scanf("%d", &value);
insert(value);
}else {
InorderTreeWalk(root);
printf("\n");
PreorderTreeWalk(root);
printf("\n");
}
}
return 0;
}
9.3 二叉搜索树——搜索
题解
代码实现
#include<iostream>
#include<cstdio>
using namespace std;
const int MAX = 100005;
struct Node {
int key;
Node *parent, *left, *right;
};
Node *root, *NIL;
//前序排序
void PreorderTreeWalk(Node *u) {
if (u == NIL)
return;
printf(" %d", u->key);
PreorderTreeWalk(u->left);
PreorderTreeWalk(u->right);
}
//中序排序
void InorderTreeWalk(Node *u) {
if (u == NIL)
return;
InorderTreeWalk(u->left);
printf(" %d", u->key);
InorderTreeWalk(u->right);
}
void insert(int value) {
Node *y = NIL, *x = root;
Node *z = new Node();
z->key = value;
z->left = z->right = NIL;
// 从根结点往下遍历
while (x != NIL) {
y = x;
// z 比 x 小, 则从x的左侧遍历
// z 比 x 大, 则从x的右侧遍历
if (z->key < x->key)
x = x->left;
else
x = x->right;
}
z->parent = y;
// 没有父结点的是root
if (y == NIL) {
root = z;
}else {
// z 比 y 小, 则在y的左侧
// z 比 y 大, 则在y的右侧
if (z->key < y->key)
y->left = z;
else
y->right = z;
}
}
Node * find(Node *u, int value) {
// 从根结点开始遍历下去
while (u != NIL && u->key != value) {
if (value < u->key)
u = u->left;
else
u = u->right;
}
return u;
}
int main() {
int n, value;
Node * f;
char s[200];
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%s", s);
if (s[0] == 'i') {
scanf("%d", &value);
insert(value);
}
else if (s[0] == 'f') {
scanf("%d", &value);
f = find(root,value);
if (f != NIL)
printf("yes\n");
else
printf("no\n");
}else {
InorderTreeWalk(root);
printf("\n");
PreorderTreeWalk(root);
printf("\n");
}
}
return 0;
}
9.4 二叉搜索树——删除
题解
代码实现
#include<iostream>
#include<cstdio>
using namespace std;
const int MAX = 100005;
struct Node {
int key;
Node *parent, *left, *right;
};
Node *root, *NIL;
//前序排序
void PreorderTreeWalk(Node *u) {
if (u == NIL)
return;
printf(" %d", u->key);
PreorderTreeWalk(u->left);
PreorderTreeWalk(u->right);
}
//中序排序
void InorderTreeWalk(Node *u) {
if (u == NIL)
return;
InorderTreeWalk(u->left);
printf(" %d", u->key);
InorderTreeWalk(u->right);
}
void insert(int value) {
Node *y = NIL, *x = root;
Node *z = new Node();
z->key = value;
z->left = z->right = NIL;
// 从根结点往下遍历
while (x != NIL) {
y = x;
// z 比 x 小, 则从x的左侧遍历
// z 比 x 大, 则从x的右侧遍历
if (z->key < x->key)
x = x->left;
else
x = x->right;
}
z->parent = y;
// 没有父结点的是root
if (y == NIL) {
root = z;
}
else {
// z 比 y 小, 则在y的左侧
// z 比 y 大, 则在y的右侧
if (z->key < y->key)
y->left = z;
else
y->right = z;
}
}
// 首先找到待删除值对应的节点,返回节点
Node * find(Node *u, int value) {
// 从根结点开始遍历下去
while (u != NIL && u->key != value) {
if (value < u->key)
u = u->left;
else
u = u->right;
}
return u;
}
// 树的最小值
Node *treeMinimum(Node * x) {
while (x->left != NIL)
x = x->left;
return x;
}
// case 3 ,搜索后一个结点
Node *treeSuccessor(Node *x) {
if (x->right != NIL)
return treeMinimum(x->right);
Node *y = x->parent;
while (y != NIL && x == y->right) {
x = y;
x = y->parent;
}
return y;
}
// 传入的节点 z 即需要删除的节点
void treeDelete(Node * z) {
Node *y, *x; // 要删除的对象和 y 的子结点
// 确定要删除的结点
if (z->left == NIL || z->right == NIL)
y = z;
else
y = treeSuccessor(z);
// 确定 y 的子结点x
if (y->left != NIL)
x = y->left;
else
x = y->right;
// 让子结点链接父结点
if (x != NIL)
x->parent = y->parent;
// 让父结点链接子结点
if (y->parent == NIL)
root = x;
else {
if (y == y->parent->left)
y->parent->left = x;
else
y->parent->right = x;
}
// case 3
if (y != z)
z->key = y->key;
delete y;
}
int main() {
int n, value;
Node * f;
char s[200];
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%s", s);
if (s[0] == 'i') {
scanf("%d", &value);
insert(value);
}
else if (s[0] == 'f') {
scanf("%d", &value);
f = find(root, value);
if (f != NIL)
printf("yes\n");
else
printf("no\n");
}
else if (s[0] == 'd') {
scanf("%d", &value);
treeDelete(find(root, value));
}
else {
InorderTreeWalk(root);
printf("\n");
PreorderTreeWalk(root);
printf("\n");
}
}
return 0;
}
第十章 堆
10.2 完全二叉树
题解
代码实现
#include <iostream>
#include <cstdio>
using namespace std;
// 分别求出结点i的父结点,左子结点、右子结点
int parent(int i) { return i / 2; }
int left(int i) { return 2 * i; }
int right(int i) { return 2 * i + 1; }
int main()
{
int H[300];
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &H[i]);
for (int i = 1; i <= n; i++) {
printf("node %d: key = %d, ", i, H[i]);
if (parent(i) >= 1)
printf("parent key = %d, ", H[parent(i)]);
if (left(i) <= n)
printf("left key = %d, ", H[left(i)]);
if (right(i) <= n)
printf("right key = %d, ", H[right(i)]);
printf("\n");
}
return 0;
}
10.3 最大/最小堆
题解
代码实现
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxx = 2000000;
int A[maxx + 1];
int n;
void maxHeapify(int i) {
int left = 2 * i, right = 2 * i + 1;
int largest;
// 从左子结点、自身、右子结点选出值最大的结点,然后继续往下递归
if (left <= n && A[left] > A[i])
largest = left;
else
largest = i;
if (right <= n && A[right] > A[largest])
largest = right;
if (largest != i) {
swap(A[i], A[largest]);
maxHeapify(largest);
}
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &A[i]);
for (int i = n / 2; i >= 1; i--)
maxHeapify(i);
for (int i = 1; i <= n; i++)
printf(" %d", A[i]);
printf("\n");
return 0;
}
10.4 优先级队列
题解
代码实现
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxx = 2000000;
const int INF = 1 << 30;
int A[maxx + 1];
int n;
void maxHeapify(int i) {
int left = 2 * i, right = 2 * i + 1;
int largest;
// 从左子结点、自身、右子结点选出值最大的结点,然后继续往下递归
if (left <= n && A[left] > A[i])
largest = left;
else
largest = i;
if (right <= n && A[right] > A[largest])
largest = right;
if (largest != i) {
swap(A[i], A[largest]);
maxHeapify(largest);
}
}
int extract()
{
int maxv;
if (n < 1)
return -INF;
maxv = A[1];
A[1] = A[n--];
maxHeapify(1);
return maxv;
}
int insert(int key) {
n++;
int i = n;
A[n] = -INF;
if (key < A[i])
return -INF;
A[i] = key;
while (i > 1 && A[i / 2] < A[i]) {
swap(A[i], A[i / 2]);
i /= 2;
}
}
int main()
{
char op[20];
int k;
while (scanf("%s", op) != EOF&&op[2] != 'd') {
if (op[0] == 'i') {
scanf("%d", &k);
insert(k);
}
else {
printf("%d\n", extract());
}
}
return 0;
}
10.5 通过标准库实现优先级队列
第十一章 动态规划
11.1 动态规划法的概念
11.2 斐波那契数列
题解
代码实现
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
int F[45],n;
F[0] = F[1] = 1;
for (int i = 2; i <= 44; i++)
F[i] = F[i - 1] + F[i - 2];
scanf("%d", &n);
printf("%d\n", F[n]);
return 0;
11.3 最长公共子序列
题解
代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxx = 1010;
int c[maxx][maxx];
int lcs(char s1[],char s2[]) {
int m = strlen(s1), n = strlen(s2);
for (int i = 1; i <= m; i++)
c[i][0] = 0;
for (int i = 1; i <= n; i++)
c[0][i] = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (s1[i] == s2[j])
c[i+1][j+1] = c[i][j] + 1;
else
c[i+1][j+1] = max(c[i][j+1], c[i + 1][j]);
}
}
return c[m][n];
}
int main()
{
char s1[maxx], s2[maxx];
int t;
scanf("%d", &t);
for (int i = 0; i < t; i++) {
scanf("%s %s", s1, s2);
printf("%d\n",lcs(s1,s2));
}
return 0;
11.4 矩阵链乘法
题解
代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxx = 110;
const int INF = 1 << 30;
int main()
{
int n, p[maxx], m[maxx][maxx];
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d %d", &p[i - 1], &p[i]);
for (int i = 1; i <= n; i++)
m[i][i] = 0;
for (int l = 2; l <= n; l++) {
for (int i = 1; i <= n - l + 1; i++) {
int j = i + l - 1;
m[i][j] = INF;
for (int k = i; k <= j - 1; k++)
m[i][j] = min(m[i][j], m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j]);
}
}
printf("%d\n", m[1][n]);
return 0;
}
第十二章 图
12.1 挑战问题之前——图
12.2 图的表示
题解
代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxx = 110;
int main()
{
int n, m[maxx][maxx]; // 邻接矩阵
int u, num, v;
scanf("%d", &n);
memset(m, 0, sizeof(m));
for (int i = 1; i <= n; i++) {
scanf("%d %d", &u, &num);
for (int j = 1; j <= num; j++) {
scanf("%d", &v);
m[u][v] = 1; // u 与 v 之间画一条边
}
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
printf("%d%c", m[i][j], j == n ? '\n' : ' ');
return 0;
}
12.3 深度优先搜索
题解
代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
using namespace std;
const int maxx = 110;
int n, m[maxx][maxx];
int u, num, v;
int d[maxx], f[maxx];
int flag[maxx];
int nowtime = 1;
void dfs(int u) {
// 入栈时间
flag[u] = 1;
d[u] = nowtime++;
for (int i = 1; i <= n; i++) {
if (flag[i] == 0 && m[u][i] == 1) {
dfs(i);
}
}
//出栈时间
f[u] = nowtime++;
}
int main()
{
scanf("%d", &n);
memset(m, 0, sizeof(m));
memset(f, 0, sizeof(f));
for (int i = 1; i <= n; i++) {
scanf("%d %d", &u, &num);
for (int j = 1; j <= num; j++) {
scanf("%d", &v);
m[u][v] = 1;
}
}
for (int i = 1; i <= n; i++) {
if(flag[i] == 0)
dfs(i);
}
for (int i = 1; i <= n; i++) {
printf("%d %d %d\n", i, d[i], f[i]);
}
return 0;
}
12.4 广度优先搜索
题解
代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int maxx = 110;
int n, m[maxx][maxx];
int u, num, v;
int flag[maxx];
int d[maxx];
void bfs() {
queue<int>q;
q.push(1);
d[1] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = 2; i <= n; i++) {
if (m[u][i] == 1 && flag[i] == 0) {
// 标记为已经访问
flag[i] = 1;
// 更新长度
d[i] = d[u] + 1;
// 压入栈内
q.push(i);
}
}
}
}
int main()
{
scanf("%d", &n);
memset(m, 0, sizeof(m));
for (int i = 1; i <= n; i++) {
scanf("%d %d", &u, &num);
// 初始化
flag[i] = 0;
d[i] = -1;
for (int j = 1; j <= num; j++) {
scanf("%d", &v);
m[u][v] = 1;
}
}
bfs();
for (int i = 1; i <= n; i++) {
printf("%d %d\n", i, d[i]);
}
return 0;
}
12.5 连 通 分 量
题解
代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
using namespace std;
const int maxx = 100010;
int n, m;
int u, num, v;
int flag[maxx];
int color[maxx];
vector<int> G[maxx];
int c = 1;
void dfs(int t) {
stack<int>s;
s.push(t);
color[t] = c;
while (!s.empty()) {
int u = s.top(); s.pop();
int len = G[u].size();
for (int i = 0; i < len; i++) {
int v = G[u][i];
if (flag[v] == 0) {
color[v] = c;
flag[v] = 1;
s.push(v);
}
}
}
}
int main()
{
int q;
memset(flag, 0, sizeof(flag));
scanf("%d %d", &n, &m);
for (int i = 0; i < m; i++) {
scanf("%d %d", &u, &v);
//将顶点v添加到G[u]数组中
G[u].push_back(v);
G[v].push_back(u);
}
for (int i = 0; i < n; i++) {
if (flag[i] == 0) {
dfs(i);
c++;
}
}
scanf("%d", &q);
for (int i = 0; i < q; i++) {
scanf("%d %d", &u, &v);
if (color[u] == color[v]) {
printf("yes\n");
}
else {
printf("no\n");
}
}
return 0;
}
第十三章 加权图
13.1 挑战问题之前——加权图
最小生成树
最短路径
13.2 最小生成树
题解
代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
using namespace std;
const int maxx = 1010;
const int INF = 1 << 30;
int n, m[maxx][maxx];
int u, num, v;
int flag[maxx];
int prim() {
// d数组是保存V-T, p数组是记录MST的
int d[maxx], p[maxx];
for (int i = 0; i < n; i++) {
d[i] = INF;
p[i] = -1;
flag[i] = 0;
}
d[0] = 0;
while (1) {
int minv = INF;
u = -1;
// 比较出权值最小的边
for (int i = 0; i < n; i++) {
if (minv > d[i] && flag[i] == 0) {
u = i;
minv = d[i];
}
}
if (u == -1)
break;
// 标记已经加入MST
flag[u] = 1;
// 更新未访问的点与已经访问的点之间的最小的边(相对于每个点)
for (int v = 0; v < n; v++) {
if (flag[v] == 0 && m[u][v] != INF && d[v] > m[u][v]) {
d[v] = m[u][v];
p[v] = u;
}
}
}
int ans = 0;
for (int i = 0; i < n; i++)
if (p[i] != -1)
ans += m[i][p[i]];
return ans;
}
int main()
{
memset(flag, 0, sizeof(flag));
scanf("%d", &n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
scanf("%d", &m[i][j]);
if (m[i][j] == -1)
m[i][j] = INF;
}
}
printf("%d\n", prim());
return 0;
}
13.3 单源最短路径
题解
代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
using namespace std;
const int maxx = 1010;
const int INF = 1 << 30;
int n, m[maxx][maxx];
int u, v ,k, c;
int flag[maxx];
void dijkstra() {
int d[maxx];
for (int i = 0; i < n; i++) {
d[i] = INF;
flag[i] = 0;
}
d[0] = 0;
while (1) {
int minv = INF;
u = -1;
// 比较出权值最小的边
for (int i = 0; i < n; i++) {
if (minv > d[i] && flag[i] == 0) {
u = i;
minv = d[i];
}
}
if (u == -1)
break;
// 标记已经加入S
flag[u] = 1;
// 更新经由u结点,V-S的v结点的最小成本
for (int v = 0; v < n; v++) {
if (flag[v] == 0 && m[u][v] != INF && d[v] > d[u] + m[u][v]) {
d[v] = d[u] + m[u][v];
}
}
}
for (int i = 0; i < n; i++)
printf("%d %d\n", i, d[i] == INF ? -1 : d[i]);
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
m[i][j] = INF;
}
}
for (int i = 0; i < n; i++) {
scanf("%d %d", &u, &k);
for (int j = 0; j < k; j++) {
scanf("%d %d", &v, &c);
m[u][v] = c;
}
}
dijkstra();
return 0;
}
堆优化的单源最短路径
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
const int maxx = 10010;
const int INF = 1 << 30;
int n;
int flag[maxx];
// 加权有向图的邻接表表示法,pair.first是顶点v,pair.second是权值
vector<pair<int, int> > adj[maxx];
void dijkstra() {
priority_queue<pair<int, int> > PQ;
int d[maxx];
for (int i = 0; i < n; i++) {
d[i] = INF;
flag[i] = 0;
}
d[0] = 0;
PQ.push(make_pair(0, 0));
while (!PQ.empty()) {
pair<int, int> f = PQ.top(); PQ.pop();
int u = f.second;
// 标记已经加入S
flag[u] = 1;
//如果不是最小最短路径则忽略
if (d[u] < f.first *(-1))
continue;
int len = adj[u].size();
for (int j = 0; j < len; j++) {
int v = adj[u][j].first;
if (flag[v] == 0 && d[v] > d[u] + adj[u][j].second) {
d[v] = d[u] + adj[u][j].second;
// priority_queue默认取最大值,所以乘以-1
PQ.push(make_pair(d[v] * (-1), v));
}
}
}
for (int i = 0; i < n; i++)
printf("%d %d\n", i, d[i] == INF ? -1 : d[i]);
}
int main()
{
int u, v, k, c;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d %d", &u, &k);
for (int j = 0; j < k; j++) {
scanf("%d %d", &v, &c);
adj[u].push_back(make_pair(v, c));
}
}
dijkstra();
return 0;
}
第十四章 高等数据结构
14.1 互质的集合
题解
代码实现
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
class DisjointSet {
public :
// rank记录树的高度
vector<int> rank, p;
DisjointSet() {};
DisjointSet(int size) {
rank.resize(size, 0);
p.resize(size, 0);
for (int i = 0; i < size; i++)
makeSet(i);
}
void makeSet(int i) {
p[i] = i;
rank[i] = 0;
}
bool same(int x, int y) {
return findSet(x) == findSet(y);
}
void unite(int x, int y) {
link(findSet(x), findSet(y));
}
void link(int x, int y) {
// 包含x的树的高度更高,则将y的树合并到x上
if (rank[x] > rank[y])
p[y] = x;
else {
p[x] = y;
if (rank[x] == rank[y])
rank[y]++;
}
}
int findSet(int x) {
if (x != p[x])
p[x] = findSet(p[x]);
return p[x];
}
};
int main()
{
int n, a, b,t, q;
scanf("%d %d", &n, &q);
DisjointSet ds = DisjointSet(n);
for (int i = 0; i < q; i++) {
scanf("%d %d %d", &t, &a, &b);
if (t == 0)
ds.unite(a, b);
else
printf("%d\n", ds.same(a, b) ? 1 : 0);
}
return 0;
}
第十五章 高等图算法
15.1 所有点对间最短路径
题解
代码实现
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxx = 110;
const LL INF = (1LL << 32);
int n,q;
LL d[maxx][maxx];
void floyd() {
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
if (d[i][k] == INF)
continue;
for (int j = 0; j < n; j++) {
if (d[k][j] == INF)
continue;
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}
}
int main()
{
int u, v, c;
scanf("%d %d", &n, &q);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++)
d[i][j] = i == j ? 0 : INF;
}
// 输入权值
for (int i = 0; i < q; i++) {
scanf("%d %d %d", &u, &v, &c);
d[u][v] = c;
}
floyd();
// 判断是否存在负环
bool negative = false;
for (int i = 0; i < n; i++)
if (d[i][i] < 0)
negative = true;
if (negative)
printf("NEGATIVE CYCLE\n");
else {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (d[i][j] == INF)
printf("INF");
else
printf("%d", d[i][j]);
printf("%c", j == n - 1 ? '\n' : ' ');
}
}
}
return 0;
}
15.2 拓 扑 排 序
题解
代码实现
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <list>
#include <algorithm>
using namespace std;
const int maxx = 100010;
const int INF = 1 << 30;
vector<int>G[maxx];
list<int>out;
bool flag[maxx];
int n;
int indeg[maxx];
void bfs(int s) {
queue<int> q;
q.push(s);
while (!q.empty()) {
int u = q.front(); q.pop();
out.push_back(u);
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
indeg[v]--;
if (indeg[v] == 0 && flag[v] == 0) {
flag[v] = true;
q.push(v);
}
}
}
}
void tsort() {
for (int i = 0; i < n; i++) {
indeg[i] = 0;
flag[i] = false;
}
for (int u = 0; u < n; u++) {
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
indeg[v]++;
}
}
for (int u = 0; u < n; u++) {
if (indeg[u] == 0 && flag[u] == 0)
bfs(u);
}
for (list<int>::iterator it = out.begin(); it != out.end(); it++)
printf("%d\n", *it);
}
int main()
{
int s, t, m;
scanf("%d %d", &n, &m);
for (int i = 0; i < m; i++) {
scanf("%d %d", &s, &t);
G[s].push_back(t);
}
tsort();
return 0;
}
15.4 树 的 直 径
题解
代码实现
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
const int maxx = 100010;
const int INF = 1 << 30;
class Edge {
public:
int t, w;
Edge() {};
Edge(int t ,int w):t(t), w(w) {};
};
vector<Edge> G[maxx];
int n, d[maxx];
bool vis[maxx];
int cnt;
void bfs(int s) {
for (int i = 0; i < n; i++)
d[i] = INF;
queue<int> Q;
Q.push(s);
d[s] = 0;
while (!Q.empty()) {
int u = Q.front(); Q.pop();
int len = G[u].size();
for (int i = 0; i < len; i++) {
Edge e = G[u][i];
if (d[e.t] == INF) {
d[e.t] = d[u] + e.w;
Q.push(e.t);
}
}
}
}
void solve() {
// 从任选的结点s出发,选择距离s最远的结点tgt
bfs(0);
int maxv = 0, tgt = 0;
for (int i = 0; i < n; i++) {
if (d[i] == INF)
continue;
if (maxv < d[i]) {
maxv = d[i];
tgt = i;
}
}
//从tgt出发,求结点tgt到最远结点的距离maxv
bfs(tgt);
maxv = 0;
for (int i = 0; i < n; i++) {
if (d[i] == INF)
continue;
maxv = max(maxv, d[i]);
}
printf("%d\n", maxv);
}
int main() {
int s, t, w;
scanf("%d", &n);
for (int i = 0; i < n - 1; i++) {
scanf("%d %d %d", &s, &t, &w);
// 无向图
G[s].push_back(Edge(t, w));
G[t].push_back(Edge(s, w));
}
solve();
return 0;
}
15.5 最小生成树
题解