数据结构上机练习——单链表的基本操作、头文件、类定义、main函数、多种链表算法的实现,含注释

news2024/11/26 2:48:22

文章目录

  • 单链表的基本操作实现
    • 1.头文件
    • 2.类定义和多种算法的实现
      • 2.1创建空表
      • 2.2头插法创建n个元素的线性链表
      • 2.3一个带头节点的链表存放一组整数,设计一个算法删除值等于x的所有节点。
      • 2.4计算线性表中值为偶数的节点个数
      • 2.5一个带头节点的单链表heada存放一组整数,设计分裂heada算法,偶数放在heada中,奇数放在headb中
    • 3.main函数和源码实现
      • 3.1测试实现:
      • 3.2LinkList.h
      • 3.3test.cpp

单链表的基本操作实现

1.头文件

  头文件和源文件分开有很多好处:可以提高编译速度、提高代码的可维护性、提高代码的可重用性和可扩展性,同时也可以使代码结构更清晰,方便代码的管理和维护。

LinkList.h

#pragma once

#include<assert.h>

//定义单链表节点
typedef struct LNode
{
	int data;
	LNode* next;

}LNode;

test.cpp

#include<iostream>
using namespace std;

#include"LinkList.h"

             

2.类定义和多种算法的实现

  (下面所有函数都默认在类中实现)

  我们以带头单向非循环链表为例:

  带头单向非循环链表是一种链表数据结构,其中每个节点包含一个数据域和一个指向下一个节点的指针域。在这种链表中,有一个特殊的节点称为头节点,它指向链表的第一个节点。头节点不是链表的一部分,仅用于方便操作。

在这里插入图片描述

             

2.1创建空表

  我们定义了一个名为LinkList的类,代表一个单链表。这个类有两个私有成员:一个指向LNode类型的指针_head,代表链表的头节点,以及一个整型变量_size,代表链表的大小。

//定义单链表类
class LinkList
{
public:
	//默认构造函数
	LinkList()
	{
		_head = new LNode(0);//创建头结点(哨兵位节点)
		_size = 0;
	}
	
private:
	LNode* _head;
	int _size;
};

             

2.2头插法创建n个元素的线性链表

  先以头插单个元素为例:

  我们可以先创建一个新的节点来存储该元素。然后,检查链表是否为空,如果为空,则新节点就是链表的第一个节点; 否则,新节点将插入到当前头节点的后面。插入完成后,_size(代表链表元素个数的变量)加1。

void push_front(const int& val)
{
	//创建一个插入的新节点,将要插入的值val赋值给它
	LNode* newnode = new LNode(val);
	LNode* cur = _head->next;//保存原来第一个结点

	//进行头插操作
	_head->next = newnode;
	_head->next->next = cur;//连接原来的第一个节点

	_size++;
}

  加上n循环即可实现头插法创建n个元素的线性链表

//头插法创建n个元素
void push_front_n()
{
	cout << "请输入要插入的元素个数:";
	int n;
	cin >> n;
	cout << endl;
	cout << "输入要插入的元素:";
	while (n)
	{
		int tmp;
		cin >> tmp;
		push_front(tmp);
		n--;
	}
}

             

2.3一个带头节点的链表存放一组整数,设计一个算法删除值等于x的所有节点。

  我们先检查链表是否为空,如果为空,则输出一条错误消息并返回。如果链表非空,它开始遍历链表,检查每个节点的下一个节点是否为要删除的节点。如果是,则删除该节点并释放其内存;如果不是,则移动到下一个节点。 在遍历过程中,保持对当前节点的引用,以防止删除连续的要删除的节点时出现问题。

	//删除所有x的节点
