quick-algo 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quick_algo-0.1.0/LICENSE.txt +21 -0
- quick_algo-0.1.0/PKG-INFO +71 -0
- quick_algo-0.1.0/README.md +32 -0
- quick_algo-0.1.0/README.rst +7 -0
- quick_algo-0.1.0/quick_algo/__init__.py +0 -0
- quick_algo-0.1.0/quick_algo/di_graph/__init__.py +3 -0
- quick_algo-0.1.0/quick_algo/di_graph/cpp/di_graph.hpp +90 -0
- quick_algo-0.1.0/quick_algo/di_graph/cpp/di_graph_impl.cpp +433 -0
- quick_algo-0.1.0/quick_algo/di_graph/di_graph.cpp +26930 -0
- quick_algo-0.1.0/quick_algo/di_graph/di_graph.pyi +189 -0
- quick_algo-0.1.0/quick_algo/pagerank/__init__.py +3 -0
- quick_algo-0.1.0/quick_algo/pagerank/cpp/pagerank_impl.cpp +234 -0
- quick_algo-0.1.0/quick_algo/pagerank/pagerank.cpp +8728 -0
- quick_algo-0.1.0/quick_algo/pagerank/pagerank.pyi +28 -0
- quick_algo-0.1.0/quick_algo.egg-info/PKG-INFO +71 -0
- quick_algo-0.1.0/quick_algo.egg-info/SOURCES.txt +26 -0
- quick_algo-0.1.0/quick_algo.egg-info/dependency_links.txt +1 -0
- quick_algo-0.1.0/quick_algo.egg-info/requires.txt +6 -0
- quick_algo-0.1.0/quick_algo.egg-info/top_level.txt +1 -0
- quick_algo-0.1.0/setup.cfg +4 -0
- quick_algo-0.1.0/setup.py +160 -0
- quick_algo-0.1.0/tests/test_di_graph.py +284 -0
- quick_algo-0.1.0/tests/test_pagerank.py +163 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Oct-autumn
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quick_algo
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A simple and fast Graph structure and PPR algorithm implementation based on Cpp&Cython.
|
|
5
|
+
Home-page: https://github.com/MaiM-with-u/MaiMBot-LPMM
|
|
6
|
+
Author: Oct Autumn
|
|
7
|
+
Author-email: octautumn2002@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
11
|
+
Classifier: Operating System :: Unix
|
|
12
|
+
Classifier: Natural Language :: Chinese (Simplified)
|
|
13
|
+
Classifier: Programming Language :: C++
|
|
14
|
+
Classifier: Programming Language :: Cython
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
19
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE.txt
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: Cython; extra == "dev"
|
|
25
|
+
Provides-Extra: zip
|
|
26
|
+
Requires-Dist: gzip; extra == "zip"
|
|
27
|
+
Dynamic: author
|
|
28
|
+
Dynamic: author-email
|
|
29
|
+
Dynamic: classifier
|
|
30
|
+
Dynamic: description
|
|
31
|
+
Dynamic: description-content-type
|
|
32
|
+
Dynamic: home-page
|
|
33
|
+
Dynamic: license
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
Dynamic: provides-extra
|
|
36
|
+
Dynamic: requires-python
|
|
37
|
+
Dynamic: summary
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# QuickAlgo
|
|
41
|
+
这是一个快速算法库,旨在为LPMM(Long-term and Persistent Memory)模块提供Graph数据结构和一些复杂算法的Cpp+Cython高效实现。
|
|
42
|
+
|
|
43
|
+
## 目录结构
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
|
|
47
|
+
/┬- quick_algo - 项目目录
|
|
48
|
+
├- quick_algo - 源码目录
|
|
49
|
+
| ├- di_graph - 有向图实现
|
|
50
|
+
| | ├- cpp - C++头文件&实现
|
|
51
|
+
| | └- ...
|
|
52
|
+
| ├- pagerank - PageRank算法实现
|
|
53
|
+
| | ├- cpp - C++头文件&实现
|
|
54
|
+
| | └- ...
|
|
55
|
+
| └- ...
|
|
56
|
+
├- tests - 测试目录
|
|
57
|
+
├- build_lib.bat - Windows下的构建脚本
|
|
58
|
+
├- build_lib.sh - Linux下的构建脚本
|
|
59
|
+
├- README.md - 本文档
|
|
60
|
+
└- setup.py - Python包安装脚本
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
## 构建
|
|
65
|
+
请在项目目录下执行`build_lib.bat`/`build_lib.sh`,这将在本目录下构建本依赖库。
|
|
66
|
+
|
|
67
|
+
在构建之前,请确保您的电脑上装有以下依赖:
|
|
68
|
+
|
|
69
|
+
- MSVC(Windows)/GCC(Linux)
|
|
70
|
+
- Python 3.12
|
|
71
|
+
- Cython(Python包,通过pip安装)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# QuickAlgo
|
|
2
|
+
这是一个快速算法库,旨在为LPMM(Long-term and Persistent Memory)模块提供Graph数据结构和一些复杂算法的Cpp+Cython高效实现。
|
|
3
|
+
|
|
4
|
+
## 目录结构
|
|
5
|
+
|
|
6
|
+
```text
|
|
7
|
+
|
|
8
|
+
/┬- quick_algo - 项目目录
|
|
9
|
+
├- quick_algo - 源码目录
|
|
10
|
+
| ├- di_graph - 有向图实现
|
|
11
|
+
| | ├- cpp - C++头文件&实现
|
|
12
|
+
| | └- ...
|
|
13
|
+
| ├- pagerank - PageRank算法实现
|
|
14
|
+
| | ├- cpp - C++头文件&实现
|
|
15
|
+
| | └- ...
|
|
16
|
+
| └- ...
|
|
17
|
+
├- tests - 测试目录
|
|
18
|
+
├- build_lib.bat - Windows下的构建脚本
|
|
19
|
+
├- build_lib.sh - Linux下的构建脚本
|
|
20
|
+
├- README.md - 本文档
|
|
21
|
+
└- setup.py - Python包安装脚本
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## 构建
|
|
26
|
+
请在项目目录下执行`build_lib.bat`/`build_lib.sh`,这将在本目录下构建本依赖库。
|
|
27
|
+
|
|
28
|
+
在构建之前,请确保您的电脑上装有以下依赖:
|
|
29
|
+
|
|
30
|
+
- MSVC(Windows)/GCC(Linux)
|
|
31
|
+
- Python 3.12
|
|
32
|
+
- Cython(Python包,通过pip安装)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
====
|
|
2
|
+
QuickAlgo for LPMM
|
|
3
|
+
====
|
|
4
|
+
|
|
5
|
+
See our github: `MaiM-with-u/MaiMBot-LPMM <https://github.com/MaiM-with-u/MaiMBot-LPMM>`_
|
|
6
|
+
|
|
7
|
+
This library is specially designed for LPMM(Long-term and Persistent Memory) and is not intended to be used in other projects. It is a lightweight library that focuses on providing high performance impl of DirectedGraph structure and PageRank algorithm.
|
|
File without changes
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#ifndef PAGERANK_H
|
|
2
|
+
#define PAGERANK_H
|
|
3
|
+
|
|
4
|
+
#define DEBUG 1 // 调试模式
|
|
5
|
+
|
|
6
|
+
#include <unordered_map>
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <exception>
|
|
9
|
+
#include <vector>
|
|
10
|
+
#include <queue>
|
|
11
|
+
|
|
12
|
+
static const long long DEFAULT_NODE_ARRAY_SIZE = 8; // 默认节点数组大小
|
|
13
|
+
|
|
14
|
+
class CDiEdge;
|
|
15
|
+
class CDiNode;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @brief 有向图 结构体
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
class CDiGraph
|
|
22
|
+
{
|
|
23
|
+
public:
|
|
24
|
+
std::vector<CDiNode *> *nodes; // 节点数组指针
|
|
25
|
+
std::queue<long long> *reusable_node_id; // 可重用节点ID数组指针
|
|
26
|
+
|
|
27
|
+
long long num_nodes; // 节点数量
|
|
28
|
+
long long num_edges; // 边数量
|
|
29
|
+
|
|
30
|
+
CDiGraph(long long size = DEFAULT_NODE_ARRAY_SIZE);
|
|
31
|
+
~CDiGraph();
|
|
32
|
+
|
|
33
|
+
// 添加节点
|
|
34
|
+
long long add_node();
|
|
35
|
+
// 添加边
|
|
36
|
+
int add_edge(long long src, long long dst, double weight);
|
|
37
|
+
// 删除节点
|
|
38
|
+
int remove_node(long long id);
|
|
39
|
+
// 删除边
|
|
40
|
+
int remove_edge(long long src, long long dst);
|
|
41
|
+
// 清空图
|
|
42
|
+
int clear();
|
|
43
|
+
// 整理节点数组的内存占用
|
|
44
|
+
int compact_nodes();
|
|
45
|
+
// 获取节点
|
|
46
|
+
CDiNode *get_node(long long id);
|
|
47
|
+
// 获取边
|
|
48
|
+
CDiEdge *get_edge(long long src, long long dst);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @brief 有向图节点 结构体
|
|
53
|
+
*
|
|
54
|
+
*/
|
|
55
|
+
class CDiNode
|
|
56
|
+
{
|
|
57
|
+
public:
|
|
58
|
+
long long id; // 节点ID
|
|
59
|
+
|
|
60
|
+
CDiEdge *first_in_edge; // 第一条入边
|
|
61
|
+
long long num_in_edges; // 入边数量
|
|
62
|
+
CDiEdge *first_out_edge; // 第一条出边
|
|
63
|
+
long long num_out_edges; // 出边数量
|
|
64
|
+
|
|
65
|
+
CDiNode(long long id);
|
|
66
|
+
~CDiNode();
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @brief 有向图边 结构体
|
|
71
|
+
*
|
|
72
|
+
*/
|
|
73
|
+
class CDiEdge
|
|
74
|
+
{
|
|
75
|
+
public:
|
|
76
|
+
long long src; // 起始节点
|
|
77
|
+
long long dst; // 结束节点
|
|
78
|
+
|
|
79
|
+
double weight; // 权重
|
|
80
|
+
|
|
81
|
+
CDiEdge *next_same_src; // 同起始节点的下一条边
|
|
82
|
+
CDiEdge *prev_same_src; // 同起始节点的上一条边
|
|
83
|
+
CDiEdge *next_same_dst; // 同结束节点的下一条边
|
|
84
|
+
CDiEdge *prev_same_dst; // 同结束节点的上一条边
|
|
85
|
+
|
|
86
|
+
CDiEdge(long long src = -1, long long dst = -1, double weight = 0.0L);
|
|
87
|
+
~CDiEdge();
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
#endif // PAGERANK_H
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
#define __USE_MINGW_ANSI_STDIO 1
|
|
2
|
+
#include "di_graph.hpp"
|
|
3
|
+
#include <exception>
|
|
4
|
+
#include <malloc.h>
|
|
5
|
+
|
|
6
|
+
CDiNode::CDiNode(long long id)
|
|
7
|
+
{
|
|
8
|
+
this->id = id; // 节点ID
|
|
9
|
+
this->first_in_edge = NULL; // 第一条入边
|
|
10
|
+
this->num_in_edges = 0; // 入边数量
|
|
11
|
+
this->first_out_edge = NULL; // 第一条出边
|
|
12
|
+
this->num_out_edges = 0; // 出边数量
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
CDiNode::~CDiNode()
|
|
16
|
+
{
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
CDiEdge::CDiEdge(long long src, long long dst, double weight)
|
|
20
|
+
{
|
|
21
|
+
this->src = src; // 起始节点
|
|
22
|
+
this->dst = dst; // 结束节点
|
|
23
|
+
this->weight = weight; // 权重
|
|
24
|
+
this->next_same_src = NULL; // 同起始节点的下一条边
|
|
25
|
+
this->prev_same_src = NULL; // 同起始节点的上一条边
|
|
26
|
+
this->next_same_dst = NULL; // 同结束节点的下一条边
|
|
27
|
+
this->prev_same_dst = NULL; // 同结束节点的上一条边
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
CDiEdge::~CDiEdge()
|
|
31
|
+
{
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
CDiGraph::CDiGraph(long long size)
|
|
35
|
+
{
|
|
36
|
+
this->num_nodes = 0; // 节点数量
|
|
37
|
+
this->num_edges = 0; // 边数量
|
|
38
|
+
this->nodes = new std::vector<CDiNode *>(); // 节点数组指针
|
|
39
|
+
this->reusable_node_id = new std::queue<long long>();
|
|
40
|
+
if (this->nodes == NULL || this->reusable_node_id == NULL)
|
|
41
|
+
throw std::bad_alloc(); // 抛出异常
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
CDiGraph::~CDiGraph()
|
|
45
|
+
{
|
|
46
|
+
// 释放节点数据结构
|
|
47
|
+
for (long long i = 0; i < this->num_nodes; i++)
|
|
48
|
+
{
|
|
49
|
+
if (this->nodes->at(i) != NULL)
|
|
50
|
+
{
|
|
51
|
+
// 释放节点的边数据结构(只需处理出边即可,入边会在其它节点的出边列表中处理)
|
|
52
|
+
CDiEdge *edge = this->nodes->at(i)->first_out_edge;
|
|
53
|
+
CDiEdge *next_edge;
|
|
54
|
+
this->nodes->at(i)->first_out_edge = NULL; // 清除出边指针,避免野指针
|
|
55
|
+
this->nodes->at(i)->first_in_edge = NULL; // 清除节点入边指针,避免野指针
|
|
56
|
+
while (edge != NULL)
|
|
57
|
+
{
|
|
58
|
+
next_edge = edge->next_same_src;
|
|
59
|
+
delete edge;
|
|
60
|
+
edge = next_edge;
|
|
61
|
+
}
|
|
62
|
+
delete this->nodes->at(i); // 释放节点
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 释放图节点数组
|
|
67
|
+
delete this->nodes;
|
|
68
|
+
|
|
69
|
+
// 释放可重用节点数组
|
|
70
|
+
delete this->reusable_node_id;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @brief 添加节点
|
|
75
|
+
*
|
|
76
|
+
* @return long long 节点ID
|
|
77
|
+
*/
|
|
78
|
+
long long CDiGraph::add_node()
|
|
79
|
+
{
|
|
80
|
+
// 创建新节点
|
|
81
|
+
CDiNode *new_node = new CDiNode(this->num_nodes);
|
|
82
|
+
if (new_node == NULL)
|
|
83
|
+
throw std::bad_alloc(); // 抛出异常
|
|
84
|
+
|
|
85
|
+
long long id;
|
|
86
|
+
|
|
87
|
+
if (this->reusable_node_id->empty())
|
|
88
|
+
{
|
|
89
|
+
// 如果可重用节点ID数组为空,则直接添加新节点
|
|
90
|
+
id = this->num_nodes;
|
|
91
|
+
new_node->id = id;
|
|
92
|
+
this->nodes->push_back(new_node);
|
|
93
|
+
}
|
|
94
|
+
else
|
|
95
|
+
{
|
|
96
|
+
// 有可重用节点ID,取用
|
|
97
|
+
id = this->reusable_node_id->front();
|
|
98
|
+
new_node->id = id;
|
|
99
|
+
this->reusable_node_id->pop();
|
|
100
|
+
if (this->nodes->at(id) == NULL)
|
|
101
|
+
this->nodes->at(id) = new_node;
|
|
102
|
+
else
|
|
103
|
+
{
|
|
104
|
+
// 异常:可重用ID位置已被占用
|
|
105
|
+
delete new_node;
|
|
106
|
+
return -1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this->num_nodes++; // 节点数量加1
|
|
111
|
+
|
|
112
|
+
return id;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @brief 添加边
|
|
117
|
+
*
|
|
118
|
+
* @param src 源节点ID
|
|
119
|
+
* @param dst 目标节点ID
|
|
120
|
+
* @param weight 边权重
|
|
121
|
+
*
|
|
122
|
+
* @return int 返回0表示成功,-1表示失败
|
|
123
|
+
*/
|
|
124
|
+
int CDiGraph::add_edge(long long src, long long dst, double weight)
|
|
125
|
+
{
|
|
126
|
+
// 检查源节点和目标节点是否存在
|
|
127
|
+
CDiNode *src_node = this->get_node(src); // 源节点
|
|
128
|
+
CDiNode *dst_node = this->get_node(dst); // 目标节点
|
|
129
|
+
if (src_node == NULL || dst_node == NULL)
|
|
130
|
+
return -1; // 返回错误代码
|
|
131
|
+
|
|
132
|
+
// 创建新边
|
|
133
|
+
CDiEdge *new_edge = new CDiEdge(src, dst, weight);
|
|
134
|
+
if (new_edge == NULL)
|
|
135
|
+
throw std::bad_alloc(); // 抛出异常
|
|
136
|
+
|
|
137
|
+
// 将新边添加到源节点的出边列表中(按目标节点ID升序插入)
|
|
138
|
+
if (src_node->first_out_edge == NULL)
|
|
139
|
+
{
|
|
140
|
+
// 如果源节点的出边列表为空,则直接添加
|
|
141
|
+
src_node->first_out_edge = new_edge; // 更新首边指针
|
|
142
|
+
}
|
|
143
|
+
else if (src_node->first_out_edge->dst > dst)
|
|
144
|
+
{
|
|
145
|
+
// 如果首边的目标节点ID大于新边的目标节点ID,则插入到首边之前
|
|
146
|
+
new_edge->next_same_src = src_node->first_out_edge; // 更新新边的同源后继指针
|
|
147
|
+
src_node->first_out_edge->prev_same_src = new_edge; // 更新首边的同源前驱指针
|
|
148
|
+
src_node->first_out_edge = new_edge; // 更新首边指针
|
|
149
|
+
}
|
|
150
|
+
else
|
|
151
|
+
{
|
|
152
|
+
// 否则按升序插入到出边列表中
|
|
153
|
+
CDiEdge *edge = src_node->first_out_edge;
|
|
154
|
+
while (edge->next_same_src != NULL && edge->next_same_src->dst < dst)
|
|
155
|
+
edge = edge->next_same_src;
|
|
156
|
+
// 此时,edge是新边的前驱边(最后一个dst小于new_dst的边)
|
|
157
|
+
|
|
158
|
+
new_edge->next_same_src = edge->next_same_src; // 更新新边的同源后继指针
|
|
159
|
+
if (edge->next_same_src != NULL) //
|
|
160
|
+
edge->next_same_src->prev_same_src = new_edge; // 如果有后继边,则更新后继边的同源前驱指针
|
|
161
|
+
edge->next_same_src = new_edge; // 更新当前边的同源后继指针
|
|
162
|
+
new_edge->prev_same_src = edge; // 更新新边的同源前驱指针
|
|
163
|
+
}
|
|
164
|
+
src_node->num_out_edges++; // 源节点的出边数量加1
|
|
165
|
+
|
|
166
|
+
// 将新边添加到目标节点的入边列表中
|
|
167
|
+
if (dst_node->first_in_edge == NULL)
|
|
168
|
+
{
|
|
169
|
+
// 如果目标节点的入边列表为空,则直接添加
|
|
170
|
+
dst_node->first_in_edge = new_edge; // 更新首边指针
|
|
171
|
+
}
|
|
172
|
+
else if (dst_node->first_in_edge->src > src)
|
|
173
|
+
{
|
|
174
|
+
// 如果首边的源节点ID大于新边的源节点ID,则插入到首边之前
|
|
175
|
+
new_edge->next_same_dst = dst_node->first_in_edge; // 更新新边的同目标后继指针
|
|
176
|
+
dst_node->first_in_edge->prev_same_dst = new_edge; // 更新首边的同目标前驱指针
|
|
177
|
+
dst_node->first_in_edge = new_edge; // 更新首边指针
|
|
178
|
+
}
|
|
179
|
+
else
|
|
180
|
+
{
|
|
181
|
+
// 否则按升序插入到入边列表中
|
|
182
|
+
CDiEdge *edge = dst_node->first_in_edge;
|
|
183
|
+
while (edge->next_same_dst != NULL && edge->next_same_dst->src < src)
|
|
184
|
+
edge = edge->next_same_dst;
|
|
185
|
+
// 此时,edge是新边的前驱边(最后一个src小于new_src的边)
|
|
186
|
+
|
|
187
|
+
new_edge->next_same_dst = edge->next_same_dst; // 更新新边的同目标后继指针
|
|
188
|
+
if (edge->next_same_dst != NULL) //
|
|
189
|
+
edge->next_same_dst->prev_same_dst = new_edge; // 如果有后继边,则更新后继边的同目标前驱指针
|
|
190
|
+
edge->next_same_dst = new_edge; // 更新当前边的同目标后继指针
|
|
191
|
+
new_edge->prev_same_dst = edge; // 更新新边的同目标前驱指针
|
|
192
|
+
}
|
|
193
|
+
dst_node->num_in_edges++; // 目标节点的入边数量加1
|
|
194
|
+
|
|
195
|
+
num_edges++; // 边数量加1
|
|
196
|
+
|
|
197
|
+
return 0;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
int CDiGraph::remove_node(long long id)
|
|
201
|
+
{
|
|
202
|
+
// 检查节点ID是否有效
|
|
203
|
+
CDiNode *node = this->get_node(id); // 节点指针
|
|
204
|
+
if (node == NULL)
|
|
205
|
+
return -1; // 返回错误代码
|
|
206
|
+
|
|
207
|
+
CDiEdge *edge;
|
|
208
|
+
CDiEdge *next_edge;
|
|
209
|
+
// 删除节点的出边
|
|
210
|
+
edge = node->first_out_edge;
|
|
211
|
+
node->first_out_edge = NULL; // 清除出边指针,避免野指针
|
|
212
|
+
while (edge != NULL)
|
|
213
|
+
{
|
|
214
|
+
next_edge = edge->next_same_src; // 保存下一条边
|
|
215
|
+
// 无需处理同源边之间的关系,因为都会被移除。
|
|
216
|
+
// 只需处理同目标边之间的关系
|
|
217
|
+
if (edge->prev_same_dst != NULL) // 如果有同目标前驱边,更新前驱边的同目标后继指针
|
|
218
|
+
edge->prev_same_dst->next_same_dst = edge->next_same_dst;
|
|
219
|
+
else // 如果是首边,更新首边指针
|
|
220
|
+
this->nodes->at(edge->dst)->first_in_edge = edge->next_same_dst;
|
|
221
|
+
if (edge->next_same_dst != NULL) // 如果有同目标后继边,更新后继边的同目标前驱指针
|
|
222
|
+
edge->next_same_dst->prev_same_dst = edge->prev_same_dst;
|
|
223
|
+
delete edge; // 删除边
|
|
224
|
+
edge = next_edge; // 更新当前边为下一条边
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// 删除节点的入边
|
|
228
|
+
edge = node->first_in_edge;
|
|
229
|
+
node->first_in_edge = NULL; // 清除入边指针,避免野指针
|
|
230
|
+
while (edge != NULL)
|
|
231
|
+
{
|
|
232
|
+
next_edge = edge->next_same_dst;
|
|
233
|
+
// 无需处理同目标边之间的关系,因为都会被移除。
|
|
234
|
+
// 只需处理同源边之间的关系
|
|
235
|
+
if (edge->prev_same_src != NULL) // 如果有同源前驱边,更新前驱边的同源后继指针
|
|
236
|
+
edge->prev_same_src->next_same_src = edge->next_same_src;
|
|
237
|
+
else // 如果是首边,更新首边指针
|
|
238
|
+
this->nodes->at(edge->src)->first_out_edge = edge->next_same_src;
|
|
239
|
+
if (edge->next_same_src != NULL) // 如果有同源后继边,更新后继边的同源前驱指针
|
|
240
|
+
edge->next_same_src->prev_same_src = edge->prev_same_src;
|
|
241
|
+
delete edge; // 删除边
|
|
242
|
+
edge = next_edge; // 更新当前边为下一条边
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 释放节点内存
|
|
246
|
+
delete node;
|
|
247
|
+
this->nodes->at(id) = NULL; // 清除节点指针,避免野指针
|
|
248
|
+
this->reusable_node_id->push(id); // 将节点ID放入可重用节点ID数组
|
|
249
|
+
|
|
250
|
+
this->num_nodes--; // 节点数量减1
|
|
251
|
+
|
|
252
|
+
return 0; // 返回成功代码
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
int CDiGraph::remove_edge(long long src, long long dst)
|
|
256
|
+
{
|
|
257
|
+
CDiEdge *edge = this->get_edge(src, dst); // 检查源节点和目标节点是否存在
|
|
258
|
+
if (edge == NULL)
|
|
259
|
+
return -1; // 返回错误代码
|
|
260
|
+
|
|
261
|
+
// 更新同源边之间的关系
|
|
262
|
+
if (edge->prev_same_dst != NULL) // 如果有同目标前驱边,更新前驱边的同目标后继指针
|
|
263
|
+
edge->prev_same_dst->next_same_dst = edge->next_same_dst;
|
|
264
|
+
else // 如果是首边,更新首边指针
|
|
265
|
+
this->nodes->at(edge->dst)->first_in_edge = edge->next_same_dst;
|
|
266
|
+
if (edge->next_same_dst != NULL) // 如果有同目标后继边,更新后继边的同目标前驱指针
|
|
267
|
+
edge->next_same_dst->prev_same_dst = edge->prev_same_dst;
|
|
268
|
+
this->nodes->at(src)->num_out_edges--; // 源节点的出边数量减1
|
|
269
|
+
|
|
270
|
+
// 更新同目标边之间的关系
|
|
271
|
+
if (edge->prev_same_src != NULL) // 如果有同源前驱边,更新前驱边的同源后继指针
|
|
272
|
+
edge->prev_same_src->next_same_src = edge->next_same_src;
|
|
273
|
+
else // 如果是首边,更新首边指针
|
|
274
|
+
this->nodes->at(edge->src)->first_out_edge = edge->next_same_src;
|
|
275
|
+
if (edge->next_same_src != NULL) // 如果有同源后继边,更新后继边的同源前驱指针
|
|
276
|
+
edge->next_same_src->prev_same_src = edge->prev_same_src;
|
|
277
|
+
this->nodes->at(dst)->num_in_edges--; // 目标节点的入边数量减1
|
|
278
|
+
|
|
279
|
+
delete edge; // 删除边
|
|
280
|
+
this->num_edges--; // 边数量减1
|
|
281
|
+
|
|
282
|
+
return 0; // 返回成功代码
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
int CDiGraph::clear()
|
|
286
|
+
{
|
|
287
|
+
// 释放节点数据结构
|
|
288
|
+
for (long long i = 0; i < this->num_nodes; i++)
|
|
289
|
+
{
|
|
290
|
+
CDiNode *node = this->nodes->at(i); // 节点指针
|
|
291
|
+
if (node != NULL)
|
|
292
|
+
{
|
|
293
|
+
// 释放节点的边数据结构(只需处理出边即可,入边会在其它节点的出边列表中处理)
|
|
294
|
+
CDiEdge *edge = node->first_out_edge;
|
|
295
|
+
CDiEdge *next_edge;
|
|
296
|
+
node->first_out_edge = NULL; // 清除出边指针,避免野指针
|
|
297
|
+
node->first_in_edge = NULL; // 清除节点入边指针,避免野指针
|
|
298
|
+
while (edge != NULL)
|
|
299
|
+
{
|
|
300
|
+
next_edge = edge->next_same_src;
|
|
301
|
+
delete edge;
|
|
302
|
+
edge = next_edge;
|
|
303
|
+
}
|
|
304
|
+
delete node; // 释放节点
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
this->num_nodes = 0; // 节点数量清零
|
|
309
|
+
num_edges = 0; // 边数量清零
|
|
310
|
+
|
|
311
|
+
// 重置图节点数组
|
|
312
|
+
this->nodes->clear(); // 清空节点数组
|
|
313
|
+
while (!this->reusable_node_id->empty())
|
|
314
|
+
{
|
|
315
|
+
// 清空可重用节点ID数组
|
|
316
|
+
this->reusable_node_id->pop();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return 0; // 返回成功代码
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
int CDiGraph::compact_nodes()
|
|
323
|
+
{
|
|
324
|
+
// 整理节点数组的内存占用(当前节点数组中有空指针时)
|
|
325
|
+
long long new_id = 0; // 新节点ID
|
|
326
|
+
for (long long i = 0; i < this->nodes->size(); i++)
|
|
327
|
+
{
|
|
328
|
+
CDiNode *node = this->nodes->at(i); // 节点指针
|
|
329
|
+
if (node != NULL)
|
|
330
|
+
{
|
|
331
|
+
if (node->id != new_id)
|
|
332
|
+
{
|
|
333
|
+
// 如果节点ID与新ID不匹配(前面有NULL),则将其移动到新位置
|
|
334
|
+
this->nodes->at(new_id) = node; // 将节点移动到新位置
|
|
335
|
+
this->nodes->at(i) = NULL; // 清除旧位置处的指针
|
|
336
|
+
node->id = new_id; // 更新节点ID
|
|
337
|
+
|
|
338
|
+
// 更新该节点出入边中对应的节点ID
|
|
339
|
+
CDiEdge *edge = node->first_out_edge;
|
|
340
|
+
while (edge != NULL)
|
|
341
|
+
{
|
|
342
|
+
edge->src = new_id;
|
|
343
|
+
edge = edge->next_same_src;
|
|
344
|
+
}
|
|
345
|
+
edge = node->first_in_edge;
|
|
346
|
+
while (edge != NULL)
|
|
347
|
+
{
|
|
348
|
+
edge->dst = new_id;
|
|
349
|
+
edge = edge->next_same_dst;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
new_id++;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
this->nodes->resize(new_id); // 调整节点数组大小
|
|
357
|
+
this->nodes->shrink_to_fit(); // 收缩节点数组的内存占用
|
|
358
|
+
|
|
359
|
+
while (!this->reusable_node_id->empty())
|
|
360
|
+
{
|
|
361
|
+
// 清空可重用节点ID数组
|
|
362
|
+
this->reusable_node_id->pop();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return 0; // 返回成功代码
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
CDiNode *CDiGraph::get_node(long long id)
|
|
369
|
+
{
|
|
370
|
+
// 检查节点ID是否有效
|
|
371
|
+
if (id < 0 || id >= this->nodes->size())
|
|
372
|
+
{
|
|
373
|
+
printf("Invalid index: %lld\n", id);
|
|
374
|
+
return NULL; // 返回空指针
|
|
375
|
+
}
|
|
376
|
+
else if (this->nodes->at(id) == NULL)
|
|
377
|
+
{
|
|
378
|
+
printf("Node %lld is NULL\n", id);
|
|
379
|
+
return NULL; // 返回空指针
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return this->nodes->at(id); // 返回节点指针
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
CDiEdge *CDiGraph::get_edge(long long src, long long dst)
|
|
386
|
+
{
|
|
387
|
+
// 检查源节点和目标节点是否存在
|
|
388
|
+
if (src < 0 || src >= this->nodes->size())
|
|
389
|
+
{
|
|
390
|
+
printf("Invalid src index: %lld\n", src);
|
|
391
|
+
return NULL; // 返回空指针
|
|
392
|
+
}
|
|
393
|
+
else if (this->nodes->at(src) == NULL)
|
|
394
|
+
{
|
|
395
|
+
printf("Src node %lld is NULL\n", src);
|
|
396
|
+
return NULL; // 返回空指针
|
|
397
|
+
}
|
|
398
|
+
if (dst < 0 || dst >= this->nodes->size())
|
|
399
|
+
{
|
|
400
|
+
printf("Invalid dst index: %lld\n", dst);
|
|
401
|
+
return NULL; // 返回空指针
|
|
402
|
+
}
|
|
403
|
+
else if (this->nodes->at(dst) == NULL)
|
|
404
|
+
{
|
|
405
|
+
printf("Dst node %lld is NULL\n", dst);
|
|
406
|
+
return NULL; // 返回空指针
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// 查找边结构体
|
|
410
|
+
CDiEdge *edge;
|
|
411
|
+
if (this->nodes->at(src)->num_out_edges < this->nodes->at(dst)->num_in_edges)
|
|
412
|
+
{
|
|
413
|
+
edge = this->nodes->at(src)->first_out_edge; // 从源节点的出边列表开始查找
|
|
414
|
+
while (edge != NULL && edge->dst != dst)
|
|
415
|
+
{
|
|
416
|
+
if (edge->dst > dst)
|
|
417
|
+
return NULL; // 如果源节点的出边列表中没有目标节点,返回空指针
|
|
418
|
+
edge = edge->next_same_src; // 否则继续遍历同源边
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
else
|
|
422
|
+
{
|
|
423
|
+
edge = this->nodes->at(dst)->first_in_edge; // 从目标节点的入边列表开始查找
|
|
424
|
+
while (edge != NULL && edge->src != src)
|
|
425
|
+
{
|
|
426
|
+
if (edge->src > src)
|
|
427
|
+
return NULL; // 如果目标节点的入边列表中没有源节点,返回空指针
|
|
428
|
+
edge = edge->next_same_dst; // 遍历同目标边
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return edge; // 返回找到的边结构体指针
|
|
433
|
+
}
|