【简化程序设计】C++STL“容器适配器“之栈和队列

news2025/2/24 15:18:50

【STL】容器适配器之栈和队列

  • stack的介绍和使用
    • stack的介绍
    • stack的使用
    • stack的模拟实现
  • queue的介绍和使用
    • queue的介绍
    • queue的使用
    • queue的模拟实现
  • priority_queue的介绍和使用
    • priority_queue的介绍
    • priority_queue的使用
    • priority_queue的模拟实现
  • 容器适配器
    • 什么是容器适配器?
    • STL标准库中stack和queue的底层结构
    • deque的原理介绍
    • deque的缺陷
    • 为什么选择deque作为stack和queue的底层默认容器
  • 🍀小结🍀

🎉博客主页:小智_x0___0x_

🎉欢迎关注:👍点赞🙌收藏✍️留言

🎉系列专栏:C++初阶

🎉代码仓库:小智的代码仓库

【本节目标】:

  1. stack的介绍和使用
  2. stack的模拟实现
  3. queue的介绍和使用
  4. queue的模拟实现
  5. priority_queue的介绍和使用
  6. priority_queue的模拟实现
  7. 容器适配器
  8. deuqe的介绍

stack的介绍和使用

stack的介绍

官方文档:
在这里插入图片描述
谷歌翻译:

  1. stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行 元素的插入与提取操作。
  2. stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定 的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。
  3. stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下 操作:
    • empty:判空操作
    • back:获取尾部元素操作
    • push_back:尾部插入元素操作
    • pop_back:尾部删除元素操作
  4. 标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器, 默认情况下使用deque。
    在这里插入图片描述

stack的使用

官网文档:
在这里插入图片描述

函数说明接口说明
stack()构造空的栈
empty()检测stack是否为空
size()返回stack中元素的个数
top()返回栈顶元素的引用
push()将元素val压入stack中
pop()将stack中尾部的元素弹出
int main()
{
	csdn::stack<int> st;
	st.push(1);
	st.push(2);
	st.push(3);
	st.push(4);

	while (!st.empty())
	{
		cout << st.top() << " ";
		st.pop();
	}
	cout << endl;
	return 0;
}

运行结果:
在这里插入图片描述

stack的模拟实现

#include<vector>
#include<list>
#include<deque>

namespace csdn
{
	// 容器适配器
	template<class T, class Container = deque<T>>  // 定义一个类模板,T表示元素类型,Container表示底层容器类型,默认为deque<T>
	class stack
	{
	public:
		void push(const T& x)  // 将元素x入栈
		{
			_con.push_back(x);  // 在底层容器的尾部插入元素x
		}

		void pop()  // 将栈顶元素出栈
		{
			_con.pop_back();  // 删除底层容器的尾部元素
		}

		T& top()  // 返回栈顶元素的引用
		{
			return _con.back();  // 返回底层容器的尾部元素的引用
		}

		size_t size()  // 返回栈中元素的个数
		{
			return _con.size();  // 返回底层容器的元素个数
		}

		bool empty()  // 判断栈是否为空
		{
			return _con.empty();  // 判断底层容器是否为空
		}
	private:
		Container _con;  // 底层容器对象
	};
}

上面实现了一个栈容器适配器,使用了模板类的特性,可以适配不同类型的元素和底层容器类型,其中底层容器默认使用deque。栈的基本操作包括入栈(push)、出栈(pop)、获取栈顶元素(top)、获取栈中元素个数(size)和判断是否为空(empty)。这些操作都是通过底层容器的相应操作实现的。

queue的介绍和使用

queue的介绍

官方文档:
在这里插入图片描述
谷歌翻译:

  1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端 提取元素。
  2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的 成员函数来访问其元素。元素从队尾入队列,从队头出队列。
  3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操 作:
    • empty:检测队列是否为空
    • size:返回队列中有效元素的个数
    • front:返回队头元素的引用
    • back:返回队尾元素的引用
    • push_back:在队列尾部入队列
    • pop_front:在队列头部出队列
  4. 标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标
    准容器deque。
    在这里插入图片描述

queue的使用

在这里插入图片描述

