数据结构与算法(算法篇)

news2024/11/15 10:03:21

学数据结构与算法不是仅仅学算法本身(经验),而是学习思维(解决问题的能力)。

数据结构与算法(算法篇)

  • 1、算法的性能分析
    • 1.1 时间复杂度
    • 1.2 空间复杂度
    • 1.3 小结
  • 2、高精度
    • 2.1 高精度加法
    • 2.2 高精度减法
    • 2.3 高精度乘法
    • 2.4 高精度除法

1、算法的性能分析

衡量指标:时间复杂度、空间复杂度、稳定性

1.1 时间复杂度

用O()表示,本质就是:算法的计算次数。

O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < …

#include<iostream>
using namespace std;

void test01()
{
	// 时间复杂度:O(n)
	int n, ans = 0;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		ans++;
	}


	// 有效计算次数 / 实际时间复杂度:O(n+3)
	// 但当运行次数趋于无穷时候,3可以忽略不计
	// 时间复杂度:O(n)
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		ans++;
	}
	ans++;
	ans++;
	ans++;
}


void test02()
{
	// 有效计算次数 / 实际时间复杂度:O(n/2)
	// 但当运行次数趋于无穷时候,1/2可以忽略不计
	// 时间复杂度:O(n)
	int n, ans = 0;
	cin >> n;
	for (int i = 0; i < n; i+=2)
	{
		ans++;
	}
}

void test02()
{
	// 有效计算次数 / 实际时间复杂度:O(n/2)
	// 时间复杂度:O(n)
	int n, ans = 0;
	cin >> n;
	for (int i = 0; i < n; i += 2)
	{
		ans++;
	}
}

void test03()
{
	int n, ans = 0;
	cin >> n;
	// 时间复杂度:O(n^2)
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			ans++;
		}
	}


	// 有效计算次数 / 实际时间复杂度:O(n^2+n)
	// 但当运行次数趋于无穷时候,n相较于n^2,可以忽略不计
	// 时间复杂度:O(n^2)
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			ans++;
		}
	}
	for (int i = 0; i < n; i++)
	{
		ans++;
	}
}

int main()
{
	test01();
	system("pause");
	return 0;
}

1.2 空间复杂度

本质就是:额外产生的空间(不是数据本身)。
额外:也就是算法为了处理数据,额外使用的空间。
空间复杂度是和内存有关。
logn的空间比较少,n^2这种空间的嵌套也比较少。

O(1) < O(n) < O(nlogn)

#include<iostream>
using namespace std;

void test01()
{
	// 冒泡排序
	// 真实时间复杂度:O((n^2-n) / 2 + 2n)
	// 时间复杂度:O(n^2)
	// 空间复杂度:O(1)
	int a[101];
	int n;
	cin >> n;
	// 这里的a占用n个空间,属于输入,不算额外产生的空间
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
	}

	for (int i = n - 1; i > 0; i--)
	{
		for (int j = 0; j < n - 1; j++)
		{
			if (a[j] > a[j + 1])
			{
				// swap底层其实是有第三变量temp交换,但是是有穷的,算O(1)
				swap(a[j], a[j + 1]);
			}
		}
	}

	for (int i = 0; i < n; i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;
}

void test02()
{
	int n;
	cin >> n;
	// 使用了额外的空间,空间复杂度:0(n)
	int* arr = new int[n];	
	for (int i = n - 1; i > 0; i--)
	{
		for (int j = 0; j < n - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				swap(arr[j], arr[j + 1]);
			}
		}
	}

	delete[] arr;
}

int main()
{
	test01();
	system("pause");
	return 0;
}

1.3 小结

时间复杂度越高,程序运行的时间就越长。
时间和空间往往是相对的,一般,可能会用时间换空间,用空间换时间。

2、高精度

为什么要接触高精度?
主要就是:解决数据溢出的问题。
下方的代码,不同的整型是有取值范围的。

int main()
{
	// int型就是:-2^31 到 2^32-1
	// long long 是:-2^63 到 2^63-1
	int a, b;
	cin >> a >> b;
	cout << a + b << endl;
	system("pause");
	return 0;
}

还没等输入第二个变量的赋值,程序就结束了。低精度,就搞不定我们这种需求了。