void erase_all_x(int x)
{
	LNode* cur = _head;
	if (cur->next == nullptr)//判断是否为空链表
	{
		cout << "该链表为空不可删除\n";
		return;
	}
	else
	{
		while (cur && cur->next)//删除的数据有可能连续,所以最好保持当前节点
		{
			if (cur->next->data == x)//如果下一个节点为要删除节点
			{
				LNode* tmp = cur->next;//用临时指针保存要删除的节点
				cur->next = cur->next->next;//链表指向删除节点的下一个节点

				delete tmp;//删除节点中的元素
				tmp = nullptr;
			}
			else//如果下个节点不是删除节点,那直接指向下个节点
			{
				cur = cur->next;
			}
		}
	}
}

             

2.4计算线性表中值为偶数的节点个数

  我们定义函数用于遍历链表并计算其中偶数节点的数量。首先,它检查链表是否为空,如果为空,则输出一条错误消息。如果链表非空,它开始遍历链表,检查每个节点的数据是否为偶数。如果是偶数,则计数器加1。 遍历完成后,输出链表中偶数节点的数量。

//打印链表中值为偶数的节点个数
void print_even_number()
{
	LNode* cur = _head->next;
	int count = 0;
	if (cur == nullptr)
	{
		cout << "该链表为空,没有节点\n";
	}
	else//核心就在不断通过指针遍历寻找即可
	{
		while (cur)//遍历链表中的每一个节点
		{
			if (cur->data % 2 == 0)
			{
				count++;//如果cur为偶数,计数++;
			}

			cur = cur->next;
		}

		cout << "该链表中偶数节点的个数为:" << count << endl;
	}
}

             

2.5一个带头节点的单链表heada存放一组整数,设计分裂heada算法,偶数放在heada中,奇数放在headb中

  我们定义该函数用于将链表中的偶数节点和奇数节点分开,使得偶数节点在heada链表中,奇数节点在headb链表中。

  函数使用两个指针cur1和cur2分别遍历heada和headb链表。在遍历过程中,如果当前节点的下一个节点是偶数节点,则保持原链表不变,移动cur1指针;

  如果当前节点的下一个节点是奇数节点,则将其从原链表中删除,并添加到headb链表的末尾,同时移动cur1和cur2指针。 最后,函数返回修改后的heada和headb链表。

//分裂链表,偶数在heada中,奇数在headb中
void divide_LinkList(LNode* heada, LNode* headb)
{
	LNode* cur1 = heada;
	LNode* cur2 = headb;

	while (cur1 && cur1->next)//退出循环的条件要cur1和cur1下个节点不为空
	{
		if (cur1->next->data % 2 == 0)//为偶数原链表不变
		{
			cur1 = cur1->next;//cur1直接向后移动
		}
		else//若链表为奇数,需要移动放入headb中
		{
			//交换链表节点操作
			LNode* tmp = cur1->next;
			cur1->next = cur1->next->next;
			
			//调整cur2,使其获得cur1的节点,断开cur1节点的后面节点的连接
			cur2->next = tmp;
			cur2->next->next = nullptr;

			//cur1和cur2各向后移动
			cur2 = cur2->next;
		}
	}
}

             

3.main函数和源码实现

3.1测试实现:

test_LinkList1();
在这里插入图片描述

test_LinkList2();
在这里插入图片描述

test_LinkList3();
在这里插入图片描述

             

3.2LinkList.h

#pragma once

#include<assert.h>

//定义单链表节点
typedef struct LNode
{
	int data;
	LNode* next;

	LNode(const int& val)
		:data(val)
		, next(nullptr)
	{}

}LNode;

//定义单链表类
class LinkList
{
public:
	//默认构造函数
	LinkList()
	{
		_head = new LNode(0);//创建头结点(哨兵位节点)
		_size = 0;
	}

	//拷贝构造函数 lt1(lt)
	LinkList(const LinkList& lt)
	{
		LNode* oldcur = lt._head->next;

		//这个this指针是新建的链表lt1的
		this->_head = new LNode(0);
		this->_size = 0;

		LNode* newcur = _head;
		while (oldcur)//深拷贝以完成链表的赋值操作
		{
			//将旧链表中的值赋值到新链表中
			LNode* tmp = new LNode(oldcur->data);

			//向后移动新旧链表节点
			newcur->next = tmp;
			newcur = newcur->next;
			oldcur = oldcur->next;

			_size++;
		}
	}

