书籍封面

第一章 前言

1. 本人衷心建议

~~~~~~       如果你是一位初学者,我指的是你只会基本的 C/C++ 编程,即使编的很烂,这本书对于你算法和数据结构的提升非常有帮助,所涉及的每一个算法案例都非常经典,且有非常细致的讲解,本人强烈推荐你理解好每一道题的细节。

~~~~~~       如果你具备一定的算法与数据结构能力,本书的内容依然很适合你,你可以有针对性的补充相应章节的内容,一定会有意想不到的收获。

~~~~~~       当你看到如此多的章节,每个章节还有如此多的内容时,一定不要被吓到,当本人对算法与数据结构一头雾水时,也是一步步算法过来的,这本书的学习大概花费了我近一个月的时间,所以你不必想着马上就可以看完很多,知识需要慢慢消化,Good luck !

注:算法涉及的题目如何在网上搜索见下文,特意强调,无需翻墙。

2. 本书涉及的所有题目以及对应的编号




3. AOJ 使用方法《挑战程序设计竞赛》

网址

https://onlinejudge.u-aizu.ac.jp/home

注册

点右上角 Guest ==> Sign Up

注册完成后点击 submit

查找题目

1.点击网站上方 Challenge ==> Search

2.直接搜索题目

3. 或者根据上面 2 中题目对应的编号进行查找

点击 Volumes ,因为题号是 0202,前两位是 02,所以选 Volume2,然后找到对应编号即可

做题

进去之后选择语言,编写代码,并提交即可。当你看到全是日语的时候,不要惊慌,你完全可以看懂题,毕竟每道题的中文翻译都在本文中了…

提交后往下翻查看状态

用 virtual judge 做 AOJ 的题目

一个非常便捷的网站:https://vjudge.net/problem#OJId=Aizu&probNum=&title=&source=&category=all
首先进行账号注册

点进去做题就可以了