在这里插入图片描述
高精度的本质就是使用字符进行计算。
但是,当发现使用字符串(字符数组)时,能够很好地解决这种数据溢出的问题,但是字符串之间不能够像数值计算那样,+号运算符重载也只是字符串拼接。

int main()
{
	string a, b;
	cin >> a >> b;
	cout << a + b << endl;
	system("pause");
	return 0;
}

在这里插入图片描述
但是,单个字符使用ASCII码,进行存储的,字符’1’是49,使用字符'1' - '0'就可以巧妙的得到数值1本身。

2.1 高精度加法

注意事项:
1、字符数组局部变量要初始化,可以写到全局变量(默认初始化为0)
2、高精度的本质就是使用字符进行计算,通过字符 - ‘0’ 转换为数值本身
3、使用两个字符数组相加,存放结果的数组的长度为这两个字符数组中的最长长度+1

#include<iostream>
using namespace std;
#include<string>

void string_to_int(string str, int dst[])
{
	for (int i = 0; i < str.size(); i++)
	{
		// "1234"
		// 要放置成
		// "4321"
		// 也就是第0个放在第3个,第1个放在第2个,
		// 如果有n个,第i个就是放在第n-i-1个
		dst[str.size() - i -1] = str[i] - '0';
	}
}


int main()
{
	//  "1234"
	// +"567"  
	// 个位不能够有效对齐,所以对字符数组取反,再计算
	//  "4321"
	// +"7650" 
	//  "1081"
	// 取反不影响计算,最后算完,再反转,得:1801就可以了。

	
	string s1, s2;
	// 字符数组局部变量要初始化,可以写到全局变量(默认初始化为0)
	int a[101]= { 0 };
	int b[101]= { 0 };
	int c[101]= { 0 };

	cin >> s1 >> s2;
	// 1.个位对齐(先反转),字符转整型
	string_to_int(s1, a);
	string_to_int(s2, b);
	for (int i = 0; i < s1.size(); i++)
	{
		cout << a[i];
	}
	cout << endl;
	for (int i = 0; i < s2.size(); i++)
	{
		cout << b[i];
	}
	cout << endl;

	// 2.对位相加,放到c数组中,但首先要计算c数组的长度
	// "9999" + "1"  是5位。所以两数相加,结果的最高的长度就是最长的数位数+1
	int c_len = max(s1.size(), s2.size()) + 1;

	// 3.对位相加,放到c数组中
	for (int i = 0; i < c_len; i++)
	{
		c[i] += a[i] + b[i];

		// 下一位进位
		c[i + 1] = c[i] / 10;

		// 两数相加可能会有进位,保留个位
		c[i] = c[i] % 10;
	}
		
		

	// 反转输出,比如相加为1801没有进位, 但是这样可能会造成c_len多1个0
	// 就会是18010
	// 从最后1位逐个判断首位是否是0,逐个去0
	while (c[c_len] == 0 && c_len>1)
	{
		c_len--;
	}
	// 倒叙打印
	for (int i = c_len; i >= 0; i--)
	{
		cout << c[i];
	}
	cout << endl;
	system("pause");
	return 0;
}

2.2 高精度减法

注意事项:
1、最好保证是的数 - 的数,最后加个负号就行;长度相同,就比字符串。
2、使用两个字符数组相减,存放结果的数组的长度为这两个字符数组中的最长长度
3、在字典序中,比较两个字符串时是逐字符进行的,每个字符的排序取决于其编码值。即使 s1 和 s2 的长度不同,只要 s1 的第一个字符在字典序中大于 s2 的第一个字符,结果就是 true。

#include<iostream>
using namespace std;
#include<string>

void string_to_int(string str, int dst[])
{
	for (int i = 0; i < str.size(); i++)
	{
		// "1234"
		// 要放置成
		// "4321"
		// 也就是第0个放在第3个,第1个放在第2个,
		// 如果有n个,第i个就是放在第n-i-1个
		dst[str.size() - i -1] = str[i] - '0';
	}
}

bool comStr(string str1, string str2)
{
	if (str1.size() != str2.size())
	{
		return str1.size() >= str2.size();
	}
	else
	{
		// 两个字符串长度相同,比大小
		return str1 >= str2;
	}
}

