DS005数据结构-单链表实现原理探究-针对严蔚敏老师教材的示例

一、顺序表那么好,为什么还需要链表

顺序表的缺点:

•顺序表的除了表尾外其他位置插入和删除元素需要移动其他元素,效率低;平均时间复杂度为O(n)。

•顺序表需要连续的空间,当数据量比较大时,如果没有足够的连续空间,顺序表失效!!

为解决该缺点,设计了链表。链表的插入和删除不需要移动其他元素,插入删除效率高;但失去了随机存取的优势。

另:这是一种颠覆性创新,另起炉灶,不在顺序存储的框架内修修补补!!

单链表是最简单的链式结构,由此发散、引申,可以构造更复杂的结构,比如树、图。

基础不牢,地动山摇,必须把链表学好。

二、单链表的定义

•单链表是通过在当前结点中存放后继结点的地址将序列数据像“链”一样连接起来,所以称为链表。

•单链表的“单”指的是当前结点中只存放一个地址数据,即后继数据结点的地址。

三、单链表的结点形态

四、单链表的整体形态

在单链表中设立头结点,是为了方便在链表的头部插入和删除结点。

      本节我们讲的单链表的头结点和数据结点是相同类型。

       有时候为了方便,头结点中可以设一个指向链表头的指针、一个指向链表尾的指针和一个表示链表长度的整数。这时候头结点的类型就和链表结点的类型就不同了。

下面程序中用到的头文件以及符号常量等如下:

#include<iostream>
using namespace std;

#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status; 
//Status 是函数返回值类型,其值是函数结果状态代码。
typedef int ElemType; 
//ElemType 为可定义的数据类型,此设为int类型

五、链表结点的C++描述

typedef struct LNode {
    ElemType data; //结点的数据域
    struct LNode *next; //结点的指针域
} LNode, *LinkList; //LinkList为指向结构体LNode的指针类型

这样,head就是头指针,length记录链表中元素结点的个数,不包括头结点。

六、生成一个结点    

LNode *CreateNode(ElemType e)
{//为元素e生成一个结点
	LNode *t=new LNode;
	if(t==0) return 0;
	t->data=e;
	t->next=0;
	return t; 
}

用new运算符,向操作系统申请node类型的空间。

若操作系统说,我也不富裕,拒绝了,则返回0,即空指针,否则返回新结点的地址。 

七、初始化

开始时,生成一个头节点,在构造函数中实现。

Status InitList(LinkList &L) 
{ //算法2.6 单链表的初始化
	//构造一个空的单链表L
	L = CreateNode(0);
	if(L==0) return ERROR;
	return OK;
}

head是一个变量,里面存的是地址,是头结点的地址,这个头结点没有名字。 

八、在某个结点后面插入元素

void insert(LNode* p, LNode* q)
{//p是链表中某个结点的指针,q是一个新结点的指针
	//将q插入到p的后面
	q->next = p->next;
	p->next = q;
}

void insert(LNode* p, ElemType x)
{//在指针p所指的结点后面插入x
	LNode* q, * t;

	t = CreateNode(x);
	insert(p, t);		

}

C++的函数重载,函数名相同,参数不同。 

在结点p后面插入q结点,一定不能先断开p后面的指针,断开前,一定要有指针指向原来p后面的结点,

不然就会丢失结点。

九、在头结点后面插入元素

void push_head(LinkList &L, ElemType e)
{//在头结点后面插入数据
	insert(L,e) ;	
}

有了前面 insert 函数的铺垫, 在头部插入结点的操作就是如此简单。

十、在链表尾部插入结点

void push_back(LinkList &L, ElemType e)
{//在头结点后面插入数据
	LNode *p=L;
	while(p->next) 
	{
		p=p->next; 
	} 
	insert(p,e) ;	
}

 先找到链表的尾部结点,用指针p指向它,然后再p后面插入数据。

十一、得到第i个结点的地址

LNode *Locate_i(LinkList &L,int i)
{//查找 第i个结点,用指针指向它,并返回该指针
	if(i<0) return 0;//不合法 
	if(i==0)return L;//把头结点看做0号结点 
	LNode *p=L->next;
	int j=1;
	while(j<i && p)
	{
		p=p->next;
		j++; 
	}
	return p;
}

在单链表中找到第 i 个结点,是一个基本操作。 

十二、在第i个结点前插入数据

Status ListInsert(LinkList &L, int i, ElemType e) 
{ //算法2.9 单链表的插入
	//在带头结点的单链表L中第i个位置插入值为e的新结点
	LNode *p=Locate_i(L,i-1);//寻找第i-1个位置 
	if(p==0) return ERROR;//找不到插入位置 
	LNode *t=CreateNode(e);//为e生成结点 
	t->next=p->next;
	p->next=t; 
	return OK;
} 

要在第 i 个结点处插入数据,就是在第 i-1 个结点后插入数据,需要先找到第 i-1 个结点。

十三、删除第i个结点

Status ListDelete(LinkList &L, int i) 
{ //算法2.9 单链表的删除
	//在带头结点的单链表L中,删除第i个位置	
	LinkList p, q;
	p=Locate_i(L,i-1);//找到第i-1个结点
	if(p==0) return ERROR; 
	q = p->next; //临时保存被删结点的地址以备释放 
	if(q==0) return ERROR;
	p->next = q->next; //改变删除结点前驱结点的指针域 
	delete q; //释放删除结点的空间 
	
	return OK;
}

要删除第 i 个结点,需要先找到第 i-1 个结点。 

十四、在链表中查找


