数据结构note(三)
-
链表与数组、向量的不同之处
链表与数组、向量不同之处在于,其元素的地址可以任意而不要求连续。元素之间通过指针相互联系。虽然其仍要求各元素在逻辑上具线性次序,但对物理地址未作任何限制,也即“动态存储”策略。
引入列表结构的目的,就在于弥补向量结构在解决某些应用问题时,在功能及性能方面的不足。其中的数据,分散为一系列称作节点(node)的单位,节点之间通过指针相互索引和访问。为了引入新节点或删除原有节点,只需在局部,调整少量相关节点之间的指针。这就意味着,采用动态存储策略,至少可以大大降低动态操作的成本。
-
单向链表
单向链表由结点构成,每个结点包含两个域:指针域和数据域。最后一个数据的指针域赋NULL
.
-
单向链表的实现
-
框架的搭建
-
准备头文件
LinkList.h
定义两个class:结点和链表,声明一些需要的接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//结点定义(这里数据域没有用抽象的数据类型,以后会改进)
class node {
public:
int data;//数据域
node* next;//指针域
};
//链表定义
class list {
public:
node* head;//指向链表首位node
int size;//链表结点个数
};
//操作接口
//初始化链表
list* LinkList_init();
//插入数据
void LinkList_Insert(list* list, int pos, int data);
//删除指定位置的值
void LinkList_Del(list* list, int pos);
//获得链表长度
int LinkList_GetSize(list* list);
//返回第一个结点数据
int LinkList_FirstData(list* list);
//释放链表内存
void LinkList_FreeSpace(list* list);
//查找某个值
int LinkList_Search(list* list, int data);
//打印链表结点
void LinkList_Print(list* list); -
准备接口定义的文件
LinkList.cpp
把每个先前声明好的复制到该文件,并且需要
#include "LinkList.h"
. -
准备用于测试的main函数.
-
具体写操作接口定义
-
初始化链表
LinkList_init()
根据前面定义的class,首先要创建一个链表,再分配一个头结点,头结点不保存数据。
1
2
3
4
5
6
7
8
9//初始化链表
list* LinkList_init() {
list* L = new list;
L->size = 0;
L->head = new node;
L->head->data = NULL;//头结点不保存数据
L->head->next = NULL;
return L;
} -
插入数据
需要处理pos越界的情况以及list、data为NULL的情况,这里由于事先将data设置为了确定的数据类型int,因此理论上可以不进行处理,而这里还是加上了处理的部分以便后续的更改。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28//插入数据
void LinkList_Insert(list* list, int pos, int data) {
if (list == NULL) {
return;
}
if (data == NULL && data!=0) {
return;
}
//友好处理pos越界的情况
if (pos<0 || pos>list->size) {
pos = list->size;//让其等于尾部
}
//创建新的结点
node* newnode = new node;
newnode->data = data;
newnode->next = NULL;
//找结点
//辅助指针变量
node* pCurrent = list->head;
for (int i = 0; i < pos; i++) {
pCurrent = pCurrent->next;
}//让指针指向pos前的结点位置
//新结点插入链表
newnode->next = pCurrent->next;
pCurrent->next = newnode;
list->size++;
} -
删除指定位置的值
这里就不能友好的处理pos越界的情况了,需要适当的修改。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 删除指定位置的值
void LinkList_Del(list* list, int pos) {
if (list == NULL) {
return;
}
if (pos<0 || pos>=list->size) {
return;
}
//定位到删除结点的前一个结点
node* pCurrent = list->head;
for (int i = 0; i < pos; i++) {
pCurrent = pCurrent->next;
}
//缓存删除的结点
node* delnode = pCurrent->next;
pCurrent->next = delnode->next;
delete delnode;
list->size--;
} -
获得链表长度
1
2
3
4
5
6
7//获得链表长度
int LinkList_GetSize(list* list) {
if (list == NULL) {
return -1;
}
return list->size;
} -
返回第一个结点的数据
1
2
3
4//返回第一个数据(不是头结点)
int LinkList_FirstData(list* list) {
return list->head->next->data;
} -
释放链表内存
辅助指针的设置和前面的2、3思路一样,下面需要遍历的时候同理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//释放链表内存
void LinkList_FreeSpace(list* list) {
if (list == NULL) {
return;
}
//辅助指针、释放结点内存
node* pCurrent = list->head;
while (pCurrent != NULL) {
//缓存下一个结点
node* pNext = pCurrent->next;
delete pCurrent;
pCurrent = pNext;
}
list->size = 0;
delete list;
} -
查找某个值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//查找某个值
int LinkList_Search(list* list, int data) {
if (list == NULL) {
return -1;
}
if (data == NULL && data != 0) {
return -1;
}
//遍历
node* pCurrent = list->head->next;
int i = 0;
while (pCurrent != NULL) {
if (pCurrent->data == data) {
break;
}
i++;
pCurrent = pCurrent->next;
}
return i;
} -
打印链表结点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//打印链表结点
void LinkList_Print(list* list) {
if (list == NULL) {
return;
}
//辅助指针变量
node* pCurrent = list->head->next;
cout << "the datum of list are:";
while (pCurrent != NULL) {
cout << pCurrent->data;
cout << " ";
pCurrent = pCurrent->next;
}
cout << endl;
}
-
-
测试情况
测试的代码如下:
1 |
|
输出: