线性表的链表实现和分析

目录

 二、代码和讲解:

结构体定义:

函数声明:

初始化:

按位插入(带头结点)

按位插入不带头结点

指定结点的后插操作

指定结点的前插操作

判空操作

按位序删除(带头结点)

指定结点的删除

按位查找(基于带头结点)

按值查找(带头结点)

求表长

链表的构建

尾插法 (带头结点)

头插法(带头结点、重要应用“数组逆置”)

显示函数

三、小结:

下一篇: 双链表


一、单链表

优点:不要求大片连续空间,改变容量方便

缺点:不可随机存取,要耗费一定空间存放指针

分为:带头结点和不带头结点

不带头结点,写代码更麻烦,头结点不存放实际数据

 二、代码和讲解:

结构体定义:

#include<bits\stdc++.h>
using namespace std;
typedef struct LNode{
	int data;//数据域
	struct LNode *next;//指向下一个结点的指针域 
}LNode,*LinkList;//*一般LNode代表返回一个结点,LinkList代表的是一整个链表 

函数声明:

bool InitList(LinkList &L);
bool InitListWithHead(LinkList &L);
bool InsertNextNode(LNode *p,int e);
bool ListInsertWithHead(LinkList &L,int i,int e);
bool ListInsert(LinkList &L,int i,int e);
bool InsertPriorNode(LNode *p,int e);
bool ListDelete(LinkList &L,int i,int &e);
LNode * GetElem(LinkList L,int i);
LNode * LocateElem(LinkList L,int e);
int Length(LinkList L); 
LinkList List_TailInsert(LinkList &L); 
LinkList List_HeadInsert(LinkList &L);

初始化:

//初始化,不带头结点的 
bool InitList(LinkList &L){
	L=NULL;//空表没有结点 ,防止脏数据 
	return true;
} 
//带头结点的初始化,头结点是不带数据的,头结点也可以视为第0个结点 
bool InitListWithHead(LinkList &L){
	L=(LNode*)malloc(sizeof(LNode));
	if(L==NULL)return false;
	L->next=NULL;//下一个结点设置为空 
}

按位插入(带头结点)

bool ListInsertWithHead(LinkList &L,int i,int e){
		if(i<1)return false;	
	//当写好查找函数后可代替以下代码	
	LNode *p=GetElem(L,i-1);
//	int j=0;
//	p=L;
//	while(p!=NULL&&j<i-1){
//		p=p->next;
//		j++;
//	}
	//当写好后插操作后就可以省略 
//	if(p=NULL)return false;
//	LNode *s=(LNode *)malloc(sizeof(LNode));
//	s->data=e;
//	s->next=p->next;
//	p->next=s;
//	return true;
	//调用后插代码
	return InsertNextNode(p,e); 
}

按位插入不带头结点

bool ListInsert(LinkList &L,int i,int e){
	if(i<1)return false;
	if(i==1){//当插入到第一个结点时要特殊处理 
		LNode *s=(LNode *)malloc(sizeof(LNode));
		s->data=e;
		s->next=L;
		L=s;//将头指针指向新结点 
		return true;
	}
	LNode *p;
	int j=1;
	p=L;//p指向第一个结点 
	while(p!=NULL&&j<1){
		p=p->next;
		j++;
	} 
	//当写好后插操作后就可以省略 
//	if(p=NULL)return false;
//	LNode *s=(LNode *)malloc(sizeof(LNode));
//	s->data=e;
//	s->next=p->next;
//	p->next=s;
//	return true;
	//调用后插代码
	return InsertNextNode(p,e); 
}

指定结点的后插操作

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;
} 

指定结点的前插操作

//前插操作有两种实现方式:

//1、将链表传进去通过遍历找到前一个结点。o(n)

//2、用使用后插法后将新结点与p结点的数据交换,间接实现前插。o(1)

//这里用第二种方法

bool InsertPriorNode(LNode *p,int e){
	if(p==NULL)
		return false;
	LNode *s=(LNode *)malloc(sizeof(LNode));
	s->next=p->next;
	p->next=s;
	s->data=p->data;
	p->data=e;
	return true;
}

判空操作

//判空操作,不带头结点的 
bool Empty(LinkList L){
	return (L==NULL);
} 
//判空操作,带头结点的 
bool EmptyWithHead(LinkList L){
	return (L->next==NULL);
}

按位序删除(带头结点)

bool ListDelete(LinkList &L,int i,int &e){	
	if(i<1)return false;	
	//当写好查找函数后可代替以下代码	
	LNode *p=GetElem(L,i-1);
//	int j=0;
//	p=L;
//	while(p!=NULL&&j<i-1){
//		p=p->next;
//		j++;
//	}
	if(p==NULL)return false;//如果i不合法则GetElem函数可能返回NULL 
	if(p->next==NULL)return false;//第i-1个结点之后无其他结点
	LNode *q=p->next;
	e=q->data;
	p->next=q->next;
	free(q);
	return true; 
} 

指定结点的删除

思路也是"偷天换日"

但当要删除的结点刚好是最后一个结点时就会报错了,这时就要传入整条链表进行操作了

bool DeleteNode(LNode *p){
	if(p==NULL)return false;
	LNode *q=p->next;//q指向当前结点的下一个结点
	//将q结点的数据和指向下一个结点的指针给p 
	p->data=q->data;
	p->next=q->next;
	free(q);
	return true;
}

按位查找(基于带头结点)

LNode * GetElem(LinkList L,int i){
	if(i<0)return NULL;
	LNode *p;
	p=L;
	int j=0;
	while(p!=NULL&&j<i){
		p=p->next;
		j++;
	}
	return p;
}

按值查找(带头结点)

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;
} 

链表的构建

尾插法 (带头结点)

//思路1、设置一个len记录链表长度,通过while循环调用按序插入,但是是o(n^2)

//思路2、用一个指针永远指向尾结点,只对该结点进行尾插操作(采用)

LinkList List_TailInsert(LinkList &L){
	int x;
	L =(LinkList)malloc(sizeof(LNode));//建立头结点 
	LNode *s,*r=L;//s为新结点,r为尾指针
	cin>>x;
	while(x!=9999){//输入9999代表结束
	s=(LNode *)malloc(sizeof(LNode));
	s->data=x;
	r->next=s;
	r=s;
	cin>>x;
	}
	r->next=NULL;
	return L;
}

头插法(带头结点、重要应用“数组逆置”)

思路:while循环:每次取一个数,不断对头结点进行插入

LinkList List_HeadInsert(LinkList &L){//逆向建立链表 
	LNode *s;
	int x;
	L=(LinkList)malloc(sizeof(LNode));
	L->next=NULL;//初始化为空链表(很重要) 
	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 Display(LinkList L){
	LNode *p;
	p=L;
	while(p->next!=NULL){
		p=p->next;
		cout<<p->data<<endl;
	}
}

 

三、小结:

 

猜你喜欢

转载自blog.csdn.net/weixin_46919419/article/details/114270307