单链表的定义
typedef struct LNode {
int data;
struct LNode *next;
} LNode, *LinkList;
单链表的初始化
不带头结点
bool InitList(LinkList &L) {
L = NULL;
return true;
}
带头结点
bool InitList(LinkList &L) {
L = (LNode *) malloc(sizeof(LNode));
if (L == NULL)
return false;
L->next = NULL;
return true;
}
头结点和头指针的区分:不管带不带头结点,头指针始终指向单链表的第一个结点,而头结点是带头结点的单链表中的第一个结点,结点内通常不存储信息。
判断单链表是否为空
不带头结点
bool Empty(LinkList L) {
return L == NULL;
}
带头结点
bool Empty(LinkList L) {
return L->next == NULL;
}
单链表的插入操作
按位序(不带头结点)
bool ListInsert(LinkList &L, int i, int e) {
if (i < 1) return false;
if (i==1) {
LNode *s = (LNode *) malloc(sizeof(LNode));
s->data = e;
s->next = L;
L = s;
return true;
}
LNode *p;
int j = 1;
p = L;
while (p != NULL && j < i - 1) {
p = p->next;
j++;
}
if (p == NULL) return false;
LNode *s = (LNode *) malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
按位序(带头结点)
bool ListInsert(LinkList &L, int i, int e) {
if (i < 1) return false;
LNode *p;
int j = 0;
p = L;
while (p != NULL && j < i - 1) {
p = p->next;
j++;
}
if (p == NULL) return false;
LNode *s = (LNode *) malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
后插操作
在p结点之后插入元素e
bool InsertNextNode(LNode *p, int e) {
if (p == NULL) return false;
LNode *s = (LNode *) malloc(sizeof(LNode));
if (s == NULL) return false;
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
简化: 在第i个位置插入元素e(带头结点)
bool ListInsert(LinkList &L, int i, int e) {
if (i < 1) return false;
LNode *p;
int j = 0;
p = L;
while (p != NULL && j < i - 1) {
p = p->next;
j++;
}
return InsertNextNode(p, e );
}
前插操作
在p结点之前插入元素e (偷天换日)
bool InsertPriorNode(LNode *p, int e) {
if (p == NULL) return false;
LNode *s = (LNode *) malloc(sizeof(LNode));
if (s == NULL) return false;
s->next = p->next;
p->next = s;
s->data = p->data;
p->data = e;
return true;
}
单链表的删除操作
按位序(带头结点)
bool ListDelete(LinkList &L, int i, int &e) {
if (i < 1) return false;
LNode *p;
int j = 0;
p = L;
while (p != NULL && j < i - 1) {
p = p->next;
j++;
}
if (p == NULL) return false;
if (p->next == NULL) return false;
LNode *q = p->next;
e = q->data;
p->next = q->next;
free(q);
return true;
}
按位序删除(不带头结点)
bool ListDeleteWithout(LinkList &L, int i, int &e) {
if (i < 1) return false;
if (i == 1) {
LNode *p = L->next;
e = p->data;
L->data = p->data;
L->next = p->next;
free(p);
return true;
}
LNode *p;
int j = 1;
p = L;
while (p != NULL && j < i - 1) {
p = p->next;
j++;
}
if (p == NULL) return false;
if (p->next == NULL) return false;
LNode *q = p->next;
e = q->data;
p->next = q->next;
free(q);
return true;
}
指定结点删除
扩展: 删除结点 *p
? 要删除某个给定结点 *p, 通常的做法实现从链表的头结点开始顺序找到其前驱结点, 然后执行删除操作, 算法的时间复杂度为O(n)
? 其实, 删除结点 *p的操作可用删除 *p的后继结点操作来实现, 实质就是将其后继结点的值赋予其身, 然后删除后继结点, 也能使得时间复杂度为O(1)
q = p->next;
p->data = q->data;
p->next = q->next;
free(q);
重点理解前插和删除的特殊操作
单链表的查找操作
按位序查找
算法思想:从单链表的第一个结点开始,顺着指针域逐个往下搜索,直到找到第 i 个结点为止,否则返回最后一个结点的指针域NULL。
核心代码
LNode *GetElem(LinkList L, int i) {
if (i < 0) return NULL;
LNode *p;
int j = 0;
p = L;
while (p != NULL && j < i) {
p = p->next;
j++;
}
return p;
}
按值查找
算法思想:从单链表的第一个结点开始,依次比较表中各个结点的数据域的值,若某结点数据域的值等于x,则返回该结点的指针并记录结点的位置index;若整个单链表中没有这样的结点,则返回空。
核心代码
LNode *LocateElem(LinkList L, int e, int &index) {
index = 1;
LNode *p = L->next;
while (p != NULL && p->data != e) {
p = p->next;
index++;
}
return p;
}
单链表的创建操作(带头结点)
头插法建立
算法思想:首先初始化一个单链表,其头结点为空,然后循环插入新结点*s:将s的next指向头结点的下一个结点,然后将头结点的next指向s。
核心代码
LinkList HeadInsert(LinkList &L) {
InitList(L);
int x;
scanf("%d", &x);
while (x != 9999) {
LNode *s = (LNode *) malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
scanf("%d", &x);
}
return L;
}
需要指出的是,头插法建立的单链表中结点的次序和输入数据的顺序不一致,是相反的。若希望两者的顺序是一致的,则可采用尾插法建立单链表。
尾插法建立
算法思想:首先初始化一个单链表,然后声明一个尾指针r,让r始终指向当前链表的尾结点,循环向单链表的尾部插入新的结点*s,将尾指针r的next域指向新结点,再修改尾指针r指向新结点,也就是当前链表的尾结点。最后别忘记将尾结点的指针域置空。
核心代码
LinkList TailInsert(LinkList &L) {
InitList(L);
int x;
LNode *s, *r = L;
scanf("%d", &x);
while (x != 9999) {
s = (LNode *) malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf("%d", &x);
}
r->next = NULL;
return L;
}
单链表的长度
算法思想:声明一个指针p,p指向头结点指向的第一个结点,如果p指向的结点不为空,那么长度加一,将p指向下一个结点,直到遍历到最后一个结点为止。
核心代码
int Length(LinkList L) {
int len = 0;
LNode *p = L;
while (p->next != NULL) {
p = p->next;
len++;
}
return len;
}
遍历单链表
算法思想:声明一个指针p,从头结点指向的第一个结点开始,如果p不为空,那么就输出当前结点的值,并将p指向下一个结点,直到遍历到最后一个结点为止。
核心代码
void PrintList(LinkList L) {
LNode *p = L->next;
while (p) {
printf("%d\t", p->data);
p = p->next;
}
printf("\n");
}
单链表的查找,创建及后的完整代码
#include "stdio.h"
#include "stdlib.h"
typedef struct LNode {
int data;
struct LNode *next;
} LNode, *LinkList;
bool InitList(LinkList &L) {
L = (LNode *) malloc(sizeof(LNode));
if (L == NULL)
return false;
L->next = NULL;
return true;
}
LinkList HeadInsert(LinkList &L) {
InitList(L);
int x;
scanf("%d", &x);
while (x != 9999) {
LNode *s = (LNode *) malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
scanf("%d", &x);
}
return L;
}
LinkList TailInsert(LinkList &L) {
InitList(L);
int x;
LNode *s, *r = L;
scanf("%d", &x);
while (x != 9999) {
s = (LNode *) malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf("%d", &x);
}
r->next = NULL;
return L;
}
void PrintList(LinkList L) {
LNode *p = L->next;
while (p) {
printf("%d\t", p->data);
p = p->next;
}
printf("\n");
}
LNode *GetElem(LinkList L, int i) {
if (i < 0) return NULL;
LNode *p;
int j = 0;
p = L;
while (p != NULL && j < i) {
p = p->next;
j++;
}
return p;
}
LNode *LocateElem(LinkList L, int e, int &index) {
index = 1;
LNode *p = L->next;
while (p != NULL && p->data != e) {
p = p->next;
index++;
}
return p;
}
int Length(LinkList L) {
int len = 0;
LNode *p = L;
while (p->next != NULL) {
p = p->next;
len++;
}
return len;
}
int main() {
LinkList L;
TailInsert(L);
PrintList(L);
int i = 3;
printf("第%d个值为%d\n", i, GetElem(L, i)->data);
int e = 4;
int index = -1;
LocateElem(L, e, index);
printf("值为%d的结点为%d\n", e, index);
printf("表长: %d\n", Length(L));
return 0;
}
运行结果
C:\Users\User\Desktop\dataStructure\cmake-build-debug\01xianxingbiao11.exe
1 5 9 8 4 5 6 2 1 4 9999
1 5 9 8 4 5 6 2 1 4
第3个值为9
值为4的结点为5
表长: 10
Process finished with exit code 0
|