函数声明接口说明
queue()构造空的队列
empty()检测队列是否为空,是返回true,否则返回false
size()返回队列中有效元素的个数
front()返回队头元素的引用
back()返回队尾元素的引用
push()在队尾将元素val入队列
pop()将队头元素出队列
int main()
{
	csdn::queue<int> q;
	q.push(1);
	q.push(2);
	q.push(3);
	q.push(4);

	while (!q.empty())
	{
		cout << q.front() << " ";
		q.pop();
	}
	cout << endl;

	return 0;
}

运行结果:
在这里插入图片描述

queue的模拟实现

#include<vector>
#include<list>
#include<deque>

namespace csdn
{
	// 容器适配器
	template<class T, class Container = deque<T>>  // 定义一个类模板,T表示元素类型,Container表示底层容器类型,默认为deque<T>
	class queue
	{
	public:
		void push(const T& x)  // 将元素x入队
		{
			_con.push_back(x);  // 在底层容器的尾部插入元素x
		}

		void pop()  // 将队头元素出队
		{
			_con.pop_front();  // 删除底层容器的头部元素
			//_con.erase(_con.begin());  // 另一种实现方式,删除底层容器的第一个元素
		}

		T& front()  // 返回队头元素的引用
		{
			return _con.front();  // 返回底层容器的头部元素的引用
		}

		T& back()  // 返回队尾元素的引用
		{
			return _con.back();  // 返回底层容器的尾部元素的引用
		}

		size_t size()  // 返回队列中元素的个数
		{
			return _con.size();  // 返回底层容器的元素个数
		}

		bool empty()  // 判断队列是否为空
		{
			return _con.empty();  // 判断底层容器是否为空
		}
	private:
		Container _con;  // 底层容器对象
	};
}

上面实现了一个队列容器适配器,使用了模板类的特性,可以适配不同类型的元素和底层容器类型,其中底层容器默认使用deque。队列的基本操作包括入队(push)、出队(pop)、获取队头元素(front)、获取队尾元素(back)、获取队列中元素个数(size)和判断是否为空(empty)。这些操作都是通过底层容器的相应操作实现的。

priority_queue的介绍和使用

priority_queue的介绍

官方文档:
在这里插入图片描述
谷歌翻译:

  1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。 2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元
    素)。
  2. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特 定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
  3. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭 代器访问,并支持以下操作:
    • empty():检测容器是否为空
    • size():返回容器中有效元素个数
    • front():返回容器中第一个元素的引用
    • push_back():在容器尾部插入元素
    • pop_back():删除容器尾部元素
  4. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指 定容器类,则使用vector。
  5. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数 make_heap、push_heap和pop_heap来自动完成此操作。

priority_queue的使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成
堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:
默认情况下priority_queue是大堆。

函数声明接口说明
priority_queue()/priority_queue(first,last)构造一个空的优先级队列
empty( )检测优先级队列是否为空,是返回true,否则返回false
top( )返回优先级队列中最大(最小元素),即堆顶元素
push(x)在优先级队列中插入元素x
pop()删除优先级队列中最大(最小)元素,即堆顶元素

【注意】

  1. 默认情况下,priority_queue是大堆。
#include <vector>
#include <queue>
#include <functional> // greater算法的头文件
void TestPriorityQueue()
{
	// 默认情况下,创建的是大堆,其底层按照小于号比较
	vector<int> v{ 3,2,7,6,0,4,1,9,8,5 };
	priority_queue<int> q1;
	for (auto& e : v)
		q1.push(e);
	cout << q1.top() << endl;
	// 如果要创建小堆,将第三个模板参数换成greater比较方式
	priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());
	cout << q2.top() << endl;
}
  1. 如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供> 或者< 的重载。
void test_priority_queue1()
{
	
	csdn::priority_queue<int, vector<int>, Greater<int>> pq;

	pq.push(3);
	pq.push(5);
	pq.push(1);
	pq.push(4);

	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;
}

运行结果:
在这里插入图片描述

priority_queue的模拟实现

#include<vector>
#include<functional>

// 仿函数/函数对象
template<class T>
class Less
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x < y;
	}
};

template<class T>
class Greater
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x > y;
	}
};

