程序设计要求
是某个cq高校期末程序设计实践作业,全部自己做的比较小众分值90+。
试建立一个继承结构,以栈、队列为派生类,建立它们的抽象基类-Bag类,写出各个类的声明及定义,并实现如下功能:
统一命名各派生类的插入操作为Add,删除操作为Remove。
统一命名各派生类的存取操作为Get和Put。
统一命名各派生类的初始化操作为MakeEmpty,判空操作为Full,计数操作为Length。
要求能将一个栈或队列的内容存入一个文件中,并可从一个文件中读入一个栈或队列。
下面是主要内容
系统应实现的主要功能
一:Queue 和 Stack 类的实现
流程图:
-
Bag 抽象基类
Bag 是一个抽象基类,定义了一系列纯虚函数,这些函数必须由继承它的类实现。它提供了以下接口:
• Add(const T& item) 添加一个元素
• Remove() 移除一个元素
• Get() const 获取一个元素
• Put(const T& item) 放置一个元素
• MakeEmpty() 清空容器
• Full() const 判断容器是否满
• Length() const 获取容器长度
• SaveToFile(const stdstring& filename) const 保存到文件
• LoadFromFile(const stdstring& filename) 从文件加载
• 虚析构函数:确保正确销毁派生类对象 -
Stack 类
Stack 类继承自 Bag,并实现了栈的功能。主要特点包括:
• 使用 stdvectorT 存储数据
• Add(const T& item) 向栈顶添加元素
• Remove() 移除栈顶元素
• Get() const 获取栈顶元素
• Put(const T& item) 替换栈顶元素
• MakeEmpty() 清空栈
• Full() const 栈可以动态增长,不会满
• Length() const 返回栈的长度
• SaveToFile(const stdstring& filename) const 将栈保存到文件
• LoadFromFile(const stdstring& filename) 从文件加载栈 -
Queue 类
Queue 类也继承自 Bag,并实现了队列的功能。主要特点包括:
• 使用 stdvectorT 存储数据
• Add(const T& item) 向队尾添加元素
• Remove() 移除队首元素
• Get() const 获取队首元素
• Put(const T& item) 替换队首元素
• MakeEmpty() 清空队列
• Full() const 队列可以动态增长,不会满
• Length() const 返回队列的长度
• SaveToFile(const stdstring& filename) const 将队列保存到文件
• LoadFromFile(const stdstring& filename) 从文件加载队列 -
init1 函数
init1 函数用于初始化栈和队列,并将它们保存到文件中:
• 创建 Stackint 对象,添加 10 个整数(假设 if1(10) 是一个循环)
• 将栈保存到 stack.txt
• 创建 Queueint 对象,添加 10 个整数
• 将队列保存到 queue.txt -
showop1 函数
showop1 函数用于展示从文件加载栈和队列的操作:
• 调用 init1 函数初始化数据
• 创建 Stackint 对象 stack2,从 stack.txt 加载数据,并输出栈顶元素直到栈为空
• 创建 Queueint 对象 queue2,从 queue.txt 加载数据,并输出队首元素直到队列为空
二:Point 类和计算几何方法类的实现 -
TPoint 模板类
流程图:
TPoint 类是一个通用的二维点或向量类,提供了以下功能:
• 构造函数:初始化二维点的坐标。
• 向量加法:重载 + 运算符,实现两个向量的加法。
• 向量减法:重载 - 运算符,实现两个向量的减法。
• 向量加法赋值:重载 += 运算符,实现向量加法并赋值。
• 向量减法赋值:重载 -= 运算符,实现向量减法并赋值。
• 点乘:计算两个向量的点积。
• 叉乘:计算两个向量的叉积。
• 向量长度:计算向量的模长。
• 角度(弧度):计算向量与 x 轴的夹角(弧度)。
2. TMath 模板类
TMath 类继承自 TPoint,提供了一些常见的几何计算功能:
• 计算三角形面积:通过三个点计算三角形的面积。
• 计算圆形面积:通过半径计算圆形的面积。
• 判断两直线的位置关系:通过四个点判断两条直线是平行、重合还是相交。
• 计算两个向量之间的夹角余弦值:通过四个点或三个点计算两个向量之间夹角的余弦值。
• 计算点到直线的距离:通过三个点计算点到直线的距离。
• 计算点到线段的距离:通过三个点计算点到线段的距离。
3. showop2 函数
showop2 对 TPoint 和 TMath 类进行一些几何计算操作的功能展示:
• 计算三角形面积:通过三个点计算并输出三角形的面积。
• 计算圆形面积:通过半径计算并输出圆形的面积。
• 判断两直线的位置关系:通过四个点判断并输出两条直线的位置关系。
• 计算两个向量之间的夹角余弦值:通过四个点或三个点计算并输出两个向量之间夹角的余弦值。
• 计算点到直线的距离:通过三个点计算并输出点到直线的距离。
• 计算点到线段的距离:通过三个点计算并输出点到线段的距离。
• 测试向量加法赋值和减法赋值:展示了向量加法赋值和减法赋值的操作结果。
三:线段树和树剖类的实现
流程图
- SegmentTree 模板类
SegmentTree 类是一个通用的线段树实现,支持区间更新和区间查询操作。主要功能包括:
• 构造函数:初始化线段树的大小。
• 建树操作:根据输入数据构建线段树。
• 区间修改操作:对指定区间进行加法更新。
• 区间查询操作:查询指定区间的和。 - HLD 类
HLD 类实现了树链剖分,用于处理树上的路径和子树的区间更新和查询操作。主要功能包括:
• 构造函数:初始化树的大小和节点的初始值。
• 添加边:构建树的邻接表。
• 预处理:进行两次深度优先搜索,计算每个节点的深度、父节点、子树大小、重儿子,以及进行重链剖分并建立线段树。
• 查询最近公共祖先(LCA):查询两个节点的最近公共祖先。
• 更新路径上的节点:对路径上的节点进行区间加法更新。
• 查询路径上的节点和:查询路径上的节点和。
• 更新子树上的节点:对以某个节点为根的子树进行区间加法更新。
• 查询子树上的节点和:查询以某个节点为根的子树的节点和。
详细功能及其作用 - SegmentTree 类
• pushup:更新父节点的值,确保父节点的值等于其子节点的和。
• pushdown:将懒惰标记传递给子节点,确保在查询或更新时子节点的值是最新的。
• build:递归建树,将输入数据构建为线段树。
• modify:递归更新指定区间的值,支持懒惰标记。
• query:递归查询指定区间的和,支持懒惰标记。 - HLD 类
• dfs1:第一次深度优先搜索,计算每个节点的深度、父节点、子树大小和重儿子。
• dfs2:第二次深度优先搜索,进行重链剖分,分配节点的链顶和位置。
• LCA:计算两个节点的最近公共祖先。
• update_path:更新路径上的节点值,使用线段树的区间更新操作。
• query_path:查询路径上的节点和,使用线段树的区间查询操作。
• update_tree:更新子树上的节点值,使用线段树的区间更新操作。
• query_tree:查询子树上的节点和,使用线段树的区间查询操作。
附上代码
自己写的时候可以取消宏定义,
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pi ;
#define if1(x) for(int i =1 ;i<=x;i++)
#define if0(x) for(int i = 0;i<x;i++)
#define jf0(x) for(int j = 0;j<x;j++)
#define jf1(x) for(int j = 1;j<=x;j++)
// 定义一个模板类 Bag,作为所有容器类的基类
template <typename T>
class Bag {
public:
virtual void Add(const T& item) = 0; // 添加一个元素
virtual void Remove() = 0; // 移除一个元素
virtual T Get() const = 0; // 获取一个元素
virtual void Put(const T& item) = 0; // 放置一个元素
virtual void MakeEmpty() = 0; // 清空容器
virtual bool Full() const = 0; // 判断容器是否满
virtual int Length() const = 0; // 获取容器长度
virtual void SaveToFile(const std::string& filename) const = 0; // 保存到文件
virtual void LoadFromFile(const std::string& filename) = 0; // 从文件加载
virtual ~Bag() {} // 虚析构函数
};
// 定义一个模板类 Stack,继承自 Bag
template <typename T>
class Stack : public Bag<T> {
private:
std::vector<T> data; // 使用 vector 存储数据
public:
void Add(const T& item) override {
data.push_back(item); // 添加元素到栈顶
}
void Remove() override {
if (!data.empty()) {
data.pop_back(); // 移除栈顶元素
}
}
T Get() const override {
if (!data.empty()) {
return data.back(); // 获取栈顶元素
}
throw std::out_of_range("栈为空!!!"); // 栈为空时抛出异常
}
void Put(const T& item) override {
if (!data.empty()) {
data.back() = item; // 替换栈顶元素
} else {
throw std::out_of_range("栈为空!!!"); // 栈为空时抛出异常
}
}
void MakeEmpty() override {
data.clear(); // 清空栈
}
bool Full() const override {
return false; // 栈可以动态增长,不会满
}
int Length() const override {
return data.size(); // 返回栈的长度
}
void SaveToFile(const std::string& filename) const override {
std::ofstream file(filename);
if (file.is_open()) {
for (const auto& item : data) {
file << item << std::endl; // 将数据写入文件
}
}
}
void LoadFromFile(const std::string& filename) override {
std::ifstream file(filename);
T item;
data.clear();
while (file >> item) {
data.push_back(item); // 从文件读取数据
}
}
};
// 定义一个模板类 Queue,继承自 Bag
template <typename T>
class Queue : public Bag<T> {
private:
std::vector<T> data; // 使用 vector 存储数据
public:
void Add(const T& item) override {
data.push_back(item); // 添加元素到队尾
}
void Remove() override {
if (!data.empty()) {
data.erase(data.begin()); // 移除队首元素
}
}
T Get() const override {
if (!data.empty()) {
return data.front(); // 获取队首元素
}
throw std::out_of_range("队列为空!!!!"); // 队列为空时抛出异常
}
void Put(const T& item) override {
if (!data.empty()) {
data.front() = item; // 替换队首元素
} else {
throw std::out_of_range("队列为空!!!!"); // 队列为空时抛出异常
}
}
void MakeEmpty() override {
data.clear(); // 清空队列
}
bool Full() const override {
return false; // 队列可以动态增长,不会满
}
int Length() const override {
return data.size(); // 返回队列的长度
}
void SaveToFile(const std::string& filename) const override {
std::ofstream file(filename);
if (file.is_open()) {
for (const auto& item : data) {
file << item << std::endl; // 将数据写入文件
}
}
}
void LoadFromFile(const std::string& filename) override {
std::ifstream file(filename);
T item;
data.clear();
while (file >> item) {
data.push_back(item); // 从文件读取数据
}
}
};
void init1(){
//栈的初始化
Stack<int> stack;
if1(10) stack.Add(i);
stack.SaveToFile("stack.txt"); // 保存栈到文件
//队列初始化
Queue<int> queue;
if1(10)queue.Add(i);
queue.SaveToFile("queue.txt"); // 保存队列到文件
}
void showop1(){
init1();//调用
Stack<int> stack2;
stack2.LoadFromFile("stack.txt"); // 从文件加载栈
while (stack2.Length() > 0) {
std::cout << stack2.Get() << " "; // 输出栈顶元素
stack2.Remove(); // 移除栈顶元素
}
std::cout << std::endl;
Queue<int> queue2;
queue2.LoadFromFile("queue.txt"); // 从文件加载队列
while (queue2.Length() > 0) {
std::cout << queue2.Get() << " "; // 输出队首元素
queue2.Remove(); // 移除队首元素
}
std::cout << std::endl;
}
// 定义模板类 TPoint
template<typename T>
class TPoint {
public:
T x, y;
TPoint(T x = 0, T y = 0) : x(x), y(y) {}
// 向量加法
TPoint operator+(const TPoint& p) const {
return TPoint(x + p.x, y + p.y);
}
// 向量减法
TPoint operator-(const TPoint& p) const {
return TPoint(x - p.x, y - p.y);
}
// 向量加法赋值
TPoint& operator+=(const TPoint& p) {
x += p.x;
y += p.y;
return *this;
}
// 向量减法赋值
TPoint& operator-=(const TPoint& p) {
x -= p.x;
y -= p.y;
return *this;
}
// 点乘
T dot(const TPoint& p) const {
return x * p.x + y * p.y;
}
// 叉乘
T cross(const TPoint& p) const {
return x * p.y - y * p.x;
}
// 向量长度
T length() const {
return std::sqrt(x * x + y * y);
}
// 角度(弧度)
T angle() const {
return std::atan2(y, x);
}
};
// 定义模板类 TMath 继承自 TPoint
template<typename T>
class TMath : public TPoint<T> {
public:
// 计算三角形面积(通过三个点)
static T triangleArea(const TPoint<T>& a, const TPoint<T>& b, const TPoint<T>& c) {
return std::abs((b - a).cross(c - a)) / 2.0;
}
// 计算圆形面积(通过半径)
static T circleArea(T radius) {
return M_PI * radius * radius;
}
// 判断两直线的位置关系
static std::string lineRelation(const TPoint<T>& a1, const TPoint<T>& a2, const TPoint<T>& b1, const TPoint<T>& b2) {
TPoint<T> da = a2 - a1;
TPoint<T> db = b2 - b1;
T crossProduct = da.cross(db);
if (std::abs(crossProduct) < 1e-10) { // 平行或重合
T dotProduct = (b1 - a1).dot(da);
if (std::abs(dotProduct) < 1e-10) {
return "这两条直线重合了!!!!"; // 重合
} else {
return "这两条直线是平行的"; // 平行
}
} else {
return "这两条直线是相交的"; // 相交
}
}
// 计算两个向量之间的夹角余弦值(通过四个点)
static T cosineAngle(const TPoint<T>& a, const TPoint<T>& b, const TPoint<T>& c, const TPoint<T>& d) {
TPoint<T> ab = b - a;
TPoint<T> cd = d - c;
T dotProduct = ab.dot(cd);
T lengthProduct = ab.length() * cd.length();
return dotProduct / lengthProduct;
}
// 计算三个点构成的两个向量之间的夹角余弦值(通过三个点)
static T cosineAngle(const TPoint<T>& a, const TPoint<T>& b, const TPoint<T>& c) {
TPoint<T> ab = b - a;
TPoint<T> ac = c - a;
T dotProduct = ab.dot(ac);
T lengthProduct = ab.length() * ac.length();
return dotProduct / lengthProduct;
}
// 计算点到直线的距离(通过三个点)
static T pointToLineDistance(const TPoint<T>& a, const TPoint<T>& b, const TPoint<T>& c) {
TPoint<T> ab = b - a;
TPoint<T> ac = c - a;
return std::abs(ab.cross(ac)) / ab.length();
}
// 计算点到线段的距离(通过三个点)
static T pointToSegmentDistance(const TPoint<T>& a, const TPoint<T>& b, const TPoint<T>& c) {
TPoint<T> ab = b - a;
TPoint<T> ac = c - a;
TPoint<T> bc = c - b;
T e = ac.dot(ab);
if (e <= 0) {//叉乘小于0,余弦值为负数,角度大于90
return ac.length();
}
T f = ab.dot(ab);
if (e >= f) {
return bc.length();
}
return std::sqrt(ac.dot(ac) - (e * e / f));
}
};
void showop2()
{
TPoint<double> p1(0, 0), p2(1, 0), p3(0, 1), p4(1, 1);
// 计算三角形面积
double area = TMath<double>::triangleArea(p1, p2, p3);
std::cout << "三角形面积: " << area << std::endl;
// 计算圆形面积
double radius = 5.0;
double circleArea = TMath<double>::circleArea(radius);
std::cout << "圆形面积: " << circleArea << std::endl;
// 判断两直线的位置关系
TPoint<double> l1a(0, 0), l1b(1, 1);
TPoint<double> l2a(0, 1), l2b(1, 2);
std::string relation = TMath<double>::lineRelation(l1a, l1b, l2a, l2b);
std::cout << "直线关系: " << relation << std::endl;
// 计算两个向量之间的夹角余弦值(通过四个点)
double cosineABCD = TMath<double>::cosineAngle(p1, p2, p3, p4);
std::cout << "向量 (p1->p2) 和 (p3->p4) 之间夹角的余弦值: " << cosineABCD << std::endl;
// 计算三个点构成的两个向量之间的夹角余弦值(通过三个点)
double cosineABC = TMath<double>::cosineAngle(p1, p2, p3);
std::cout << "向量 (p1->p2) 和 (p1->p3) 之间夹角的余弦值: " << cosineABC << std::endl;
// 计算点到直线的距离
double distanceToLine = TMath<double>::pointToLineDistance(p1, p2, p3);
std::cout << "点 p1 到直线 (p2, p3) 的距离: " << distanceToLine << std::endl;
// 计算点到线段的距离
double distanceToSegment = TMath<double>::pointToSegmentDistance(p1, p2, p3);
std::cout << "点 p1 到线段 (p2, p3) 的距离: " << distanceToSegment << std::endl;
// 测试向量加法赋值和减法赋值
TPoint<double> p5(2, 3);
p5 += p1;
std::cout << "p5 += p1 后: (" << p5.x << ", " << p5.y << ")" << std::endl;
p5 -= p2;
std::cout << "p5 -= p2 后: (" << p5.x << ", " << p5.y << ")" << std::endl;
}
template<typename T>
class SegmentTree {
private:
struct Node {
int l, r;
T sum;
T add;
};
vector<Node> tr;
const int N;
void pushup(int u){
int sl = 0,sr = 0;
sl = (tr[u<<1].r-tr[u<<1].l+1)*tr[u<<1].add;
sr = (tr[u<<1|1].r-tr[u<<1|1].l+1)*tr[u<<1|1].add;
tr[u].sum = tr[u<<1].sum +tr[u<<1|1].sum+sl+sr;
}
void pushdown(int u) {
if (tr[u].add == 0) return;
tr[u].sum += (tr[u].r - tr[u].l + 1) * tr[u].add;
tr[u << 1].add += tr[u].add;
tr[u << 1 | 1].add += tr[u].add;
tr[u].add = 0;
}
// 建树操作
void build(int u, int l, int r, const vector<T>& w) {
tr[u] = {l, r, 0, 0};
if (l == r) {
tr[u].sum = w[l];
return;
}
int mid = (l + r) >> 1;
build(u << 1, l, mid, w);
build(u << 1 | 1, mid + 1, r, w);
pushup(u);
}
// 区间修改操作
void modify(int u, int l, int r, T x) {
if (tr[u].l >= l && tr[u].r <= r) {
if (tr[u].l == tr[u].r) tr[u].sum += x;
else tr[u].add += x;
return;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if (mid >= l) modify(u << 1, l, r, x);
if (r > mid) modify(u << 1 | 1, l, r, x);
pushup(u);
}
// 区间查询操作
T query(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) {
return tr[u].sum + tr[u].add * (tr[u].r - tr[u].l + 1);
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
T v = 0;
if (mid >= l) v += query(u << 1, l, r);
if (r > mid) v += query(u << 1 | 1, l, r);
return v;
}
public:
SegmentTree(int size) : N(size) {
tr.resize(4 * N);
}
void build(const vector<T>& data) {
build(1, 0, N - 1, data);
}
void modify(int l, int r, T x) {
modify(1, l, r, x);
}
T query(int l, int r) {
return query(1, l, r);
}
};
class HLD {//树链剖分, Heavy-light Decomposition
public:
int n, m;
vector<int> a;//记录初始状态的节点的权值。
vector<vector<int>> adj; // 邻接表
vector<int> id, nw, top, fa, dep, sz, son;
int idx;
SegmentTree<int> segTree;
// 深度优先搜索1,用于计算每个节点的深度、父节点、子树大小和重儿子
void dfs1(int u, int f, int d) {
dep[u] = d, fa[u] = f, sz[u] = 1;
for (int j : adj[u]) {
if (j == f) continue;
dfs1(j, u, d + 1);
sz[u] += sz[j];
if (sz[son[u]] < sz[j]) son[u] = j;
}
}
// 深度优先搜索2,用于分配节点的链顶、位置和重链剖分
void dfs2(int u, int t) {
id[u] = ++idx, nw[idx] = a[u], top[u] = t;
if (!son[u]) return;
dfs2(son[u], t);
for (int j : adj[u]) {
if (j == fa[u] || j == son[u]) continue;
dfs2(j, j);
}
}
public:
HLD(int n, const vector<int>& values) : n(n), a(values), segTree(n) {
adj.resize(n + 1);
id.resize(n + 1);
nw.resize(n + 1);
top.resize(n + 1);
fa.resize(n + 1);
dep.resize(n + 1);
sz.resize(n + 1);
son.resize(n + 1, 0);
idx = 0;
}
// 添加边
void add_edge(int a, int b) {
adj[a].push_back(b);
adj[b].push_back(a);
}
// 预处理,进行深度优先搜索和建树
void preprocess() {
dfs1(1, -1, 1);
dfs2(1, 1);
vector<int> data(n + 1);
for (int i = 1; i <= n; ++i) data[i] = nw[i];
segTree.build(data);
}
// 查询两个节点的最近公共祖先
int LCA(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
// 更新路径上的节点
void update_path(int u, int v, int k) {
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
segTree.modify(id[top[u]], id[u], k);
u = fa[top[u]];
}
if (dep[u] < dep[v]) swap(u, v);
segTree.modify(id[v], id[u], k);
}
// 查询路径上的节点和
int query_path(int u, int v) {
int res = 0;
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
res += segTree.query(id[top[u]], id[u]);
u = fa[top[u]];
}
if (dep[u] < dep[v]) swap(u, v);
res += segTree.query(id[v], id[u]);
return res;
}
// 更新子树上的节点
void update_tree(int u, int k) {
segTree.modify(id[u], id[u] + sz[u] - 1, k);
}
// 查询子树上的节点和
int query_tree(int u) {
return segTree.query(id[u], id[u] + sz[u] - 1);
}
};
void showop3()
{
int n = 5;
vector<int> values = {0, 1, 2, 3, 4, 5}; // 0号索引未使用
HLD tree(n, values);
tree.add_edge(1, 2);
tree.add_edge(1, 3);
tree.add_edge(2, 4);
tree.add_edge(2, 5);
tree.preprocess();
cout << "从节点 4 到节点 5 的路径和: " << tree.query_path(4, 5) << endl;
tree.update_path(4, 5, 1);
cout << "更新后从节点 4 到节点 5 的路径和: " << tree.query_path(4, 5) << endl;
cout << "以节点 2 为根的子树和: " << tree.query_tree(2) << endl;
tree.update_tree(2, 1);
cout << "更新后以节点 2 为根的子树和: " << tree.query_tree(2) << endl;
}
signed main() {
showop1();
showop2();
showop3();
return 0;
}