2-2-1链表的基本操作

本文探讨了带头结点和不带头结点链表在初始化、销毁、判断空链表、计算长度、返回元素及位置、插入和删除节点等操作上的区别。通过实例分析,强调了在处理逻辑位置和物理地址位置时的注意事项,以及不同操作对链表指针的影响。

2-2-1链表的基本操作(头结点与不带头结点的链表)

下面不带头结点的链表操作,与带头结点的链表操作,都用到同一个数据存储结构LinkListType.h

//LinkListType.h线性表的单链表存储结构
#pragma	once

#ifndef __LINKLISTTYPE_H__
#define __LINKLISTTYPE_H__
typedef int ElemType;
struct LNode
{
	ElemType data;
	LNode *next;
};
typedef LNode *LinkNode; // 另一种定义LinkList的方法

#endif

*LinkNode和*next都是指向LNode类型的节点

不带头结点的LinkListBasicOperation.h

#pragma	once

#ifndef _LINKLISTBASICOPERATION_H__
#define _LINKLISTBASICOPERATION_H__
#include "c1.h"
#include "LinkListType.h"
#define DestroyList ClearList // DestroyList()和ClearList()的操作是一样的
void InitList(LinkNode &L);
void ClearList(LinkNode &L);
Status ListEmpty(LinkNode L);
int ListLength(LinkNode L);
Status GetElem(LinkNode L,int pos,ElemType &e);
int LocateElem(LinkNode L,ElemType e,Status(*compare)(ElemType,ElemType));
Status PriorElem(LinkNode L,ElemType cur_e,ElemType &pre_e);
Status NextElem(LinkNode L,ElemType cur_e,ElemType &next_e);
Status ListInsert(LinkNode &L,int pos,ElemType e);
Status ListDelete(LinkNode &L,int pos,ElemType &e);
void ListTraverse(LinkNode L,void(*vi)(ElemType));
#endif





不带头结点的ListInsert和ListDelete都需要用到&L引用

带头结点的LinkListBasicOperationWithHead.h

#pragma	once

#ifndef _LINKLISTBASICOPERATIONWITHHEAD_H__
#define _LINKLISTBASICOPERATIONWITHHEAD_H__
#include "c1.h"
#include "LinkListType.h"
void InitList(LinkNode &L);
void DestroyList(LinkNode &L);
void ClearList(LinkNode L);
Status ListEmpty(LinkNode L);
int ListLength(LinkNode L);
Status GetElem(LinkNode L,int i,ElemType &e);
int LocateElem(LinkNode L,ElemType e,Status(*compare)(ElemType,ElemType));
Status PriorElem(LinkNode L,ElemType cur_e,ElemType &pre_e);
Status NextElem(LinkNode L,ElemType cur_e,ElemType &next_e);
Status ListInsert(LinkNode L,int i,ElemType e);
Status ListDelete(LinkNode L,int i,ElemType &e);
void ListTraverse(LinkNode L,void(*vi)(ElemType));
#endif

带头结点的ListInsert和ListDelete则没有用到&L引用

下面透过对比12个操作来区分头结点和不带头结点的区别

1.初始化链表

//不带
void InitList(LinkNode &L)
{ // 操作结果:构造一个空的线性表L
	L=NULL; // 指针为空
}
//带
void InitList(LinkNode &L)
{ // 操作结果:构造一个空的线性表L
	L=(LinkNode)malloc(sizeof(LNode)); // 产生头结点,并使L指向此头结点
	if(!L) // 存储分配失败
		exit(OVERFLOW);
	L->next=NULL; // 指针域为空
}

不带头结点{

初始化第一个节点的指针

}

带头结点{

 创建节点

如果创建失败 则返回错误

否则初始化第一个节点

}

2.销毁链表和清空链表

//不带
#define DestroyList ClearList // DestroyList()和ClearList()的操作是一样的
void ClearList(LinkNode &L)
{ // 初始条件:线性表L已存在。操作结果:将L重置为空表
	LinkNode p;
	//int i=0;
	while(L!=NULL) // L不空
	{
		p=L;  // p指向首元结点
		L=L->next;// L指向第2个结点(新首元结点)
		//printf("i=%d,L->next=%x\n",++i,L);
		free(p); // 释放首元结点
		//printf("p=%x\n",p);

	}
	/*
	LinkNode p;
	while(L!=NULL){
		p=L->next;
		free(L);
		L=p;
	}
	*/
}}
}
//带
void DestroyList(LinkNode &L)
{ // 初始条件:线性表L已存在。操作结果:销毁线性表L
	LinkNode p;
	while(L)
	{
		p=L->next;//把第一个节点地址赋值给p,p指向第一个节点
		free(L);//清空头结点
		//printf("****p=%d\n",p);
		//printf("************L=%d\n",L);
		L=p;//将第一个节点(新)的地址给
	}
}
//带
void ClearList(LinkNode L) // 不改变L
{ // 初始条件:线性表L已存在。操作结果:将L重置为空表
	LinkNode pre,nex;
	pre=L->next; // p指向第一个结点
	while(pre!=NULL) // 没到表尾
	{
		nex=pre->next;
		free(pre);
		pre=nex;
	}
	L->next=NULL; // 头结点指针域为空
}

