单链表中有头结点 & 无头结点的各种基本操作(完整代码)

基础薄弱,理解尚浅,若有错误,欢迎指正。
线性表-单链表(Ⅲ)

单链表中有头结点 & 无头结点的各种基本操作(完整代码)

有头结点

#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、无表头结点单链表:删除或添加时都得需要判断一次首元结点。
  • 14
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值