C++ 链表(上)

本博客来源于之前C++老师课堂小测和上课讲解的内容,小测的时候写代码很困难,课后又重新复习了一下。又结合了西北工业大学mooc上的内容,整理如下:
(初学者可能会有错误,欢迎大家批评指正,且仅用于个人学习使用,若有侵权必删除)

写在前面

  • 主要参考:西北工业大学mooc;相当于对魏老师讲解的笔记。
  • 使用:visual studio 2010

基本概念

链表的定义

下面是百度百科中对于链表的定义:

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

可以看到,“链表由一系列结点组成”,因此,首先要了解一下结点:

由定义可以看到,结点由两部分组成:

  • 存储数据元素的数据域;
  • 存储下一个结点地址的指针域;

写成C++代码为:

struct Node
{
	ElemType data;  	//存储数据元素的数据域
	Node* link;         //存储下一个结点地址的指针域;
};

然后分别对数据域和指针域做进一步的解释:

  • 数据域:
typedef int ElemType; // 建立了一个整型类型 ElemType
这里采用的是`typedef`进行定义的。然鹅我自己看的时候,根本不知道什么是`typedef`,查了很多博客才明白。所以在此做一个解释:

简单来说,typedef 就是: 任何声明变量的语句前面加上typedef后,原来是变量的都变成一种类型。
因此,可以这样使用:

typedef int NUM;
NUM a =10; // 或者NUM(a) = 10; 
  • 指针域:
    由于结构体一经声明,就可以进行指针和引用的相关操作,所有是完全合理合法的。link成员表示指针域,存放此结点链接的下一个结点的地址。其示意图大概是这样的:
    在这里插入图片描述

链表的分类

有了上面对于link指针域的了解,就可以对链表进行分类了。链表可以按照不同的分类方式,分成以下几种常见的类:

单链表

单链表某个结点的仅包含一个指向直接后继结点的指针域。
在这里插入图片描述
用代码对单链表结点做定义:

// 定义数据域
typedef int ElemType;

// 定义结点
struct LNode 
{
	ElemType data;
	LNode* next;
};

// 定义单链表的指针类型LinkList
typedef LNode* LinkList;
双链表

双链表的每一个结点中都包含两个指针,一个指向它前面的结点,一个指向它后面的结点。其形式为:
在这里插入图片描述
其代码描述为:

typedef int ElemType;
struct DNode
{
	ElemType data;
	DNode* prev; * next;
};
typedef DNode* DLinkList;
循环链表

若单链表尾结点指向头节点而不是0,则该链表是循环单链表。同理,若双链表尾结点next指向头节点而不是0,头节点prev指向尾结点而不是0,则该链表是循环双链表。

单链表的创建

头插法(逆序)

头插法建立链表CreateLink(&L,n,input())

该方法先建立一个头节点*L,然后产生新结点,设置新结点的数据域;再将新结点插入到当前链表的表头,直至指定数目的元素都增加到链表中为止。其步骤为:

① 创建头节点*L,设置*L的next为NULL;

② 动态分配一个结点s,输入s的data;

③ 将s插入开始节点之前,头节点之后,即如下图所示:
在这里插入图片描述
④ 重复②~④步骤,加入更多的结点。

其代码为:

// 头插法创建单链表
# include <iostream>

using namespace std;

/* 定义数据域 */

typedef int ElemType;

/* 定义结点结构体 */
struct LNode
{
	ElemType data;
	LNode* next;
};

/* 定义单链表指针类型 */
typedef LNode* LinkList;

/* 从键盘获取数据 *ep,显然ep为指针 */
void input(ElemType *ep)
{
	cout << "请您输入数据:";
	cin >> *ep;
}

/* 创建单链表 */
void CreateLinkF(LinkList *L, int n, void(*input)(ElemType*))
{
	LinkList s;  // 创建一个结点地址s;
	*L = new LNode;  // 头结点,申请内存;
	(*L) -> next = NULL; //一开始链表为空;
	for(; n>0; n--)
	{
		s = new LNode; // 申请内存;
		input( & s -> data);  // 调用函数;
		s -> next = (*L) -> next;
		(*L) -> next = s;  // 完成上面的头插法循环算法
	}
}