L指针向右走

1方法.p=L->next; L=p;

3方法p=L,L=L->next;L=p

L指针不移动

1方法pre=L->next; nex=pre->next; pre=nex;

L指针向左移动(s为新节点)

1.s->next=L;L=s

3.判断是否是空链表

//不带
Status ListEmpty(LinkNode L)
{ // 初始条件:线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE
	if(L==NULL)
		return TRUE;
	else
		return FALSE;
}
//带
Status ListEmpty(LinkNode L)
{ // 初始条件:线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE
	if(L->next==NULL) // 空
		return TRUE;
	else
		return FALSE;
}

4.求链表的长度

//不带
int ListLength(LinkNode L)
{ // 初始条件:线性表L已存在。操作结果:返回L中数据元素个数
	int count=0;
	LinkNode p=L;
	while(p!=NULL) // p指向结点(没到表尾)
	{
		p=p->next; // p指向下一个结点
		++count;
		//printf("ListLength=%d\n",i);
	}
	return count;
}
//带
int ListLength(LinkNode L)
{ // 初始条件:线性表L已存在。操作结果:返回L中数据元素个数
	int count=0;
	LinkNode p=L->next; // p指向第一个结点
	while(p!=NULL) // 没到表尾
	{
		++i;
		p=p->next;
	}
	return count;
}

int x=a;

while(a<b){

 a++;

}

则循环中执行了b-a次;

InitList(L);
	for (j=0;j<10;j++){
		ListInsert(L,j+1,100-j);
	}
	LinkList p=L;
	int count=0;
	while (p){
		printf("p=%x,%d,%d\n",p,p->data,count);
		p=p->next;
		count++;
	}
	printf("count=%d",count-1);

我们会发现带有10个元素的链表中(带头结点),while执行了11次

如果p=L->next;

InitList(L);
	for (j=0;j<10;j++){
		ListInsert(L,j+1,100-j);
	}
	LinkList p=L->next;
	int count=0;
	while (p){
		printf("p=%x,%d,%d\n",p,p->data,count);
		p=p->next;
		count++;
	}
	printf("count=%d",count);

所以我们想知道元素的index下标的话要制定规则

|头|->|第1个|->|第2个|->............................|第n个|->null

0         1               2                                        n         n+1

|头|->|第1个|->|第2个|->............................|第n个|->null

             0         1               2                           n-1       n

|头|->|第1个|->|第2个|->.............................|第n个|->null

            1                2                                        n       n+1

5.通过位置返回元素

//不带
Status GetElem(LinkNode L,int pos,ElemType &e)
{ // L为不带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
	int j=1;
	LinkNode p=L;
	if(i<1) // i值不合法
		return ERROR;
	while(j<i&&p) // 没到第i个元素,也没到表尾
	{
		j++;
		p=p->next;
	}
	if(j==i) // 存在第i个元素
	{
		e=p->data;
		return OK;
	}
	else
		return ERROR;
}
//带
Status GetElem(LinkNode L,int i,ElemType &e) // 算法2.8
{ // L为带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR
	int j=0; // j为计数器
	LinkNode p=L->next; // p指向第一个结点
	while(p&&j<i) // 顺指针向后查找,直到p指向第i个元素或p为空
	{	
		j++;
		p=p->next;
	}
	if(!p||j>i) // 第i个元素不存在
		return ERROR;
	e=p->data; // 取第i个元素
	return OK;
}

6.通过元素返回元素所在的位置

//不带
int LocateElem(LinkNode L,ElemType e,Status(*compare)(ElemType,ElemType))
{ // 初始条件:线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0)
	// 操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。
	//           若这样的数据元素不存在,则返回值为0
	int i=0;
	LinkNode p=L;
	while(p)
	{
		i++;
		if(compare(p->data,e)) // 找到这样的数据元素
			return i;
		p=p->next;
	}
	return 0;
}
//带
int LocateElem(LinkNode L,ElemType e,Status(*compare)(ElemType,ElemType))
{ // 初始条件: 线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0)
	// 操作结果: 返回L中第1个与e满足关系compare()的数据元素的位序。
	//           若这样的数据元素不存在,则返回值为0
	int i=0;
	LinkNode p=L->next;
	while(p)
	{
		i++;
		if(compare(p->data,e)) // 找到这样的数据元素
			return i;
		p=p->next;
	}
	return 0;
}

返回元素前驱

//不带
Status PriorElem(LinkNode L,ElemType cur_e,ElemType &pre_e)
{ // 初始条件:线性表L已存在
	// 操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,
	//           返回OK;否则操作失败,pre_e无定义,返回INFEASIBLE
	LinkNode q,p=L; // p指向第一个结点
	while(p->next) // p所指结点有后继
	{
		q=p->next; // q为p的后继
		if(q->data==cur_e)
		{
			pre_e=p->data;
			return OK;
		}
		p=q; // p向后移
	}
	return INFEASIBLE;
}
//带
Status PriorElem(LinkNode L,ElemType cur_e,ElemType &pre_e)
{ // 初始条件: 线性表L已存在
	// 操作结果: 若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,
	//           返回OK;否则操作失败,pre_e无定义,返回INFEASIBLE
	LinkNode q,p=L->next; // p指向第一个结点
	while(p->next) // p所指结点有后继
	{
		q=p->next; // q为p的后继
		if(q->data==cur_e)
		{
			pre_e=p->data;
			return OK;
		}
		p=q; // p向后移
	}
	return INFEASIBLE;
}

