数据结构与算法学习笔记(一)-----链表(上)


前言

链表是线性表的链式存储方式,逻辑上相邻的数据在计算机内的存储位置不必须相邻,那么怎么表示逻辑上的相邻关系呢?可以给每个元素附加一个指针域,指向下一个元素的存储位
置。如图所示:
在这里插入图片描述
从图中可以看出,每个结点包含两个域:数据域和指针域,指针域存储下一个结点的地址,因此指针指向的类型也是结点类型。


一、单链表

链表的节点均单向指向下一个节点,形成一条单向访问的数据链。

1.单链表的初始化

单链表每个结点有两个域——数据域和指针域,多个节点连接在一起形成链表,初始化时要动态分配一块内存。

代码如下:

typedef struct _LinkNode
{
	int data; //结点的数据域
	struct _LinkNode *next; //结点的指针域
}LinkNode, LinkList; //链表节点、链表

bool InitList(LinkList* &L)//构造一个空的单链表 L
{
	L=new LinkNode; //生成新结点作为头结点,用头指针 L 指向头结点
	if(!L)
	{
		return false; //生成结点失败
	}
	L->next=NULL; //头结点的指针域置空
	return true;
}

2.单链表前插

在这里插入图片描述
我们需要处理的是将头节点的指针域赋给节点1的指针域(node1->next = L->next;),再让头节点指向节点1即可,以此类推。

代码如下:

bool ListInsert_front(LinkList* &L, LinkNode * node)
{
	if(!L || !node )
	{
		return false;
	}
	node->next = L->next;  //此时node->next==NULL
	L->next = node;
	return true;
}

3.单链表尾插

在这里插入图片描述
这里我们需要先找到最后一个节点,然后将新结点指针域滞空,再将新节点插入即可。

代码如下:

bool ListInsert_back(LinkList* &L, LinkNode *node)
{
	LinkNode *last = NULL;
	if(!L || !node ) 
	{
		return false;
	}
	//找到最后一个节点
	last = L;
	while(last->next) 
	{
		last=last->next;
	}
	//新的节点链接到最尾部
	node->next = NULL;
	last->next = node;
	return true;
}

4.任意位置插入元素

在这里插入图片描述
直接看代码:

bool LinkInsert(LinkList* &L, int i, int &e)//单链表的插入
{
	//在带头结点的单链表 L 中第 i 个位置插入值为 e 的新结点
	int j;
	LinkList *p, *s;
	p=L;
	j=0;
	while (p&&j<i-1) //查找第 i-1 个结点,p 指向该结点
	{
		p=p->next;
		j++;
	}
	if (!p || j>i-1)//i>n+1 或者 i<1
	{
		return false;
	}
	s=new LinkNode; //生成新结点
	s->data=e; //将新结点的数据域置为 e
	s->next=p->next; //将新结点的指针域指向结点 ai
	p->next=s; //将结点 p 的指针域指向结点 s
	return true;
}

5.打印单链表中的元素

逐步遍历链表各个节点,节点不为空时打印数据域中的元素,继续遍历下一节点,直至遇空为止。
代码如下:

void LinkPrint(LinkList* &L) //单链表的输出
{
	LinkNode* p;
	p=L->next;
	while (p)
	{
	cout <<p->data <<"\t";
	p=p->next;
	}
	cout<<endl;
}

6.单链表获取元素

思路:i 代表所要获取的元素的位置,我们用 j 配合 p 去找 i ,从而获取想要的元素。
代码如下:

bool Link_GetElem(LinkList* &L, int i, int &e)//单链表的取值
{
	//在带头结点的单链表 L 中查找第 i 个元素
	//用 e 记录 L 中第 i 个数据元素的值
	int j;
	LinkList* p;
	p=L->next;//p 指向第一个结点,
	j=1; //j 为计数器
	while (j<i && p) //顺链域向后扫描,直到 p 指向第 i 个元素或 p 为空
	{
		p=p->next; //p 指向下一个结点
		j++; //计数器 j 相应加 1
	}
	if (!p || j>i)
	{
		return false; //i 值不合法 i>n 或 i<=0
	}
	e=p->data; //取第 i 个结点的数据域
	return true;
}

