遍历链表很重要。 因为一旦可以遍历一个链表了, 我们就可以很容易的实现operations like print the data of the linkedc list, 或者将一个节点插入到某个特殊的位置处。
或者删除某个特定的节点, 修改节点的数据等等。
由于链表的第一个节点是由指针head指向。 所以我们可以存取第一个链表节点的数据, 语句为:
head -> Data;
上面的语句实际上做的事解参考我们的指针, 然后取出指针对应节点的数据:
即等价于:
(*head).Data; // using dot operator
不过一般采用的存取方式是head -> Data, 因为写法上比较简单。
当要存取第二个节点的Data 的时候, 我们当然可以用语句: head -> next -> Data;
我们可以采用访问第三个节点数据, 第四个,。。。
然后当访问第n 个节点的数据的时候呀, 我们会想想最后head 之后跟着多长的链了, complex, messy。
所以这个方法不可取。
如果采用如下的方法是:
如上图, 500, 700 为节点的内存地址。 linked list 的节点的位置地址是不连续的, 后面的节点的内存地址可能在前者节点的前面。
如果我们采用迭代语句:
head = head -> next;
我们当然不需要链接那么的多的链去访问后面的节点数据, 我们采用了head 指针, 沿着链表向后面运动。
然而这个方法是有问题的。 因为我们如果这样移动head 指针的位置, 就会丢失掉head之前的节点无法存取, 而且产生了无法访问的没存垃圾。
所以我们的解决办法是, 定义一个遍历指针tp, 最开始tp初始化为head的内容, 也就是说tp 最开始执行第一个节点。 保持head不变, 这样我们就能够保证链表不会丢失了。
tp = tp -> next;
这样我们就可以去遍历我们的指针了。 然而由于是循环语句, 我们还必须知道loop何时停止。 由于最后一个节点的next指针的内容为null, 所以这就是我们终止遍历的条件。
每次遍历的时候都要检查当前的node 是否能是最后的一个节点。
语句是:
while(tp != 0) {
//some node operation
tp = tp -> next; // update our traversing pointer to next node
}
下面实现。 (下面是工程中改动的文件, 要查看整个工程的文件, 参看上一个linked list 的学习笔记) 打开工程NewlinkedList中的ContactList.h, 添加printList()函数如下, 变为:
/*
*ContactList.h
*
* created on Jul 6, 2014
* Author: ***
*
*
*/
#ifndef CONTACT_LIST_H
#define CONTACT_LIST_H
#include "Contact.h" // the first thing to do is to include the node header file
class ContactList {
public:
ContactList(); // prototype for constructor
void AddToHead(const std::string&);//reference, 避免复制, 更快, const, 所以不让修改
void printList();
private:
Contact* head;
int length;
};
#endif /*end of CONTACT_LIST_H*/
打开文件ContactList.cpp, 实现上面添加的接口函数:
// an implementation of ContactList.h
#include "ContactList.h"
using namespace std;
ContactList::ContactList():head(0), length(0) {
}
void ContactList::AddToHead(const string& name) {
Contact* newOne = new Contact(name);
if(head == 0) {
head = newOne;
}
else {
newOne -> next = head;
head = newOne;
}
length++;
}
void ContactList::printList() {
Contact* tp;
tp = head;
while(tp != NULL) {
cout << tp -> name << endl;
tp = tp -> next;
}
}
下面到NewContactListApp 做如下修改一验证遍历效果:
#include "ContactList.h"
using namespace std;
int main() {
ContactList* cl1 = new ContactList;
string name;
while(true) {
cout << "Enter the name of the contact, or q to quit:";
cin >> name;
if(name == "q")
break;
cl1->AddToHead(name);
}
cl1 -> printList();
return 0;
}
编译运行如下:
可以看出输出的顺序和我们输入的顺序是相反的, 这是正确的。 因为我们是Build the list backward。