第二章 算法与复杂度

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.tint c = min(q, u.t);// 任务执行了 c msu.t -= c;// 当前时间加上c msnowtime += 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.tint c = min(q, u.t);// 任务执行了 c msu.t -= c;// 当前时间加上c msnowtime += 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;//找到keyif (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;//找到keyif (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");elseprintf("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;elseleft = 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");elseprintf("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, ");elseprintf("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)  // 最左侧子结点的深度设置为自己的深度 + 1rec(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;elseTree[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");elseprintf("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后乘2f((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;elsex = x->right;}z->parent = y;// 没有父结点的是rootif (y == NIL) {root = z;}else {// z 比 y 小, 则在y的左侧// z 比 y 大, 则在y的右侧if (z->key < y->key)y->left = z;elsey->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;elsex = x->right;}z->parent = y;// 没有父结点的是rootif (y == NIL) {root = z;}else {// z 比 y 小, 则在y的左侧// z 比 y 大, 则在y的右侧if (z->key < y->key)y->left = z;elsey->right = z;}
}Node * find(Node *u, int value) {// 从根结点开始遍历下去while (u != NIL && u->key != value) {if (value < u->key)u = u->left;elseu = 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");elseprintf("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;elsex = x->right;}z->parent = y;// 没有父结点的是rootif (y == NIL) {root = z;}else {// z 比 y 小, 则在y的左侧// z 比 y 大, 则在y的右侧if (z->key < y->key)y->left = z;elsey->right = z;}
}
// 首先找到待删除值对应的节点,返回节点
Node * find(Node *u, int value) {// 从根结点开始遍历下去while (u != NIL && u->key != value) {if (value < u->key)u = u->left;elseu = 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;elsey = treeSuccessor(z);// 确定 y 的子结点xif (y->left != NIL)x = y->left;elsex = 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;elsey->parent->right = x;}// case 3if (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");elseprintf("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;elselargest = 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;elselargest = 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;elsec[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;// 标记已经加入MSTflag[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;// 标记已经加入Sflag[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;// 标记已经加入Sflag[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默认取最大值,所以乘以-1PQ.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);elseprintf("%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");elseprintf("%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最远的结点tgtbfs(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到最远结点的距离maxvbfs(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 最小生成树



题解



代码实现

第十七章 动态规划 略

第十八章 数论

算法之经典数学知识

第十九章 启发式搜索 略

19.1 八皇后问题

19.2 九宫格拼图

【操作指导 | 代码实现】挑战程序设计竞赛2:算法和数据结构相关推荐

  1. 挑战程序设计竞赛(算法和数据结构)——19.2九宫格拼图问题的JAVA实现

    题目&解题思路: 代码: import java.io.PushbackInputStream; import java.util.*;public class Puzzle_9 {publi ...

  2. 挑战程序设计竞赛(算法和数据结构)——14.1互质的集合(并查集)的JAVA实现

    题目与思路: 代码: import java.util.Scanner; import java.util.Vector;public class DisjointSet {public static ...

  3. 挑战程序设计竞赛2:算法和数据结构 (渡部有隆 著)

    1 转载于:https://www.cnblogs.com/revoid/p/9596083.html

  4. 挑战程序设计竞赛:反转法

    挑战程序设计竞赛:反转法 1. 题目介绍 1.1 题目 1.2 样例 2. 思路讲解 2.1 视频讲解 2.1 反转法(开关问题) 3. 练习题 4. 附录:程序代码 4.1 Java 4.2 C++ ...

  5. 《挑战程序设计竞赛》 读后感(转载)

    <挑战程序设计竞赛> 读后感 最近要开始准备面试找工作,算法是准备的重中之重,舍友推荐了<挑战程序设计竞赛>这本书.花了一周的时间大体过了一遍,该书真切地让我理解了" ...

  6. 《挑战程序设计竞赛》--初级篇习题POJ部分【动态规划】

    关于基本的动态规划和经典的动态规划,在之前已经总结过了,可以温习一下: 传送门 这次是延续上次的<挑战程序设计竞赛>初级篇,总结部分poj上的练习题,主要是DP方面的练习题: 一.基础的动 ...

  7. 挑战程序设计竞赛(第二章习题总结)

    文章目录 搜索 Curling 2.0(POJ 3009) Meteor Shower(POJ 3669) Smallest Difference(POJ 2718) Hopscotch(POJ 30 ...

  8. 挑战程序设计竞赛(第二章:2.5 图论)

    文章目录 RoadBlocks Conscription Layout RoadBlocks 参考博文:挑战程序设计竞赛: Roadblocks 参考博文:[dijkstra优化/次短路径]POJ32 ...

  9. 《挑战程序设计竞赛》--初级篇习题POJ部分【2.4 - 2.6】

    这次是延续上次的<挑战程序设计竞赛>初级篇,总结部分poj上的练习题,主要是2.4 ~ 2.6部分: 导航 2.4 加工并存储的数据结构 优先队列 Sunscreen MooUnivers ...

最新文章

  1. [HNOI 2011]XOR和路径
  2. 小米手机怎么获取 ROOT 权限
  3. 打开高效文本编辑之门_Linux Awk之条件判断与循环
  4. 设置progressbar进度条颜色
  5. Spring Boot JPA的查询语句
  6. 2017浙江省赛 B - Problem Preparation ZOJ - 3959
  7. scala 隐式转换与隐式参数
  8. Python 源代码代码打包成 whl 文件
  9. win教程:如何查看本机的IP地址
  10. HCIP—223难题精析
  11. java 模板 word转pdf 可分页 带图片
  12. Android之使用HttpPost提交数据到服务器(Android手机客户端和后台服务器交互)
  13. JavaScript动态向表格添加数据
  14. 华为p20所有权限都打开了,QQ消息依然不弹窗提醒?
  15. flutter TextDecoration 和 TextDecorationStyle
  16. js如何实现重定当前向页面并刷新?
  17. Windows电脑重装系统亲身经历
  18. [CDH官方文档]--CDH最新官方文档(最全)
  19. FFmpeg常用命令大全,并简单封装
  20. java毕业设计一点就到家外卖订餐系统Mybatis+系统+数据库+调试部署

热门文章

  1. 华硕电脑重装系统后fn组合键部分屏幕无显示
  2. 单TYPE-C口 可支持快充又可传输USB2.0数据方案
  3. eclipse创建Javaweb项目
  4. 用手机远程控制扫描仪
  5. 算法的时间与空间复杂度(一看就懂)
  6. vue+pdf.js 印章签署完后鼠标滑过显示签章信息
  7. 行业分析报告-全球与中国客户满意度(CSAT)调查软件市场现状及未来发展趋势
  8. 在Domino上部署运行在Web浏览器中的Notes客户机
  9. c++11:std::declval、decltype
  10. 编写一个程序,计算学生的总分和平均成绩(一)