/* 主函数 */
int main()
{
	LinkList L; 
	int n;
	cout << "请输入您要创建多大的链表:";
	cin >> n;

	CreateLinkF(&L,n,input);

	cout << "创建完成!hahaha";

	cout << endl;

	system("pause");
	return 0;

}

用visual studio执行如下:

结果为:
在这里插入图片描述

尾插法(顺序)

头插法建立的链表中结点的次序与元素输入的顺序相反,若希望两者次序一致,可采用尾插法建立链表。该方法是将新结点插到当前链表的末尾上,其步骤为:

① 创建头节点*L,设置*L的next为0,且令指针p指向*L

② 动态分配一个结点s,输入s的数据;

③ 将s插入到当前链表末尾;
在这里插入图片描述
④ 重复②~④步

其代码为:

// 尾插法创建单链表
# include <iostream>

using namespace std;

/* 定义数据域 */
typedef int ElemType;

/* 定义结点结构体 */
struct LNode
{
	ElemType data;
	LNode* next;
};

/* 定义单链表指针类型 */
typedef LNode* LinkList;

/* 从键盘获取数据,显然,ep为指针 */
void input(ElemType *ep)
{
	cout << "请输入数据:";
	cin >> *ep;
}

/* 尾插法创建单链表,这个注释就不写了,算法完全参照前面画的图*/
void CreateLinkR(LinkList *L, int n, void(*input)(ElemType*))
{
	LinkList p,s;
	p = *L = new LNode;
	for(;n>0;n--)
	{
		s = new LNode;
		input (& s->data);
		p -> next = s;
		p = s;
	}
	p -> next = NULL;
}

/* 主函数 */
int main()
{
	LinkList L;
	int n;
	cout << "请输入链表的长度:";
	cin >> n;

	CreateLinkR(&L, n, input);

	cout << "创建成功!hahaha" << endl;

	system("pause");
	return 0;
}

结果为:
在这里插入图片描述

销毁链表

销毁链表DestroyList(&L)

按照动态内存的使用要求,当不再使用链表时或程序结束前,需要将创建链表时分配的所有结点的内存释放掉,即销毁链表。

由于我们创建链表的时候,采用了动态内存分配的方法,因此,销毁链表是十分必要的。

销毁链表的步骤如下:

① 若*L为0,表示已到链尾,销毁链表结束;

② 令指针p指向结点*L的next,释放内存*L

*L置换为p,即*L指向直接后继节点,重复①~③步骤直至销毁链表结束。

其代码为:

// 销毁链表
void DestroyList(LinkList *L)
{
	LinkList q,p = *L;
	while (p != NULL)
	{
		q = p -> next;
		delete p;
		p = q;
	}
	*L = NULL;
}

链表的运算

链表遍历(打印 & 求长度)

链表遍历ListTraverse(L,visit())

与数组不同,链表不是用下标而是用指针运算查找数据元素的。通过链表的头指针L可以访问开始结点p=L->next,令p=p->next,即p指向直接后继节点,如此循环可以访问整个链表中的全部结点。

链表遍历算法的实现步骤为:

① 令指针p指向L的开始结点;

② 若p为0,表示已到链尾,遍历结束;

③ 令p指向直接后继节点,即p = p -> next。重复②~③步骤直至遍历结束;

代码为:

// 遍历链表

/* 输出数据 */
void visit(ElemType *ep)
{
	cout << *ep << " ";
}

/* 遍历函数 */
void ListTraverse(LinkList L, void(*visit)(ElemType*))
{
	LinkList p = L -> next;
	int len = 0;
	while(p!=NULL)
	{
		visit(&(p->data));
		len += 1;
		p = p -> next;
	}

	cout << "您创建链表的长度为:" << len << endl;
}

结果为:
在这里插入图片描述

小结

今天先写到这里,明天写后半部分(插入、删除等和习题)。复习一下:

  • 概念和分类;
  • 创建(2种)
  • 遍历
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值