2.3.3笔记-不带头结点的单链表

       单链表有两种形式:带头结点的单链表和不带头结点的单链表。带头结点的单链表的表示已经在 

2.3笔记-线性表的链式表示和实现中做了解释。现在再在这次笔记里对不带头结点的单链表做个解释。不带头结点的单链表L是把头结点去掉,然后关于单链表L的基本操作跟带头结点的单链表的基本操作稍有不同,特别的是在ListInsert函数和ListDelete函数中都加入了对第一个结点的判断,因为在插入和删除中第一个结点和其他结点时操作不同,要改变链表头指针的值。而带头结点的单链表无论插入和删除第几个元素,其操作都是统一的。那么不设头结点的单链表有什么用呢?据高一凡的《数据结构算法分析》说是在第七章会有应用。

不带头结点的单链表的源代码

#include<stdio.h>
#include<malloc.h>
#include<math.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
typedef int ElemType;
typedef int Status;
//该链表为不带头结点的单链表
struct LNode{//线性表的单链表结构
	ElemType data;
	LNode *next;
};
typedef LNode *LinkList;
void InitList(LinkList &L){
	//操作结果:构造一个空的线性表
	L=NULL;//指针为空,此处只设一个空的头指针,如果是带头结点的单链表还要创建一个头结点
}
Status ListInsert(LinkList &L,int i,ElemType e){
	//在不设头结点的单链线性表L中第i个位置之前插入元素e
	int j=1;//计数器初值为1
	LinkList s,p=L;//p指向第一结点
	if(i<1) return ERROR;
	s=(LinkList)malloc(sizeof(LNode));
	s->data=e;//给s的data域赋值e
	if(i==1)//插在表头
	{
		s->next=L;//新结点指向原第一结点,成为新的第一结点
		L=s;//L指向新结点
	}else{
	while(p&&j<i-1){
	j++;//计数器+1
	p=p->next;//p指向下个结点
	}
	if(!p||j>i-1)
		return ERROR;
	s->next=p->next;//新结点指向元第i个结点
	p->next=s;//原第i-1个结点指向新结点
	}
	return OK;//插入成功
}
void print(ElemType e){
printf("%d ",e);
}
void ListTraverse(LinkList L,void (*visit)(ElemType )){
	//初始条件:线性表L已存在。操作结果:依次对L的每个数据元素调用函数visit()
	LinkList p=L->next;
	while(p)//p所指结点存在
	{
		visit(p->data);//对p所指结点调用函数visit()
		p=p->next;//p指向下一个结点
	}
	printf("\n");
}
Status ListEmpty(LinkList L){
	//初始条件:线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE
	if(L)
		return FALSE;
	else
		return TRUE;
}