int main()
{
	int a[101] = { 0 };
	int b[101] = { 0 };
	int c[101] = { 0 };

	string s1, s2;
	cin >> s1 >> s2;
	if (comStr(s1, s2) == false)
	{
		// 如果s1比s2长度短,或s1 < s2,进行替换,这样就能保证大数减小数
		/*string temp = s1;
		s1 = s2;
		s2 = temp;*/
		swap(s1, s2);
	}

	// 1.反转
	string_to_int(s1, a);
	string_to_int(s2, b);

	// 2.对位相减,放到c数组中,但首先要计算c数组的长度
	// "9999" - "1"  是4位。所以两数相减,结果的最高的长度就是最长的数位数
	int c_len = max(s1.size(), s2.size());

	// 3.对位相减,放到c数组中
	//  "1234"
	//- "567"
	// 反转
	//   "4321"
	// - "765" 
	for (int i = 0; i < c_len; i++)
	{
		// 要判断个位够不够减
		if (a[i] < b[i])
		{
			a[i] += 10;  //借位
			a[i + 1]--;  // 高位减1
		}
		c[i] = a[i] - b[i];
	}
	while (c[c_len] == 0 && c_len > 0)
	{
		c_len--;
	}
	// 倒叙打印
	for (int i = c_len; i >= 0; i--)
	{
		cout << c[i];
	}
	cout << endl;
	system("pause");
	return 0;
}

2.3 高精度乘法

注意事项:
1、乘法里还是有加法的影子,找通项公式
2、使用两个字符数组相乘,存放结果的数组的长度最大为这两个字符数组中的长度之和
3、在字典序中,比较两个字符串时是逐字符进行的,每个字符的排序取决于其编码值。即使 s1 和 s2 的长度不同,只要 s1 的第一个字符在字典序中大于 s2 的第一个字符,结果就是 true。

#include<iostream>
using namespace std;
#include<string>

void string_to_int(string str, int* a)
{
	for (int i = 0; i < str.size(); i++)
	{
		// "1234"
		// 要放置成
		// "4321"
		// 也就是第0个放在第3个,第1个放在第2个,
		// 如果有n个,第i个就是放在第n-i-1个
		a[str.size() - i - 1] = str[i] - '0';
	}
}

int main()
{
	int a[201] = { 0 };
	int b[201] = { 0 };
	int c[201] = { 0 };

	string s1, s2;
	cin >> s1 >> s2;

	// 1.反转
	string_to_int(s1, a);
	string_to_int(s2, b);

	// 2.对位相乘,放到c数组中,但首先要计算c数组的长度
	// "1000" * "10"  是5位。所以两数相乘,结果的最高的长度就是最长的数位数
	int c_len = s1.size() + s2.size();

	// 3.对位相乘,放到c数组中
	//                 a3         a2        a1        a0
	//*                                     b1        b0
	//                 a3b0       a2b0     a1b0       a0b0
	//+       a3b1     a2b1       a1b1     a0b1
	// 
 	//对应     c[4]     c[3]     c[2]       c[1]        c[0]
 	//结合a和b的下标,规律就是:c[i+j] = a[i] + b[j]
	//      c[3+1]   c[3+0]  c[2+0]  c[1+0]   c[0+0]
	for (int i = 0; i < s1.size(); i++)
	{
		for (int j = 0; j < s2.size(); j++)
		{
			// 这里应该是累加
			// 比如:这里c[2] = a3b1 + a2b2  两项
			c[i + j] += a[i] * b[j];

			// 进位,也是累加,可能是多次进位
			c[i + j + 1] += c[i + j] / 10;
			
			// 留下个位
			c[i + j] = c[i + j] % 10;
			
		}
	}
	while (c[c_len] == 0 && c_len > 1)
	{
		c_len--;
	}
	// 倒叙打印
	for (int i = c_len; i >= 0; i--)
	{
		cout << c[i];
	}
	cout << endl;
	system("pause");
	return 0;
}

2.4 高精度除法

注意事项:
1、不用字符串取反
2、只用考虑够除余数就行。

#include<iostream>
using namespace std;


void string_to_int(string str, int* a)
{
	for (int i = 0; i < str.size(); i++)
	{
		// 将每个字符转换成数值
		a[i] = str[i] - '0';
	}
}
	