LNode *LocateElem(LinkList L, int e) 
{ //算法2.8 按值查找
	//在带头结点的单链表L中查找值为e的元素
	LinkList p = L->next;
	//顺链域向后扫描,直到p为空或p所指结点的数据域等于e
	while (p)
	{
		if(p->data==e)
			return p;//查找成功 
		p=p->next;
	}	
	return 0; //查找失败 
}

十五、释放单链表占用的空间

在析构函数中实现

void freeAll(LinkList &L)
{//释放所有结点空间
	LNode* p;	
	while (L->next)
	{
		p = L->next;
		L->next = p->next;
		delete p;
	}
	delete L;
	L = 0;	
}

十六、完整代码

#include<iostream>
using namespace std;

#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status; 
//Status 是函数返回值类型,其值是函数结果状态代码。
typedef int ElemType; 
//ElemType 为可定义的数据类型,此设为int类型

typedef struct LNode {
	ElemType data; //结点的数据域
	struct LNode *next; //结点的指针域
} LNode, *LinkList; //LinkList为指向结构体LNode的指针类型


LNode *CreateNode(ElemType e)
{//为元素e生成一个结点
	LNode *t=new LNode;
	if(t==0) return 0;
	t->data=e;
	t->next=0;
	return t; 
}

Status InitList(LinkList &L) 
{ //算法2.6 单链表的初始化
	//构造一个空的单链表L
	L = CreateNode(0);
	if(L==0) return ERROR;
	return OK;
}

void insert(LNode* p, LNode* q)
{//p是链表中某个结点的指针,q是一个新结点的指针
	//将q插入到p的后面
	q->next = p->next;
	p->next = q;
}

void insert(LNode* p, ElemType x)
{//在指针p所指的结点后面插入x
	LNode* q, * t;

	t = CreateNode(x);
	insert(p, t);		

}

void push_head(LinkList &L, ElemType e)
{//在头结点后面插入数据
	insert(L,e) ;	
}

void push_back(LinkList &L, ElemType e)
{//在头结点后面插入数据
	LNode *p=L;
	while(p->next) 
	{
		p=p->next; 
	} 
	insert(p,e) ;	
}

LNode *Locate_i(LinkList &L,int i)
{//查找 第i个结点,用指针指向它,并返回该指针
	if(i<0) return 0;//不合法 
	if(i==0)return L;//把头结点看做0号结点 
	LNode *p=L->next;
	int j=1;
	while(j<i && p)
	{
		p=p->next;
		j++; 
	}
	return p;
}


Status ListInsert(LinkList &L, int i, ElemType e) 
{ //算法2.9 单链表的插入
	//在带头结点的单链表L中第i个位置插入值为e的新结点
	LNode *p=Locate_i(L,i-1);//寻找第i-1个位置 
	if(p==0) return ERROR;//找不到插入位置 
	LNode *t=CreateNode(e);//为e生成结点 
	t->next=p->next;
	p->next=t; 
	return OK;
} 


Status GetElem(LinkList L, int i, ElemType &e)
 { //算法2.7 单链表的取值
	//在带头结点的单链表L中查找第i个元素
	//用e返回L中第i个数据元素的值
	LNode *p=Locate_i(L,i);
	if(p==0) return ERROR;//i不合法 
	e = p->data; //取第i个结点的数据域
	return OK;
} 


LNode *LocateElem(LinkList L, int e) 
{ //算法2.8 按值查找
	//在带头结点的单链表L中查找值为e的元素
	LinkList p = L->next;
	//顺链域向后扫描,直到p为空或p所指结点的数据域等于e
	while (p)
	{
		if(p->data==e)
			return p;//查找成功 
		p=p->next;
	}	
	return 0; //查找失败 
}

Status ListDelete(LinkList &L, int i) 
{ //算法2.9 单链表的删除
	//在带头结点的单链表L中,删除第i个位置	
	LinkList p, q;
	p=Locate_i(L,i-1);//找到第i-1个结点
	if(p==0) return ERROR; 
	q = p->next; //临时保存被删结点的地址以备释放 
	if(q==0) return ERROR;
	p->next = q->next; //改变删除结点前驱结点的指针域 
	delete q; //释放删除结点的空间 
	
	return OK;
}

void freeAll(LinkList &L)
{//释放所有结点空间
	LNode* p;	
	while (L->next)
	{
		p = L->next;
		L->next = p->next;
		delete p;
	}
	delete L;
	L = 0;	
}

void print(LinkList &L)
{
	if(L==0)
		cout<<"链表没有头结点"<<endl;
	LNode *p=L->next;
	while(p)
	{
		cout<<p->data<<" ";
		p=p->next;
	}
	cout<<"\n";
}

int main()
{
	LNode *H;
	InitList(H);
	push_head(H,1);
	print(H);
	push_head(H,3);
	print(H);
	push_head(H,5);
	print(H);
	
	push_back(H,6);push_back(H,7);push_back(H,8);
	print(H);
	 
	
	int x;
	GetElem(H,2,x);
	cout<<x<<endl;
	
	LNode *p=LocateElem(H,3);
	cout<<p->data<<endl;
	
	print(H);
	
	ListDelete(H,3);
	print(H);
	
		
	ListInsert(H,1,1);
	print(H);
	ListInsert(H,2,2);
	print(H);
	ListInsert(H,3,3);
	print(H);
	ListInsert(H,4,4);
	print(H);
	freeAll(H);
	print(H);
	
	
	return 0;
}

十七、运行结果

猜你喜欢

转载自blog.csdn.net/weixin_43917370/article/details/108688559