折腾了相当长一段时间,今天终于把代码整理好了,小编是个自学的beginner,可以看到有些文字还只是摘抄的,好不容易才弄出一篇有自己想法的东西。我目前的学习策略是,先把目标算法的相关代码实现了,然后整体阅读代码,在代码中加上自己的看法。代码书上是有的,但是结构分散,有些细节的东西并没有说明清楚,这对初学者相当考验。关于模拟指针,在网上搜索一下,不知道是自己搜索方式不好还是怎么,得到的中文信息较少,基本上也是把书上的东西贴上。我没有搜索英文方面的文章。
由于小编水平有限,发表的看法可能会有误,有不好的地方请指教。
有时候采用一个节点数组以及对该数组进行索引的模拟指针,可以使设计更方便、更高效。
假定采用一个数组node,该数组的每个元素中都包含两个域:data和link。数组中的节点分别是:node[0]、node[1]、…、node[NumberOfNodes-1]。以下用节点i来代表node[i]。如果一个单向链表c由节点10,5和24按序构成,将得到c=10 (指向链表c的第一个节点的指针是整数类型),node[10].link=5 (指向第二个节点的指针),node[5].link=24(指向下一个节点的指针),node[24].link=-1(表示节点24是链表中的最后一个节点)。
模拟指针的类定义及其操作
Allocate从存储池中取出节点,每次取出一个。Deallocate则将节点放入存储池中,每次放入一个。存储池为可用空间表。
这是SimSpace类,模拟可用空间表
#pragma once
#include<iostream>
//#include"SimulChain.h"
using namespace std;
template <class T>
class SimSpace
{
friend class SimChain<T>;
public:
SimSpace(int MaxSpaceSize = 100);
~SimSpace() { delete[] node; }
int Allocate(); //分配一个节点
void Deallocate(int& i); //释放节点i
template<class T>
class SimNode //刚开始并不知道把SimNode类作为SimSpace类的嵌套类,
//这两个类彼此有利用,把谁放到前面都不合适。
{
friend class SimSpace<T>;
friend class SimChain<T>;
private:
T data;
int link;
};
private:
int NumberOfNodes, first;
SimNode<T> *node;//节点数组
};
template<class T>
SimSpace<T>::SimSpace(int MaxSpaceSize)
{
// 构造函数
NumberOfNodes = MaxSpaceSize;
node = new SimNode<T>[NumberOfNodes];
// 初始化可用空间表
// 创建一个节点链表
for (int i = 0; i < NumberOfNodes - 1; i++)
node[i].link = i + 1; //第(i)0个节点的link是1,最后一个节点的link是-1
//link和i之间的联系就是,这个节点的link是下个节点的i
// 链表的最后一个节点
node[NumberOfNodes - 1].link = -1;
// 链表的第一个节点
first = 0;
};
template<class T>
int SimSpace<T>::Allocate()
{
//这个操作相当于删除可用空间表中的第一个节点,拿出来用了
// 分配一个自由节点
if (first == -1) throw NoMem();
int i = first; //分配第一个节点
first = node[i].link; //first指向下一个自由节点
return i;
};
template<class T>
void SimSpace<T>::Deallocate(int& i)
{
//这个函数相当于在可用空间表中添加一个节点,存进去了
// 释放节点i .
// 使i 成为可用空间表的第一个节点
node[i].link = first; //first此时指向可用空间表的第一个节点
first = i;
//i = -1; //这一步是为什么?这个注释与否对目前的测试代码的结果表面上没有影响。
};
//使用两个空间表的部分函数版本,需要把first1和first2设置位SimSpace的私有成员
//template<class T>
//SimSpace<T>::SimSpace(int MaxSpaceSize)
//{
// // 使用两个可用空间表的构造函数
// NumberOfNodes = MaxSpaceSize;
// node = new SimNode<T>[NumberOfNodes];
// // 初始化可用空间表
// firstl = 0;
// first2 = -1; //模拟指针和C++指针的区别
//};
//
//template<class T>
//int SimSpace<T>::Allocate()
//{
// // 分配一个自由节点
// if (first2 == -1) {// 第2个表为空
// if (firstl == NumberOfNodes) throw NoMem();
// return firstl++;
// }
// // 分配链表中的第一个节点
// int i = first2;
// first2 = node[i].link;
// return i;
//}
————————
采用可用空间表模式分解一个链表将比采用C + +指针更高效。例如,如果一个单向链表的首部和尾部分别为f 和e,可以采用如下语句来释放链表中的所有节点:
node[e].link = first; first = f; //既然所有节点都在链表中,那么可用空间表中的自由节点为零,所以first等于-1?
————————
如果c 是一个循环链表,则采用如下程序释放表中所有节点
template<class T>
void SimSpace<T>::DeallocateCircular(int& c)
{
// 释放一个循环链表c
if (c != -1) {
int next = node[c].link;
node[c].link = first;
first = next;
c = -1;
}
};
这是SimChain类。
#pragma once
#include<iostream>
//#include"SimulSpace.h" //这里包不包含都对下面SimChain类使用SimSpace类无帮助
//一直都是不会划红线但是编译会报奇怪的错误,
using namespace std;
template<class T> class SimSpace; //在这里添加了这句,编译就没有报出那个错误了
//但是类模板声明和定义不是应该放在一起的吗?
template<class T>
class SimChain
{
public:
SimChain() { first = -1; }
~SimChain() { Destroy(); }
void Destroy(); // 使表为空
int Length() const;
bool Find(int k, T& x) const;
int Search(const T& x) const;
SimChain<T>& Delete(int k, T& x);
SimChain<T>& Insert(int k, const T& x);
void Output(ostream& out) const;
private:
int first; // 第一个节点的索引
static SimSpace<T> S;
};
template<class T>
void SimChain<T>::Destroy()
{
// 释放链表节点
int next;
while (first != -1)
{
next = S.node[first].link; //next指向第二个节点
S.Deallocate(first);
first = next;
}
}
template<class T>
int SimChain<T>::Length() const
{
// 返回链表的长度。我们可以算出链表的长度,
//但是无法计算出可用空间的长度?计算可用空间的长度也没有意义?
int current = first, // 链节点的当前位置
len = 0; //元素计数
while (current != -1)
{
current = S.node[current].link;
len++;
}
return len;
}
template<class T>
bool SimChain<T>::Find(int k, T& x) const
{
// 取第k个元素至x
// 如果不存在第k个元素,函数返回false,否则返回true
if (k < 1) return false;
int current = first, // 链节点的当前位置
index = 1; //当前节点的索引
// 移动current至第k个节点
while (index < k && current != -1)
{
current = S.node[current].link;
index++;
}
// 验证是否到达了第k个节点
if (current != -1)
{
x = S.node[current].data;
return true;
}
return false; // 不存在第k个元素
}
template<class T>
SimChain<T>& SimChain<T>::Delete(int k, T& x)
{
// 把第k个元素取至x,然后删除第k个元素
// 如果不存在第k个元素,则引发异常OutOfBounds
if (k < 1 || first == -1)
throw OutOfBounds(); // 不存在第k个元素
// p最终将指向第k个节点
int p = first;
// 将p移动至第k个节点,并从链表中删除该节点
if (k == 1) // p已经指向第k个节点
first = S.node[first].link; // 从链表中删除
//这里表明k与数值node中的下标i是没有必然联系的。
//k==1定位到SimChain链表中的第一个节点,first指向这个链表
//中的第一个节点,但是node[first]可以是数组中的任何位置的元素
else
{
// 使用q指向第k - 1个元素
int q = first;
for (int index = 1; index < k - 1 && q != -1; index++)
q = S.node[q].link;
// 验证第k个元素的存在性
if (q == -1 || S.node[q].link == -1)
throw OutOfBounds(); // 不存在第k个元素
// 使p指向第k个元素
p = S.node[q].link;
// 从链表中删除第k个元素
S.node[q].link = S.node[p].link;//再次强调,这里用p、q的目的之一是取出所要的节点在node
//数组中的下标,传给Deallocate函数的也是这个下标,
//从这里可以得到一条经验:传给SimSpace表相关操作
//的参数应该都是node数组的下标。而k这里是SimChain链表
//索引。
}
// 保存第k个元素并释放节点p
x = S.node[p].data;
S.Deallocate(p); //释放这个已经删除的节点的目的就是为了让这个节点回归到
//可用空间表中。否则,它就处于“游离”状态,下次就没法使用,
//相当于“内存泄漏”了。
return *this;
}
template<class T>
SimChain<T>& SimChain<T>::Insert(int k, const T& x)
{
// 在第k个元素之后插入x
// 如果不存在第k个元素,则引发异常OutOfBounds
// 如果没有足够的空间,则传递NoMem异常
if (k < 0)
throw OutOfBounds();
// 定义一个指针p,p最终将指向第k个节点
int p = first;
// 将p移向第k个节点
for (int index = 1; index < k && p != -1; index++)
p = S.node[p].link;
// 验证第k个节点的存在性
if (k > 0 && p == -1)
throw OutOfBounds();
// 为插入操作分配一个新节点
int y = S.Allocate();
S.node[y].data = x;
// 向链表中插入新节点
// 首先检查新节点是否要插到链表的首部
if (k)
{
//在p之后插入
S.node[y].link = S.node[p].link;
S.node[p].link = y;
}
else
{
// 作为链表首节点
S.node[y].link = first; first = y;
}
return *this;
}
template<class T>
int SimChain<T>::Search(const T& x) const
{
// 寻找x,如果发现x,则返回x的地址
// 如果x不在链表中,则返回0
int current = first, //链节点的当前位置
index = 1; // current的索引
while (current != -1 && S.node[current].data != x)
{
current = S.node[current].link;
index++;
}
if (current != -1)
return index;
return 0;
}
template<class T>
void SimChain<T>::Output(ostream& out) const
{
// 将链表元素送至输出流
int current;
for (current = first; current != -1; current = S.node[current].link)
out << S.node[current].link <<":"<<S.node[current].data << " ";//输出link是为了方便
//比较link与插入序位k之间的不同
}
// 重载<<
template <class T>
ostream& operator<<(ostream& out, const SimChain<T>& x)
{
x.Output(out);
return out;
}
异常处理
#pragma once
#include <iostream>
using namespace std;
// 内存不足
class NoMem
{
public:
NoMem() {}
};
// 使new引发NoMem异常而不是xalloc异常
void my_new_handler()
{
throw NoMem(); //什么意思
}
new_handler Old_Handler_ = set_new_handler(my_new_handler);
class OutOfBounds
{
public:
OutOfBounds() {}
};
主函数
//头文件包含的顺序很重要,在两个头文件中分别不包含彼此但有相互引用
//的情况下,Space.h对Chain.h的依赖比反之更强。在目前这种包含关系下,编译
//产生的错误跟之前的错误一样,即在两个头文件都彼此包含的情况下编译产生的错误。
//这也就是说,在头文件和源文件都包含了相关文件的情况下,编译器选择了按源文件中的包含顺序
//来执行?那我现在把源文件中的相关头文件注释掉,然后在两个自编头文件中包含彼此,会怎么样?
//原文件一注释掉就划红线,所以不能这样。现在搞清楚了一个关系,同一个项目中的头文件,只需要
//在源文件中包含就行了,但这要注意顺序!
//不得不注意,SimChain类和SimSpace类中都有一个first变量,应该区分清楚,
//SimChaim.first初始化为-1,SimSpace初始化为0。
//这是很自然的,从各个类的结构中看,实例化后,可用空间充满自由节点,
//它的first理应指向第一个节点(first=0);而SimChain链表还没有节点,所以它的first=-1。
#include <iostream>
#include "SimulChain.h"
#include "SimulSpace.h"
#include "Xcept.h"
using namespace std;
SimSpace<int> SimChain<int>::S; //实例化可用空间S(调用SimSpace类构造函数),
//这时存在一个足够大的数组,存放很多节点,
//每个节点的link已经在构造函数中指定,
//第0个节点node[0]的link部分是1,...
void main(void)
{
int x;
SimChain<int> c;
cout << "Chain length is" << c.Length() << endl;
c.Insert(0, 2).Insert(1, 6);
cout << "Chain length is" << c.Length() << endl;
c.Find(1, x);
cout << "First element is" << x << endl;
c.Delete(1, x);
cout << "Deleted" << x << endl;
cout << "New length is" << c.Length() << endl;
cout << "Position of 2 is" << c.Search(2) << endl; //此处返回了0,表示不已经在链表中
cout << "Position of 6 is" << c.Search(6) << endl;
c.Insert(0, 9).Insert(1, 8).Insert(2, 7);
cout << "Current chain is" << c << endl;
cout << "Its length is" << c.Length() << endl;
cout << "Position of 2 is" << c.Search(2) << endl;
cout << "Position of 6 is" << c.Search(6) << endl;
}
以上内容部分整理自网络电子资料,仅供学习交流用,勿作商业用途。转载请注明来源。