王道数据结构——链表、循环单链表实现

ADT抽象数据类型

ADT 单链表(LinkList)
Data
	同线性表。元素具有相同的类型,相邻的元素具有前驱和后继关系。

Operation
	InitlList(LinkList &L);//初始化单链表
	List_HeadInsert(LinkList &L);//头插法建立单链表
	List_TrailInsert(LinkList &L);//尾插法建立单链表
	PrintList(LinkList &L);//打印单链表
	GetElemByOrder(LinkList &L,int i);//按位序查找元素
	GetElemByValue(LinkList &L,ElemType e);//按值查找
	GetLenth(LinkList &L);//返回单链表的长度
	InsertByPriorNode(Lnode *p,ElemType e);//单链表的前插
	InsertByPriorNode(Lnode *p,ElemType e);//单链表的后插
	ListInsert(LinkList &L,int i,ElemType e);//带头结点按位序插入
	NListInsert(LinkList &L,int i,ElemType e);//不带头结点按位序插入
	DeletNode(Lnode *p);//删除指定结点的数据.
	ListDelete(LinkList &L,int i,ElemType &e);//删除单链表

endADT

单链表的结构体定义

typedef int ElemType;
typedef struct Lnode{
	ElemType data;
	struct Lnode *next;
}Lnode,*LinkList;

单链表的初始化

/* 初始化链表,带头结点*/
bool InitlList(LinkList &L){
	L=(LinkList)malloc(sizeof(Lnode));
	if(L==NULL)return false;//空间分配失败
	L->next=NULL;
	return true; 
}

单链表的创建——头插法

/*头插法*/
LinkList List_HeadInsert(LinkList &L){
	int x;
	L=(LinkList)malloc(sizeof(Lnode));
	L->next=NULL;//初始化尾空链表 
	Lnode *s;
	cin>>x;
	while(x!=9999){
		s=(Lnode  *)malloc(sizeof(Lnode));
		s->data=x;
		s->next=L->next;
		L->next=s;
		cin>>x;		 
	}
	return L;
}

单链表的创建——尾插法

/* 尾插法 */ 
LinkList List_TrailInsert(LinkList &L){
	int x;
	L=(LinkList)malloc(sizeof(Lnode)); 
	Lnode *r=L,*s;	
	cin>>x;
	while(x!=9999){
		s=(Lnode *)malloc(sizeof(Lnode));
		s->data=x;
		r->next=s;
		r=s;
		cin>>x;
	}
	r->next=NULL;
	return L;
}

单链表的尾插法和头插法注意的事项:
采用尾插法,需要最后指明L的尾指针的指向为空,并且需要两个指针
采用头插法,需要一开始就要指定L的尾指针为空,不然遍历的时候,会死循环。

打印链表

void PrintList(LinkList &L){
	Lnode *p=L->next;//这里默认跳过了头节点,也可以改成L
	while(p){//while(p)表示当前的指向,这里会判断尾指针指向的NULL,为了安全,可以改成p->next!=NULL
		cout<<p->data<<" ";
		p=p->next; 
	}
	cout<<endl;
}

查找值——按位查找

/*
	按位查找
	1.判断位序是否合法
	2.定义结点指向,开始遍历,直到i的位置
	3.返回节点
	
*/
Lnode * GetElemByOrder(LinkList &L,int i){
	if(i<1){
		cout<<"查找位置不合法,查找失败!";
		return false;
	}
	Lnode *p=L->next;
	int j=1;
	while(p!=NULL&j<i){
		p=p->next;
		j++;
	}
	return p;
}

查找值——按值查找

/* 按值查找*/
Lnode * GetElemByValue(LinkList &L,ElemType e){
	Lnode *p=L->next;
	while(!p){
		if(p->data==e)
			return p;
		p=p->next;
	}
}

返回单链表的长度

/*求单链表的长度*/
int GetLenth(LinkList &L){
	int len=1;
	Lnode *p=L->next;//从第一个节点开始统计 
	while(p->next!=NULL) {
		p=p->next;
		len++;
	}
	return len;
}

前插

