为什么要使用链表
我们知道,数组式计算机根据事先定义好的数组类型与长度自动为其分配一连续的存储单元,相同数组的位置和距离都是固定的,也就是说,任何一个数组元素的地址都可一个简单的公式计算出来,因此这种结构可以有效的对数组元素进行随机访问。但若对数组元素进行插入和删除操作,则会引起大量数据的移动,从而使简单的数据处理变得非常复杂,低效。
为了能有效地解决这些问题,一种称为“链表”的数据结构得到了广泛应用。
什么是链表
链表是一种动态数据结构,他的特点是用一组任意的存储单元(可以是连续的,也可以是不连续的)存放数据元素。
链表中每一个元素称为节点,每一个节点都是由数据域和指针域组成的,每个结点中的指针域指向下一个结点。链表的第一个节点称为表头。Head是头指针,表示链表的开始,用来指向第一个节点,而最后一个指针的指针域为NULL(空地址),表示链表的结束。
可以看出链表结构必须利用指针才能实现,即一个节点中必须包含一个指针变量,用来存放下一个节点的地址。
实际上,链表中的每个节点可以用若干个数据和若干个指针。结点中只有一个指针的链表称为单链表,这是最简单的链表结构。
再c++中实现一个单链表结构比较简单。例如,可定义单链表结构的最简单形式如下
struct Node
{
int Data;
Node *link;
};
typedef Node* NodePter;
首先构造链表的开头部分
NodePtr head;
用new操作符创建动态变量,它将成为链表的第一个节点
head = new Node;
然后,为新节点的成员变量赋值:
head -> data = 3;
head -> link = Null;
在表头插入节点
struct Node
{
int Data;
Node *link;
};
typedef Node* NodePter;
void head_insert(NodePtr& head, int the_number);
void head_insert(NodePtr& head, int the_number)
{
NodePtr temp_ptr;
temp_ptr = new Node; //新建由temp_ptr指向的动态变量
temp_ptr->data = the_number;//将数据放到新节点中
temp_ptr->link = head;//使新节点的link成员指向原始列表的head节点
head = temp_ptr;//使head指针指向新节点
}
要考虑到列表不包含任何内容的可能性。什么都没有的列表称为空列表。如果指针变量head本应指向链表的表头节点,但你希望指出链表为空,可以令:
head = NULL;
搜索链表
struct Node
{
int Data;
Node *link;
};
typedef Node* NodePter;
NodePtr search(NodePtr head, int target);
//使用cstddef:
NodePtr search(NodePtr head, int target)
{
NodePtr here = head;
if (here == NULL) //处理空列表
{
retrun NUll;
}
else
{
while(here->data != target && here->link != NULL)
here = here->link;
if (here->data == target)
return here;
else
return NULL;
]
}
在列表中插入和删除节点
插入
struct Node
{
int Data;
Node *link;
};
typedef Node* NodePter;
void insert(NodePtr after_me, int the_number);
//after_me指向链表中的某一个节点
void insert(NodePtr after_me, int the_number)
{
NodePtr temp_ptr;
temp_ptr = new Node;
temp_ptr->data = the_number;
temp_ptr->link = after_me->link;
after_me->link = temp_ptr;
}
删除
before->link = discard->link;
delete discard;
链表的变体
双链表
struct Node
{
int Data;
Node *forward_link;
Node *back_link;
};
typedef Node* NodePter;
树
树是特殊的数据结构,具体的说,在树中可沿着某个路径跟随链接,从顶部(根)节点抵达任何节点。注意,树中没有回路。
每个节点都有两个链接指向其他节点(或为NULL)。这种形式的树称为二叉树。
树不是链表,但采用和链表相似的方法使用链接(指针)。在二叉树中,节点类型的定义在本质上等同于双链表的节点类型定义。
二叉树能高效率的存储和检索数据,留作以后学习。
类构成的链表实战
1.定义Node类的接口文件
//头文件Node.h
//这是一个节点类的接口
namespace linkedlistofclasses
{
class Node
{
public:
Node();
Node(int value, Node *next);
//初始化节点的构造函数
int getData() const;
//检索这个节点的值
Node *getLink() const;
//检索链表的下一个节点
void setData(int value);
void setLink(Node *next);
private:
int data;
Node *link;
};
typedef Node* NodePtr;
}
2.Node类的实现文件
#include <iostream>
#include "Node.h"
namespace linkedlistofclasses
{
Node::Node() : data(0), link(NULL)
{
//有意留空
}
Node::Node(int value, Node *next) : data(value), link(next)
{
//有意留空
}
int Node::getData() const
{
return data;
}
Node* Node::getLink() const
{
return link;
}
void Node::setData(int value)
{
data = value;
}
void Node::setLink(Node *next)
{
link = next;
}
}
3.使用Node类的程序
#include <iostream>
#include "Node.h"
using namespace std;
using namespace linkedlistofclasses;
void head_insert(NodePtr &head, int the_number)
{
NodePtr temp_ptr;
temp_ptr = new Node(the_number, head);
head = temp_ptr;
}
int main(){
NodePtr head, tmp;
head = new Node(0, NULL);
for (int i = 1; i < 5; i++)
{
head_insert(head, i);
}
tmp = head;
while (tmp != NULL)
{
cout << tmp->getData() << endl;
tmp = tmp->getLink();
}
//退出程序前删除链表中的所有节点
tmp = head;
while (tmp != NULL)
{
NodePtr nodeToDelete = tmp;
tmp = tmp->getLink();
delete nodeToDelete;
}
return 0;
}
至此,完成对链表的初步学习