7.单链表查找元素

遍历单链表,用 e 去匹配单链表中的元素。
代码如下:

bool Link_FindElem(LinkList *L, int e) //按值查找
{
	//在带头结点的单链表 L 中查找值为 e 的元素
	LinkList *p;
	p=L->next;
	while (p && p->data!=e)
	{//顺链域向后扫描,直到 p 为空或 p 所指结点的数据域等于 e
		p=p->next; //p 指向下一个结点
	}
	if(!p)return false; //查找失败 p 为 NULL
	return true;
}

8.单链表删除元素

代码如下:

bool LinkDelete(LinkList* &L, int i) //单链表的删除
{
	//在带头结点的单链表 L 中,删除第 i 个位置
	LinkList *p, *q;
	int j;
	p=L;
	j=0;
	while((p->next)&&(j<i-1)) //查找第 i-1 个结点,p 指向该结点
	{
		p=p->next;
		j++;
	}
	if (!(p->next)||(j>i-1))//当 i>n 或 i<1 时,删除位置不合理
		return false;
	q=p->next; //临时保存被删结点的地址以备释放空间
	p->next=q->next; //改变删除结点前驱结点的指针域
	delete q; //释放被删除结点的空间
	return true;
}

9.单链表销毁

代码如下:

	void LinkDestroy(LinkList* &L) //单链表的销毁
	{
	//定义临时节点 p 指向头节点
	LinkList *p = L;
	cout<<"销毁链表!"<<endl;
	while(p)
	{
		L=L->next; //L 指向下一个节点
		cout<<"删除元素: "<<p->data<<endl;
		delete p; //删除当前节点
		p=L; //p 移向下一个节点
	}
}

二、源码

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

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

bool InitList(LinkList* &L)
{
	L = new LinkNode;
	if(!L)
		return false;//生成节点失败
	L->next = NULL;
	L->data = -1;
	return true;
}

//前插法
bool ListInsert_front(LinkList* &L, LinkNode *node)
{
	if(!L || !node)
		return false;
	node->next = L->next;
	L->next = node;
	return true;
}

//尾插法
bool ListInsert_back(LinkList* &L, LinkNode *node)
{
	LinkNode *last = NULL;
	if(!L || !node)
		return false;
	last = L;
	while(last->next)
	{
		last = last->next;
	}
	node->next = NULL;
	last->next = node;
	return true;
}

//指定位置插入
bool LinkInsert(LinkList* &L, int i, int &e)
{
	if(!L)
		return false;
	int j =0;
	LinkList *p, *s;
	p = L;
	while(p && j<i-1)
	{//查找位置为 i-1 的结点,p 指向该结点
		p = p->next;
		j++;
	}
	if(!p || j>i-1)
		return false;
	s=new LinkNode;//生成新节点
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
}

void LinkPrint(LinkList* &L)
{
	LinkNode *p = NULL;
	if(!L)
	{
		cout<<"链表为空."<<endl;
		return ;
	}
	p = L->next;
	while(p)
	{
		cout<<p->data<<"\t";
		p = p->next;
	}
	cout<<endl;
}

bool Link_GetElem(LinkList* &L, int i, int &e)//单链表的取值
{
	//在带头结点的单链表 L 中查找第 i 个元素
	//用 e 记录 L 中第 i 个数据元素的值
	int index;
	LinkList *p;
	if(!L || !L->next)
		return false;
	p = L->next;
	index = 1;
	while(p && index<i)
	{//顺链表向后扫描,直到 p 指向第 i 个元素或 p 为空
		p = p->next; //p 指向下一个结点
		index++; //计数器 index 相应加 1
	}
	if(!p || index>i)
	{
		return false; //i 值不合法,i>n 或 i<=0
	}
	e=p->data;
	return true;
}

