2.5单链表的基本运算

2.5单链表的基本运算

1、求单链表长度操作

在顺序表中,线性表的长度是它的属性,数组定义时就已确定。
在单链表中,整个链表由“头指针”来表示,单链表的长度在从头到尾遍历的过程中统计计数得到。

算法思路】采用“数”结点的方法求出单链表的长度。即从“头”开始 “数(p=L->next),用指针 p 依次指向各个结点,一直“数”到最后一个结点(p->next= =NULL),从而得到单链表的长度。

  • 顺链头开始,计数器 j 初值为 0
  • 当前指针 p 指向链表 L 的首元结点
  • p=L->next p 依次往后(计数 j++)直到表尾(p==NULL )

算法描述

int ListLength(LinkList L)  /*求带头结点的单链表 L 的长度*/ 
{   
	Node *p; 
	p=L->next;  
	j=0;   /*用来存放单链表的长度*/ 
	while(p!=NULL) 
	{    
		p=p->next; j ++; 
	}
	 return j; /*j 为求得的单链表长度*/   
}  /* ListLength */

算法分析】若单链表 L 为空表,p 的初值为“NULL”,算法中 while 循环未执行, 则返回链表长度 j 为 0。 若单链表 L 为非空表,算法中 while 循环执行次数为表长度 n,故算法的时间复 杂度为 O(n)。

2、建立空单链表

建立由头指针 L 指向的头结点,头结点的指针域置为空,完成空的单链表 L 的初始化。

InitList(LinkList *L) 
{      
	*L = (LinkList)malloc(sizeof(Node)) ; /*建立头结点*/      
(*L)->next = NULL;                /*建立空的单链表 L*/  
} 

3、 建立单链表

建立单链表的方法主要由头插法和尾插法。

1)头插法建表

算法思想】已知空链表 L,依次读入结点数据头插入,直到读入结
束标志为止。

每插入一个结点到表头的步骤:

  • 生成新结点 s 并赋值;
  • 将结点 s 插入到首元结点之前,即表头结点之后;
  • 重复以上过程,生成并插入第 i个结点,直到读取到结束字符为$时, 结束读取数据,建表完毕。

在这里插入图片描述
在这里插入图片描述
算法描述

void CreateFromHead(LinkList  L) /* L 是已经初始化好的空链表的头指针,通过键盘输入表中元素值,利用头 插法建单链表 L */   
{  
	Node   *s;      
	char  c;      
	int  flag=1;      
	while(flag)   /* flag 初值为 1,当输入“$”时,置 flag 为 0, 建表结束*/ 
	{                 
		c=getchar();                    
		if(c!=’$’)                   
		{ 
			s=(Node*)malloc(sizeof(Node));  /*建立新结点 s*/ 
			s->data=c; s->next=L->next;  /*将 s 结点插入表头*/ 
			L->next=s;                    
		}         
		else 
		flag=0; 
	} 
} 

注:头插法得到的单链表的逻辑顺序与输入的顺序相反,所以也称为逆序建表法。

2)尾插法建表

头插法建立链表虽然算法简单,但生成的链表中结点的次序和输入的顺序相反。若希望二者次序一致,可采用尾插法建表。该方法是将新节点插到当前单链表的表尾上。为此需增加一个尾指针 r,始终指向当前链表的表尾。

算法思想
已知空链表 L,设置尾指针 r 指向当前表尾 r=L
依次读入结点数据尾插入,直到读入结束标志时将表尾结点链域置空

每插入一个结点到表尾的步骤:

  • 生成新结点 s;
  • 将结点 s 插入到表尾 r 结点之后;该结点作为当前表尾结点

在这里插入图片描述

算法描述