namespace csdn
{
	template<class T, class Container = vector<T>, class Comapre = less<T>>  // 定义一个类模板,T表示元素类型,Container表示底层容器类型,默认为vector<T>,Compare表示比较函数对象,默认为less<T>(小根堆)
	class priority_queue
	{
	private:
		void AdjustDown(int parent)  // 向下调整堆
		{
			Comapre com;  // 定义比较函数对象

			size_t child = parent * 2 + 1;  // 左孩子下标
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && com(_con[child],_con[child + 1]))  // 如果右孩子存在且比左孩子大
				{
					++child;  // 将child指向右孩子
				}

				if (com(_con[parent],_con[child]))  // 如果父节点比孩子节点小
				{
					swap(_con[child], _con[parent]);  // 交换父节点和孩子节点的值
					parent = child;  // 更新parent和child的值,继续向下调整
					child = parent * 2 + 1;
				}
				else
				{
					break;  // 如果父节点已经比孩子节点大了,退出循环
				}
			}	
		}

		void AdjustUp(int child)  // 向上调整堆
		{
			Comapre com;  // 定义比较函数对象

			int parent = (child - 1) / 2;  // 父节点下标
			while (child > 0)
			{
				if (com(_con[parent],_con[child]))  // 如果父节点比孩子节点小
				{
					swap(_con[child], _con[parent]);  // 交换父节点和孩子节点的值
					child = parent;  // 更新parent和child的值,继续向上调整
					parent = (child - 1) / 2;
				}
				else
				{
					break;  // 如果父节点已经比孩子节点大了,退出循环
				}
			}
		}


	public:
		priority_queue()  // 默认构造函数
		{}

		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)  // 构造函数,接受两个迭代器作为参数,用于初始化堆
		{
			while (first != last)
			{
				_con.push_back(*first);  // 将[first, last)区间内的元素插入到底层容器中
				++first;
			}

			// 建堆
			for (int i = (_con.size()-1-1)/2; i >= 0; i--)  // 从最后一个非叶子节点开始向下调整堆
			{
				AdjustDown(i);
			}
		}

		void pop()  // 弹出堆顶元素
		{
			swap(_con[0], _con[_con.size() - 1]);  // 将堆顶元素和最后一个元素交换
			_con.pop_back();  // 删除最后一个元素

			AdjustDown(0);  // 向下调整堆,使其重新成为堆
		}

		void push(const T& x)  // 插入元素x到堆中
		{
			_con.push_back(x);  // 将元素x插入到底层容器的尾部

			AdjustUp(_con.size() - 1);  // 向上调整堆,使其重新成为堆
		}

		const T& top()  // 返回堆顶元素的引用
		{
			return _con[0];
		}

		bool empty()  // 判断堆是否为空
		{
			return _con.empty();
		}

		size_t size()  // 返回堆中元素的个数
		{
			return _con.size();
		}
	private:
		Container _con;  // 底层容器对象
	};
}

上面实现了一个优先队列容器适配器,使用了模板类的特性,可以适配不同类型的元素、底层容器类型和比较函数对象。其中底层容器默认使用vector。优先队列的基本操作包括插入(push)、弹出堆顶元素(pop)、获取堆顶元素(top)、判断是否为空(empty)和获取堆中元素个数(size)。这些操作都是通过调整堆来实现的,其中AdjustDown函数用于向下调整堆,AdjustUp函数用于向上调整堆。

容器适配器

什么是容器适配器?

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总
结),该种模式是将一个类的接口转换成客户希望的另外一个接口。
在这里插入图片描述

STL标准库中stack和queue的底层结构

虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配
器,这是因为stack和队列只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque,比如:
在这里插入图片描述

deque的原理介绍

deque(双端队列):是一种双开口的"连续"空间的数据结构, 双开口的含义是:可以在头尾两端进行插入和 删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比 较高。
在这里插入图片描述
deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示:
在这里插入图片描述
双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落
在了deque的迭代器身上
,因此deque的迭代器设计就比较复杂,如下图所示:

在这里插入图片描述
那deque是如何借助其迭代器维护其假想连续的结构呢?
在这里插入图片描述

deque的缺陷

与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。
与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。
但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到 某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作 为stack和queue的底层数据结构。

为什么选择deque作为stack和queue的底层默认容器

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可 以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据结构,只要具有 push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stack和 queue默认选择deque作为其底层容器,主要是因为:1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。 2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长 时,deque不仅效率高,而且内存使用率高。结合了deque的优点,而完美的避开了其缺陷。

🍀小结🍀