// 高精度除法(高精度 / 低精度)
int main()
{
	string s;
	int a[201] = { 0 };  // 高精度
	long long b;  // 低精度
	int c[201] = { 0 };  // 存放结果(高精度)

	int temp = 0;

	cin >> s;
	cin >> b;
	string_to_int(s, a);
	for (int i = 0; i < s.size(); i++)
	{
		c[i] = (temp * 10 + a[i]) / b;
		// 余数
		temp = (temp * 10 + a[i]) % b;
	}

	int lc = 0;
	while (c[lc] == 0 && lc < s.size())
	{
		lc++;
	}
	for (int i = 0; i < s.size(); i++)
	{
		cout << c[i];
	}
	cout << endl;
	system("pause");
	return 0;
}

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

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

相关文章

【鸿蒙学习】HarmonyOS应用开发者高级认证 - 一次开发,多端部署

一、学习目的 掌握鸿蒙的核心概念和端云一体化开发、数据、网络、媒体、并发、分布式、多设备协同等关键技术能力&#xff0c;具备独立设计和开发鸿蒙应用能力。 二、总体介绍 HarmonyOS 系统面向多终端提供了“一次开发&#xff0c;多端部署”&#xff08;后文中简称为“一…

win双击运行jar文件

常规运行&#xff1a;java -jar xxx.jar 方法一、 1、jar包右键属性 2、更改打开方式&#xff0c;设置为默认 选择打开方式使用 javaw.exe C:\Program Files\Java\jdk1.8.0_201\bin\javaw.exe 3、修改注册表 winr 中 regedit 打开注册表 计算机\HKEY_CLASSES_ROOT\Appli…

同态加密及HElib

一、实验原理 1.同态加密概念 同态加密是密码学领域自1978年以来的经典难题,也是实现数据隐私计算的关键技术,在云计算、区块链、隐私计算等领域均存在着广泛的应用需求和一些可行的应用方案。 同态加密(Homomorphic Encryption)是很早之前密码学界就提出来的一个Open Pr…

Java Web —— 第七天(Mybatis案例1)

