一个生成随机树(此树非彼数)的算法,树的结点编号从1开始,这个算法生成了树的结点个数、树的结点的权值、树的每条边的结点。
如下面是一棵10结点的二叉树的生成结果:
1
2
3
4
5
6
7
8
9
10
11
|
10
-23 -44 -51 -9 13 51 62 11 -63 19
6 9
6 4
9 2
9 3
4 7
4 1
2 5
2 8
3 10
|
第一次更新
想到一种O(n)复杂度的随机树生成算法。
设想有树结点1、2、3、4、5,生成它们的一个随机排列,如4、1、3、5、2;
那么,如果我们设定每个结点的子节点数量为2,或者设置其他区间(如[1, 3])。
那么根节点就是4,它的子节点为1、3,以BFS的方式遍历生成子节点,1的子节点为5、2,就可以生成随机树了。
生成随机排列的算法复杂度为O(n)。
对于a[0], a[1], a[2], a[3], a[4],如何生成随机排列?
获得x = random(0, 3),(区间[0, 3]的下标),然后交换a[x], a[3],就生成了一个随机值
接下来,x = random(0, 2),然后交换a[x], a[2]
不断地依次生成,就可以得到一个随机排列,且时间复杂度为O(n)。
所以整个算法的时间复杂度为O(n)。
下面的代码将生成随机排列和BFS遍历的过程融合在了一起,建议分开实现,更加清晰、模块化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <queue>
#define random(a, b) rand()%(b-a+1) + a
using namespace std;
void creatData(int n, string filename) {
fstream file(filename.c_str(), ios::out);
int *tree = new int [n];
for (int i = 0; i < n; ++i) {
tree[i] = i + 1;
}
int root = random(0, n - 1);
swap(tree[root], tree[n - 1]);
int nxt_idx = n - 2;
queue<int> Que;
file << n << endl;
for (int i = 0; i < n; ++i) {
file << random(-1024, 1024) << ' ';
}
file << endl;
Que.push(tree[n - 1]);
while (!Que.empty()) {
int now = Que.front();
Que.pop();
int cnt = random(1, 3);
for (int i = 0; i < cnt; ++i) {
int tmp_idx = random(0, nxt_idx);
swap(tree[tmp_idx], tree[nxt_idx]);
file << now << ' ' << tree[nxt_idx] << endl;
Que.push(tree[nxt_idx]);
nxt_idx--;
if (nxt_idx == -1) break;
}
if (nxt_idx == -1) break;
}
}
int main()
{
creatData(10, "creatTree10.txt");
creatData(1000, "creatTree1000.txt");
creatData(10000, "creatTree10000.txt");
creatData(100000, "creatTree100000.txt");
creatData(1000000, "creatTree1000000.txt");
}
|
原文
思路是将结点
编号1-n push进vector中,然后随机选一个点为root,并从vector中删除这个点。然后使用基于BFS的方式,从root扩展,随机选在(m, n)区间的子节点个数,同时使用随机方法获取在剩余的vector中获取子节点编号,然后从vector中删除。就这样不断地扩散,当vector的size为0时,说明无子节点可选,从而可以结束算法。
可以设置子节点的随机范围,结点权值的随机范围。
需要注意的是,该算法时间复杂度大概高达O(n^2),生成100000个数据要40+s,所以要生成更大规模的数据需要较长的时间。
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
#include <iostream>
#include <vector>
#include <cstdlib>
#include <queue>
#include <fstream>
#define random(a, b) rand()%(b-a+1) + a
using namespace std;
vector<int> que;
queue<int> seq;
void deleteOne(int one) {
vector<int>::iterator it = que.begin();
while (it != que.end()) {
if (*it == one) {
que.erase(it);
break;
}
it++;
}
}
void creat(int n, string filename) {
que.clear();
fstream outfile(filename.c_str(), ios::out);
outfile << n << endl;
for (int i = 1; i <= n; ++i) {
int x = random(-64, 64);
outfile << x << ' ';
que.push_back(i);
}
outfile << endl;
int root = random(0, n - 1);
root = que[root];
// outfile << "root = " << root << endl;
deleteOne(root);
seq.push(root);
while (que.size()) {
int now = seq.front();
seq.pop();
int ns = random(2, 2); // 将子节点范围取做(2,2)就生成了二叉树
int len = que.size();
for (int i = 0; i < ns; ++i) {
int x = random(0, len - 1);
outfile << now << ' ' << que[x] << endl;
seq.push(que[x]);
deleteOne(que[x]);
len--;
if (len == 0) break;
}
}
}
int main()
{
creat(10, "creatTree10.txt");
creat(1000, "creatTree1000.txt");
creat(10000, "creatTree10000.txt");
creat(100000, "creatTree100000.txt");
}
|