bool Link_FindElem(LinkList *L, int e, int &index) //按值查找
{
	//在带头结点的单链表 L 中查找值为 e 的元素
	LinkList *p;
	p=L->next;
	index = 1;
	if(!L || !L->next)
	{
		index = 0;
		return false;
	}
	while(p && p->data!=e)
	{
		p=p->next;
		index ++;
	}
	if(!p)
	{
		index = 0;
		return false;//查无此值
	}
	return true;
}

bool LinkDelete(LinkList* &L, int i) //单链表的删除
{
	LinkList *p, *q;
	int index = 0;
	p=L;
	if(!L || !L->next)
		return false;
	while((p->next) && (index<i-1))
	{
		p = p->next;
		index++;
	}
	if(!p->next || (index>i-1) )
	//当 i>n 或 i<1 时,删除位置不合理
		return false;
	q = p->next; //临时保存被删结点的地址以备释放空间
	p->next = q->next;//改变删除结点前驱结点的指针域
	delete q; //释放被删除结点的空间
	return true;
}
void LinkDestroy(LinkList* &L) //单链表的销毁
{
	//定义临时节点 p 指向头节点
	LinkList *p = L;
	cout<<"销毁链表!"<<endl;
	while(p)
	{
		L=L->next;//L 指向下一个节点
		cout<<"删除元素: "<<p->data<<endl;
		delete p; //删除当前节点
		p = L; //p 移向下一个节点
	}
}
int main(void){
	LinkList *L = NULL;
	LinkNode *s = NULL;
	//1. 初始化一个空的链表
	InitList(L);
//2. 使用前插法插入数据
/*int n;
cout<<"前插法创建单链表"<<endl;
std::cout<<"请输入元素个数 n:";
cin>>n;
cout<<"\n 请依次输入 n 个元素:" <<endl;
while(n>0){
s = new LinkNode; //生成新节点 s
cin>>s->data;
ListInsert_front(L, s);
n--;
}
*/
//3. 使用尾插法插入数据
/*int n;
cout<<"尾插法创建单链表"<<endl;
std::cout<<"请输入元素个数 n:";
cin>>n;
cout<<"\n 请依次输入 n 个元素:" <<endl;
while(n>0){
s = new LinkNode; //生成新节点 s
cin>>s->data;
ListInsert_back(L, s);
n--;
}
//4. 单链表的输出
LinkPrint(L);
*/
//5. 任意位置插入元素
	for(int j=0; j<3; j++)
	{
		int i, x;
		cout << "请输入插入的位置和元素(用空格隔开):";
		cin >> i;
		cin >> x;
		if(LinkInsert(L, i, x))
		{
			cout << "插入成功.\n\n";
		}
		else
		{
			cout << "插入失败!\n\n";
		}
		LinkPrint(L);
	}
	//6. 单链表根据位置获取元素
	int element = 0;
	if(Link_GetElem(L, 2, element))
	{
		cout<<"获取第二个元素成功, 值:"<<element<<endl;
	}
	else 
	{
		cout<<"获取第二个元素失败!"<<endl;
	}
	//7. 单链表根据值查询元素所在的位置
	int index=0;
	if(Link_FindElem(L, 10, index))
	{
		cout<<"查找元素 10 存在,所在位置: "<<index<<endl;
	}
	else 
	{
		cout<<"不存在元素 10."<<endl;
	}
	//8. 单链表删除元素
	if(LinkDelete(L, 2))
	{
		cout<<"删除第 2 个元素成功!"<<endl;
		LinkPrint(L);
	}
	else 
	{
		cout<<"删除第 2 个元素失败!"<<endl;
	}
	//9. 销毁单链表
	LinkDestroy(L);
	system("pause");
	return 0;
}

总结

对链表的处理关键在对指针域与数据域的处理,理清了这两者,会降低学习难度。以上内容只是个人短见,如有误处,还请指正,共同学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值