今天我们认识了【C++STL】"容器适配器"之栈和队列相信大家看完有一定的收获。
种一棵树的最好时间是十年前,其次是现在! 把握好当下,合理利用时间努力奋斗,相信大家一定会实现自己的目标!加油!创作不易,辛苦各位小伙伴们动动小手,三连一波💕💕~~~,本文中也有不足之处,欢迎各位随时私信点评指正!
本篇代码已上传gitee仓库

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

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

相关文章

【数据分享】1999—2021年地级市文化/体育/卫生/社会保障等公共服务相关指标(Excel/Shp格式)

1999-2021年地级市的人口相关数据、各类用地面积数据、污染物排放和环境治理相关数据、房地产投资情况和商品房销售面积、社会消费品零售总额和年末金融机构存贷款余额、地方一般公共预算收支状况、工业企业数、固定资产投资和对外经济贸易数据、科技创新指标数据、劳动力就业及…

一起学算法(枚举篇)

概念 枚举&#xff1a;就是把满足题目条件的所有情况都列举出来&#xff0c;然后一一判断寻找最优解的过程 1.最值问题 1.两个数的最值问题 两个数的最小值&#xff0c;利用Java的运算符就可以实现 int Min(int a,int b){return a<b?a:b; } 2.n个数的最值问题 当有n…

禁用右键菜单AMD Software: Adrenalin Edition

本文参考链接&#xff1a; 删除win11右键一级菜单的AMD驱动栏 - 哔哩哔哩 windows11 求助删除右键菜单方法_windows11吧_百度贴吧 Windows安装最新的AMD显卡驱动后&#xff0c;右键菜单会多出AMD Software: Adrenalin Edition。使用一些右键菜单管理工具也没能屏蔽禁用掉该功…

机器学习---线性回归、多元线性回归、代价函数

1. 线性回归 回归属于有监督学习中的一种方法。该方法的核心思想是从连续型统计数据中得到数学模型&#xff0c;然后 将该数学模型用于预测或者分类。该方法处理的数据可以是多维的。 回归是由达尔文的表兄弟Francis Galton发明的。Galton于1877年完成了第一次回归预测&…

数据分析基础-Excel图表的美化操作(按照教程一步步操作)

一、原始数据 包含月份和对应的销量和产量。 时间销量产量1月60722月38673月28344月58685月67596月72357月61428月24319月556710月243511月122112月2645 二、原始的图表设计-采用Excel自带模板 三、优化思路 1、删除多余元素 2、弱化次要元素 对于可以弱化的元素&#xff0c…

NASM汇编

1. 前置知识 1. 汇编语言两种风格 intel&#xff1a;我们学的NASM就属于Intel风格AT&T&#xff1a;GCC后端工具默认使用这种风格&#xff0c;当然我们也可以加选项改成intel风格 2. 代码 1. 段分布 .text: 存放的是二进制机器码&#xff0c;只读.data: 存放有初始化的…

弯道超车必做好题锦集一(C语言选择题)

前言&#xff1a; 编程想要学的好&#xff0c;刷题少不了&#xff0c;我们不仅要多刷题&#xff0c;还要刷好题&#xff01;为此我开启了一个弯道超车必做好题锦集的系列&#xff0c;每篇大约10题左右。此为第一篇选择题篇&#xff0c;该系列会不定期更新&#xff0c;后续还会…

《EalsticSearch从入门到实战》-CRUD+JAVA常用操作

目录 《EalsticSearch从入门到实战》 windows环境安装elasticsearchkibana并完成JAVA客户端查询《EalsticSearch从入门到实战》-CRUDJAVA常用操作 前言 上一篇《windows环境安装elasticsearchkibana并完成JAVA客户端查询》中我们已经完成了EalsticSearchKibana环境的安装&a…

页面生成图片或PDF node-egg

没有特别的幸运&#xff0c;那么就特别的努力&#xff01;&#xff01;&#xff01; 中间件&#xff1a;页面生成图片 node-egg 涉及到技术node egg Puppeteer 解决文书智能生成多样化先看效果环境准备初始化项目 目录结构核心代码 完整代码https://gitee.com/hammer1010_ad…

web自动化测试,定位不到元素的原因及解决方案