int ListLength(LinkList L){
	//初始条件:线性表L已存在。操作结果;返回L中数据元素的个数
LinkList p;
int i=0;//计数器初值为0
p=L;//p指向第1个结点
while(p){//未到表尾
	i++;//计数器+1
	p=p->next;//p指向下个结点
}
return i;
}
void DestroyList(LinkList &L){
//初始条件:线性表L已存在。操作结果:销毁线性表L
	LinkList q;
	while(L){//L指向的结点(非空)
		q=L->next;//q指向现在线性表的首元结点
		free(L);//释放现在线性表的头结点
		L=q;//L指向现头结点
	}
}
void ClearList(LinkList L){
	//初始条件:线性表L已存在。操作结果:将L重置为空表(只留下头指针和头结点)
	//线性表L的结构;  L+头结点+头结点指向的单链表
	LinkList p=L->next;//p指向第1个结点
	L->next=NULL;//将头结点指针域为空,断开头结点和头结点指向的单链表
	DestroyList(p);//销毁p所指的单链表

}
int LocateElem(LinkList L,ElemType e, Status(* compare)(ElemType,ElemType)){
//初始条件:线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0)
//操作结果:返回L中第1个与e满足关系compare()的数据元素的位序
//若这样的数据元素不存在,则返回值为0
int i=0;
LinkList p=L;//p指向第1个结点
while(p)//未到表尾
 {
	 i++;
	 if(compare(p->data,e))//找到这样的数据元素
		 return i;
	 p=p->next;//p指向下一个结点
 }
	return 0;//满足关系的数据元素不存在
}
Status equal(ElemType c1,ElemType c2){
	//判断是否相等的函数
	if(c1==c2)
		return TRUE;
	else
		return FALSE;
}
Status GetElem(LinkList L,int i,ElemType &e){//算法2.8
	//L为不设头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回ok,否则返回ERROR
	int j=1;//计数器初值为1
	LinkList p=L;//p指向第1个结点
	if(i<1) return ERROR;
	while(p&&j<i)
	 {
	   j++;
	   p=p->next;//p指向下一个结点
	}
    if(j==i&&p)//存在第i个元素
	{
		e=p->data;//取第i个元素的值赋给e
		return OK;
	}
	return ERROR;//不存在第i个元素,失败返回ERROR
}
Status PriorElem(LinkList L,ElemType cur_e,ElemType &pre_e){
	//初始条件:线性表L已存在
	//操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,返回ok,否则操作失败,pre_ew无定义,返回ERROR
	LinkList q,p=L;//p指向第1个结点
	while(p->next){//p所指结点有后继
		q=p->next;
		if(q->data==cur_e)//p的后继为cur_e
		{
			pre_e=p->data;//将p所指元素的值赋给pre_e
			return OK;//
		}
		p=q; //p的后继不为cur_e,p向后移
	}
	return ERROR;
}
Status NextElem(LinkList L,ElemType cur_e,ElemType &next_e){
  //初始条件:线性表L已存在
 //操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e 返回它的后继,返回OK
	LinkList p=L;//p指向第一个结点
	while(p->next) //p所指结点有后继
	 {
		 if(p->data==cur_e)//p所指结点的值为cur_e
		   {
			   next_e=p->next->data;
               return OK;   
		 }
		 p=p->next;//p指向下个结点
	  } 
	return ERROR;
}
Status ListDelete(LinkList L,int i,ElemType &e){//算法2.10 不改变L
//在不设头结点的单链线性表L中,删除第i个元素,并由e返回其值
  int j=1;//计数器初值为1
  LinkList q,p=L;//p指向第一个结点
  if(!L)//表L空
	  return ERROR;//删除失败
  else if(i==1)//删除第一个结点
  {
	  L=p->next;//L由第二个结点开始
	  e=p->data;//将待删结点的值赋给e
	  free(p);//删除并释放第一个结点
  }else{
  while(p->next && j<i-1){//寻找第i个结点,并令p指向其前驱
     j++;
	 p=p->next;//p指向下一个结点
  }
  if(!p->next || j>i-1)//删除位置不合理
	  return ERROR;
  q=p->next;
  p->next=q->next;//待删除结点的前驱指向待删结点的后继
  e=q->data;//将待删结点的值赋给e
  free(q);//释放待删结点
  }
  return OK;//删除成功
  
}
void main(){
	LinkList L;//首先定义指针L
	ElemType e,e0;
	Status i;
	int j,k;
	InitList(L);//初始化线性表
	for(j=1;j<=5;j++)
		i=ListInsert(L,1,j);//从L的表头插入j
	printf("在L的表头依次插入1~5后,L=");
	ListTraverse(L,print);//依次对元素调用print(),输出元素的值
	i=ListEmpty(L);//检测表L是否为空
	printf("L是否为空?i=%d(1:是 0:否),表L的长度=%d\n",i,ListLength(L));
	ClearList(L);//清空表L
	printf("清空L后,L=");
	ListTraverse(L,print);
	i=ListEmpty(L);//检测表L是否为空
	printf("L是否为空?i=%d(1:是 0:否),表L的长度=%d\n",i,ListLength(L));
    for(j=1;j<=10;j++)
		ListInsert(L,j,j);//在L的表尾插入j
	printf("在L的表尾依次插入1~10后,L= ");
	ListTraverse(L,print);//依次输出表L中的元素
	for(j=0;j<=1;j++)
		k=LocateElem(L,j,equal);//查找表L中与j相等的元素,并将其在链表中的排序赋给k
	    if(k) //k不为0,表明有符合条件的元素
		   printf("第%d个元素的值为%d\n",k,j);
    for(j=1;j<=2;j++)//测试头2个数据
	{
		GetElem(L,j,e0);//把表L中的第j个数据赋给e0
		i=PriorElem(L,e0,e);//把表L中的第j个数据赋给e0
		if(i==ERROR)
			printf("元素%d无前驱\n",e0);
		else
		    printf("元素%d的前驱为%d\n ",e0,e);
	}
	for(j=ListLength(L)-1;j<=ListLength(L);j++)  //最后2个数据
	  {
	     GetElem(L,j,e0);//把表L中的第j个数据赋给e0
		 i=NextElem(L,e0,e);//求e0的后继,如成功,将值赋给e
		 if(i==ERROR)
			 printf("元素%d无后继\n",e0);
		 else
			 printf("元素%d有后继%d\n",e0,e);
	   } 
	k=ListLength(L);//k为表长
	for(j=k+1;j>=k;j--)
	{
	  i=ListDelete(L,j,e);//删除第j个数据
	  if(i==ERROR)//表中不存在第j个数据
		  printf("删除第%d个元素失败(不存在此元素)。",j);
	  else //表中存在第j个数据,删除成功,其值赋给e
		  printf("删除第%d个元素成功,其值为%d\n",j,e);
	}
	printf("依次输出L的元素:");
	ListTraverse(L,print);//依次输出表L中的元素
	DestroyList(L);//销毁表L
	printf("销毁L后,L=%u\n",L);
}
运行结果:

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、查找 1. 算法设计题 :已知n元顺序表a0, a1, … , an-1按关键字递增有序存储。给定关键字值key,编写算法用对分查找求下标i,满足ai-1<key且aikey。 2. 编程题:输入n个两两互不相等的整数,以这些整数为关键字建立平衡的二叉排序树。判断该二叉树是否为平衡的,输出判断结果;输出该二叉树的中序遍历关键字访问次序。 3. 从空树起连续插入以下20个关键字构建m=4的B-树。 50, 15, 09, 18, 03, 85, 33, 72, 48, 22, 91, 88, 11, 99, 06, 56, 68, 77, 43, 36。 4. 16个关键字组成的5阶B-树如下图所示,请按关键 字递减的次序删除所有结点至空树,画出每删除1个关键字后得到B-树,直至空树。 5. 12个关键字如本电子教案例1所示,设H(K)=K mod 13,地址空间范围0~15,用二次探测再散列解决冲突。画出哈希表;若各元素等概率查找,求成功查找时的平均查找长度。 二、内部排序 1.算法设计与分析题:将直接插入排序的内循环改造为使用对分查找实现元素插入,请写出基于对分查找的插入排序算法并给出其时间复杂度分析。 2.算法设计:将教案给出的非递归直接插入排序和冒泡排序算法用递归算法实现。 3.算法设计:带附加头结点单链表将各数据结点按关键字升序连接。 4.编程题:键盘输入n个无符号整数,用链式基数排序实现由小到大排序,输出排序结果。 提示:对于C语言32bit宽的unsigned类型,可以采用16进制形式来实现基数排序,即32bit共有8个16进制位,每个16进制位进行一趟分配和收集,共8趟。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值