void  CreateFromTail(LinkList L) /* L 是已经初始化好的空链表的头指针,通过键盘输入元素值,利用尾插法建 单链表 L */ 
{    
	Node *r, *s; 
	int   flag =1; /*设置一个标志,初值为 1,当输入“$”时,flag 为 0, 建表结束*/ 
	r=L;     /* r 指针动态指向链表的当前表尾,以便于做尾插入,其初值指 向头结点*/ 
 	while(flag)    /*循环输入表中元素值,将建立新结点 s 插入表尾*/ 
	{ 
		c=getchar(); 
		if(c!=’$’)  
		{ 
			s=(Node*)malloc(sizeof(Node)); 
			s->data=c; r->next=s; r=s;   
		} 
		else  
		{   
			flag=0;   
			r->next=NULL;   /*将最后一个结点的 next 链域置为空,表示 链表的结束*/  
		} 
	}   /*while*/ 
} /*CreateFromTail*/ 

4、单链表的查找

单链表查找可分为按序号查找按值查找的两种方式。

1)按序号查找

  • 在单链表 L 中查找第 i 个结点,

基本思想】链表的头指针出发,顺着链域 next 逐个数结点,直
至搜索到第 i 个结点为止。
算法思想

  • 从头查找,当前指针 p 指向表头 L;
  • 顺链计数,表未查完且未找到 则计数器 j 加 1,p 指针后移;继续找下一个.
  • 结果判断 如找到第 i 个结点,则返回结点 p; 如表查完未找到,则返回空。

算法描述

Node * Get (LinkList  L, int i) / * 在带头结点的单链表 L 中查找第 i 个结点,若找到(1≤i≤n),则返回 该结点的存储位置;  否则返回 NULL * / 
{  
	int j;  
	Node  *p;
	p=L;
	j=0;  / * 从头结点开始扫描 * /   
 	while ((p->next!=NULL)&&(j<i)    
	{ 
		p=p->next;   / * 扫描下一结点 * /        
 		j++;   / * 已扫描结点计数器 * /    
 	}    
 	if(i= =j)
		return p;   / * 找到了第 i 个结点 * /    
 	else   
 		return NULL;  / * 找不到,i≤0 或 i>n * /
 }    / * Get * /  

问题】:在单链表中可否直接找到第 i 个结点?
答案:不行。链表中结点只能顺链查找,不能直接随机存取。 因为第 i 个结点的位置信息是在第 i-1 个结点的链域中存放,无法直接随机存取,这是由链表结构的特征决定的,必须从链表的表头开始查找。

2) 按值查找

算法思想

  • 从表头开始, p 指向链表 L 首元结点 p=L->next;
  • 当表未查完时, 若当前结点值不为 key,则指针 p 后移 比较当前结点值为 key,则跳出循环
  • 结束返回 p 指针的位置。

算法描述

Node *Locate( LinkList L,ElemType key) / * 在带头结点的单链表 L 中查找其结点值等于 key 的结点,若找到则返 回该结点的位置 p,否则返回 NULL * / 
{ 
	Node *p;    
	p=L->next;   / * 从表中第一个结点开始 * /    
	while (p!=NULL)    
		if (p->data!=key)        
			p=p->next;         
		else  
			break;     / * 找到结点值=key 时退出循环 * /    
		return p; 
}      / * Locate * / 

算法时间性能分析】按序号查找和按值查找由于都是需要从表头开
始逐个搜索,最多走完整个表,因而这两个算法的平均时间复杂度是
相同的即 O(n)。

5、单链表插入操作

要在带头结点的单链表L中第i个数据元素之前插入一个数据元素e。

算法思想

  • 确定第 i-1 个结点的位置 (同按序号查找算法)
  • 申请新结点 s
  • 插入挂链:新结点 s 插至第 i-1 个结点之后
    S 结点的后继指向第 i 个结点 s->next=pre->next
    第 i-1 个结点的指针指向 s pre->next=s

在这里插入图片描述
算法描述

