First we try, then we trust

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

十四、 CircularLinkedList的设计

CircularLinkedList类是整个程序的核心,所有搜索操作都由CircularLinkedList来触发。CircularLinkedList是一个环形链表,每个链表中的元素都含有下一元素的一个指针,元素与元素链接成封闭的环形。下面是该环型链表的节点定义:

protected class LinkedListNode
{
  
public Layout info;
  
public LinkedListNode link;
}

该节点包含一个Layout类型的info,还有一指向下一节点的指针link。CircularLinkedList自身在构造时传入一个ILayoutFactory类型的工厂,用于加工生成Layout(确保环形链表一旦溢出,能够生成新节点元素)。下面是CircularLinkedList的构造函数:

public CircularLinkedList(ILayoutFactory layoutFactory)
{
  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指针处插入新节点。具体插入操作可以参考下面的代码。

private void insertNode(Layout newitem)
{
  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方法来实现的:

private bool NextStep()
{
  stepCounter
++;
  _mediator.HandleInfo(stepCounter);

  
if(allocate == last)
    
return false//棋局无解

  current 
= last.link;
  last 
= allocate;
  _mediator.MoveCurrentToNext();
  
return true;
}

注意这里调用了中介者的HandleInfo方法,允许外部程序得到当前系统运行状态的一个通知。

 _mediator.HandleInfo(stepCounter);

另外,如果allocate指针未能分配出任何布局,说明程序运行到此处已经没有任何可行的解了,程序就此终止。否则重置各指针位置并通过中介者通知TreeLinkedList移动current指针。

CircularLinkedList中的核心运算方法就是BeginProcess方法。该方法通过递归调用完成搜索操作。首先对当前层级的每个节点运算可行的下步走法,然后调用NextStep方法继续搜索下一步直至搜索完成或系统判断处棋局无解。期间通过stop属性判断是否终止搜索。我们可以通过CircularLinkedList中的Stop方法随时终止搜索进程。在WinHRD的例子中,通过将搜索放到单独的线程中运行确保不影响主程序,同时也可以让用户随时通过Stop方法终止程序运行。BeginProcess方法与Stop方法的定义如下:

public bool BeginProcess()
{
  
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对象。

posted on 2005-02-10 16:17  吕震宇  阅读(2643)  评论(0编辑  收藏  举报