单链表的定义
由于顺序表的插入删除操作需要移动大量的元素,影响了运行效率,因此引入了线性表的链式存储——单链表。
单链表通过一组任意的存储单元来存储线性表中的数据元素,不需要使用连续的存储单元,因此它不要求在逻辑上相邻的两个元素在物理位置上也相邻。
单链表的特点:
-
单链表不要求逻辑上相邻的两个元素在物理位置上也相邻,因此不需要连续的存储空间。
-
单链表是非随机的存储结构,既不能直接找到表中某个特定的结点。查找某个特定的结点时,需要从表头开始遍历,依次查找。对于每个链表结点,除了存元素自身的信息外,还需要存放一个指向后继的指针。
单链表的创建
1.单链表中节点类型的描述:
typedef struct node{ //定义单链表结点类型
data_t data; //数据域,可以是别的各种数据类型,本文同意用int类型
struct node *next; //指针域
}listnote, *linklist;
2.初始化
通常会用头指针来表示一个单链表,头指针为NULL时表示一个空表。但是,为了操作方便,会在单链表的第一个结点之前附加一个结点,称为头结点。头结点的数据域可以不设任何信息,也可以记录表长等信息。头结点的指针域指向线性表的第一元素结点。如下图所示:
头结点和头指针的区分:不管带不带头结点,头指针始终指向单链表的第一个结点,而头结点是带头结点的单链表中的第一个结点,结点内通常不存储信息。
那么单链表的初始化操作就是申请一个头结点,将指针域置空。
linklist list_create() {
linklist H;
H = (linklist)malloc(sizeof(listnode)); //申请结点空间
if (H == NULL) { //判断空间是否申请成功
printf("malloc failed\n");
return H;
}
H->data = 0; //将头结点内容置空
H->next = NULL; //将next设置为NULL,初始长度为0的单链表
}
建立单链表
//头插法建立单链表
LinkList HeadInsert(LinkList &L){
InitList(L); //初始化
int x;
scanf();
while(x!=9999){ //输入9999表示结束
LNode *s = (LNode *)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
cin>>x;
}
return L;
}
1、尾插法建立单链表
所谓尾插法建立单链表,就是将新结点插入到当前链表的表尾。如下图所示:
算法思想:
(1).首先初始化一个单链表,然后声明一个尾指针q,让q始终指向当前链表的尾结点;
(2).循环向单链表的尾部插入新的结点*p,将尾指针q的next域指向新结点;
(3).再修改尾指针q指向新结点,也就是当前链表的尾结点;
(4). 最后别忘记将尾结点的指针域置空。
(5). 实现代码:
int list_tail_insert(linklist H, data_t value) {
linklist p;
linklist q;
if (H == NULL) {
printf("malloc failed\n"); //若单链表空间申请失败则直接退出
return -1;
}
//1 new node p
if((p = (linklist)malloc(sizeof(listnode))) == NULL) { //判断new node p是否成功,失败则直接退出
printf("malloc failed\n");
return -1;
}
p->data = value;
p->next = NULL;
//2 locate tail node
q = H;
while(q->next != NULL) {
q = q->next;
}
// 3 insert 尾部插入
q->next = p;
return 0;
}
2、按位查找操作
按位查找函数结构
Getlinklist(h, i-1)
算法思想
-
判断链表是否存在;
2.寻找索访问结点的位置;
3.判断该位置是否越界,若未越界,则返回该结点位的地址。
代码实现
linklist list_get(linklist H, int pos) {
linklist p;
int i;
//1、判断链表是否存在
if (H == NULL) {
printf("H is NULL\n");
return NULL;
}
if (pos == -1) {
return H;
}
//2 寻找访问结点的位置
p = H;
i = -1;
while(i < pos) {
p = p->next;
// 3 判断位置是否越界
if (p == NULL) {
printf("pos is invalid\n");
return NULL;
}
i++;
}
return p;
}
3、按位插入操作
按位插入函数结构
insertLinklist(H, x, i)
功能:实现将x插在ai之前;
算法思路:
(1)调用函数Getlinklist(h, i-1)函数找到ai的前驱指针变量p;
(2)申请一个指针变了q存入x,并将其插入p指向的结点的后面;
(3)更新链表。
代码实现
int list_intser(linklist H, data_t value, int pos) {
linklist p, q;
if (H == NULL) {
printf("H is NULL\n");
return -1;
}
//1 locate node p (pos-1)
p = list_get(H, pos-1);
if (p == NULL) {
retirn -1;
}
//2 new node q
if ((q = (linklist)malloc(sizeof(listnode))) == NULL) {
printf("malloc failed\n");
return -1;
}
q->data = value;
p->next = q;
//3 insert
q->next = p->next; //防止ai丢失先进性此步
p->bext = q;
return 0;
}
4、遍历单链表
算法思想:声明一个指针变量p,从头结点指向的第一个结点开始,如果p不为空,那么就输出当前结点的值,并将p指向下一个结点,直到遍历到最后一个结点为止。
实现代码:
//遍历操作
int list_show(linklist H) {
linklist p;
if (H == NULL) {
printf("H is NULL\n");
return -1;
}
p = H;
while(p->next != NULL) {
printf("%d ", p->next->data);
p = p->next;
}
puts("");
return 0;
}