链表的实现

本文详细介绍了链表数据结构,包括单链表、双链表和循环链表的定义、节点类型、初始化、判断空、插入和删除操作,以及它们的时间复杂度分析。
摘要由CSDN通过智能技术生成

 链表

链表:包括单链表、双链表和循环链表三种。

通过“链”建起来的表,在进行插入和删除的操作时不需要移动元素,而只需要通过修改指针,但也会失去顺序表一样的随机访问的优点

(1). 单链表

线性表的链式存储也称为单链表。包含两个部分:data数据域,存储数据;next指针域,存储指针。

单链表也分为带有头结点和不带头结点的情况

单链表结点类型:

typedef struct LNode{//单链表结点类型 
	int data;//数据域 ,每个结点存放一个数据元素 
	struct LNode *next;//指针域 ,指针指向下一个节点 
}LNode,*LinkList;
typedef LNode{//单链表结点类型 
	int data;//数据域 ,每个结点存放一个数据元素
	struct LNode *next;//指针域 ,指针指向下一个节点 
}; 

两种结点类型定义方法是等价的

typedef struct LNode LNode;//强调这是一个结点 
typedef struct LNode *LinkList; //强调这是一个单链表LinkList 
//上述两个是等价的 

增加一个新结点:在内存中为这个新结点申请所需空间,并用指针p指向这个结点

struct LNode *p=(struct LNode*)malloc(sizeof(struct LNode));

 1. 不带头结点

单链表初始化
bool InitList(LinkList &L){
	L=NULL;//头指针指向空 
	return true;
}

void test(){
	LinkList L;
	InitList(L);
}
判断单链表是否为空
bool Empty(LinkList L){
	if(L==NULL)
	 return true;
	else
	 return false;
}
//或:
bool Empty(LinkList L){
	return(L==NULl);
}
 插入(不带头结点)按位序插入

ListInsert(&L,i,e):插入操作。在表L中的第i个位置插入指定元素e。想要在i个位置插入元素,就要先找到i-1个结点,将新结点查到其后。

如果不带头结点,则插入、删除第一个元素时,需要更改头指针。

bool ListInsert(LinkList &L,int i,int e){
	if(i<0)
	 return false;
	if(i==1){
		LNode *s=(LNode *)malloc(sizeof(LNode));//申请一个新的指针指向新的结点 
		s->data=e;//将数据赋给新结点 
		s->next=L;//将新指针指向L指向的结点 
		L=s;//将头指针指向新结点 
		 return true;//插入元素成功 
	}
	LNode *p;//定义一个p指针 
	int j=1;//当前p指向的是第几个结点 
	p=L;//当前p指向第一个结点(注意:不是头结点) 
	while(p!=NULL && j<i-1){//循环找到i-1结点 
		p=p->next;//p指针依次往后移动直到找到i-1结点 
		j++;
	}
	if(p==NULL)//i不合法 
	   return false;
	LNode *s=(LNode*)malloc(sizeof(LNode));
	s->data=e;
	s->next=p->next;
	p->next=s;
	return true;
} 

2. 带头结点

用头插法和尾插法的方法建立单链表,将输入的元素,一个个插入后形成一个完整的单链表,都只讨论带头结点的情况

头插法建立单链表:
LinkList List_HeadInsert(LinkList &L){//头插法逆向建立单链表 
	struct *s;
	int x;
	L=(LNode *)malloc(sizeof(LNode));//申请一个头结点 
	L->next=NULL;//头指针指向空 
	scanf("%d",&x); 
	while(x!=9999){
		s=(LNode *)malloc(sizeof(LNode));
		s->data=x;
		s->next=L->next;
		L->next=s;
		scanf("%d",&x); 
	}
	return L;//返回头结点 
}

采用头插法建立单链表,读入数据的顺序与生成的链表的数据是相反的。每个结点插入的时间为O(1),设单链表的长度为n,则总时间复杂度为O(n)。

例题:

1. 给定义一个n个元素的一维数组,建立一个有序单链表的最低时间复杂度。