/*
	如果采用扫描定位到当前待插入的结点之前,那么时间复杂度就是O(N) 
	这里可以采用后插的方法来实现,把插入的结点中的数据修改为当前结点的数据,
	再把当前结点的数据更改为待插入的元素 
	O(1)
	 
*/
bool InsertByPriorNode(Lnode *p,ElemType e) {
	if(p==NULL)return false;
	Lnode *s =(Lnode *)malloc(sizeof(Lnode));
	if(s==NULL)return false;
	
	s->next=p->next;
	p->next=s;
	s->data=p->data;
	p->data=e;
	return true;
}

后插

/* 后插操作
	1.判断传入的结点是否为空
	不为空则执行2 
	2.申请结点s,把e装进所申请结点中的data中
	3.修改申请结点s的指向,把s放在p的后面,即s->next=p->next 
	4.再把p的指向更改为s 
	O(1)
*/
bool InsertByPriorNode(Lnode *p,ElemType e) {
	if(p==NULL)return false;
	Lnode *s =(Lnode *)malloc(sizeof(Lnode));
	if(s==NULL)return false;
	
	s->next=p->next;
	p->next=s;
	s->data=p->data;
	p->data=e;
	return true;
}

带头节点的按位序插入

/*
	带头结点 
	按位序插入:
	1.判断插入位置是否合法
	2.定位到需要插入的i-1位置
	3.开始插入数据 
*/
bool ListInsert(LinkList &L,int i,ElemType e){
	if(i<1)	{
		cout<<"插入的位置有误!"<<endl;
		return false;
	}
	Lnode *p=L;//从头节点开始 
	int j=0;
	while(p!=NULL&&j<i-1){
		p=p->next;
		j++;
	}
	/*
	
	从这里开始的代码可以进行封装,调用 InsertByNextNode()函数就可以实现后插操作。 
	*/
	if(p==NULL){
		cout<<"插入错误!"<<endl;
		return false;
	}
	Lnode *s=(Lnode * )malloc(sizeof(Lnode));
	s->data=e;
	s->next=p->next;
	p->next=s;
	return true;
}

不带头节点的按位序插入

/*
	与带头结点的相比,仅仅多了一道处理头节点的手续。
	所以带头结点的单链表利于处理。
*/
bool NListInsert(LinkList &L,int i,ElemType e){
	if(i<1)	{
		cout<<"插入的位置有误!"<<endl;
		return false;
	}
	//插入第一个结点的操作与其他结点的操作不同
	if(i==1) {
		Lnode *s=(Lnode *)malloc(sizeof(Lnode));
		s->data=e;
		s->next=L;
		L=s;
		return true;
	}
	 
	Lnode *p=L;//从头节点开始 
	int j=0;
	while(p!=NULL&&j<i-1){
		p=p->next;
		j++;
	}
	if(p==NULL){
		cout<<"插入错误!"<<endl;
		return false;
	}
	Lnode *s=(Lnode * )malloc(sizeof(Lnode));
	s->data=e;
	s->next=p->next;
	p->next=s;
	return true;
}

删除指定节点的数据

/*
	删除指定结点的数据
	1.找到当前结点的下一个结点
	2.将下一个结点的数据替换掉当前结点
	3.释放下一个结点 
	O(1) 
*/	
bool DeletNode(Lnode *p){
	if(p==NULL)return false;	
	//如果p为最后一个结点,那么q的指向为空,空中的数据则会引发空指针异常。 
	Lnode *q=p->next;
	p->data=q->data;
	p->next=q->next;
	free(q) ;
	return true;
}

删除单链表

/*
	按位序删除,带头结点 
	O(N)
*/

bool ListDelete(LinkList &L,int i,ElemType &e){
	if(i<1)	{
		cout<<"删除结点有误!" <<endl;
		return false;
	}
	
	Lnode *p=L;
	int j=0;//从头结点开始定位
	while(p!=NULL&j<i-1) {
		p=p->next;
		j++;
	}
	if(p==NULL){
		//链表大小为5,删除为7,后面的结点不存在
		cout<<"删除失败!"<<endl; 
		return false; 
	}
	if(p->next==NULL){
		//当前所要删除的第i个结点不存在 ,定位在i-1之后没有结点,删除5,4之后就没有结点了,
		cout<<"删除失败!" ;
		return false;
	}
	
	/*
	e=p->next->data;
	free(p->next);
	直接这么写的后果就是造成了链表最后指向了脏数据 
	所以,要引入指针变量 
	*/
	
	Lnode *q=p->next;
	e=q->data;//带回所要删除的对象数据
	p->next=q->next;//这一步很关键,不指明p后面的元素,直接删除会造成断链或者指向脏数据 
	free(q) ;
	
	return true;
	
}

