3.2.2 单链表的节点类(程序3-5和程序3-6)有两个变量:一个指向下一个节点的指针和一个指向Ting对象的指针。节点类必须可以修改它指向它指向下一个节点的指针(setNext),并且取得该指针的值(getNext).此外,它还必须能够取得它所指向指针对象的地址(getThing).
[程序3-5]单链表中节点类的声明
#ifndef NODE
#define NODE
#include “thing.h”
Class Node
{
Private:
Thing*theThing;//pointer to object being linked
Node*next;//pointer to next node in list
Public:
Node(Thing*);
Node*getNext();
Thing*getThing();
Void setNext (Node*);
};
#endif
[程序3-6]单链表中节点类的实现
#include “node.h”
Node::Node(Thing*theObject)
{
theThing=theObject;
next=0;
}
Node*Node::getNext()
{
Return next;}
Node*Node::getThing()
{return theThing;}
void Node::setNext(Node*nextNode)
{next=nextNode;}
指向下一节点的指针在何时进行修改并不由节点进行控制。链表管理器决定了在什么时间对哪些指针加以修改,以及以何种顺序修改这些指针。在修改指针后,链表管理器还要负责再次保存哪些需要保存的指针。
注意:这里没有提供能够对节点所指向对象进行修改的函数。正如将看到的,如果需要插入或删除一个元素,可以添加或删除一个节点,并不需要修改节点指向的对象。
3.2.3 链表管理器
如前所述,对链表的所有访问都从链表管理器对象开始。如程序3-7所示,链表管理器类除了保存指向链表节点的指针之外,没有提供任何其他信息。链表管理器不需要知道链表中有多少元素,以及在哪一个元素是链表的最后一个元素。只要节点的“next”指针为0,那么该节点就是指向最后一个元素。
链表管理器能够完成4项工作:
l 在链表中插入新的节点
l 通过遍历链表和匹配元素的关键字来查找下一个元素
l 从链表中是删除节点
l 返回指向链表头节点的指针。链表的迭代函数使用本函数,以了解应该从何处开始对链表进行遍历。
【程序3-7】链表管理器类的声明
#ifndef LISIMGR
#define LISTMGR
#include “thing.h”
#include “node.h”
class ListMgr
{
private:
Node*first
public:
ListMgr();
void insert(Thing*);
Thing*find(int);//traverse list to locate byID
int remove (int);//use ID number to locate for removal
Node*getfirst();
};
#endif
1. 向链表中添加元素
程序3-8提供了能够向链表添加元素的程序代码。Thing 对象以关键字(在本例中是数学顺序)的顺序进行存放。
【程序3-8】在单链表中的插入一个元素。
Void ListMgr::insert(Thing*theThing)
{
Node *newNode,*current,*previous;
Thing*currentThing;
newNode= new Node (theThing);//create a node object
int newKey=theThing->getKey();
if(!newNode)
{
Cout<< “/n Cannot create node.”<<endl;
}
if (first==0)//list in empty
first=newNode;
else
{
int firstNode=true;
current=first;//start ar head of list
while(current!=0)
{
currentThing=current->getThing();
if(newKey<currentThing->getKey())
//spot found (between current and current’s previous node
Break;
//save preceding because there aren’t backward pointers
previous=current;
current=current->getNext90;
firstNode=fasle;//not the first node
}
//set previous node to point to new node except when first in list
if(!firstNode)
previous->setNext(newNode);
else
first=newNode;//have new first in list
//set new node to point to following node
newNode->setNext(current);
}
}
插入元素的过程如下:
1) 创建一个新的Node对象,在对象中插入一个指针,指向接受管理的对象
2) 取得该对象关键字的值
3) 检查first变量的内容,确定链表是否为空。如果该变量的值为0,则说明链表中没有任何节点。在这种情况下,将first设置为新建节点,然后退出。否则,继续插入操作。
4) 假定被插入的节点奖成为新的头节点。
5) 设置一个指向链表中下一个待处理节点的指针(变量current)。在插入过程开始时,当前节点为头节点。
6) 进入一个循环,在不存在任何当前节点(current节点包含0)时退出循环。如果该过程达到了链表的末端,则跳到步骤(15),否则,继续步骤(7)。
7) 取得当前节点所链接的对象
8) 取得对象的关键字
9) 将对象的关键字与被插入对象的而关键字进行比较
10) 如果被插入对象的关键字小于当前节点的关键字,那么被插入节点应该插入到当前节点和当前节点前面的节点(前驱节点)之间(如果当前节点是头节点,那么被插入的节点将成为头节点)。退出循环。
11) 将当前节点保存为先前节点。这一步骤是必须的,因为没有任何指针指向前驱节点,但是两个指针之间的插入操作需要访问前驱节点。
12) 取得当前节点的next指针的内容,让当前节点变为“next”(下一个)节点
13) 将firstNode设置为fasle,表明被插入的节点不是一个新的头节点。
14) 回到步骤(6),以确定工程是否达到了链表的末尾。
在链表中插入新节点的实际过程包括:打断被插入节点位置两侧的节点,然后将两个现有节点与新节点链接在一起。例如,如图3-3所示,你希望在节点4和节点5之间,插入节点7。为了实现该目的,必须让节点5指向节点7,让节点7指向节点4.节点4和节点5之间的现有链接则消失。(图略,在单链表中插入新节点)
15)如果被插入的节点不是一个新的头节点,将先前的头节点的下一个节点指向被插入的节点。否则,设置链表管理器的first变量,使其指向被插入的节点。
16)将被插入节点的next指针指向当前节点。如果新节点位于链表末尾,则将next指针设为0,因为此时当前节点为0.
注意:虽然,通过关键字对链表中的元素进行排序是一种很常见的做法。但是也可以采用另外一种策略,让所有新元素均添加到链表的头部和尾部,在后一种情况下,需要编写的代码更简单,只需保持一个指向链表尾节节点的指针,并将其作为链表管理对象的一部分即可以。
2. 从链表中删除元素
从链表中删除元素的操作基本上和添加元素的操作相互对应。正如在图3-4中所看到的,删除节点5需要在节点4和节点6之间创建一个新的链接。虽然只要节点5存在于主内存中,它就会仍然指向节点6,但是节点5中的指针不会对链表产生任何影响。
程序3-9提供了删除链表元素的程序源代码,调用函数提供被删除对象的关键字。然后,该函数必须遍历链表,以找到需要删除的正确节点。
具体的删除过程如下:
(1) 确定链表是否为空,如果空,则退出函数。
(2) 假定被删除的节点是头节点
(3) 将当前节点设置为指向头节点
(4) 进入循环,只要存在当前节点就继续执行循环。如果没有当前节点,则跳到步骤(11)
(5) 取回当前节点所链接的对象
(6) 比较被删除节点的关键字和当前节点的关键字。如果两个关键字相匹配,就表明找到了需要删除的节点。退出循环,继续执行步骤(11)。否则,继续步骤(7)
(7) 将当前节点保存为先前节点。
(8) 设置当前节点,使其指向链表中的下一个节点。
(9) 指出被删除的节点不是链表的头节点
(10) 返回步骤(4)
(11) 如果当前节点为0,说明被删除节点的关键字在链表中不存在。退出函数。
(图略,从单链表中删除一个节点)
【程序3-9】从单链表中删除一个元素
int ListMgr::remove(int searchNumb)
{
Thing*currrentThing;
Node*current,*previous,*next;
if(first==0)
return false;//list is empty
int firstNode=true;
current=first;
while(current!=0)
{
currentThing=current->getThing();
if(searching=currentThing->getkey())
break;//jump out of loop
//save preceding because there aren’t backward pointers
Previous=current;
current=current->getNext9();
firstNode=false;//not the first node
}
If(current==0)
Return false;//node not found
if(!firstNode)
{
//gets node after node being removed
next=current->getnext();
previous->setNext(Next);
}
else
//sets first to node after node being removed
first=current->getNext();
delete current ->getThing()//remove thing object from memory
delete current;//remove node object from memory
return true;//remove was successful
}
(12)如果待删除节点不是头节点,取得待删除节点后面的一个节点。将当前节点的指针指向待删除节点的后一个节点。跳到步骤(13)
(13)如果待删除节点是头节点,将链表管理器的头节点指向待删除节点后面的节点。
(14)从内存中删除待删除节点所链接的对象
(15)从内存中删除待删除节点。
在节点从它所在的链表中被删除之后,一般总是希望从主内存中删除该节点所链接的对象。但是,是否删除链接对象要根据程序的性质而定。如果对象也参与了其他的数据结构,或者今后还会以某种方式使用,那么应该将它保存在内存中。
注意:确定是否从主内存中删除一个元素的方法之一是为元素提供一个整数变量,该变量保存了对元素的引用计数(reference count)。在每次将该元素添加到一个数据结构中的时候,则将引用计数的值加1,同样,在每次从数据结构中删除该元素的时候,则将引用计数的值减1,如果引用计数的值为0,就可以从内存中删除该元素。
3. 单链表的查找
对单链表而言只有一种查找方法,那就是顺序查找。在使用单链表时,必须从链表的第一个元素开始查找,然后按照“next“(一个接一个的)顺序检查每个被链接的对象是否满足查找条件,这里没有什么更快的方法可以使用,因为链表只提供对其成员的顺序访问。
下面一个例子,请阅读程序3-10。列表管理器的find函数收到一个值,该值将作为输入函数与出储存在链表中的对象的关键字进行对比。在开始函数时,需要将当前节点初始化为链表的头节点,因为头节点是链表管理器可以直接访问到的唯一节点。
【程序3-10】链表的查找
Thing*ListMgr::find(int searchNumb)
{
Thing*currentThing;
Node*current;
current=first;//start at head of list
while (current!=0)
{
currentThing=current->getThing();
if(searchNumb==currentThing->getKey())
return =current->getNext();
}
return 0;//not found
}
程序的搜索过程按照如下过程进行:
(1) 进入一个循环,只要存在当前节点则继续循环。如果不存在当前节点,则跳到步骤(7)
(2) 取得当前节点所链接的对象
(3) 将该对象的关键字和被搜索的关键字进行比较。
(4) 如果两个关键字相同,则返回一个指向该对象的指针。否则,继续步骤(5)
(5) 从当前节点取得指向下一个节点的指针,让该节点成为新的当前节点。
(6) 返回步骤(7)
(7) 返回0,指出链表中没有元素包含正在搜素的关键字。
(these notes coming from 《面向对象c++数据结构》)【美】Jan Harrington著。陈博译,科学出版社)
*********************以上内容均用于团队内部学习之用,不涉及任何商业用途**********
}