方法一:直接插入排序,每插入一个元素就序遍历链表寻找插入位置,时间复杂度O(n2),

方法二:若先将数组排好序,然后建立链表,建立链表的时间复杂度O(n),

数组排序的最好时间复杂度为O(nlog2n) 

尾插法建立单链表:
bool List_TailInsert(LinkList &L){
	int x;
	L=(LNode *)malloc(sizeof(LNode));//创造头结点 
	LNode *s,*r;//s指针指向要插入的新结点,r始终指向当前链表的尾结点,也叫尾指针
	scanf("%d",&x);
	while(x!=9999){
		s=(LNode *)malloc(sizeof(LNode));//创造新结点
		s->data=x;
		r->next=s;
		r=s; 
		scanf("%d",&x);
	} 
	r->next=NULL;//尾指针置空
	return L; 
}
单链表初始化(带头结点): 
bool InitList(LinkList &L){
	L=(LNode *)malloc(sizeof(LNode));//分配一个头结点 
	if(L==NULL)//内存不足分配失败 
	  return false;
	L->next=NULL;//头指针指向空 
	return true;
} 
判断链表是否为空(带头结点): 
bool Empty(LinkList L){
	if(L->next==NULL)
	  return true;
	else
	  return false;s
} 
 按位序插入元素: 
bool ListInsert(LinkList &L,int i,int e){
	if(i<0)
	  return false;
	struct *p;
	int j=0;//头结点相当于位序为0的结点
	p=L;
	while(p!=NULL && j<i-1){
		p=p->next;
		p++; 
	}
	if(p==NULL)
	  return false;
	LNode *s=(LNode*)malloc(sizeof(LNode));
	s->data=e;
	s->next=p->next;
	p->next=s;
	return true;
} 
指定结点的后插操作:
bool InsertNextNode(LNode *p,int e){
	if(p==NUll)
	   return false;
	LNode *s=(LNode *)malloc(sizeof(LNode));
	if(s==NULL)
	  return false;
	s->data=e;
	s->next=p->next;
	p->next=s;
	return true
} 
指定结点的前插操作:
bool InsertPriorNode(LNode *p,int e){//在p结点之前插入元素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结点中的元素赋给s结点 
	p->data=e;//将要插入的数据赋给p结点 
	return true;  
}
按位序删除元素(带头结点)

ListDelete(&L,i,&e):删除操作。删除表中L中第i个位置的元素,并用e返回删除元素的值。找到第i-1个结点即要删除结点的前驱结点,删除了i位置上的元素,将指针指向第i+1个结点,并释放i个结点。