返回后驱

//不带
Status NextElem(LinkNode L,ElemType cur_e,ElemType &next_e)
{ // 初始条件:线性表L已存在
	// 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,
	//           返回OK;否则操作失败,next_e无定义,返回INFEASIBLE
	LinkNode p=L; // p指向第一个结点
	while(p->next) // p所指结点有后继
	{
		if(p->data==cur_e)
		{
			next_e=p->next->data;
			return OK;
		}
		p=p->next;
	}
	return INFEASIBLE;
}
//带
Status NextElem(LinkNode L,ElemType cur_e,ElemType &next_e)
{ // 初始条件:线性表L已存在
	// 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,
	//           返回OK;否则操作失败,next_e无定义,返回INFEASIBLE
	LinkNode p=L->next; // p指向第一个结点
	while(p->next) // p所指结点有后继
	{
		if(p->data==cur_e)
		{
			next_e=p->next->data;
			return OK;
		}
		p=p->next;
	}
	return INFEASIBLE;
}

插入节点

//不带
Status ListInsert(LinkNode &L,int pos,ElemType e)
{ // 在不带头结点的单链线性表L中第i个位置之前插入元素e
	int count=0;
	int index=pos-1;//链表的指针
	LinkNode p=L,s;
	if(index<0) // i值不合法
		return ERROR;
	s=(LinkNode)malloc(sizeof(LNode)); // 生成新结点
	s->data=e; // 给s的data域赋值
	if(index==0) // 插在表头
	{
		s->next=L;
		L=s; // 改变L
	}else{ // 插在表的其余处
		while(p!=NULL&&count<index-1) // 寻找第i-1个结点
		{
			p=p->next;
			++count;
		}
		if(!p) // i大于表长+1
			return ERROR;
		s->next=p->next;
		p->next=s;
	}
	return OK;
}
//带
Status ListInsert(LinkNode L,int pos,ElemType e) // 算法2.9。不改变L
{ // 在带头结点的单链线性表L中第i个位置之前插入元素e
	int count=0;
	int index=pos-1;
	LinkNode p=L,s;
	while(p!=NULL&&count<index) // 寻找第i-1个结点
	{
		p=p->next;
		++count;
	}
	if(p==NULL||count>index) // i小于1或者大于表长
		return ERROR;
	s=(LinkNode)malloc(sizeof(LNode)); // 生成新结点
	s->data=e; // 插入L中
	s->next=p->next;
	p->next=s;
	return OK;
}

pos是逻辑位置

index是物理地址位置,所以index=pos-1

count是记录指针的移动次数

删除节点

//不带
Status ListDelete(LinkNode &L,int pos,ElemType &e)
{ // 在不带头结点的单链线性表L中,删除第i个元素,并由e返回其值
	int j=1;
	LinkNode p=L,q;
	if(i==1) // 删除第1个结点
	{
		L=p->next; // L由第2个结点开始
		e=p->data;
		free(p); // 删除并释放第1个结点
	}
	else
	{
		while(p->next&&j<i-1) // 寻找第i个结点,并令p指向其前趋
		{					
			p=p->next;
			j++;		
		}
		if(!p->next||j>i-1) // 删除位置不合理
			return ERROR;
		q=p->next; // 删除并释放结点
		p->next=q->next;
		e=q->data;
		free(q);
	}
	return OK;
}
//带
Status ListDelete(LinkNode L,int i,ElemType &e) // 算法2.10。不改变L
{ // 在带头结点的单链线性表L中,删除第i个元素,并由e返回其值
	int j=0;
	LinkNode p=L,q;
	while(p->next&&j<i-1) // 寻找第i个结点,并令p指向其前驱
	{
		p=p->next;
		j++;
	}
	if(!p->next||j>i-1) // 删除位置不合理
		return ERROR;
	q=p->next; // 删除并释放结点
	p->next=q->next;
	e=q->data;
	free(q);
	return OK;
}
//不带
void ListTraverse(LinkNode L,void(*vi)(ElemType))
{ // 初始条件:线性表L已存在。操作结果:依次对L的每个数据元素调用函数vi()
	LinkNode p=L;
	while(p)
	{
		vi(p->data);
		p=p->next;
	}
	printf("\n");
}
//带
void ListTraverse(LinkNode L,void(*vi)(ElemType))
// vi的形参类型为ElemType,与bo2-1.cpp中相应函数的形参类型ElemType&不同
{ // 初始条件:线性表L已存在。操作结果:依次对L的每个数据元素调用函数vi()
	LinkNode p=L->next;
	while(p)
	{
		vi(p->data);
		p=p->next;
	}
	printf("\n");
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值