1.动态id定位不到元素 分析原因&#xff1a;每次打开页面&#xff0c;ID都会变化。用ID去找元素&#xff0c;每次刷新页面ID都会发生变化。 解决方案&#xff1a;推荐使用xpath的相对路径方法或者cssSelector查找到该元素。        2.iframe原因定位不到元素 分析原因…

SQL注入之布尔盲注

SQL注入之布尔盲注 一、布尔盲注介绍二、布尔盲注的特性三、布尔盲注流程3.1、确定注入点3.2、判断数据库的版本3.3、判断数据库的长度3.4、猜解当前数据库名称&#xff08;本步骤需要重复&#xff09;3.5、猜解数据表的数量3.6、猜解第一个数据表名称的长度3.7、猜解第一个数据…

【React】关于组件之间的通讯

&#x1f31f;组件化&#xff1a;把一个项目拆成一个一个的组件&#xff0c;为了便与开发与维护 组件之间互相独立且封闭&#xff0c;一般而言&#xff0c;每个组件只能使用自己的数据&#xff08;组件状态私有&#xff09;。 如果组件之间相互传参怎么办&#xff1f; 那么就要…

Python 算法交易实验65 算法交易二三事

说明 对算法交易的一些内容做一些回顾和反思吧。 老规矩&#xff0c;先chat一下 道理说的都对&#xff0c;如果要补充就推荐再看一本书量化交易:如何建立自己的算法交易事业,我觉得这样就比较完整了。 简单来说&#xff0c;把量化当成事业&#xff0c;而不是一种投机&#…

一起学算法(计算排序篇)

概念&#xff1a; 计数排序&#xff08;Counting sort&#xff09;是一个非基于比较稳定的线性时间的排序算法 非基于比较&#xff1a;之前学的排序都是通过比较数据的大小来实现有序的&#xff0c;比如希尔排序等&#xff0c;而计数排序不需要比较数据的大小而进行排序&…

数据结构:谈快速排序的多种优化和非递归展开,以及排序思想归纳

文章目录 写在前面快速排序的基本体系快速排序的优化快速排序的非递归实现排序分类总结插入排序选择排序交换排序归并排序 写在前面 快速排序作为效率相当高的排序算法&#xff0c;除了对于特殊数据有其一定的局限性&#xff0c;在大多数应用场景中都有它特有的优势和应用&…

PHP8的数据类型转换-PHP8知识详解

什么是数据类型转换&#xff1f; 答&#xff1a;数据从一个类型转换成另外一个类型&#xff0c;就是数据类型转换。 在PHP8中&#xff0c;变量的类型就是由赋值决定的&#xff0c;也就是说&#xff0c;如果 string 赋值给 $var&#xff0c;然后 $var 的类型就是 string。之后…

Python:给MySQL创建1000张表和创建1张有50个字段的表

1、创建1000张表 import pymysqldbhost "10.1.1.143" dbuser "root" dbpassword "123456" dbname "demo_cg1000" dbport 3306 dbconn pymysql.connect(hostdbhost, userdbuser, passworddbpassword, dbdbname, portdbport)mycu…

前端学习--vue2--2--vue指令基础

写在前面&#xff1a; 前置内容 - vue配置 文章目录 插值表达式v-html条件渲染v-show和v-ifv-ifv-if的扩展标签复用组件 v-show v-on /事件v-bind /&#xff1a;属性v-modelv-for 循环元素v-slotv-prev-cloak vue指令只的是带有v-前缀的特殊标签属性 插值表达式 插值表达式{…

【MATLAB第62期】基于MATLAB的PSO-NN、BBO-NN、前馈神经网络NN回归预测对比

【MATLAB第62期】基于MATLAB的PSO-NN、BBO-NN、前馈神经网络NN回归预测对比 一、数据设置 1、7输入1输出 2、103行样本 3、80个训练样本&#xff0c;23个测试样本 二、效果展示 NN训练集数据的R2为&#xff1a;0.73013 NN测试集数据的R2为&#xff1a;0.23848 NN训练集数据的…

【机器学习】Feature Engineering and Polynomial Regression

Feature Engineering and Polynomial Regression 1. 多项式特征2. 选择特征3. 缩放特征4. 复杂函数附录 首先&#xff0c;导入所需的库&#xff1a; import numpy as np import matplotlib.pyplot as plt from lab_utils_multi import zscore_normalize_features, run_gradien…