注:本文为根据《数据结构与算法分析》一书所做笔记与理解
首先,介绍相关的定义:
我们称大小为0的表为空表。
除空表外的任何表(A1 , A2 , A3 , …… , AN),我们说 Ai+1 后继 Ai (或继 Ai 之后)并称 A i-1 (i < N)前驱A i ( i > 1)。
元素 Ai 在表中的位置为i。
接下来,介绍要在表ADT中进行的操作:
PrintList:打印表
MakeEmpty:清空表
Find:返回关键字首次出现的位置
Insert:从表的某个位置插入某个关键字
Delete:从表的某个位置删除某个关键字
1 数组实现
如果用数组来实现表这个数据结构,打印、清空、查找元素还好说。但是,如果要插入一个元素在某个位置,那就得从这个位置开始,后面的元素全部往后挪一位。同样的,如果要在某个位置删除一个元素,就得从这个位置开始,后面的元素全部往前挪一位。这也太麻烦了。另外,在定义数据结构的时候,还需要预估一下这个表的大小,从而初始化数组的大小。
由于这些原因,我们一般不用数组。
2 链表
为了避免插入和删除时的移动操作,考虑使用指针。这样,表中的元素就可以不用连续存储,随便放在内存中的哪个位置,只要通过指针指过去就可以了。
链表中每个j节点包括两部分——值和指向下一个节点的指针(即下一个j节点的地址)。
有可能存在的问题是:如果我们删掉了第一个元素,或者在第一个元素前面再插入了一个元素,这样,我们的首元素的指针就会发生变化。因此,一般来说,我们会流出一个标志节点——表头。表头不存储任何值,只是为了保证链表的第一个节点一直不变。
以int型元素为例,放上C++代码
class Node{
public:
int element;
Node* next;
Node(){
next = NULL;
}
Node(int x){
element = x;
next = NULL;
}
Node(int x , Node* next){
element = x;
this->next = next;
}
};
class List{
private:
Node* head;
Node* cur;
public:
//构造函数
List(){
head = new Node();
cur = head;
}
List(Node* node){
head = new Node(0 , node);
cur = head;
}
//其他一些成员函数
……
};
添加一些辅助的成员函数:
//清空链表
void makeEmpty(){
head->next = NULL;
cur = head;
}
//判断是否为空
bool isEmpty(){
if(head->next == NULL)
return true;
else
return false;
}
//判断当前是否为链表结尾
bool isLast(){
return cur->next == NULL;
}
//移动到表头
void toHead(){
cur = head;
}
//往后移动一位
void toNext(){
if(!isLast())
cur = cur->next;
}
//往前移动一位
void toPre(){
Node* t = cur;
toHead();
while(cur->next != t)
toNext();
}
2.1 链表的打印和查找
对于PrintList或Find操作,只需要从表头的指针开始,依次获取下一个,下下个……节点的指针即可。
//打印链表
void print(){
Node* t = cur;
cur = head;
while(!isLast()){
cout << cur->next->element << " ";
toNext();
}
cout << endl;
cur = t;
}
//查找元素,返回位置,没有找到返回-1
int find(int k){
int pos = 0;
cur = head;
while(cur->next != NULL){
cur = cur->next;
if(cur->element == k){
return pos;
}
++pos;
}
return -1;
}
2.2链表的删除
对于删除元素,只需要从要删除的节点那里修改一个指针即可。如下图,以要删除节点3为例
先令当前节点的指针(cur指针)指向要删除的节点的前一个节点,即节点2
然后将节点2的next指针直接指向节点4即可。这样,在链表中,就没有节点3这个结构了。
相应代码如下:
//删除值为k的元素
void deleteNode(int k){
Node* t = cur;
findPrevious(k);
if(!isLast()){
cur->next = cur->next->next;
}
cur = t;
}
//找到值为k的节点的前驱节点
void findPrevious(int k){
toHead();
while(cur->next != NULL && cur->next->element != k){
toNext();
}
}
2.3链表的元素插入
对于插入元素,只需要新建一个新的节点,将其插入相应的位置,只需要修改两个指针。以下图中链表为例,在节点2和节点4之间插入新的节点3.
首先,令当前节点指针(cur指针)指向要插入的位置的前一个节点,然后新建一个节点。新建这个节点的值为要插入的值,next指针为插入位置的下一个节点的地址(即节点4的地址)
然后,将cur节点的next指针指向新节点即可。
代码就很简单了,假设已经将cur指针移动到了指定位置,只需新建节点并修改相应的next指针即可。
//在当前位置插入值为k的元素
void insert(int k){
Node* temp = new Node(k , cur->next);
cur->next = temp;
}