用一组任意的存储单元存储线性表的数据结构(这组数据结构单元可以是连续的,也可以是不连续的)
优点:不要求大片连续空间,改变容量方便
缺点:不可随机存取,要耗费一定空间存放指针
typedef struct LNode
{
ElemType data; //节点的数据域
struct LNode * next; //节点的指针域
}LNode, *LinkList; //LinkList为指向结构体LNode的指针类型
这是下面所使用的函数原型
//定义单链表
typedef struct LNode
{
int data; //节点的数据域
struct LNode* next; //节点的指针域
}LNode, * LinkList; //LinkList为指向结构体LNode的指针类型
//初始化链表
void InitList(LinkList &L)
{
L = (LNode *)malloc(sizeof(LNode)); //生成新节点作为头节点,用头指针L指向头节点
L->next = NULL; //头节点指针域置空
}
//创建单链表(头插法)
void CreateList_H(LinList &L, int i)
{//逆序位输入n个元素的值, 建立带头节点的单链表L
InitList(L); //先建立一个带头节点的空链表
int i;
LNode* p;
for(i = 0; i < n; ++i)
{
p = (LNode*)malloc(sizeof(LNode)); //生成新节点*p
scanf_s("%d", &p->data); //输入元素值赋给新节点*p的数据域
//将新节点*p插入到头节点之后
p->next = L->next;
L->next = p;
}
}
//创建单链表(尾插法)
void GreateList_R(LinkList &L, int n)
{//正序位输入n个元素的值, 建立带头节点的单链表L
InitList(L); //先建立一个带头节点的空链表
LNode* p,*r;
r = L; //尾指针r指向头节点
for(i = 0; i < n; ++i)
{
p = (LNode*)malloc(sizeof(LNode)); //生成新节点
scanf_s("%d", &p->data); //输入元素值赋给新节点*p的数据域
//将新节点*p插入到 *r之后
p->next = NULL;
r->next = p;
r = p; //尾指针r指向新的尾节点*p
}
}
//取值
int GetElem(LinkList L, int i, int &e)
{ //在带头节点的单链表L中根据序号i获取元素的值,用e返回L中第i个数据元素的值
LNode *p;
int j = 1;
p = L -> next; //初始化,p指向首元节点,计数器j赋为1
while(p && j < i) //顺链域向后查找,直到p为空或p指向第i个元素
{
p = p->next; //p指向下一个节点
++j; //计数器j加1
}
if(!p || j > i) //i的值不合法i > 0或 i <= 0
{
printf("取值失败!\n");
exit(0);
}
e = p->data; //去第i个节点的数据域
return OK;
}
// 查找
LNode *LocateElem(LinkList L, int e)
{//在带头节点的单链表中查找值为e的元素
LNode* p;
p = L->next; //初始化p指向的首元节点
while (p && p->data != e) //依次顺着链域next向后查找,直到p为空或p所指向节点的数据域等于e
{
p = p->next; //p指向下一个节点
}
if (p == NULL)
{
printf("查找失败!");
exit(0);
}
return p; //查找成功返回值为e的节点地址p,查找失败p为NULL
}
//插入
void ListInsert(LinkList& L, int i, int e)
{//在带头节点的单链表L中第i个位置插入值为e的新节点
LNode *p = L;
int j = 0;
while (p && j < i - 1) //查找第i - 1个节点,p指向该节点
{
p = p->next;
++j;
}
if (!p || j > i - 1) //i > n + 1或者i < 1
{
printf("删除失败!");
exit(0);
}
LNode* s = (LNode*)malloc(sizeof(LNode)); //生成一个新节点*s
s->data = e; //将新节点*s的数据域置为e
s->next = p->next; //将新节点*s的指针域指向节点a~i~
p->next = s; //将节点p的指针域指向新节点*s
}
//删除
void ListDelete(LinkList& L, int i)
{//在带头节点的单链表L中,删除第i个元素
LNode *p = L;
int j = 0;
while ((p->next) && (j < i - 1)) //查找第i - 1个节点,p指向该节点
{
p = p->next;
++j;
}
if (!(p->next) || (j > i - 1)) //i >n 或者i < 1,删除位置不合理
{
printf("删除失败!");
exit(0);
}
LNode *q = p->next; //临时保存被删除节点 的地址在q中,以备释放
p->next = q->next; //改变删除节点前驱节点的指针域
free(q); //释放节点的空间
}
//遍历链表
void pritfLNode(LinkList L)
{
LNode* p;
p = L->next;
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
}
(输入顺序和线性表中的逻辑顺序相反)
算法步骤
① 创建只有一个头节点的空链表
;
②根据带创建链表包括的元素个数n,循环n次执行以下操作:
生成一个新节点 *p
输入元素值赋给新节点 *p 的数据域
将新节点 *p 插入到头节点之后
void CreateList_H(LinList &L, int i)
{//逆序位输入n个元素的值, 建立带头节点的单链表L
InitList(L); //先建立一个带头节点的空链表
for(i = 0; i < n; ++i)
{
p = new LNode; //生成新节点*p
cin>>p->data; //输入元素值赋给新节点*p的数据域
//将新节点*p插入到头节点之后
p->next = L->next;
L->next = p;
}
}
例
int main(void)
{
int n;
LNode* L; //声明链表
InitList(L); // 初始化链表
printf("请输入链表长度:");
scanf_s("%d", &n);
printf("请输入链表数据:");
CreateList_H(L,n); //创建单链表(头插法)
printf("链表:");
pritfLNode(L); //打印单链表
return 0;
}
(输入顺序和线性表中的逻辑顺序相反)
算法步骤
① 创建只有一个头节点的空链表
;
② 尾指针r初始化,指向头节点;
③ 根据创建链表包括的元素个数n,循环n次执行以下操作
生成一个新节点*p;
输入元素值赋给新节点*p的数据域
将新节点*p插入到 *r之后
尾指针r指向新的尾节点*p
算法描述
void GreateList_R(LinkList &L, int n)
{//正序位输入n个元素的值, 建立带头节点的单链表L
InitList(L); //先建立一个带头节点的空链表
r = L; //尾指针r指向头节点
for(i = 0; i < n; ++i)
{
p = new LNode; //生成新节点
cin>>p->data; //输入元素值赋给新节点*p的数据域
//将新节点*p插入到 *r之后
p->next = NULL;
r->next = p;
r = p; //尾指针r指向新的尾节点*p
}
}
例
int main(void)
{
int n;
LNode* L; //声明链表
InitList(L); // 初始化链表
printf("请输入链表长度:");
scanf_s("%d", &n);
printf("请输入链表数据:");
CreateList_R(L, n); //创建单链表(尾插法)
printf("链表:");
pritfLNode(L); //打印单链表
return 0;
}
创建只有一个头节点的空链表
)算法描述
//带头节点
Status InitList(LinkList &L)
{
L = new LNode; //生成新节点作为头节点,用头指针L指向头节点
L->next = NULL; //头节点指针域置空
return OK;
}
//不带头节点
Status InitList_N(LinkList &L)
{
L->next = NULL; //头节点指针域置空
return OK;
}
例
void InitList(LinkList &L)
{
L = new LNode; //生成新节点作为头节点,用头指针L指向头节点
L->next = NULL; //头节点指针域置空
}
算法步骤
①用指针p指向首元节点,用j做计数器初值赋为1。
②从首元节点开始依次顺着链域next向下访问,只要指向当前节点的指针不为空(NULL),并且没有到达序号为i的节点,则循环执行以下内容:
p 指向下一个节点
计数器j加一
③退出循环,如果指针p为空,或者计数器j大于i,说明指定的序号i值不合法(i大于表长或i小于等于0),取值失败返回ERROR;否则取值成功,此时j = i,p指向的节点就是要找的第i个节点,用参数e保存当前节点的数据域,返回OK。
算法描述
Status GetElem(LinkList L, int i, ElemType &e)
{ //在带头节点的单链表L中根据序号i获取元素的值,用e返回L中第i个数据元素的值
p = L -> next; //初始化,p指向首元节点,计数器j赋为1
j = 1;
while(p && j < i) //顺链域向后查找,直到p为空或p指向第i个元素
{
p = p->next; //p指向下一个节点
++j; //计数器j加1
}
if(!p || j > i) //i的值不合法i > 0或 i <= 0
{
return ERROR;
}
e = p->data; //去第i个节点的数据域
return OK;
}
例
int main(void)
{
int n; //链表长度
int e, i;
LNode* L; //声明链表
InitList(L); // 初始化链表
printf("请输入链表长度:");
scanf_s("%d", &n);
printf("请输入链表数据:");
CreateList_R(L, n);
printf("链表:");
pritfLNode(L); //打印单链表
printf("\n请输入要取值的元素序号:");
scanf_s("%d", &i);
e = GetElem(L, i, e); //取值
printf("取值结果:%d", e);
return 0;
}
算法步骤
① 用指针p指向首元节点
② 从首元节点开始依次顺着链域next向下查找,只要指向当前节点的指针p不为空,并且p所指向的数据域不等于给定值e,则循环执行以下操作:p指向下一个节点
③ 返回p。若查找成功,p此时指向节点的地址值,若查找失败,则p的值为NULL
算法描述
LNode *LocateElem(LinkList L, ElemType e)
{//在带头节点的单链表中查找值为e的元素
p = L->next; //初始化p指向的首元节点
while (p && p->data != e) //依次顺着链域next向后查找,直到p为空或p所指向节点的数据域等于e
{
p = p->next; //p指向下一个节点
}
return p; //查找成功返回值为e的节点地址p,查找失败p为NULL
}
例
int main(void)
{
int n; //链表长度
int e, i;
LNode* L; //声明链表
InitList(L); // 初始化链表
printf("请输入链表长度:");
scanf_s("%d", &n);
printf("请输入链表数据:");
CreateList_R(L, n);
printf("链表:");
pritfLNode(L); //打印单链表
printf("\n请输入要查找的元素:");
scanf_s("%d", &e);
LNode* p = LocateElem(L, e); //查找
printf("查找结果:%d", p->data);
return 0;
}
算法步骤
① 查找节点ai-1 并由指针p指向该节点
② 生成一个新节点*s
③ 将新节点*s的数据域置为e
④ 将新节点*s的指针域指向节点ai
⑤ 将节点p的指针域指向新节点*s
算法描述
Status ListInsert(LinkList &L, int i ,ElemType e)
{//在带头节点的单链表L中第i个位置插入值为e的新节点
p = L;
j = 0;
while(p && j < i - 1) //查找第i - 1个节点,p指向该节点
{
p = p->next;
++j;
}
if(!p || j >i - 1) //i > n + 1或者i < 1
{
return ERROR;
}
s = new LNode; //生成一个新节点*s
s->data = e; //将新节点*s的数据域置为e
s-next = p->next; //将新节点*s的指针域指向节点a~i~
p->next = s; //将节点p的指针域指向新节点*s
return OK;
}
例
int main(void)
{
int n; //链表长度
int e, i;
LNode* L; //声明链表
InitList(L); // 初始化链表
printf("请输入链表长度:");
scanf_s("%d", &n);
printf("请输入链表数据:");
CreateList_R(L, n);
printf("链表:");
pritfLNode(L); //打印单链表
printf("\n请输入要插入的位置:");
scanf_s("%d", &i);
printf("\n请输入要插入的元素:");
scanf_s("%d", &e);
ListInsert(L, i, e); //插入
printf("链表:");
pritfLNode(L); //打印单链表
return 0;
}
算法步骤
① 查找节点ai-1 并由指针p指向该节点
② 临时保存待删除节点ai 的地址在q中,以备释放
③ 将节点*p的指针域指向ai 的直接后继节点
④ 释放节点ai 的空间
算法描述
Status ListDelete(LinkList &L, int i)
{//在带头节点的单链表L中,删除第i个元素
p = L;
j = 0;
while((p->next) && (j < i - 1)) //查找第i - 1个节点,p指向该节点
{
p = p->next;
++j;
}
if( !(p->next) || (j > i - 1)) //i >n 或者i < 1,删除位置不合理
{
return ERROR;
}
q = p->next; //临时保存被删除节点 的地址在q中,以备释放
p->next = q->next; //改变删除节点前驱节点的指针域
delete q; //释放节点的空间
return OK;
}
算法描述
int main(void)
{
int n; //链表长度
int i;
LNode* L; //声明链表
InitList(L); // 初始化链表
printf("请输入链表长度:");
scanf_s("%d", &n);
printf("请输入链表数据:");
CreateList_R(L, n);
printf("链表:");
pritfLNode(L); //打印单链表
printf("\n请输入要删除的位置:");
scanf_s("%d", &i);
ListDelete(L, i); //删除
printf("链表:");
pritfLNode(L); //打印单链表
return 0;
}
单链表完