数据结构—开端“比数组好用的线性表”(1)

线性表–顺序表

说到线性表大家肯定都不陌生了,而顺序表又是线性表的一种简单结构

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

线性表是干嘛的呢?这就涉及到了我们实际中数组的应用,在实际应用中,数组的容量内容并不是一成不变的,恰恰相反,我们很多时候需要的是一个特别的容器来储存我们需要的内容。每当我们需要一个容量内容可变的数组的时候我们都会头疼不已,而线性表就是来帮助我们解决这个问题的。

本文先声明可能出现错误,并且应该很少有一些标准化的语言出现。
因为我写的初衷是巩固自己(As a undergraduate),因为很多时候我也不会emmm,QAQ

0、头文件:写程序头文件肯定是必不可少的嘛

#include <iostream>
constexpr auto MAXSIZE = 105;
#define OK 1
#define ERROR 0
typedef int ElemType;
typedef int Status;
using namespace std;

1、初始化:众所周知,线性表是一种基于结构体而衍生出来的数据结构,因此我们在初始化的时候必不可少的是要使用结构体的

typedef struct {
	ElemType *elem;
	int length;
}List;

PS:线性表我会多时候会说成链表

2、基本功能:链表能有什么基本功能,不就是增删改查吗

2.1、构造一个链表:咳咳,话是这么说,路还得一步一步走

构造,怎么构造呢?上面也说了,链表是可变数组,因此我们也要用构造数组的方法就构造一个数组
因此在刚开始我们需要一个有最大长度的数组(可以很大,根据实际需要)。既然是构造,万一不成功怎么办呢?自然也需要一个成功的判定。之后将链表初始化为0即可。最后return 成功就好。

Status CreateList(List &L)
{
	L.elem = new ElemType[MAXSIZE];	//生成
	if (!L.elem) exit(OVERFLOW);	//不成功
	L.length = 0;					//初始化长度
	return 1;
}
2.2、删除,置空:

删除,置空听起来的区别是不大的。实际上也是如此,删除当然就是删掉整个内存空间,恢复内存的可用性;而置空就是将链表恢复到初始化的状态。

Status DestroyList(List &L)
{
	if (L.elem) delete[] L.elem;	//存在则销毁,使用delete
	L.length = 0; L.elem = NULL;	//清空长度以及对象空间
}

Status ClearList(List &L)
{
	L.length = 0;					//清空及长度等于0
}
2.3求链表长,判断是否为空

这都没啥好说的,直接上代码了

Status LengthList(List L)
{
	return L.length;				//返回长度
}

bool EmptyList(List L)
{
	if (L.elem == 0) return true;	//长度为0则确实为空
	else return false;
}
2.4增删改查:四种基本操作嘛,当然还是会逐步分析的
2.4.1增加:增加的难点就是在于你需要改变元素在链表中的位置

怎么办呢,所谓增加不就是,在两个元素之间插入一个新的元素,然后将后面得元素依次后推一位即可啦。
当然,在末尾添加只需要直接添加即可。
温馨提示:无论何时都不要忘记将length+1

Status AddElem(List &L, ElemType e, int i)	//在第i个位置添加e
{
	if (i<1 || i>L.length + 1) return ERROR;
	if (L.length == MAXSIZE) return ERROR;
	for (int j = L.length - 1; j >= i - 1; j--)	//L的最后一个到第i个依次后移一位
	{
		L.elem[j + 1] = L.elem[j];
	}
	L.elem[i - 1] = e;
	++L.length;
	return OK;
}

Status AddElem(List &L, ElemType e)	//在末尾添加元素
{
	if (L.length == MAXSIZE) return ERROR;
	
	L.elem[L.length] = e;	
	++L.length;
	return OK;
}
2.4.2删除:所谓删除就是和增加恰恰相反,但是删除不一定是删除某个位置的了,可能是删除某个特定的元素。

以下列举了可能出现的情况:

Status DeleteElem(List &L, int i)	//删除第i个位置的元素
{
	if (i<1 || i>L.length + 1) return ERROR;
	for (int j = i; j < L.length; j--)	//L的第i+1个(下标为i)到最后一个依次前移一位
	{
		L.elem[j - 1] = L.elem[j];
	}

	--L.length;
	return OK;
}

