基础薄弱,理解尚浅,若有错误,欢迎指正。
线性表-单链表(Ⅲ)
单链表中有头结点 & 无头结点的各种基本操作(完整代码)
有头结点
#include<stdio.h>
#include<malloc.h> //用malloc函数开辟新单元时需用此头文件
typedef int ElemType;
typedef struct LNode //定义单链表结点类型
{
ElemType data; //数据域
struct LNode *next; //指针域
}LNode, * LinkList;
void InitList(LinkList &L) //初始化线性表
{
L = (LNode *)malloc(sizeof(LNode)); //创建头结点
L->next = NULL; //其next域置为NULL
}
bool ListEmpty(LinkList &L) //判断线性表是否为空表
{
return(L->next == NULL); //C语言中返回0或1,C++中返回true或false
}
void HeadInsert(LinkList &L) //逆向建立单链表(头插法)
{
LNode *s;
int num;
printf("输入:");
scanf("%d", &num);
while(num!=999)
{
s = (LNode *)malloc(sizeof(LNode));
s->data = num; //为数据域赋值
s->next = L->next;
L->next = s;
scanf("%d", &num);
}
}
void TailInsert(LinkList& L) //正向建立单链表(尾插法)
{
LNode* s, * p = L;
int num;
printf("输入:");
scanf("%d", &num);
while(num!=999)
{
s = (LNode*)malloc(sizeof(LNode));
s->data = num; //为数据域赋值
s->next = p->next;
p->next = s;
p = p->next;
scanf("%d", &num);
}
}
void DispList(LinkList &L) //输出线性表
{
LNode *p = L->next; //p指向首结点
while (p != NULL)
{
printf("%d ", p->data); //输出p结点的data域
p = p->next; //p移向下一个结点
}
}
int ListLength(LinkList &L) //求线性表的长度
{
int n = 0; //n用来计数,初始为0
LNode * p = L->next; //p指向首结点
while (p != NULL) //当p的指针域不为NULL时循环
{
n++; //计数值+1
p = p->next; //p移向下一个结点
}
return n;
}
bool GetElem(LinkList &L, int i, ElemType &e) //求线性表中的某个数据元素值
{
LNode *p = L; //p指向头结点,j置0(即头结点的序号为0)
int j = 0;
if (i <= 0) return false; //i错误返回假
while (p != NULL && j < i)
{
j++;
p = p->next; //p移向下一个结点
}
if (p == NULL) return false; //不存在第i个数据结点,返回false
else //存在第i个数据结点,返回true
{
e = p->data;
return true;
}
}
bool LocateElem(LinkList &L, ElemType e) //按元素值查找
{
LNode *p = L->next; //p指向首结点,i置为i(即首结点的序号为1)
ElemType i = 1;
while (p != NULL && p->data != e) //查找data值为e的结点,其序号为i
{
p = p->next; //p移向下一个结点
i++;
}
if (p == NULL) return 0; //不存在值为e的结点,返回0
else return i; //存在值为e的结点,返回其逻辑序号i
}
bool ListInsert(LinkList L, int i, ElemType e) //插入数据元素
{
LNode *p = L, *s; //p指向头结点,j置为0(即头结点的序号为0)
int j = 0;
if (i <= 0) return false; //i错误返回false
while (p != NULL && j < i - 1) //查找第i-1个节点
{
j++;
p = p->next; //p移向下一个结点
}
if (p == NULL) //未找到第i-1个结点,返回false
return false;
else //找到第i-1个结点p,插入新结点并返回true
{
s = (LNode *)malloc(sizeof(LNode));
s->data = e; //创建新结点s,其data域置为e
s->next = p->next;
p->next = s; //将结点s插入到结点p之后
return true; //返回true表示成功插入第i个节点
}
}
bool ListDelete(LinkList &L, int i, ElemType &e) //删除数据元素
{
LNode *p = L, *q; //p指向头结点,j置为0(即头结点的序号为0)
int j = 0;
if (i <= 0) return false; //i错误返回false
while (p != NULL && j < i - 1) //查找第i-1个节点
{
j++;
p = p->next; //p移向下一个结点
}
if (p == NULL) return false; //未找到第i-1个结点,返回false
else //找到第i-1个结点p
{
q = p->next; //q指向第i个结点
if (q == NULL) //若不存在第i个结点,返回false
return false;
e = q->data;
p->next = q->next; //从单链表中删除q结点
free(q); //释放q结点
return true; //返回true表示成功删除第i个节点
}
}
void DestoryList(LinkList &L) //销毁单链表
{
LNode* pre = L, * p = L->next; //pre指向结点p的前驱结点
while (p != NULL) //扫描单链表L
{
free(pre); //释放free结点
pre = p; //pre、p同步后移一个结点
p = pre->next;
}
free(pre); //循环结束时,p为NULL,pre指向尾结点,释放它
}
int main()
{
LinkList L;
InitList(L); //初始化线性表,创建一个空的单链表
printf("现在是空链吗:%d\n\n", ListEmpty(L)); //返回1则表示当前线性表为空
HeadInsert(L); //调用函数,头插法创建单链表
printf("现在是空链吗:%d\n\n", ListEmpty(L));
LNode *p = L;
printf("输出线性表中的值:");
DispList(L); //调用函数,输出单链表
printf("\n\n");
printf("建立的线性表的长度为:%d\n\n", ListLength(L)); //调用函数,接收返回的线性表长度
int num;
printf("该位置的值为:%d\n查找成功了吗?:%d\n\n", num, GetElem(L, 4, num));
//调用函数,判断单链表L中有没有第i个数据结点,如果有则返回该值
printf("第一个与该值相等的逻辑序号为:%d\n\n",LocateElem(L, 3));
//调用函数,在单链表L中从头开始找第一个值域与e相等的结点,如果有则返回逻辑序号
printf("向某个位置插入一个元素,插入成功了吗?:%d\n",ListInsert(L, 3, 4));
printf("插入后线性表的长度为:%d\n", ListLength(L));
printf("输出插入后线性表中的值:");
DispList(L);
printf("\n\n");
ElemType e;
printf("删除某个位置的元素,删除成功了吗?:%d\n",ListDelete(L, 2, e));
printf("删除的元素值为:%d\n", e);
printf("删除后线性表的长度为:%d\n", ListLength(L));
printf("输出删除后线性表中的值:");
DispList(L);
printf("\n\n");
DestoryList(L); //销毁线性表
return 0;
}
程序分析:
- 强调这是一个链表——使用LinkList;
- 强调这是一个结点——使用LNode *;
- 运行结果:
无头结点
#include<iostream>
using namespace std;
#include<malloc.h> //用malloc函数开辟新单元时需用此头文件
typedef int ElemType;
typedef struct LNode //定义单链表结点类型
{
ElemType data; //数据域
struct LNode *next; //指针域
}LNode, * LinkList;
bool ListEmpty(LinkList &L) //判断线性表是否为空表
{
return(L == NULL);
}
void InitList(LinkList &L) //初始化线性表
{
L = NULL;
}
void HeadInsert(LinkList &L) //逆向建立单链表
{
LNode *s;
int i;
int num;
printf("输入:");
scanf("%d", &num);
while(num!=999)
{
s = (LNode *)malloc(sizeof(LNode)); //创建新结点
s->data = num;
if (L == NULL)
{
L = s;
s->next = NULL;
}
else
{
s->next = L;
L = s;
}
scanf("%d", &num);
}
}
void TailInsert(LinkList& L) //正向建立单链表
{
LNode* s, * p;
int num;
printf("输入:");
scanf("%d", &num);
while(num!=999)
{
s = (LNode*)malloc(sizeof(LNode)); //创建新结点
s->data = num;
if (L == NULL)
{
L = s;
s->next = NULL;
p = L;
}
else
{
s->next = p->next;
p->next = s;
p = p->next;
}
scanf("%d", &num);
}
}
void DispList(LinkList &L) //输出线性表
{
LNode *p = L; //p指向首结点
while (p != NULL)
{
cout<<p->data<<' '; //输出p结点的data域
p = p->next; //p移向下一个结点
}
}
int ListLength(LinkList& L) //求线性表的长度
{
int n = 0; //n用来计数,初始为0
LNode * p = L; //p指向首结点
while (p != NULL)
{
n++; //计数值+1
p = p->next; //p移向下一个结点
}
return n;
}
bool GetElem(LinkList &L, int i, ElemType &e) //求线性表中的某个数据元素值
{
LNode* p = L; //p指向头结点
int j = 1; //j置1(即头结点的序号为1)
if (i <= 0) return false; //i错误返回假
while (p != NULL && j < i)
{
j++;
p = p->next; //p移向下一个结点
}
if (p == NULL) return false; //不存在第i个数据结点,返回false
else //存在第i个数据结点,返回true
{
e = p->data;
return true;
}
}
bool LocateElem(LinkList &L, ElemType e) //按元素值查找
{
LNode *p = L; //p指向首结点
int i = 1; //i置为i(即首结点的序号为1)
while (p != NULL && p->data != e) //查找data值为e的结点,其序号为i
{
i++;
p = p->next; //p移向下一个结点
}
if (p == NULL) return 0; //不存在值为e的结点,返回0
else return i; //存在值为e的结点,返回其逻辑序号i
}
bool ListInsert(LinkList &L, int i, ElemType e) //插入数据元素
{
LNode *p = L, *s; //p指向头结点
int j = 1; //j置为1(即头结点的序号为1)
if (i <= 0) return false; //i错误返回false
while (p != NULL && j < i - 1) //查找第i-1个节点
{
j++;
p = p->next; //p移向下一个结点
}
if (p == NULL) //未找到第i-1个结点,返回false
return false;
else //找到第i-1个结点p,插入新结点并返回true
{
s = (LNode *)malloc(sizeof(LNode));
s->data = e; //创建新结点s,其data域置为e
if (i == 1)
{
s->next = L;
L = s;
}
else
{
s->next = p->next;
p->next = s;
}//将结点s插入到结点p之后
return true; //返回true表示成功插入第i个节点
}
}
bool ListDelete(LinkList &L, int i, ElemType &e) //删除数据元素
{
LNode *p = L, *q;
int j = 1; //p指向头结点,j置为1(即头结点的序号为1)
if (i <= 0) return false; //i错误返回false
while (p != NULL && j < i - 1) //查找第i-1个节点
{
j++;
p = p->next; //p移向下一个结点
}
if (p == NULL) return false; //未找到第i-1个结点,返回false
else //找到第i-1个结点p
{
if (i == 1)
{
e = p->data;
L = p->next;
free(p);
return true;
}
else
{
q = p->next; //q指向第i个结点
if (q == NULL) //若不存在第i个结点,返回false
return false;
e = q->data;
p->next = q->next; //从单链表中删除q结点
free(q); //释放q结点
return true; //返回true表示成功删除第i个节点
}
}
}
void DestoryList(LinkList &L) //销毁线性表
{
LNode* pre = L, * p = L->next; //pre指向结点p的前驱结点
while (p != NULL) //扫描单链表L
{
free(pre); //释放free结点
pre = p; //pre、p同步后移一个结点
p = pre->next;
}
free(pre); //循环结束时,p为NULL,pre指向尾结点,释放它
}
int main()
{
LinkList L;
InitList(L); //初始化线性表,创建一个空的单链表
cout<<"现在是空链吗?-"<<' ' <<boolalpha<< ListEmpty(L) << endl; //返回true则表示当前线性表为空
HeadInsert(L); //调用函数,头插法创建单链表
cout << "现在是空链吗?-" << ' ' << boolalpha << ListEmpty(L) << endl;
LNode * p=L;
printf("输出线性表中的值:");
DispList(L); //调用函数,输出单链表
printf("\n\n");
printf("建立的线性表的长度为:%d\n\n", ListLength(L)); //调用函数,接收返回的线性表长度
int num;
cout << "查找成功了吗?- " << boolalpha << GetElem(L, 6, num)<<endl;
printf("该位置的值为:", num);
printf("\n");
//调用函数,判断单链表L中有没有第i个数据结点,如果有则返回该值
printf("第一个与该值相等的逻辑序号为:%d\n\n",LocateElem(L, 7));
//调用函数,在单链表L中从头开始找第一个值域与e相等的结点,如果有则返回逻辑序号
cout<<"向某个位置插入一个元素,插入成功了吗?-"<<boolalpha<<ListInsert(L, 1, 6)<<endl;
printf("插入后线性表的长度为:%d\n", ListLength(L));
printf("输出插入后线性表中的值:");
DispList(L);
printf("\n\n");
ElemType e;
cout<<"删除某个位置的元素,删除成功了吗?:"<<boolalpha<<ListDelete(L, 1, e)<<endl;
printf("删除的元素值为:%d\n", e);
printf("删除后线性表的长度为:%d\n", ListLength(L));
printf("输出删除后线性表中的值:");
DispList(L);
printf("\n\n");
DestoryList(L); //销毁线性表
return 0;
}
程序分析:
- 从有头结点的运行结果可以看到,当函数返回值类型为bool时,C语言中只能用1代表true,用0代表false。因此在无头结点的这段程序里,有些行采用了C++的cout输出,因为C++可以使bool类型返回true或false,只需要加上"boolalpha"即可。
- !有头结点和无头结点的一些函数中,循环条件要先写"p != NULL",因为如果p=NULL了,逻辑与操作就不会再继续执行后面的操作了,此时说明链表已经遍历结束了,整个链表中没有找到该值,此时应返回false。
有头结点和无头结点的对比
-有表头结点和无表头结点单链表的区别为:指向不同、数据域不同、简洁性不同。
- 指向不同
1、有表头结点单链表:头指针指向头结点。
2、无表头结点单链表:头指针指向第一个首元结点。- 数据域不同
1、有表头结点单链表:数据域可以不设任何信息,也可以记录表长等信息。
2、无表头结点单链表:数据域可以不存任何信息。- 简洁性不同
1、有表头结点单链表:减少了单链表添加、删除时特殊情况的判断,减少了程序的复杂性。
2、无表头结点单链表:删除或添加时都得需要判断一次首元结点。