	//析构函数
	~LinkList()
	{
		LNode* cur = _head->next;

		while (cur)
		{
			LNode* tmp = cur;
			cur = cur->next;

			delete tmp;
			tmp = nullptr;
		}
	}

	//单链表打印
	void print()
	{
		LNode* cur = _head->next;

		if (cur == nullptr)
		{
			cout << "该单链表为空\n";
		}
		else
		{
			cout << "该单链表中的元素为:";
			
			while (cur)
			{
				printf("%d->", cur->data);
				cur = cur->next;
			}

			cout << "NULL\n";
		}
	}

	//单链表尾插
	void push_back(const int& val)
	{
		LNode* newnode = new LNode(val);
		LNode* cur = _head;
		
		while (cur && cur->next)//找到尾结点
		{
			cur = cur->next;
		}

		cur->next = newnode;//尾插

		_size++;
	}

	//单链表头插
	void push_front(const int& val)
	{
		LNode* newnode = new LNode(val);
		LNode* cur = _head->next;

		_head->next = newnode;
		_head->next->next = cur;

		_size++;
	}

	//单链表尾删
	void pop_back()
	{
		LNode* cur = _head->next;
		LNode* prev = _head;

		if (cur == nullptr)
		{
			cout << "单链表为空不可删除\n";
		}
		else
		{
			while (cur && cur->next)//找到尾结点和前一个节点
			{
				cur = cur->next;
				prev = prev->next;
			}

			prev->next = nullptr;
			delete cur;
			cur = nullptr;
		
			_size--;
		}
	}

	//单链表头删
	void pop_front()
	{
		LNode* cur = _head->next;

		if (cur == nullptr)
		{
			cout << "单链表为空不可删除\n";
		}
		else
		{
			_head->next = cur->next;

			delete cur;
			cur = nullptr;
		
			_size--;
		}
	}

	//头插法创建n个元素
	void push_front_n()
	{
		cout << "请输入要插入的元素个数:";
		int n;
		cin >> n;
		cout << endl;
		cout << "输入要插入的元素:";
		while (n)
		{
			int tmp;
			cin >> tmp;
			push_front(tmp);
			//LNode* newnode = new LNode(tmp);
			//LNode* cur = _head->next;
			//if (cur == nullptr)
			//{
			//	_head->next = newnode;
			//}
			//else
			//{
			//	newnode->next = cur;
			//	_head->next = newnode;
			//}

			n--;
			//_size++;
		}
	}

	//删除第n个元素
	void erase(int n)
	{
		assert(n > 0 && n <= _size);

		LNode* cur = _head;
		if (cur->next == nullptr)
		{
			cout << "该链表为空不可删除\n";
			return;
		}
		else
		{
			LNode* tmp = cur;
			while (n)//找到删除节点的前一个位置
			{
				tmp = cur;
				cur = cur->next;
				n--;
			}

			tmp->next = tmp->next->next;
			delete cur;
			cur = nullptr;
		}
	}

	//单链表节点个数
	void print_size()
	{
		cout << "单链表节点个数为:" << _size << endl;
	}

	//删除所有x的节点
	void erase_all_x(int x)
	{
		LNode* cur = _head;
		if (cur->next == nullptr)
		{
			cout << "该链表为空不可删除\n";
			return;
		}
		else
		{
			while (cur && cur->next)//删除的数据有可能连续,所以最好保持当前节点
			{
				if (cur->next->data == x)
				{
					LNode* tmp = cur->next;
					cur->next = cur->next->next;//删除x节点

					delete tmp;
					tmp = nullptr;
				}
				else//如果下个节点不是删除节点,那直接指向下个节点
				{
					cur = cur->next;
				}
			}
		}
	}

