十四、 CircularLinkedList的设计
CircularLinkedList类是整个程序的核心,所有搜索操作都由CircularLinkedList来触发。CircularLinkedList是一个环形链表,每个链表中的元素都含有下一元素的一个指针,元素与元素链接成封闭的环形。下面是该环型链表的节点定义:
{
public Layout info;
public LinkedListNode link;
}
该节点包含一个Layout类型的info,还有一指向下一节点的指针link。CircularLinkedList自身在构造时传入一个ILayoutFactory类型的工厂,用于加工生成Layout(确保环形链表一旦溢出,能够生成新节点元素)。下面是CircularLinkedList的构造函数:
{
current = null;
last = null;
allocate = null;
end = null;
count = 0;
stepCounter = 1;
this.layoutFactory = layoutFactory;
}
在传入工厂对象的同时初始化四个指针:current、last、allocate与end,除此之外还初始化两个计数器count(用于计算环型链表总共包含多少个节点)与stepCounter(用于计算当前搜索层级,搜索到了第几步)。current、last、allocate三个指针的作用在前面的内容中已经说的很清楚了。这里的end指针是用于防止溢出,记录溢出位置的指针。
分配可用Layout的函数以及确认分配的函数定义如下:
// 获取下一个可用空节点
//======================================================
public Layout AllocateLayout()
{
if(allocate.link == current)
{
end = allocate;
insertNode(layoutFactory.Create());
}
return allocate.link.info;
}
//======================================================
// 确认分配的空间
//======================================================
public void ConfirmAllocation()
{
allocate = allocate.link;
}
在这里需要注意的是当分配可用Layout时,注意防止溢出发生。溢出判断的方式是:allocate.link == current,如果相同表明将会发生溢出,这时候需要先插入新节点,然后再分配可用布局。确认分配的动作很简单,就是将allocate指针指向下一节点而已。
插入节点时,首先判断当前环形链表是否为空,如果为空,新节点的下一个节点就是自己本身,单一节点构成一封闭的环。如果环形链表不为空,则在end指针处插入新节点。具体插入操作可以参考下面的代码。
{
LinkedListNode trail;
LinkedListNode newNode;
newNode = new LinkedListNode();
newNode.info = newitem;
newNode.link = null;
if(current == null)
{
current = newNode;
current.link = newNode;
end = current;
}
else
{
trail = end.link;
end.link = newNode;
end = newNode;
newNode.link = trail;
}
count++;
}
当某一步搜索完成后,就会进入下一轮搜索,系统重置各指针位置。这是由NextStep方法来实现的:
{
stepCounter++;
_mediator.HandleInfo(stepCounter);
if(allocate == last)
return false; //棋局无解
current = last.link;
last = allocate;
_mediator.MoveCurrentToNext();
return true;
}
注意这里调用了中介者的HandleInfo方法,允许外部程序得到当前系统运行状态的一个通知。
另外,如果allocate指针未能分配出任何布局,说明程序运行到此处已经没有任何可行的解了,程序就此终止。否则重置各指针位置并通过中介者通知TreeLinkedList移动current指针。
CircularLinkedList中的核心运算方法就是BeginProcess方法。该方法通过递归调用完成搜索操作。首先对当前层级的每个节点运算可行的下步走法,然后调用NextStep方法继续搜索下一步直至搜索完成或系统判断处棋局无解。期间通过stop属性判断是否终止搜索。我们可以通过CircularLinkedList中的Stop方法随时终止搜索进程。在WinHRD的例子中,通过将搜索放到单独的线程中运行确保不影响主程序,同时也可以让用户随时通过Stop方法终止程序运行。BeginProcess方法与Stop方法的定义如下:
{
while(current != last.link)
{
current.info.CheckAvailableSteps();
if(stop)
return true;
current = current.link;
if(current != last.link)
_mediator.MoveCurrentToNext();
}
if(this.NextStep())
{
return BeginProcess();
}
else
return false;
}
public void Stop()
{
this.stop = true;
}
CircularLinkedList是程序求解的关键部件,我们从上面的递归调用中可以看出求解的思路。在下部分内容中,将介绍中介者Mediator对象。