循环单链表的初始化

/*初始化循环单链表*/
bool InitiList(LinkList &L){
	L=(Lnode * )malloc(sizeof(Lnode));
	if(L=NULL)//初始化失败,
		return false;
	L->next=L;
	return true; 
}

循单判空

/*判断循环单链表是否为空*/
bool IsEmpty(LinkList &L){
	return (L->next==L)?true:false;
}

判断节点是否为尾结点

/*判断节点p是否为循环单链表的表尾节点*/ 
bool isTrail(LinkList &L, Lnode *p){
	return(p->next==L)?true:false;
}

由于单链表跟循环单链表之间的区别,仅仅在于尾指针的指向是否是L,其它的代码实现方式跟单链表相差不多,仅在于尾指针的处理,所以,下面只给出了单链表的具体实现。


代码实现—单链表

#include<iostream> 
#include<stdlib.h>
using namespace std;

typedef int ElemType;

/*定义链表结构体的类型 */
typedef struct Lnode{
	ElemType data;
	struct Lnode *next;
}Lnode,*LinkList;

/* 初始化链表,带头结点*/
bool InitlList(LinkList &L){
	L=(LinkList)malloc(sizeof(Lnode));
	if(L==NULL)return false;//空间分配失败
	L->next=NULL;
	return true; 
}

/*判断一个链表是否为空*/
bool isEmpty(LinkList &L){
	return (L->next==NULL)?true:false;
}

/* 尾插法 */ 
LinkList List_TrailInsert(LinkList &L){
	int x;
	L=(LinkList)malloc(sizeof(Lnode)); 
	Lnode *r=L,*s;	
	cin>>x;
	while(x!=9999){
		s=(Lnode *)malloc(sizeof(Lnode));
		s->data=x;
		r->next=s;
		r=s;
		cin>>x;
	}
	r->next=NULL;
	return L;
}


/*头插法*/
LinkList List_HeadInsert(LinkList &L){
	int x;
	L=(LinkList)malloc(sizeof(Lnode));
	L->next=NULL;//初始化尾空链表 
	Lnode *s;
	cin>>x;
	while(x!=9999){
		s=(Lnode  *)malloc(sizeof(Lnode));
		s->data=x;
		s->next=L->next;
		L->next=s;
		cin>>x;		 
	}
	return L;
}

/*输出这个链表*/
void PrintList(LinkList &L){
	Lnode *p=L->next;
	while(p){
		cout<<p->data<<" ";
		p=p->next; 
	}
	cout<<endl;
}

/*按位查找*/
Lnode * GetElemByOrder(LinkList &L,int i){
	if(i<1){
		cout<<"查找位置不合法,查找失败!";
		return false;
	}
	Lnode *p=L->next;
	int j=1;
	while(p!=NULL&j<i){
		p=p->next;
		j++;
	}
	return p;
}

/* 按值查找*/
Lnode * GetElemByValue(LinkList &L,ElemType e){
	Lnode *p=L->next;
	while(!p){
		if(p->data==e)
			return p;
		p=p->next;
	}
}

/*求单链表的长度*/
int GetLenth(LinkList &L){
	int len=1;
	Lnode *p=L->next;//从第一个节点开始统计 
	while(p->next!=NULL) {
		p=p->next;
		len++;
	}
	return len;
}


/*
	带头结点 
	按位序插入:
	1.判断插入位置是否合法
	2.定位到需要插入的i-1位置
	3.开始插入数据 
*/
bool ListInsert(LinkList &L,int i,ElemType e){
	if(i<1)	{
		cout<<"插入的位置有误!"<<endl;
		return false;
	}
	Lnode *p=L;//从头节点开始 
	int j=0;
	while(p!=NULL&&j<i-1){
		p=p->next;
		j++;
	}
	/*
	
	从这里开始的代码可以进行封装,调用 InsertByNextNode()函数就可以实现后插操作。 
	*/
	if(p==NULL){
		cout<<"插入错误!"<<endl;
		return false;
	}
	Lnode *s=(Lnode * )malloc(sizeof(Lnode));
	s->data=e;
	s->next=p->next;
	p->next=s;
	return true;
}


