(二)《数据结构与算法》 青岛大学-王卓 线性表 C++

《数据结构与算法》 青岛大学-王卓 线性表(链式存储)C++

B站链接:[https://www.bilibili.com/video/BV1nJ411V7bd?p=20]

本人能力有限,本想利用类和模板去实现一个链表,但有点问题,最终没能实现,最后跟随视频利用结构体实现的链表。
没能实现的原因,我觉得模板不能够这么用,包括上一章线性表的顺序存储内容,线性表就是一种数据结构,如果想存放什么类型,只要将此类型重定义typedef就可以了。没必要利用模板进行,另外模板和重定义不能重新出现,因为重定义必须是对已知类型的定义。而模板是未知类型,只有程序运行到那才知道,所以这一想法以失败告终。
本人理解:数据结构没必要利用模板,等我们需要此种链表形式的数据类型的时候,需要的数据data是何种数据类型,那么直接typedef就可以用了。
链表只是一种数据结构的表现形式,完全可以将此种形式应用到各个示例,比如图书管理系统的数据结构可以采用链表形式,也可以采用顺序表形式,完全可以根据应用环境选择数据结构形式。
单纯用类实现的话,应该还是可以实现的,后续有空的话就补充上。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(注:图片截取自《数据结构与算法》-青岛大学王卓bilibili视频)

  1. 数据类型:线性表
  2. 数据存储方式:链式存储
/*
	利用结构体去实现一个单链表
	数据类型:线性表
	储存类型:链式存储
	结构上 实现一个单链表
	功能上 初始化、是否为空、表长、销毁;
	重点:取值、查找、插入、删除、头插法与尾插法
*/

#include<iostream>
using namespace std;
#define MAX_SIZE 100
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;  // Status是函数的类型,其值是函数结果状态代码


typedef int TypeData;
typedef struct Lnode
{
	TypeData data;	// 数据域
	struct Lnode* next;	// 指针域
}LNode,*LinkList;

// 初始化链表
Status InitList(LinkList& L)	// L为头指针
{
	L = new LNode;	// 生成新结点作为头结点,用头指针L指向头结点
	L->next = NULL;
	return OK;
}

// 判断是否为空
int ListEmpty(LinkList& L)
{
	if (L->next)return 0;	// 非空
	return 1;
}

// 销毁单链表  链表的头指针、头结点不存在
Status DestoryList_L(LinkList& L)
{
	LinkList p;	// 指针,或LNode* p
	while (L!=NULL)		// 直到L为空值
	{
		p = L;		// 将此时L指针,即地址赋值给临时变量p,以便于对L进行操作!!
		L = L->next;	// 重点:将L的下一个结点的地址赋值给L,相当于L指向了下一个结点,即移动到后面的结点处!!!
		delete p;	// 释放内存
	}
	return OK;
}

// 清空链表
Status ClearList(LinkList& L)
{
	LinkList p;	// 指针
	p = L->next;	// 将p指针指向 头指针L的下一个地址,即头结点的地址
	LinkList q;	// 不能够直接释放p,不然就不知道头结点的下一个地址了,所以还需要临时变量q

	while (p!=NULL)	// 结束条件
	{
		q = p->next;	// 将p的下一个地址,即头结点的下一个地址,即第二个结点地址赋值给q
		delete p;		// 此时可以释放p的内存
		p = q;		// 释放掉之后将q的地址赋值给p,再让p去释放
	}
	L->next = NULL;	// 全部释放完毕,将头结点指针域的置空
	return OK;
}

// 求链表长度
int LengthList(LinkList& L)
{
	int i = 0;
	LinkList p;
	p= L->next;	// 不能将指向首结点L,改为指向第一个节点L->next;即L = L->next,因为这样后续L的指向将失效!!!
	//我们只是记录长度,不能改变原本的数据,所以必须创建临时变量
	while (p!=NULL)
	{
		i++;	
		p = p->next;
	}
	return i;
}

// 取值——取链表中第i个元素的内容
Status GetValueList(LinkList& L, int i,TypeData &e)
{
	if (i <= 0)return ERROR;
	LinkList p;		// 创建临时变量p
	p = L->next;	// 此时p指向第一个结点的地址,此时j=1
	int j = 1;		// 创建计数器j,当j=i时,说明找到
	while (p!=NULL && j<i)
	{
		p = p->next;
		++j;
	}
	if (p==NULL || j > i)return ERROR;
	e = p->data;
	return OK;
}

// 查找——按值查找,返回地址
Lnode* LocateElem_L(LinkList& L, TypeData e)
{
	LinkList p;
	p = L->next;
	// 只有不为空或不等于目标值时才进行遍历,如果没有目标值,此时的p指向也是NULL空值,所以直接返回p
	while (p!=NULL && p->data!=e)	
	{
		p = p->next;
	}
	return p;
}

// 查找——按值查找,返回位置
int PostionElem_L(LinkList& L, TypeData e)
{
	LinkList p;
	p = L->next;
	int j = 1;
	while (p != NULL && p->data != e)	
	{
		p = p->next;
		j++;
	}
	if (p)return j;	// p!=NULL  等价于 p
	else 0;
}

// 插入——在第i个结点前插入值为e的新结点
/*
	步骤:
	1、首先找到 a(i-1) 的存储位置p
	2、生成一个数据域为e的新结点s
	3、插入新结点: 1)新结点的指针域指向结点ai s->next = p->next  2)结点a(i-1)的指针域指向新结点  p->next = s
	注意:顺序不能颠倒,除非找临时变量,保存p->next的地址
*/
Status ListInsert_L(LinkList& L, int i, TypeData e)
{
	LinkList p = L;
	int j = 0;
	while (p && j<i-1)	// 要插入第i个结点,则要找到第i-1个结点
	{
		p = p->next;
		++j;
	}
	if (!p || j > i - 1)return ERROR; // 查看位置是否非法
	LNode* s = new LNode;	// 创建新结点s
	s->data = e;			// 将数据e写入
	s->next = p->next;		// 将原本p下一个结点的地址,赋值给s的下一个地址,从而让s与链表建立联系
	p->next = s;			// 将新结点s的地址,赋值给p的下一个地址,从而完成插入
	return OK;
}

// 删除——删除第i个结点
Status ListDelete_L(LinkList& L, int i, TypeData &e)
{
	LinkList p = L;
	int j = 0;
	while (p && j < i - 1)	// 要删除第i个结点,则要找到第i-1个结点
	{
		p = p->next;
		++j;
	}
	if (!p || j > i - 1)return ERROR; // 查看位置是否非法
	LinkList q;
	q = p->next;	// 第i个结点
	e = q->data;	// 保存第i个结点的值
	p->next = q->next;	// 将p点处的结点下一个指向,赋值为指向为p->next的->next
	delete q;
	return OK;
}

// 显示,打印单链表
void ListPrint_L(LinkList& L)
{
	LinkList p;
	p = L->next;
	while (p)
	{
		cout << p->data << "\t";
		p = p->next;
	}
	cout << endl;
}

// 头插法——创建单链表
void CreatList_L(LinkList& L, int n)
{
	L = new LNode;
	L->next = NULL;		// 先创建一个带头结点的单链表
	cout << " 请依次输入数据 :" << endl;
	for (size_t i = n; i >0; --i)
	{
		LinkList p = new LNode;		// 创建一个新结点
		cin >> p->data;			// 输入数据
		p->next = L->next;	// 插入到表头,将原本表头指向的地址,赋值给新结点的下一个地址	=>换尾巴
		L->next = p;		// 将表头指向的地址 更新为 新结点的地址			=>换头
	}
}

// 尾插法——创建单链表
void BackCreatList_L(LinkList& L, int n)
{
	L = new LNode;
	L->next = NULL;		// 先创建一个带头结点的单链表
	LNode* r = new LNode;	// 创建尾指针
	r= L;	// 尾指针和L的指向相同
	cout << " 请依次输入数据 :" << endl;
	for (size_t i = 0; i < n; ++i)
	{
		LinkList p = new LNode;		// 创建一个新结点
		cin >> p->data;			// 输入数据
		p->next = NULL;	
		r->next = p;		//  尾指针插入表尾
		r = p;		// 		移动尾指针	
	}
}

// 链表的合并,按照数据大小排序
void MergeList_L(LinkList& La, LinkList& Lb, LinkList& Lc)
{
	LNode* pa = La->next;	// pa的头结点的地址
	LNode* pb = Lb->next;	// pb的头结点的地址
	Lc = La;		// 将La的头指针 赋值为 Lc
	LNode* pc = La;		//	将头指针赋值给Pc的头指针
	while (pa && pb)
	{
		if (pa->data <= pb->data)
		{
			pc->next = pa;	// 将pa头结点,赋值给pc头指针
			pc = pa;		// 将pa的指针,赋值给pc,以便于pc的下一步操作
			pa = pa->next;	// 将pa移动到下一个结点
		}
		else
		{
			pc->next = pb;
			pc = pb;
			pb = pb->next;
		}
	}
	pc->next = pa ? pa : pb;	// pa是否为空,不为空则pa的剩余段赋值给pc的next
	delete Lb;	// 释放Lb的头结点
}

int main()
{
	LinkList L;
	InitList(L);
	cout << ListEmpty(L) << endl;
	BackCreatList_L(L, 3);
	ListInsert_L(L, 4, 10);
	ListInsert_L(L, 5, 30);
	ListInsert_L(L, 6, 40);
	ListInsert_L(L, 7, 60);
	ListInsert_L(L,8, 90);
	cout << LengthList(L) << endl;
	ListPrint_L(L);
	DestoryList_L(L);
	return 0;
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值