	//打印链表中值为偶数的节点个数
	void print_even_number()
	{
		LNode* cur = _head->next;
		int count = 0;
		if (cur == nullptr)
		{
			cout << "该链表为空,没有节点\n";
		}
		else
		{
			while (cur)//遍历链表中的每一个节点
			{
				if (cur->data % 2 == 0)
				{
					count++;//如果cur为偶数,计数++;
				}

				cur = cur->next;
			}

			cout << "该链表中偶数节点的个数为:" << count << endl;
		}
	}

	//返回当前链表的头结点
	LNode* get_head()
	{
		return _head;
	}

	//分裂链表,偶数在heada中,奇数在headb中
	void divide_LinkList(LNode* heada, LNode* headb)
	{
		LNode* cur1 = heada;
		LNode* cur2 = headb;

		while (cur1 && cur1->next)
		{
			if (cur1->next->data % 2 == 0)//为偶数原链表不变
			{
				cur1 = cur1->next;
			}
			else//若链表为奇数,需要移动放入headb中
			{
				//交换链表节点操作
				LNode* tmp = cur1->next;
				cur1->next = cur1->next->next;

				cur2->next = tmp;
				cur2->next->next = nullptr;

				//cur1和cur2各向后移动
				cur2 = cur2->next;
			}
		}
	}

private:
	LNode* _head;
	int _size;
};

             

3.3test.cpp

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

#include"LinkList.h"

void test_LinkList1()
{
	LinkList lt;
	//链表打印
	lt.print();

	//测试空链表删除
	lt.pop_front();

	//尾插
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.print();

	//头插
	lt.push_front(5);
	lt.push_front(6);
	lt.push_front(7);
	lt.push_front(8);
	lt.print();
	
	//打印链表节点
	lt.print_size();

	//尾删
	lt.pop_back();
	lt.pop_back();
	lt.print();

	//头删
	lt.pop_front();
	lt.pop_front();
	lt.print();
	lt.print_size();
}

void test_LinkList2()
{
	//头插法创建n个元素的链表
	LinkList lt;
	lt.push_front_n();
	lt.print();
	lt.print_size();
}

void test_LinkList3()
{
	LinkList lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);
	lt.push_back(6);
	lt.push_back(7);
	lt.push_back(8);
	lt.push_back(9);
	lt.push_back(10);
	lt.print();
	lt.print_size();

	lt.push_back(6);
	lt.push_back(6);
	lt.push_back(6);

	//删除第11节点的元素
	lt.erase(11);
	lt.print();

	//删除所有元素为6的节点
	lt.erase_all_x(6);
	lt.print();

	//打印所有节点为偶数的个数
	lt.print_even_number();

	//拷贝构造函数
	LinkList lt1(lt);
	lt1.print();
	lt1.print_size();

	//编译器生成了默认的赋值运算符重载
	LinkList lt2 = lt1;
	lt2.print();

	//创建空链表
	LinkList lt3;
	lt3.print();

	lt1.push_back(11);
	lt1.push_back(14);
	lt1.push_back(12);
	lt1.push_back(13);
	lt1.print();

	//分离链表lt1,使lt1只含有偶数,lt3只含有奇数
	lt1.divide_LinkList(lt1.get_head(), lt3.get_head());
	lt1.print();
	lt3.print();
}