/*
	不带头结点 
*/

bool NListInsert(LinkList &L,int i,ElemType e){
	if(i<1)	{
		cout<<"插入的位置有误!"<<endl;
		return false;
	}
	//插入第一个结点的操作与其他结点的操作不同
	if(i==1) {
		Lnode *s=(Lnode *)malloc(sizeof(Lnode));
		s->data=e;
		s->next=L;
		L=s;
		return true;
	}
	 
	Lnode *p=L;//从头节点开始 
	int j=0;
	while(p!=NULL&&j<i-1){
		p=p->next;
		j++;
	}
	if(p==NULL){
		cout<<"插入错误!"<<endl;
		return false;
	}
	Lnode *s=(Lnode * )malloc(sizeof(Lnode));
	s->data=e;
	s->next=p->next;
	p->next=s;
	return true;
}

/* 后插操作
	1.判断传入的结点是否为空
	不为空则执行2 
	2.申请结点s,把e装进所申请结点中的data中
	3.修改申请结点s的指向,把s放在p的后面,即s->next=p->next 
	4.再把p的指向更改为s 
	O(1)
*/
bool InsertByNextNode(Lnode *p,ElemType e){
	if(p==NULL)
		return false;
	
	Lnode *s =(Lnode *)malloc(sizeof(Lnode));
	//这里可以进行一个判断,如果s申请空间失败,也是插入失败
	if(s==NULL) return false;
	s->data=e;
	s->next=p->next;
	p->next=s;
	return true;
}

/*
	如果采用扫描定位到当前待插入的结点之前,那么时间复杂度就是O(N) 
	这里可以采用后插的方法来实现,把插入的结点中的数据修改为当前结点的数据,
	再把当前结点的数据更改为待插入的元素 
	O(1)
	 
*/
bool InsertByPriorNode(Lnode *p,ElemType e) {
	if(p==NULL)return false;
	Lnode *s =(Lnode *)malloc(sizeof(Lnode));
	if(s==NULL)return false;
	
	s->next=p->next;
	p->next=s;
	s->data=p->data;
	p->data=e;
	return true;
}


/*
	按位序删除,带头结点 
	O(N)
*/

bool ListDelete(LinkList &L,int i,ElemType &e){
	if(i<1)	{
		cout<<"删除结点有误!" <<endl;
		return false;
	}
	
	Lnode *p=L;
	int j=0;//从头结点开始定位
	while(p!=NULL&j<i-1) {
		p=p->next;
		j++;
	}
	if(p==NULL){
		//链表大小为5,删除为7,后面的结点不存在
		cout<<"插入失败!"<<endl; 
		return false; 
	}
	if(p->next==NULL){
		//当前所要删除的第i个结点不存在 ,定位在i-1之后没有结点,删除5,4之后就没有结点了,
		cout<<"删除失败!" ;
		return false;
	}
	
	/*
	e=p->next->data;
	free(p->next);
	直接这么写的后果就是造成了链表最后指向了脏数据 
	所以,要引入指针变量 
	*/
	
	Lnode *q=p->next;
	e=q->data;//带回所要删除的对象数据
	p->next=q->next;//这一步很关键,不指明p后面的元素,直接删除会造成断链或者指向脏数据 
	free(q) ;
	
	return true;
	
}

/*
	删除指定结点的数据
	1.找到当前结点的下一个结点
	2.将下一个结点的数据替换掉当前结点
	3.释放下一个结点 
	O(1) 
*/	
bool DeletNode(Lnode *p){
	if(p==NULL)return false;	
	//如果p为最后一个结点,那么q的指向为空,空中的数据则会引发空指针异常。 
	Lnode *q=p->next;
	p->data=q->data;
	p->next=q->next;
	free(q) ;
	return true;
}


int main(){
	LinkList L;
	InitlList(L);
	List_TrailInsert(L);
	ListInsert(L,3,5)	;
	PrintList(L);
	int e;
	ListDelete(L,3,e);
	cout<<"被删除的元素是:"<<e<<endl;
	PrintList(L);
	return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值