bool ListDelete(LinkList &L,int i,int e){
	if(i<1)
	  return false;
	LNode *p;
	int j=0;
	p=L;//L指向头结点,头结点是第0个结点(不存在数据) 
	while(p!=NULL && j<i-1){
		p=p->next;
		j++;
	}
	if(p==NULL)//i值不合法 
	  return false;
	if(p->next==NULL)//i-1结点后没有其他节点 
	  return false; 
	LNode *q;
	q=p->next;//另q指针指向要删除的结点
	e=q->data; //将q结点的数据复制给变量e
	p->next=q->next;
	free(q);
	return true; 
}
删除指定结点(带结点):
bool ListDelete(LNode *p){
	if(p==NULL)
	  return false;
	LNode *q;
	q=p->next;
	p->data=p->next->data;//将p后继节点的数据赋给p结点,也叫交换数据域 
	p->next=q->next;
	free(q);
	return true;
按位查找:

GetElem(L,i):按位查找操作。获取表L中第i个位置的元素的值。

LNode *GetElem(LinkList L,int i){
	if(i<1)
	 return NULL;
	int j=1;
	LNode *P;
	p=L->next;
	while(p!=NULL && j<i){
		p=p->next;
		j++;
	}
	return p;
}
 按值查找:

LocateElem(L,e):按值查找操作。在表L中查找具有给定关键字值的元素。 

LNode *LocateElem(LinkList L,int e){
	LNode *p=L->next;
	while(p!=NULL && p->data!=e){
		p=p->next;
	return p;
	}
}
补充单链表长度:
int length(LinkList L){
	int len=0;//统计表长
	LNode *p=L;
	while(p->next!=NULL){
		p=p->next;
		len++;
	} 
	return len;
}

双链表

双链表和单链表的区别:单链表结点只有一个指向后继的指针,使得单链表只能从结点顺序地向后遍历,要访问某个结点的前驱结点(插入、删除操作时),只能从头遍历,访问后继结点的时间复杂度O(1),访问前驱结点的时间复杂度O(n)。双链表为了克服上述困难,双链表结点中有两个指针prior和next指针,分别指向前驱结点和后继结点。

但双链表不可随机存取,按位查找和按值查找都只能用遍历的方式实现,时间复杂度为O(n)

双链表的插入、删除操作时间复杂度都均为O(1)

定义双链表的结点类型

typedef struct DNode{
	int data;
	struct DNode *prior,*next;
}DNode,*DLinkList;

初始化

bool InitList(DLinkList &L){
	L=(DNode *)malloc(sizeof(DNode));
	if(L==NULL)
	  return false;
	L->prior=NULL;//头结点的前驱指针指向空 
	L->next=NULL;//头结点的后继指针也指向空 
    return true;
}

判断双链表是否为空

bool Empty(DLinkList &L){
	if(L->next==NULL)
	  return true;
	else
	  return false;
} 

双链表的插入操作

bool InsertNextDNode(DNode *p,DNode *s,int e){
	if(p==NULL||s==NULL)//指针指向空 
	  return false;
	s->data=e;
	s->next=p->next;
	if(p->next!=NULL)
	p->next->prior=s;
	s->prior=p;
	p->next=s;
	return true;
} 

双链表的删除操作

bool DeletNextDNode(DNode *p){
	DNode *q=p->next;
	if(p==NULL||q==NULL)
	  return false;
	p->next=q->next;
	if(q->next!=NULL)
	q->next->prior=p;
	free(q);
} 

双链表的遍历

//双链表向后遍历
//while(p!=NULL){
//    p=p->next;
//}
//双链表向前遍历
//while(p!=NULL){
//    p=p->prior;
//} 

销毁双链表

void DestroyList(DLinkList &L){
	while(L->next!=NULL)
	  DeletNextDNode(L);
	free(L);
	L=NULL;
} 

 循环链表

循环链表分为循环单链表和循环双链表

单链表:表尾结点的next指针指向NULL

循环单链表:表尾结点的next指针指向头结点,从一个结点出发可以找到其他任何一个结点

循环单链表

定义循环单链表的结点类型 

typedef struct LNode{
	int data;
	struct LNode *next;
}LNode,*LinkList;

初始化循环单链表

bool InitList(LinkList &L){
	L=(LNode *)malloc(sizeof(LNode));
	if(L==NULL)
	  return false;
	L->next=L;
	return true;
} 

判断循环单链表是否为空

bool Empty(LinkList &L){
	if(L->next==NULL)
	  return true;
	else
	  return false;
}

判断结点p是否为循环单链表的表尾结点

bool isTail(LinkList L,LNode *p){
	if(p->next==L)
	  return true;
	else 
	  return false;
} 

循环双链表

表头结点的prior指针指向表尾结点,表尾结点的next指针指向表头元素。

定义循环双链表的结点类型

typedef struct DNode{
	int data;
	struct DNode *prior,*next;
}DNode,*DLinkList;

初始化

bool InitList(DLinkList &L){
	L=(DNode *)malloc(sizeof(DNode));
	if(L==NULL)
	  return false;
	L->next=L;
    L->prior=L;
	return true;
} 

判断循环双链表是否为空

bool Empty(DLinkList &L){
	if(L->next==L && L->prior=L)
	   return true;
	else
	   return false;
} 

判断结点p是否为循环双链表的表尾结点 

bool isTail(DLinkList &L,DNode *p){
	if(p->next==L)
	   return true;
	else;
	   return false;
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值