void InsList(LinkList L,int i,ElemType e) /*在带头结点的单链表 L 中第 i 个位置插入值为 e 的新结点 s*/ 
{    
	Node *pre,*s;  
	int k;  
	pre=L;  
	k=0;/*从“头”开始,查找第 i-1 个结点*/ 
	while(pre!=NULL&&k<i-1)  /*表未查完且未查到第 i-1 个时重复,找到 pre 指向第 i-1 个*/     
	{ 
		pre=pre->next;       
		k=k+1;         
	}         /*查找第 i-1 结点*/  
	if(!pre)      /*如当前位置 pre 为空表已找完还未数到第 i 个,说明插入位置不合理*/  
    { 
    	printf(“插入位置不合理!”)return ERROR;          
     }   
     s=(Node*)malloc(sizeof(Node));   /*申请一个新的结点 S */   
     s->data=e;                       /*值 e 置入 s 的数据域*/   
     s->next=pre->next;    /*修改指针,完成插入操作*/   
     pre->next=s; 
     return OK; 
} 

6、单链表删除

算法思想

  • 确定第 i-1 个结点的位置 p(同按序号查找算法)
  • 删除并释放第 i 个结点
    指针 r 指向被删结点 r=p->next
    删除第 i 个结点 r p->next=r ->next
    释放 r 结点 free(r )

算法描述

int DelList(LinkList L,int i,ElemType *e) /*在带头结点的单链表 L 中删除第 i 个元素,并将删除的元素保存到变量*e 中 */ 
{     
	Node *pre,*r;   
	int k;   
	pre=L;k=0;   
	while(pre->next!=NULL&&k<i-1)     /*寻找被删除结点 i 的前驱结点 i-1 使 p 指向它*/           
	{ 
		pre=pre->next;                       
		k=k+1;           
	}        /*查找第 i-1 个结点*/ 
	if(!(pre->next))     /* 即while 循环是因为p->next=NULL 或 i<1 而 跳出的,而是因为没有找到合法的前驱位置, 说明删除位置 i 不合法。*/ 
	{     
		printf(“删除结点的位置 i 不合理!”);        
		return ERROR; 
	} 
	r=pre->next; 
	pre->next=r->next;    /*修改指针,删除结点 r*/ 
	*e=r->data; free(r);    /*释放被删除的结点所占的内存空间*/ 
	return OK; 
} 

说明:删除算法中的循环条件(pre->next!=NULL && k<i-1)与前插算法中的循 环条件(pre!=NULL &&k<i-1)不同,因为前插时的插入位置有 m+1 个(m 为当 前单链表中数据元素的个数)。i=m+1 是指在第 m+1个位置前插入,即在单链表 的末尾插入。而删除操作中删除的合法位置只有 m 个,若使用与前插操作相同的循环条件,则会出现指针指空的情况,使删除操作失败。

单链表是一种常用的数据结构,它由若干个结点组成,每个结点包含一个数据域和一个指向下一个结点的指针。单链表基本操作包括:初始化、插入结点、删除结点、查找结点等。下面是单链表基本运算算法的实现。 首先,定义单链表的结点结构体: ```c typedef struct LNode { DataType data; struct LNode *next; } LNode, *LinkList; ``` 其中,`DataType`为数据类型,可以根据实际需进行定义。 初始化单链表: ```c void InitList(LinkList *L) { *L = (LinkList) malloc(sizeof(LNode)); (*L)->next = NULL; } ``` 插入结点: ```c void InsertList(LinkList L, int pos, DataType x) { int index = 0; LinkList p = L, q; while (p && index < pos - 1) { p = p->next; index++; } if (!p || index > pos - 1) { printf("Invalid position.\n"); return; } q = (LinkList) malloc(sizeof(LNode)); q->data = x; q->next = p->next; p->next = q; } ``` 删除结点: ```c void DeleteList(LinkList L, int pos) { int index = 0; LinkList p = L, q; while (p->next && index < pos - 1) { p = p->next; index++; } if (!p->next || index > pos - 1) { printf("Invalid position.\n"); return; } q = p->next; p->next = q->next; free(q); } ``` 查找结点: ```c LinkList FindList(LinkList L, DataType x) { LinkList p = L->next; while (p != NULL && p->data != x) { p = p->next; } return p; } ``` 需要注意的是,在插入和删除结点时,需要保证传入的位置参数`pos`合法,即在链表的范围之内。此外,当查找结点时,如果找到了指定的元素,则返回它所在的结点的指针;如果没有找到,则返回`NULL`。 完整代码如下:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值