int main()
{
	//不想输入数据就调用test_LinkList1()或test_LinkList3();
	//test_LinkList1();
	//test_LinkList2();
	test_LinkList3();
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1020226.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

科技云报道:云安全的新战场上,如何打破“云威胁”的阴霾?

科技云报道原创。 近年来&#xff0c;在云计算和网络安全产业的蓬勃发展下&#xff0c;我国云安全行业市场规模呈现高速增长态势&#xff0c;在网络安全市场总体规模中占比不断上升。 据统计&#xff0c;近5年我国云安全市场保持高速增长&#xff0c;2021年我国云安全市场规模…

VMware workstation 中centos7虚拟机在nat模式下怎么配置网卡,指定我想要的IP并且可以联网

1、首先打开我们的虚拟网络编辑器 2、查看我们的网关 3、查看IP池&#xff0c;根据需求自己设置 4、打开centos7虚拟机 编辑网卡配置 vim /etc/sysconfig/network-scripts/ifcfg-ens160####我的网卡是ens160TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOstatic …

全新交友平台,探索在线交流的乐趣!

大家好&#xff01;今天给大家介绍一款全新的交友平台&#xff0c;让您轻松享受在线交流的乐趣&#xff01;这个平台以其丰富多样的功能和互动体验而广受欢迎。无论您是想结识新朋友还是找到心仪的主播&#xff0c;这里都能满足您的需求。 首先&#xff0c;我们来看一下首页列表…

基于SpringBoot的超市管理系统

基于SpringBootVue的超市管理系统、超市进销存系统&#xff0c;前后端分离 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 【主要功能】 角色&#xff1a;管理员…

三、C#—变量,表达式,运算符(1)

&#x1f33b;&#x1f33b; 目录 一、变量1.1 变量1.2 使用变量的步骤1.3 变量的声明1.4 变量的命名规则1.5 变量的初始化1.6 变量初始化的三种方法1.7 变量的作用域1.8 变量使用实例1.9 变量常见错误 二、C#数据类型2.1 数据类型2.2 值类型2.2.1 值类型直接存储值2.2.2 简单类…

Vivado初体验LED工程

文章目录 前言一、PL 和 PS二、LED 硬件介绍三、创建 Vivado 工程四、创建 Verilog HDL 文件五、添加管脚约束六、添加时序约束七、生成 BIT 文件八、仿真测试九、下载测试 前言 本节我们要做的是熟练使用 Vivado 创建工程并实现对 LED 灯控制&#xff0c;每秒钟控制开发板上的…

微信小程序手写时间间隔组件,可设置间隔时间一分钟,半小时,一小时的间隔

纯手写时间间隔组件 需求&#xff1a;小程序中可以根据时间段进行选择开始时间和结束时间&#xff0c;如&#xff1a;当前时间是09&#xff1a;00&#xff0c; 则我可以从9点开始选择时间&#xff0c;每半个小时为间隔&#xff0c;那么下一个时间就算9&#xff1a;30&#xff…

vue基础知识十三:Vue中的$nextTick有什么作用?

一、NextTick是什么 官方对其的定义 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法&#xff0c;获取更新后的 DOM 什么意思呢&#xff1f; 我们可以理解成&#xff0c;Vue 在更新 DOM 时是异步执行的。当数据发生变化&#xff0c;Vue将开启一个异…

阿里测试岗:惨不忍睹的三面,幸好做足了准备,月薪17k,已拿offer....

我今年25岁&#xff0c;专业是电子信息工程本科&#xff0c;19年年末的时候去面试&#xff0c;统一投了测试的岗位&#xff0c;软件硬件都有&#xff0c;那时候面试的两家公司都是做培训的&#xff0c;当初没啥钱&#xff0c;他们以面试为谎言再推荐去培训这点让我特别难受。 …

开启编程之门

自我介绍 目前已经大二了&#xff0c;计算机专业在读&#xff0c;是一个热爱编程&#xff0c;做事踏实专注的人。转眼间一年已经过去了&#xff0c;也接触编程一年了&#xff0c;但开始并没有对所学所想进行很好的总结和输出&#xff0c;这一年也有了新的很多感悟与心得&#x…

JVM——5.类文件结构

这篇文章我们来讲一下jvm的类文件结构 目录 1.引言 2.类文件结构概论 3.魔数与class文件的版本 4.常量池 5.访问标志 6.类索引、父类索引与接口索引集合 7.字段表集合 8.方法表集合 9.属性表集合 9.1code属性 9.2 Exception属性 10小结 1.引言 代码编译的结果从本…

vue学习-02vue入门之组件

删除Vue-cli预设 在用户根目录下(C:\Users\你的用户名)这个地址里有一个.vuerc 文件,修改或删除配置 组件 Props(组件之间的数据传递) Prop 的大小写 (camelCase vs kebab-case)不敏感Prop 类型: String Number Boolean Array Object Date Function Symbol传递静态或动态 Pr…

「聊设计模式」之抽象工厂模式(Abstract Factory)

&#x1f3c6;本文收录于《聊设计模式》专栏&#xff0c;专门攻坚指数级提升&#xff0c;助你一臂之力&#xff0c;带你早日登顶&#x1f680;&#xff0c;欢迎持续关注&&收藏&&订阅&#xff01; 前言 在软件开发中&#xff0c;设计模式是一种被广泛使用的经验…

kudu 1.4.0 离线安装

1.准备rpm安装包 kudu-1.4.0: kudu的基础安装包 kudu-client0-1.4.0: kudu的c++客户端共享库 kudu-client-devel-1.4.0: kudu的c++客户端共享库sdk kudu-master-1.4.0: kudu master kudu-tserver-1.4.0: kudu tserver

任意输入一个整数m,若m不是素数,则对m进行质因数分解,并以质因数从小到大顺序排列的乘积形式输出

每个合数都可以写成几个质数&#xff08;也可称为素数&#xff09;相乘的形式 &#xff0c;这几个质数就都叫做这个合数的质因数。 #include <stdio.h> int isPrime(int num)// 判断一个数是否是素数 {if (num < 2) {return 0;}for (int i 2; i * i < num; i) {…

汽车电子 -- CAN总线波特率计算方法

上一篇文章介绍 PCAN View 安装与使用 的时候&#xff0c;留下了两个问题&#xff0c;CAN总线波特率该怎么计算&#xff1f; 下图里的这些 Prescaler、tseg1、tseg2、sync Jump Width是什么意思&#xff1f; CAN2.0协议中定义标称位速率为一理想的发送器在没有重新同步的情况…

2023年毫米波行业研究报告

第一章 行业概况 1.1 定义 毫米波是一种电磁波&#xff0c;其波长范围在1毫米至10毫米之间&#xff0c;频率介于30GHz至300GHz。与sub-6G (6GHz以下频段&#xff09;的5G系统相比&#xff0c;5G毫米波通信在带宽、时延和灵活弹性空口配置方面具有明显优势。这使其能够有效地满…

风车时间锁管理 - 构建IPA文件加锁+签名+管理一站式解决方案

时间锁管理&#xff1a;是一种用于控制对某些资源、功能或操作的访问权限的机制&#xff0c;它通过设定时间限制来限制对特定内容、系统或功能的访问或执行&#xff0c;以提高安全性和控制性&#xff0c;时间锁管理常见于以下场景&#xff1a; 1. 文件或文档的保密性&#xff…

STL list

文章目录 一、list 类的模拟实现 list 是一个带头双向循环链表&#xff0c;可以存储任意类型 模板参数 T 表示存储元素的类型&#xff0c;Alloc 是空间配置器&#xff0c;一般不用传 一、list 类的模拟实现 iterator 和 const_iterator 除了下述不同外&#xff0c;其他代码基…

优优嗨聚集团:抖音外卖转为区域代理,美团外卖是否胜利

在外卖市场日益激烈的竞争中&#xff0c;抖音和美团两大巨头都有着不同的策略。近期&#xff0c;抖音外卖宣布转为区域代理模式&#xff0c;而美团外卖则持续扩大市场份额。 外卖市场近年来呈现出爆炸性增长&#xff0c;成为消费者日常生活中不可或缺的一部分。根据艾媒咨询数据…