Status DeleteElem(List &L, ElemType e)	//删除第一个e,类比查找还能重构数种
{
	int i = FindFirstElem(L, e);	//第一个e的位置
	if (i<1 || i>L.length + 1) return ERROR;
	DeleteElem(L, i);
	return OK;
}

Status DeleteAllElem(List &L, ElemType e)	//删除所有的元素e
											//找到第一个e,删除第一个,后面所有元素往前进一;
											//之后边前进边查找,找到第二个,删除,后面所有元素进二;以此类推。
{
	int n = 0;	//每次需要进位数
	for (int i = 0; i < L.length; i++)		//遍历一次,每一次执行:是否是e,若是n++;若不是向前进n位;
	{
		if (e == L.elem[i])
		{
			n++;
		}
		else
		{
			L.elem[i - n] = L.elem[i];
		}
	}
	L.length -= n;	//共删去几个长度减几
	return OK;
}

删除所有的我要吭一句:老师让用的是O(n)时间复杂度的方法,现在你应该能看懂了吧。

2.4.3改:改不就是直接替换嘛,这里就不写了也不放代码了

我可不会告诉你们因为我自己没敲

2.4.4查找:这里给出通过指针带出或者返回值带出两种,个人喜欢后者,但是不知道为什么老师讲了第一种
Status GetElem(List L, int i, ElemType &e)	//通过e带出
{
	if (i<1 || i>L.length) return ERROR;
	else e = L.elem[i - 1];					//第i个实际下标为i-1
	return OK;
}

Status GetElem(List L, int i)	//通过返回值得到
{
	if (i<1 || i>L.length) return ERROR;
	return L.elem[i - 1];
}

当然查找肯定不限于只有这些,比如删除会删去所有e,当然你可以查找所有e的位置,然后用数组带出。
有规律的链表你可以用各种查找算法啊都是可以很方便的实现的,这里就不一一赘述的,当然不是因为我懒。

3:额外功能

唔,什么是额外功能呢,那当然你制造出来链表不能只局限于一个咯。比如并交差补啊很多两个或以上链表的相互操作。这里放上我已经写好的,比较晚了多的我也不写了。
交集:查找相同的放到新的链表中:你也可以根据需要指针带入,最终由其中一个带出。
并集:我采用的方法是两个加到一起再减去交集(集合相减:这是个差吗),主要还是不想再写新方法了,用现有的改变一下它不香吗。
后来我才意识到: 集合的话,不会有重复元素的 啊啊啊awsl,这么多年学白上了。
在这里插入图片描述
小生愚昧,愿后来者不要像我一样犯这种低级错误。

List InterList(List L1,List L2)	//交集
{
	List newList;
	CreateList(newList);
	for (int i = 0; i < L1.length; i++)
	{
		for (int j = 0; j < L2.length; j++)
		{
			if (L1.elem[i] == L2.elem[j])	//两个集合中有相同的元素
			{
				AddElem(newList, L1.elem[i]);	//添加到新的交集中
				newList.length++;				//不要忘记长度加一
				DeleteElem(L2, L1.elem[i]);		//删掉第二个集合中的该元素,不删第一个的原因是,第一个集合只遍历一次无影响
												//而且如果删掉,会影响L1.length,而删第二个因为每次二重循环重新开始L2.length无所谓
				break;							//退出该次循环,不然可能一次删掉多个重复元素
			}
		}
	}	//???重复元素,一个集合能有两个重复元素吗
	return newList;
}

List UnionList(List L1,List L2)	//并集:加一起删掉交集
{
	List tempList;
	tempList = InterList(L1, L2);	//求交集
	for (int i = 0; i < L2.length; i++)	//L1=L1+L2
	{
		AddElem(L1, L2.elem[i]);
	}
	for (int i = 0; i < tempList.length; i++)	//合集-=交集
	{
		DeleteElem(L1, tempList.elem[i]);
	}
	L1.length -= tempList.length;	//长度也是如此
	return L1;
}

最后声明:本篇内容代码暂未经过测试,仅为demo版本,如果我测试了,我一定会回来更新的。
如果我长时间没来,那就说明,我咕了嘻嘻
原作者:凡尘戏梦——一个不正经的大学生
2020/3/5 23:05记录第一次正式开始写博客(不正式的都不作数)


  1. 本段话来自360百科 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值