链表是什么
现在我们可以回头总结一下我们的链表:
- 链表是一个由节点组成的一条链。
- 每个节点包含两条信息:
- 序列中存储的是一些数据
- 节点中有指向列表中下一个节点的链接。
- 我们可以从第一个单元格开始跟随着link指针,遍历整个链表。
如同下图所示(左边是一个节点,右边是由节点组成的链表)
- 链表是用于存储元素序列的数据结构。
- 每个元素与其他元素分开存储。
- 然后将这些元素链接在一起成为一个序列
例如我们从尾部插入一个元素,过程如下:
从中间插入元素过程如下:
删除元素如下:
为什么要使用链表?
- 可以高效效地将新元素拼接到列表中,或者删除列表中任何位置的现有元素。
- 可以避免做大量的复制和移动步骤。
- 有一些可以权衡的地方,我们稍后会看到。
为了简单起见,假设我们正在构建一个链接的字符串列表。我们可以将链表中的一个节点表示为一个结构:
struct Node {
string value;
/* ? */ next;
};
那么问题来了,这里的next是什么类型呢?显然我们指向的是下一个节点,当然是指向Node类型的指针了,所以应该这样写:
struct Node {
string value;
Node* next;
};
发现没,这个结构居然是被递归定义的!!,也就是说每生成一个新的节点,就必然有一个指向下一个节点的指针生成。
在分析链表的过程中,一定要记住,我们是需要画图理解的!!!
链表的传递
在Peter Jackson的电影《指环王》——“国王回归”中,Rohan通过山顶的一连串信号火灾警告了Gondor的危险。 这个场景是一个很好的例子,说明了在链表中传递消息的这个想法。
我们来模拟一下这个过程,假如我们要点燃两座城市,如下图,我们第一步,先建立一个链接:
1. Make this linked list
2.Light the fires….
首先,我们要创建一个tower,里面包含了这个地方的名字,当然还有包括它链接的下一个城市,那么它应该是这样的:
struct Tower{
string name;/*这个城市的名称*/
Tower* link;/*链接下一个城市的指针*/
};
接下来,我们应该是创建第一个节点:
// 为什么我们要从最后一个节点开始?因为它的成员是确定的
Tower * head = new Tower;
head->name = “San Jose”;
head->link = NULL;
建立完之后我们要做什么呢?我们当然将它链接到下一个节点,但是下一个节点我们又不知道在哪,所以我们需要自己建立一个:
//创建一个节点,并返回一个指向当前节点的指针
Tower *createTower(string name, Tower *link){
Tower *tp = new Tower; //在heap中新建一个节点,并为此赋值
tp->name = name;
tp->link = link;
return tp; //返回的是一个指针,指向此时的节点
}
接下来我们要把他们串起来:
head = createTower("Santa Clara", head);
head = createTower("Mountain View", head);
head = createTower("Palo Alto", head);
head = createTower("Menlo Park", head);
head = createTower("Redwood City", head);
head = createTower("Millbrae", head);
head = createTower("Bayshore", head);
head = createTower("San Francisco", head);
当调用第一个createTower的时候,他这样执行(2)过程,为了按照要求,我也画图分析
最后我们展示一下我们的链表:
//遍历
void signal(Tower *start) {
if (start != NULL) {
cout << "Lighting " << start->name << endl;
signal(start->link);//再次执行signal函数,参数指向下一个节点(是整一个节点)
}
}
当节点不为空时,输出节点的数据。从头到尾遍历。
我们最后测试一下
#include <iostream>
using namespace std;
/*声明部分*/
struct Tower{
string name;
Tower* link;
};
/*函数声明*/
Tower *createTower(string name, Tower* link);
void signal(Tower *start);
/*main函数*/
int main()
{
/*创建最后应该被点燃的城市*/
Tower * head = new Tower;
head -> name = "San Jose";
head -> link = NULL;
/*利用链表,将他们串起来*/
head = createTower("Santa Clara", head);
head = createTower("Mountain View", head);
head = createTower("Palo Alto", head);
head = createTower("Menlo Park", head);
head = createTower("Redwood City", head);
head = createTower("Millbrae", head);
head = createTower("Bayshore", head);
head = createTower("San Francisco", head); /*这里是我们的第一个初始节点
*相当于从后数的最后一个节点
/*标记我们的链表,即从我们的头结点开始遍历*/
signal(head);
}
//创建一个节点,并返回一个指向当前节点的指针
Tower *createTower(string name, Tower *link){
Tower *tp = new Tower; //在heap中新建一个节点,并为此赋值
tp->name = name;
tp->link = link;
return tp; //返回的是一个指针,指向此时的节点
}
//遍历
void signal(Tower *start) {
if (start != NULL) {
cout << "Lighting " << start->name << endl;
signal(start->link);//再次执行signal函数,参数指向下一个节点(是整一个节点)
}
}
测试结果:
由此我们得到一般的节点的结构组成:
struct Node{
int value; /* The value of this elem */
Node *link; /* Pointer to the next node */
};