数据结构_线性表_c++

声明:该文章仅为我个人的学习记录,由于我只是个新手,代码也全是自己打的,因此有错误之处还请各位见谅!也恳请大家能积极提出建议!

线性表

线性指的是逻辑结构,所以有顺序线性表和链式线性表
关键词:有限(可为0),有序(有先来后到,且头元素无前驱,末元素无后继,其余元素有且只有一个前驱和后继),就像排队一样,一条线把所有元素连接在一起

顺序线性表

关于顺序线性表操作的具体实现代码就不写了,太简单熟悉了

操作时间复杂度
查找O(1)
插入O(n)
删除O(n)

单链表

单链表的定义:当一个序列中只含有指向它的后继结点的链接时,就称该链表为单链表。

特点:
内存地址可以不连续,而且除了存储数据的空间外,还需要另外的空间来存储指针,称为数据域和指针域,一个数据域和一个对应的指针域在一起被称为结点(头指针比较特殊)

如果有头结点,那么头结点在第一个元素之前,头指针指向头结点(注意:头指针不是头结点里的指针),头结点里的指针指向第一个元素,头结点一般不存储数据,(也可以用来存储链表的长度)

头指针是必须的,而头结点却不是必须的,如果不存在头结点,那么头指针就会直接指向第一个元素
(本文中的链表均没有头结点)

最后一个元素的指针一般为NULL

单链表的创建

尾插法
顾名思义,插在链表尾部

#include <iostream>

//这就是个基本的结点类,有数据域(data),也有指向下一个结点的指针(next)
class node 
{
public:
	int data;
	node* next;
};

//尾插法创建链表
int createlist(node** L, int size)
{
	//如果是要创建空链表,就直接返回
	if (size == 0)
	{
		return 0;
	}	
//定义一个中间指针p,并通过p来达到创建链表的目的
	node* q = nullptr;//这里的q用来记录p前面一个结点的位置
	node* p = new node;//创建第一个结点
	*L = p;//让我的头指针指向第一个结点

	for (int idx = 0; idx < size; idx++)
	{
		q = p;//如果不加这句,最后就不能保存真正的末元素的地址了
		p->data = idx;//这是初始化赋值,可以用0 ,也可以用随机数
		p->next = new node;//创建新结点
		p = p->next;//使p指向新结点
	}
//因为循环的最后是创建新结点,用不上的话,就浪费了内存,所以这里delete掉
	delete p; 
//在我们delete掉p以后,真正的末尾元素还是指向p的,
//因此我们需要保存这个位置,然后让他指向null
	q->next = nullptr;
	p = nullptr;
	q = nullptr;
	return 0;
}
int main()
{
	node* head = nullptr;//定义了一个空链表(只有头指针)
	createlist(&head, 30);

	printlist(&head);
	
	std::cin.get();
}

输出结果:
在这里插入图片描述
头插法
顾名思义,插在链表头部

//头插法创建
int createlist_2(node** L, int size)
{
	if (size == 0)//同尾插
	{
		return 0;
	}
	node* p = new node;//和尾插一样的套路
//这里由于是头插,所以第一个进来的元素就是链表里的最后一个元素,所以要指向null;
	p->next = nullptr;
	for (int idx = 0;idx<size;idx++)
	{
		*L = p;
		p->data = idx;
		p = new node;
		p->next = *L;
	}
	delete p;//同尾插一样的道理,释放掉多余的内存
	p = nullptr;
	return 0;
}

输出结果:
在这里插入图片描述
特定位置插入元素

/在特定位置插入元素
int insertlist(node** L,int idx,int data)
{
	int size = 0;
	node* p = *L;
	node* q = nullptr;
	while (p)//求长度,懒得优化了
	{
		size++;
		p = p->next;
	}
	p = *L;
	if (idx == 0 || idx > size+1)//这里记得加1,因为要实现在最末尾插入
	{
		std::cout << "参数输入错误" << std::endl;
		return -1;
	}
	if (idx == 1)//因为没有头结点,情况不太一样
	{
		p = new node;
		p->data = data;
		p->next = *L;
		*L = p;
		p = nullptr;
		q = nullptr;
		return 0;
	}
	for (int i = 1; i < idx - 1; i++)
	{
		p = p->next;
	}
	q = new node;
	q->data = data;
	q->next = p->next;
	p->next = q;
	p = nullptr;
	q = nullptr;
	return 0;
}
int main()
{
	node* head = nullptr;//定义了一个空链表(只有头指针)
	createlist(&head, 30);
	printlist(&head);
	insertlist(&head, 31,30);
	printlist(&head);
	std::cin.get();
}

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

单链表的删除

整表删除

//整表删除
int clearlist(node** L)
{
//定义两个指针,p用来指向即将被删除的结点,q用来给p传递位置,一步步的将所有结点删除
//假设p走过的路面都会塌陷,于是为了让所有路面塌陷,我们就让q在前面拉着p走
	node *p  , *q;
	p = *L;//让p指向第一个元素结点
	while (p)
	{
		q = p->next;// q指向p的下一个结点
		delete p;
//让p指向q所指的结点
		p = q;
	}
	q = nullptr;
	p = nullptr;
	*L = nullptr;
	return 0;
}

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

指定位置删除

//删除特定位置的元素
int deletelist(node** L,int idx)
{
	int data,size = 0;//返回值和记录链表长度的值
	node* p = *L;
	node* q = nullptr;
	while (p)//求得链表长度
	{
		size++;
		p = p->next;
	}
	if (idx == 0 || idx > size)//判断输入参数是否有误
	{
		std::cout << "参数输入错误" << std::endl;
		return -1;
	}
	p = *L;
	if (idx == 1)//因为没有头结点,所以删除第一个元素的情况比较特殊
	{
		q = p->next;
		data = p->data;
		*L = q;
		delete p;
		p = nullptr;
		return data;
	}
	for (int i = 1; i < idx-1; i++)//将p移动到要删除的结点的前面
	{
		p = p->next;
	}
	q = p->next;//让q指向被删结点
	data = q->data;//记录被删的数据
	p->next= q->next;//跳过被删结点,链接到下一个结点
	delete q;//删除结点
	q = nullptr;
	p = nullptr;
	return data;
}
int main()
{
	node* head = nullptr;//定义了一个空链表(只有头指针)
	createlist(&head, 30);
	printlist(&head);
	std::cout << "被删的元素是" << deletelist(&head, 3) <<std::endl;
	printlist(&head);
	std::cin.get();
}

输出结果:
此处第三个元素的值就是2
在这里插入图片描述

如果把以上的方法都写进类里面,那么求size的循环以及很多其他地方都可以优化掉,但我这里懒了,反正自己能看懂就行了

循环(单)链表

循环链表和普通链表的差别就是,循环链表的最后一个元素不是nullptr,而是指向第一个元素
关于循环链表的创建,清空,插入,删除等操作等只要稍微改下单链表的就行了;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timathy33

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值