环境搭建 准备数据库表(dept、emp) -- 部门管理 create table dept(id int unsigned primary key auto_increment comment 主键ID,name varchar(10) not null unique comment 部门名称,create_time datetime not null comment 创建时间,update_time datetime not null commen…

Ubuntu24.04安装MYSQL8.0

更新源 sudo apt update安装mysql服务 默认安装最新版本 sudo apt install mysql-server检查安装版本 mysql --version检查mysql运行状态 systemctl status mysql开启远程访问&#xff0c;在ubuntu下mysql默认是只允许本地访问 sudo vim /etc/mysql/mysql.conf.d/mysqld.…

jdbc连接池之C3P0

C3P0&#xff1a;JDBC 连接池概述 C3P0 是一个开源的 JDBC 连接池库&#xff0c;用于管理数据库连接的获取与释放。它提供了连接池的自动管理和高效复用&#xff0c;从而减少了创建数据库连接所需的时间和资源消耗。C3P0 的核心功能是优化和管理数据库连接&#xff0c;以提高应…

python : Requests请求库入门使用指南 + 简单爬取豆瓣影评

Requests 是一个用于发送 HTTP 请求的简单易用的 Python 库。它能够处理多种 HTTP 请求方法&#xff0c;如 GET、POST、PUT、DELETE 等&#xff0c;并简化了 HTTP 请求流程。对于想要进行网络爬虫或 API 调用的开发者来说&#xff0c;Requests 是一个非常有用的工具。在今天的博…

如何取消Excel表格的“打开密码”

在日常工作和学习中&#xff0c;Excel表格作为一种强大的数据处理工具&#xff0c;被广泛应用。而我们为了保护excel文件中的数据&#xff0c;会为Excel设置打开密码&#xff0c;但随着时间和需求的变化&#xff0c;当我们需要取消密码保护时要怎么做呢&#xff1f; 方法一&…

【前端基础篇】HTML零基础速通

文章目录 前言HTML结构认识HTML标签 HTML文件基本结构标签层次结构 快速生成代码框架HTML常见标签注释标签标题标签段落标签换行标签格式化标签图片标签超链接标签表格标签基本使用合并单元格 列表标签表单标签form标签input标签 label标签select标签textarea标签无语义标签 HT…

ESP RainMaker OTA 自动签名功能的安全启动

【如果您之前有关注乐鑫的博客和新闻&#xff0c;那么应该对 ESP RainMaker 及其各项功能有所了解。如果不曾关注&#xff0c;建议先查看相关信息&#xff0c;知晓本文背景。】 在物联网系统的建构中&#xff0c;安全性是一项核心要素。乐鑫科技对系统安全给予了极高的重视。ES…

AgileTC测试用例管理平台的基本使用

1. 背景 使用企业微信在线文档对测试用例进行管理&#xff0c;存在以下问题&#xff0c;比如新建版本&#xff0c;每次都要设置下执行测试用例那一栏&#xff0c;要提供通过、失败、阻塞等选项&#xff0c;操作异常繁琐&#xff0c;比如版本管理问题&#xff0c;多个版本之后&a…

【企业高性能web服务器】

目录 一、Nginx 介绍1、 Nginx 功能介绍2、基础特性3、Nginx 模块介绍 二、Nginx 编译安装1、编写systemd服务 三、平滑升级和回滚1、平滑升级的流程2、升级2、回滚 四、 Nginx 核心配置详解1、实现 nginx 的高并发配置2、Nginx 账户认证功能3、nginx作为下载服务器配置 五、re…

为了改善您的网络安全状况,请专注于数据

有效地融合、管理和使用企业数据是一项艰巨的任务。企业拥有大量的数据&#xff0c;但这些数据存在于各自为政的系统和应用程序中&#xff0c;需要高技能的数据科学家进行大量的手动操作。工程师和分析师从所有数据中提取价值。 数据准备是一项基本且必要的任务&#xff0c;但…

IOS 11 通用Base控制器封装

整体规划 BaseController&#xff1a;把viewDidLoad逻辑拆分为三个方法&#xff0c;方便管理。 BaseCommonController&#xff1a;不同项目可以复用的逻辑&#xff0c;例如&#xff1a;设置背景颜色方法等 BaseLogicController&#xff1a;本项目的通用逻辑&#xff0c;主要…

Spring的核心启动流程

前言 Spring启动时候整个入口是这么一个方法 AbstractApplicationContext#refresh 总共有12个方法&#xff0c;也就是启动时的核心步骤 AbstractApplicationContext有众多实现&#xff0c;这里我选择SpringBoot Web应用默认的实现来讲 AnnotationConfigServletWebServerAppli…

MySQL系列—MySQL编译安装常见问题(或缺少依赖)及解决方法,MySQL 编译安装时需要安装的依赖(全)

MySQL系列—MySQL编译安装常见问题(或缺少依赖)及解决方法 MySQL 编译安装时需要安装的依赖(全)&#xff1a; yum install -y cmake yum install ncurses ncurses-devel -y yum install -y libarchive yum install -y gcc gcc-c yum install -y openssl openssl-devel yum inst…

Python.NET:打开Python与.NET世界互通的大门

Python.NET 是一个强大的工具&#xff0c;它为 Python 程序员提供了一种与 .NET 公共语言运行时 (CLR) 无缝集成的途径。它就像一座桥梁&#xff0c;将 Python 的灵活性与 .NET 的强大功能连接起来&#xff0c;为开发者提供了前所未有的自由和可能性。 1. Python.NET 的核心价值…

内核代码中的路障宏

路障宏包含&#xff1a; mb()/rmb()/wmb barrier() __asm__:GCC关键字&#xff0c;用来声明一个内联汇编表达式。 __volatile__:告诉编译器&#xff0c;不要优化后面表达式中的内联汇编代码。 内联汇编表达式中的(“memory”):告诉GCC如下两个条件 If your assembler ins…

Flink常见数据源开发(DataStream API)

前言 一个 Flink 程序,其实就是对 DataStream 的各种转换。具体来说,代码基本上都由以下几部分构成,如下图所示: 获取执行环境(execution environment)读取数据源(source)定义基于数据的转换操作(transformations)定义计算结果的输出位置(sink)触发程序执行(exec…

Veritas NBU8.3.0.2安装Media Server(篇三)

一、环境自检阶段 1、Media角色地址为192.168.189.3&#xff0c;计算机名称为bakmedia&#xff0c;域名为sszz.com 2、防火墙均已关闭 二、hosts解析配置 在安装之前需要在hosts文件中配置解析&#xff0c;master和media都需要配置&#xff1b;后期如果备份客户端也需要为客户…