从C语言到C++_20(仿函数+优先级队列priority_queue的模拟实现+反向迭代器)

news2024/11/27 9:55:40

目录

1. priority_queue的模拟实现

1.1 未完全的priority_queue

1.2 迭代器区间构造和无参构造

1.3 仿函数的介绍和使用

1.4 完整priority_queue代码:

1.5 相关笔试选择题

答案:

2. 反向迭代器

2.1 反向迭代器的普通实现

reverse_iterator.h(不对称版)

2.2 反向迭代器的对称实现

reverse_iterator.h 

list.h :

vector.h

3.  迭代器的功能分类

本章完。


1. priority_queue的模拟实现

默认情况下的priority_queue是大堆,我们先不考虑用仿函数去实现兼容大堆小队排列问题,

我们先去实现大堆,先把基本的功能实现好,带着讲解完仿函数后再去进行优化实现。

优先级队列相较于普通的队列,其区别主要是在 push 和 pop 上

即需要在插入 / 删除数据的同时,增添调整的功能,其也是对适配器的封装,

push 和 pop 需要上调和下调操作,我们不可避免地需要实现上调和下调的算法。

我们这里暂时写死,设计成大堆的 adjust_up 和 adjust_down:

对调整算法不熟悉的可以看这里;

数据结构与算法⑪(第四章_中)堆的分步构建_GR C的博客-CSDN博客

1.1 未完全的priority_queue

入队时将数据插入后,从最后一个元素开始进行上调。

出队时采用堆的删除手法,将根元素(即队头)和最后一个元素进行交换,并调用尾删掉队头。

既然用了头尾交换法,我们就不得不对队列重新调整,所以从根位置进行下调,即可完成出队。

除了插入和删除,其它接口和queue差不多,PriorityQueue.h:

#pragma once
#include <iostream>
#include <vector> 
#include <algorithm>
#include <functional> // greater和less的头文件
using namespace std;

namespace rtx
{
	template<class T, class Container = vector<T>>
	class priority_queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
			adjust_up(_con.size() - 1);
		}

		void adjust_up(size_t child)
		{
			size_t father = (child - 1) / 2;
			while (child > 0)
			{
				if (_con[father] < _con[child])
				{
					swap(_con[father], _con[child]);
					child = father;// 交换后向上走
					father = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

		void pop()
		{
			std::swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();

			adjust_down(0);
		}

		void adjust_down(size_t father)
		{
			size_t child = father * 2 + 1;// 默认左孩子大
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && _con[child] < _con[child + 1])//先考虑右孩子是否存在
				{
					child += 1;
				}
				if (_con[child] > _con[father])
				{
					swap(_con[child], _con[father]);
					father = child;
					child = father * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

		const T& top()
		{
			return _con[0];
		}

		bool empty()  const
		{
			return _con.empty();
		}

		size_t size() const
		{
			return _con.size();
		}

	private:
		Container _con;
	};
}

测试一下,Test.c:

#include "PriorityQueue.h"

void test_priority_queue()
{
	rtx::priority_queue<int> pq;

	pq.push(3);
	pq.push(1);
	pq.push(2);
	pq.push(5);
	pq.push(0);
	pq.push(8);
	pq.push(1);

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

int main()
{
   test_priority_queue();
    
    return 0;
}

1.2 迭代器区间构造和无参构造

我们前面没写无参构造代码也跑出来了,先写下迭代器区间构造,和以前一样;

但这里不要直接push,push的时间是O(N*logN),建堆的时间是logN:

		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
			:_con(first,last) // 初始化列表初始化代替下面被注释的
		{
			//while (first != last)
			//{
			//	_con.push_back(*first);
			//	++first;
			//}
			for (int i = (_con.size() - 1 - 1) / 2;i >= 0;--i)
			{
				adjust_down(i);
			}
		}

这样的话运行代码就发现前面的无参构造报错了:没有默认的构造函数使用。

所以我们还要写上无参构造,但实际里面什么都不用写:

		priority_queue()
		{}

 测试一下,Test.c:

#include "PriorityQueue.h"

void test_priority_queue()
{
	rtx::priority_queue<int> pq;

	pq.push(3);
	pq.push(1);
	pq.push(2);
	pq.push(5);
	pq.push(0);
	pq.push(8);
	pq.push(1);

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

	int arr[] = { 0,3,2,1,6,5,4,9,8,7 };
	rtx::priority_queue<int> heap(arr, arr + sizeof(arr) / sizeof(arr[0]));
	while (!heap.empty())
	{
		cout << heap.top() << " ";
		heap.pop();
	}
	cout << endl;
}

int main()
{
   test_priority_queue();
    
    return 0;
}

 我们写死的最大问题是 —— 如果想按升序排列,就没办法排了。

我们这里写的用来排降序的大堆,如果想排升序我们需要写小堆,

C++ 在这里有一种叫仿函数的东西,可以控制这些东西,我们下面先来介绍一下仿函数。

1.3 仿函数的介绍和使用

概念:使一个类的使用看上去像一个函数。可以像函数一样使用的对象,称为函数对象。

其实现就是在类中重载 operator(),使得该类具备类似函数的行为,就是一个仿函数类了。

C语言优先级,() 圆括号使用形式为 表达式 或 "作为函数形参列表的括号" 

写一个最简单的仿函数:

#include<iostream>
using namespace std;

namespace rtx
{
	struct less
	{
		bool operator()(const int& left, const int& right) const
		{
			return left < right;
		}
	};
}

int main()
{
	int a = 10, b = 20;

	rtx::less lessCmp;

	cout << lessCmp(a, b) << endl;

	return 0;
}

仿函数(函数对象)—— 对象可以像调用函数一样去使用

我们还可以使其能够支持泛型,我们加一个 template 模板,加一个 greater 仿函数:

#include<iostream>
using namespace std;

namespace rtx
{
	template<class T>
	struct less
	{
		bool operator()(const T& left, const T& right) const
		{
			return left < right;
		}
	};

	template<class T>
	struct greater
	{
		bool operator()(const T& left, const T& right) const
		{
			return left > right;
		}
	};
}

int main()
{
	int a = 10, b = 20;

	rtx::less<int> lessCmp;
	cout << lessCmp(a, b) << endl;

	rtx::greater<int> gtCmp;
	cout << gtCmp(a, b) << endl;

	return 0;
}

 一个对象能像函数一样用,看上去很神奇,实际上调用的只是我们重载的 operator() 而已。

我们在C阶段不是学过函数指针么,用函数指针不行吗?

函数指针确实可以做到这些功能。但是在这里函数指针跟仿函数比,一点都方便。

在 C++ 里其实是非常不喜欢使用函数指针的,函数的指针在一些地方用起来非常复杂。

所以巨佬才发明了仿函数等,代替函数指针。

1.4 完整priority_queue代码:

我们现在用仿函数去比较那些数据(在模板再加一个参数),这样就不会写死了。

直接用库的less,PriorityQueue.h:

#pragma once
#include <iostream>
#include <vector> 
#include <algorithm>
#include <functional> // greater和less的头文件
using namespace std;

namespace rtx
{
	// Compare进行比较的仿函数 less->大堆
	// Compare进行比较的仿函数 greater->小堆
	template<class T, class Container = vector<T>,class Compare = less<T>>
	class priority_queue
	{
	public:
		priority_queue()
		{}

		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
			:_con(first,last) // 初始化列表初始化代替下面被注释的
		{
			//while (first != last)
			//{
			//	_con.push_back(*first);
			//	++first;
			//}
			for (int i = (_con.size() - 1 - 1) / 2;i >= 0;--i)
			{
				adjust_down(i);
			}
		}

		void push(const T& x)
		{
			_con.push_back(x);
			adjust_up(_con.size() - 1);
		}

		void adjust_up(size_t child)
		{
			Compare cmp;

			size_t father = (child - 1) / 2;
			while (child > 0)
			{
				//if (_con[father] < _con[child])
				if (cmp(_con[father], _con[child]))//仿函数想函数一样使用
				{
					swap(_con[father], _con[child]);
					child = father;// 交换后向上走
					father = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

		void pop()
		{
			std::swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();

			adjust_down(0);
		}

		void adjust_down(size_t father)
		{
			Compare cmp;
			size_t child = father * 2 + 1;// 默认左孩子大
			while (child < _con.size())
			{
				//if (child + 1 < _con.size() && _con[child] < _con[child + 1])
				if (child + 1 < _con.size() && cmp(_con[child], _con[child + 1]))//先考虑右孩子是否存在
				{
					child += 1;
				}
				//if (_con[child] > _con[father]) 这里写了大于,所以是不好的(下次一定)
				if (cmp(_con[father], _con[child]))
				{
					swap(_con[child], _con[father]);
					father = child;
					child = father * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

		const T& top()
		{
			return _con[0];
		}

		bool empty()  const
		{
			return _con.empty();
		}

		size_t size() const
		{
			return _con.size();
		}

	private:
		Container _con;
	};
}

测试,直接传greater<int> 建个小堆康康:

#include "PriorityQueue.h"

void test_priority_queue()
{
	rtx::priority_queue<int,vector<int>, greater<int>> pq;

	pq.push(3);
	pq.push(1);
	pq.push(2);
	pq.push(5);
	pq.push(0);
	pq.push(8);
	pq.push(1);

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

	int arr[] = { 0,3,2,1,6,5,4,9,8,7 };
	rtx::priority_queue<int, vector<int>, greater<int>> heap(arr, arr + sizeof(arr) / sizeof(arr[0]));
	while (!heap.empty())
	{
		cout << heap.top() << " ";
		heap.pop();
	}
	cout << endl;
}

int main()
{
   test_priority_queue();
    
    return 0;
}

 值得一提的是我们这里传的是类型,因为是类模板,

sort里传的是对象(用匿名对象就好),是函数模板:

1.5 相关笔试选择题

1. STL中的priority_queue使用的底层数据结构是什么( )

A.queue

B.heap

C.deque

D.vector

2. 下列代码的运行结果是( )

int main()
{
    priority_queue<int> a;
    priority_queue<int, vector<int>, greater<int> > c;
    priority_queue<string> b;
    for (int i = 0; i < 5; i++)
    {
        a.push(i);
        c.push(i);
    }
    while (!a.empty())
    {
        cout << a.top() << ' ';
        a.pop();
    }
    cout << endl;
    while (!c.empty())
    {
        cout << c.top() << ' ';
        c.pop();
    }
    cout << endl;
    b.push("abc");
    b.push("abcd");
    b.push("cbd");
    while (!b.empty())
    {
        cout << b.top() << ' ';
        b.pop();
    }
    cout << endl;
    return 0;
}

A.4 3 2 1 0 0 1 2 3 4 cbd abcd abc

B.0 1 2 3 4 0 1 2 3 4 cbd abcd abc

C.4 3 2 1 0 4 3 2 1 0 abc abcd cbd

D.0 1 2 3 4 4 3 2 1 0 cbd abcd abc

3. 以下说法正确的是( )

A.deque的存储空间为连续空间

B.list迭代器支持随机访问

C.如果需要高效的随机存取,还要大量的首尾的插入删除则建议使用deque

D.vector容量满时,那么插入元素时只需增加当前元素个数的内存即可

4. 仿函数比起一般函数具有很多优点,以下描述错误的是( )

A.在同一时间里,由某个仿函数所代表的单一函数,可能有不同的状态

B.仿函数即使定义相同,也可能有不同的类型

C.仿函数通常比一般函数速度快

D.仿函数使程序代码变简单

5. 假设cont是一个Container 的示例,里面包含数个元素,那么当CONTAINER为:

1.vector 2.list 3.deque 会导致下面的代码片段崩溃的Container 类型是( )

int main()
{
	Container cont = { 1, 2, 3, 4, 5 };
	Container::iterator iter, tempIt;
	for (iter = cont.begin(); iter != cont.end();)
	{
		tempIt = iter;
		++iter;
		cont.erase(tempIt);
	}
}

A.1, 2

B.2, 3

C.1, 3

D.1, 2, 3

答案:

1. D

分析:优先级队列priority_queue底层采用vector容器作为底层数据结构

2. A

分析:优先级队列默认情况为:大堆,这是解题的关键

  priority_queue<int> a; //a是大堆

  priority_queue<int, vector<int>, greater<int> > c; //c指定了比较规则,是小堆

  priority_queue<string> b; //b是大堆

  因此分别建堆的过程是建立a大堆,c小堆,b大堆

  所以出队的顺序就按照其优先级大小出队

3. C

A.deque底层总体为不连续空间

B.不支持,因为底层是一个个的不连续节点

C.正确

D.一般会以容量的2倍扩充容量,这是为了减少扩容的次数,减少内存碎片

4. C

A.仿函数是模板函数,可以根据不同的类型代表不同的状态

B.仿函数是模板函数,可以有不同类型

C.仿函数是模板函数,其速度比一般函数要慢,故错误

D.仿函数在一定程度上使代码更通用,本质上简化了代码

5. C

分析:此题主要考察cont.erase(tmpit)删除数据之后,迭代器失效相关问题

本题重点要关注的是底层实现

vector、deque底层都是用了连续空间,所以虽然++iter迭代器了,但是erase(tempit)以后

底层是连续空间,删除会挪动数据,最终导致iter意义变了,已失效了。

而list,不是连续空间,删除以后tempIt虽然失效了,但是不影响iter。

  

2. 反向迭代器

(此篇文章两万多字,可以在这分两部分看了)

前面讲 list 我们没实现反向迭代器,计划放在这里讲,反向迭代器怎么实现呢,

 反向迭代器和正向迭代器加加的方向不一样(唯一区别)。

(即正向迭代器 ++ 是向后走,反向迭代器 ++ 是向前走)

正常思维是把正向迭代器复制粘贴一份,把 ++ 改成 -- 啥的,

前面讲了容器适配器,大佬就实现了一个迭代器适配器:

库里的反向迭代器其实就是对正向迭代器的一种封装 —— 适配器模式(配接器模式)。

用传过来的正向迭代器实现反向迭代器,直接建一个reverse_iterator.h,

2.1 反向迭代器的普通实现

如果知道反向迭代器其实就是对正向迭代器的一种封装 ,因为以前认为rbegin()指向的是

最后一个数据,rend()指向的是第一个数据的前一个(哨兵位头结点)(库里面不是的)

 所以现在普通思路实现出来是这样的:(虽然库里面不是的,但也可以实现出来)

reverse_iterator.h(不对称版)

#pragma once

namespace rtx
{
	template<class Iterator, class Ref, class Ptr>// 为了适配const迭代器,再加两个参数
	struct __reverse_iterator  //普通迭代器传的就是 T& 和 T*  const 迭代器传的就是 const T& 和 const T* 
	{
		Iterator _cur;
		typedef __reverse_iterator<Iterator, Ref, Ptr> RIterator;

		__reverse_iterator(Iterator it)
			:_cur(it)
		{}

		RIterator operator++()
		{
			--_cur; // 正向迭代器 ++ 就是反向迭代器 --
			return *this;
		}

		RIterator operator--()
		{
			++_cur;
			return *this;
		}

		Ref operator*()
		{
			return *_cur;
		}

		Ptr operator->()
		{
			//return _cur.operator->();
			//return &(*_cur); // 如果这样写,对称的时候就要改了
			return &(operator*());
		}

		bool operator!=(const RIterator& it)
		{
			return _cur != it._cur;
		}
	};
}

如果你 vector 需要反向迭代器,那就把 vector 的正向迭代器传给 Iterator,

它就可以通过正向迭代器转换出 vector 的反向迭代器。

也就是说,我们实现的反向迭代器并包装的这个类,不是针对某个容器而是针对所有容器的。

任何一个容器只要你实现了正向迭代器,就可以通过其适配出反向迭代器。

先拿list.h 试一下,包一下头文件"reverse_iterator.h",在以前实现的 list类 加上了下面的代码:

		typedef __reverse_iterator<iterator, T&, T*> reverse_iterator; // 传正向迭代器封装出反向迭代器
		typedef __reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;
	
     	reverse_iterator rbegin()//按照以前的理解,rbegin指向最后一个数据
		{
			//return reverse_iterator(_head->_prev);
			return reverse_iterator(--end());
		}
		reverse_iterator rend()//按照以前的理解,rend指向第一个数据前一个(哨兵位)
		{
			//return reverse_iterator(_head);
			return reverse_iterator(end());
		}

		const_reverse_iterator rbegin() const
		{
			//return const_reverse_iterator(_head->_prev);
			return const_reverse_iterator(--end());
		}
		const_reverse_iterator rend() const
		{
			//return const_reverse_iterator(_head);
			return const_reverse_iterator(end());
		}

现在的 list.h 就是这样的:

#pragma once

#include<iostream>
#include<assert.h>
#include "reverse_iterator.h"
using namespace std;

namespace rtx
{
	template<class T>
	struct list_node // 定义结点
	{
		T _data;
		list_node* _prev;
		list_node* _next;

		list_node(const T& x = T())
			:_data(x)
			,_prev(nullptr)
			,_next(nullptr)
		{}
	};

	template<class T, class Ref, class Ptr>
	struct __list_iterator // 定义迭代器
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr> iterator;
		// 在list类里面:
		// typedef __list_iterator<T, T&, T*>             iterator;
        // typedef __list_iterator<T, const T&, const T*> const_iterator;
		Node* _node;

		__list_iterator(Node* node)
			:_node(node)
		{}

		bool operator!=(const iterator& it)
		{
			return _node != it._node;
		}
		//T& operator*()
		Ref operator*()
		{
			return _node->_data;  // 返回结点的数据
		}
		//T* operator->()
		Ptr operator->()
		{
			return &(operator*());
			//即 return &(_node->_data);
		}
		iterator& operator++()
		{
			_node = _node->_next;
			return *this; // 返回加加后的值
		}
		iterator operator++(int)
		{
			iterator tmp(*this); // 拷贝构造一个tmp存储原来的值
			_node = _node->_next;
			return tmp; // 返回加加前的值
		}
		iterator& operator--()
		{
			_node = _node->_prev;
			return *this; // 返回减减后的值
		}
		iterator operator--(int)
		{
			iterator tmp(*this); // 拷贝构造一个tmp存储原来的值
			_node = _node->prev;
			return tmp; // 返回减减前的值
		}
	};

	template<class T>
	class list // 定义list类
	{
		typedef list_node<T> Node;
	public:
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<T, const T&, const T*> const_iterator;

		typedef __reverse_iterator<iterator, T&, T*> reverse_iterator; // 传正向迭代器封装出反向迭代器
		typedef __reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;

		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}
		const_iterator end() const
		{
			return const_iterator(_head);
		}

		iterator begin()// begin是实际数据的第一个,_head 是哨兵位头结点
		{
			return iterator(_head->_next);
		}
		iterator end()// end是实际数据的下一个
		{
			return iterator(_head);
		}

		reverse_iterator rbegin()//按照以前的理解,rbegin指向最后一个数据
		{
			//return reverse_iterator(_head->_prev);
			return reverse_iterator(--end());
		}
		reverse_iterator rend()//按照以前的理解,rend指向第一个数据前一个(哨兵位)
		{
			//return reverse_iterator(_head);
			return reverse_iterator(end());
		}

		const_reverse_iterator rbegin() const
		{
			//return const_reverse_iterator(_head->_prev);
			return const_reverse_iterator(--end());
		}
		const_reverse_iterator rend() const
		{
			//return const_reverse_iterator(_head);
			return const_reverse_iterator(end());
		}

		void empty_init()// 创建并初始化哨兵位头结点(即构造函数)
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}
		template <class InputIterator>
		list(InputIterator first, InputIterator last)
		{
			empty_init();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
		list()
		{
			empty_init();
		}
		void swap(list<T>& x)//提一嘴:void swap(list& x)也行(类里面可以省略<T>,类外不行>
		{
			std::swap(_head, x._head); // 换哨兵位头结点就行
		}

		list(const list<T>& lt)//lt2(lt1)
		{
			empty_init();
			list<T> tmp(lt.begin(), lt.end()); // 迭代器区间构造一个(lt1)
			swap(tmp); // 直接和lt2换哨兵位头结点
		}
		list<T>& operator=(list<T> lt)//lt3 = lt1 这里lt1直接深拷贝给lt,lt是局部对象,出来作用域直接调析构
		{
			swap(lt);// 把深拷贝出来的lt和lt3交换
			return *this; // 把lt3返回
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it); // 返回删除位置的下一个结点
			}
		}

		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* NewNode = new Node(x);

			prev->_next = NewNode;
			NewNode->_prev = prev;
			NewNode->_next = cur;
			cur->_prev = NewNode;

			return iterator(NewNode);
		}
		void push_back(const T& x)
		{
			//Node* tail = _head->_prev;
			//Node* NewNode = new Node(x);
			 思路图:head        tail  NewNode
			//tail->_next = NewNode;
			//NewNode->_prev = tail;
			//_head->_prev = NewNode;
			//NewNode->_next = _head;
			insert(end(), x);
		}
		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());
			Node* cur = pos._node; // 删cur
			Node* prev = cur->_prev;

			prev->_next = cur->_next; // cur前一个指向cur后一个
			cur->_next->_prev = prev; // cur后一个指回cur前一个

			delete cur;
			return iterator(prev->_next); // 返回删除位置下一个
		}
		void pop_back()
		{
			erase(begin());
		}
		void pop_front()
		{
			erase(--end());
		}

	private:
		Node* _head; // 哨兵位头结点
	};
}

测试一下:

#define _CRT_SECURE_NO_WARNINGS 1

#include "list.h"

namespace rtx
{
	void Test_reverse_iterator()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);

		list<int>::iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		list<int>::reverse_iterator rit = lt.rbegin();
		while (rit != lt.rend())
		{
			cout << *rit << " ";
			++rit;
		}
		cout << endl;
	}
}

int main()
{
	rtx::Test_reverse_iterator();

	return 0;
}

 这样实现也行,但是rbegin()和rend()就没那么美观,

所以巨佬就设计了下面的艺术行为:(也只是为了对称,没别的)

2.2 反向迭代器的对称实现

前面说到,我们认为应该是这样的:

但是库里面是这样实现的:

rbegin 和 rend 是和 end 和 begin 是相对称的形式设计的,

你的 end 就是我的 rbegin,你的 begin 就是我的 rend。

如果这样实现的话,对rbegin解引用就不对了,

我们先看看库里反向迭代器的解引用:

这样取的就是前一个位置了,rbegin() 位置的前一个位置正是想要取的地方。

来改一下,解引用应该是这样的:

		Ref operator*()
		{
			//return *_cur;
			auto tmp = _cur;
			--tmp;
			return *tmp;
		}

还有那四个接口改成这样:

		reverse_iterator rbegin()// 对称实现
		{
			//return reverse_iterator(_head->_prev);
			//return reverse_iterator(--end());
			return reverse_iterator(end());
		}
		reverse_iterator rend()
		{
			//return reverse_iterator(_head);
			//return reverse_iterator(end());
			return reverse_iterator(begin());
		}

		const_reverse_iterator rbegin() const
		{
			//return const_reverse_iterator(_head->_prev);
			return const_reverse_iterator(end());
		}
		const_reverse_iterator rend() const
		{
			//return const_reverse_iterator(_head);
			return const_reverse_iterator(begin());
		}

reverse_iterator.h 

#pragma once

namespace rtx
{
	template<class Iterator, class Ref, class Ptr>// 为了适配const迭代器,再加两个参数
	struct __reverse_iterator  //普通迭代器传的就是 T& 和 T*  const 迭代器传的就是 const T& 和 const T* 
	{
		Iterator _cur;
		typedef __reverse_iterator<Iterator, Ref, Ptr> RIterator;

		__reverse_iterator(Iterator it)
			:_cur(it)
		{}

		RIterator operator++()
		{
			--_cur; // 正向迭代器 ++ 就是反向迭代器 --
			return *this;
		}

		RIterator operator--()
		{
			++_cur;
			return *this;
		}

		Ref operator*()
		{
			//return *_cur;
			auto tmp = _cur;
			--tmp;
			return *tmp;
		}

		Ptr operator->()
		{
			//return _cur.operator->();
			//return &(*_cur); // 如果这样写,对称的时候就要改了
			return &(operator*());
		}

		bool operator!=(const RIterator& it)
		{
			return _cur != it._cur;
		}
	};
}

list.h :

#pragma once

#include<iostream>
#include<assert.h>
#include "reverse_iterator.h"
using namespace std;

namespace rtx
{
	template<class T>
	struct list_node // 定义结点
	{
		T _data;
		list_node* _prev;
		list_node* _next;

		list_node(const T& x = T())
			:_data(x)
			,_prev(nullptr)
			,_next(nullptr)
		{}
	};

	template<class T, class Ref, class Ptr>
	struct __list_iterator // 定义迭代器
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr> iterator;
		// 在list类里面:
		// typedef __list_iterator<T, T&, T*>             iterator;
        // typedef __list_iterator<T, const T&, const T*> const_iterator;
		Node* _node;

		__list_iterator(Node* node)
			:_node(node)
		{}

		bool operator!=(const iterator& it)
		{
			return _node != it._node;
		}
		//T& operator*()
		Ref operator*()
		{
			return _node->_data;  // 返回结点的数据
		}
		//T* operator->()
		Ptr operator->()
		{
			return &(operator*());
			//即 return &(_node->_data);
		}
		iterator& operator++()
		{
			_node = _node->_next;
			return *this; // 返回加加后的值
		}
		iterator operator++(int)
		{
			iterator tmp(*this); // 拷贝构造一个tmp存储原来的值
			_node = _node->_next;
			return tmp; // 返回加加前的值
		}
		iterator& operator--()
		{
			_node = _node->_prev;
			return *this; // 返回减减后的值
		}
		iterator operator--(int)
		{
			iterator tmp(*this); // 拷贝构造一个tmp存储原来的值
			_node = _node->prev;
			return tmp; // 返回减减前的值
		}
	};

	template<class T>
	class list // 定义list类
	{
		typedef list_node<T> Node;
	public:
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<T, const T&, const T*> const_iterator;

		typedef __reverse_iterator<iterator, T&, T*> reverse_iterator; // 传正向迭代器封装出反向迭代器
		typedef __reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;

		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}
		const_iterator end() const
		{
			return const_iterator(_head);
		}

		iterator begin()// begin是实际数据的第一个,_head 是哨兵位头结点
		{
			return iterator(_head->_next);
		}
		iterator end()// end是实际数据的下一个
		{
			return iterator(_head);
		}

		//reverse_iterator rbegin()//按照以前的理解,rbegin指向最后一个数据
		//{
		//	//return reverse_iterator(_head->_prev);
		//	return reverse_iterator(--end());
		//}
		//reverse_iterator rend()//按照以前的理解,rend指向第一个数据前一个(哨兵位)
		//{
		//	//return reverse_iterator(_head);
		//	return reverse_iterator(end());
		//}

		//const_reverse_iterator rbegin() const
		//{
		//	//return const_reverse_iterator(_head->_prev);
		//	return const_reverse_iterator(--end());
		//}
		//const_reverse_iterator rend() const
		//{
		//	//return const_reverse_iterator(_head);
		//	return const_reverse_iterator(end());
		//}

		reverse_iterator rbegin()// 对称实现
		{
			//return reverse_iterator(_head->_prev);
			//return reverse_iterator(--end());
			return reverse_iterator(end());
		}
		reverse_iterator rend()
		{
			//return reverse_iterator(_head);
			//return reverse_iterator(end());
			return reverse_iterator(begin());
		}

		const_reverse_iterator rbegin() const
		{
			//return const_reverse_iterator(_head->_prev);
			return const_reverse_iterator(end());
		}
		const_reverse_iterator rend() const
		{
			//return const_reverse_iterator(_head);
			return const_reverse_iterator(begin());
		}

		void empty_init()// 创建并初始化哨兵位头结点(即构造函数)
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}
		template <class InputIterator>
		list(InputIterator first, InputIterator last)
		{
			empty_init();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
		list()
		{
			empty_init();
		}
		void swap(list<T>& x)//提一嘴:void swap(list& x)也行(类里面可以省略<T>,类外不行>
		{
			std::swap(_head, x._head); // 换哨兵位头结点就行
		}

		list(const list<T>& lt)//lt2(lt1)
		{
			empty_init();
			list<T> tmp(lt.begin(), lt.end()); // 迭代器区间构造一个(lt1)
			swap(tmp); // 直接和lt2换哨兵位头结点
		}
		list<T>& operator=(list<T> lt)//lt3 = lt1 这里lt1直接深拷贝给lt,lt是局部对象,出来作用域直接调析构
		{
			swap(lt);// 把深拷贝出来的lt和lt3交换
			return *this; // 把lt3返回
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it); // 返回删除位置的下一个结点
			}
		}

		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* NewNode = new Node(x);

			prev->_next = NewNode;
			NewNode->_prev = prev;
			NewNode->_next = cur;
			cur->_prev = NewNode;

			return iterator(NewNode);
		}
		void push_back(const T& x)
		{
			//Node* tail = _head->_prev;
			//Node* NewNode = new Node(x);
			 思路图:head        tail  NewNode
			//tail->_next = NewNode;
			//NewNode->_prev = tail;
			//_head->_prev = NewNode;
			//NewNode->_next = _head;
			insert(end(), x);
		}
		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());
			Node* cur = pos._node; // 删cur
			Node* prev = cur->_prev;

			prev->_next = cur->_next; // cur前一个指向cur后一个
			cur->_next->_prev = prev; // cur后一个指回cur前一个

			delete cur;
			return iterator(prev->_next); // 返回删除位置下一个
		}
		void pop_back()
		{
			erase(begin());
		}
		void pop_front()
		{
			erase(--end());
		}

	private:
		Node* _head; // 哨兵位头结点
	};
}

Test.cpp:(写到这忽然发现以前的Test.cpp都习惯写成Test.c 了,懂就刑)

#define _CRT_SECURE_NO_WARNINGS 1

#include "list.h"

namespace rtx
{
	void Test_reverse_iterator()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);

		list<int>::iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		list<int>::reverse_iterator rit = lt.rbegin();
		while (rit != lt.rend())
		{
			cout << *rit << " ";
			++rit;
		}
		cout << endl;
	}
}

int main()
{
	rtx::Test_reverse_iterator();

	return 0;
}

以后为了和库里面的类似,所以还是艺术地实现对称的反向迭代器好。

写到这可能还不知道我们实现的反向迭代器有多强(只要提供一个正向迭代器就行)

我们试试把 vector 的弄出来,在vector.h里 #include "reverse_iterator.h"

和上面一样只需在vector类里面加上下面的代码:

一模一样,你甚至可以一步复制粘贴,位置也不改了:

		typedef __reverse_iterator<iterator, T&, T*> reverse_iterator; // 传正向迭代器封装出反向迭代器
		typedef __reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;

		reverse_iterator rbegin()// 对称实现
		{
			//return reverse_iterator(_head->_prev);
			//return reverse_iterator(--end());
			return reverse_iterator(end());
		}
		reverse_iterator rend()
		{
			//return reverse_iterator(_head);
			//return reverse_iterator(end());
			return reverse_iterator(begin());
		}

		const_reverse_iterator rbegin() const
		{
			//return const_reverse_iterator(_head->_prev);
			return const_reverse_iterator(end());
		}
		const_reverse_iterator rend() const
		{
			//return const_reverse_iterator(_head);
			return const_reverse_iterator(begin());
		}

所以vector.h就是这样的:

vector.h

#pragma once

#include<iostream>
#include<assert.h>
#include<string>
#include "reverse_iterator.h"
using namespace std;

namespace rtx
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;

		typedef __reverse_iterator<iterator, T&, T*> reverse_iterator; // 传正向迭代器封装出反向迭代器
		typedef __reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;

		reverse_iterator rbegin()// 对称实现
		{
			//return reverse_iterator(_head->_prev);
			//return reverse_iterator(--end());
			return reverse_iterator(end());
		}
		reverse_iterator rend()
		{
			//return reverse_iterator(_head);
			//return reverse_iterator(end());
			return reverse_iterator(begin());
		}

		const_reverse_iterator rbegin() const
		{
			//return const_reverse_iterator(_head->_prev);
			return const_reverse_iterator(end());
		}
		const_reverse_iterator rend() const
		{
			//return const_reverse_iterator(_head);
			return const_reverse_iterator(begin());
		}

		vector()
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{}
		~vector()
		{
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
		}

		size_t size() const
		{
			return _finish - _start;
		}
		size_t capacity() const
		{
			return _end_of_storage - _start;
		}

		void push_back(const T& x)
		{
			//if (_finish == _end_of_storage)
			//{
			//	reserve(capacity() == 0 ? 4 : capacity() * 2);
			//}
			//*_finish = x;
			//++_finish;
			insert(end(), x);
		}
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t sz = size();
				T* tmp = new T[n];
				if (_start)
				{
					//memcpy(tmp, _start, sizeof(T) * sz); //浅拷贝,不行

					for (size_t i = 0; i < sz; i++)// 如果T是int,一个一个拷贝没问题
					{
						tmp[i] = _start[i];// 如果T是string等自定义问题,一个一个拷贝调用的是T的深拷贝,也不会出问题。
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = tmp + sz;
				_end_of_storage = tmp + n;
			}
		}

		T& operator[](size_t pos)
		{
			assert(pos < size());
			return *(_start + pos);
		}
		const T& operator[](size_t pos) const
		{
			assert(pos < size());
			return *(_start + pos);
		}

		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const_iterator begin() const
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}

		template<class InputInterator>
		vector(InputInterator first, InputInterator last)
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		void resize(size_t n, const T& val = T())
		{
			if (n > capacity())
			{
				reserve(n);
			}
			if (n > size())
			{
				while (_finish != _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
			else
			{
				_finish = _start + n;
			}
		}

		void pop_back()
		{
			assert(_finish > _start);
			--_finish;
		}

		iterator insert(iterator pos, const T& val)
		{
			assert(pos >= _start);// ①检查pos是否越界
			assert(pos <= _finish);

			if (_finish == _end_of_storage)// ②检查是否需要扩容
			{
				size_t len = pos - _start;// 记录一下pos到_start的距离
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + len;// 迭代器失效问题,扩容后pos还是指向原来的空间,更新一下pos,
				//而且形参不会影响实参,传引用的话begin等就传不了,所以用返回解决
			}

			iterator right = _finish - 1;// ③移动数据
			while (right >= pos)
			{
				*(right + 1) = *right;
				--right;
			}

			*pos = val;// ④插入数据
			++_finish;
			return pos;
		}

		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);// 不能<= 因为_finish指向的是最后一个数据的下一个

			iterator left = pos + 1;
			while (left < _finish)
			{
				*(left - 1) = *left;
				++left;
			}
			--_finish;
			return pos;//此时pos就是删除位置下一个位置迭代器
		}

		//vector(const vector<T>& v)// 传统写法
		//{
		//	reserve(v.capacity());
		//	// memcpy(_start, v._start, v.size() * sizeof(T));  // 会翻车
		//	for (const auto& e : v)
		//	{
		//		push_back(e);
		//	}
		//}
		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
		}

		vector(const vector<T>& v)// 现代写法
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{
			vector<T> tmp(v.begin(), v.end());
			swap(tmp);
		}

		vector<T>& operator=(vector<T> v)// 现代写法
		{
			swap(v);
			return *this;
		}

	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_storage;
	};
}

Test.cpp

#define _CRT_SECURE_NO_WARNINGS 1

#include "list.h"
#include "vector.h"

namespace rtx
{
	void Test_reverse_iterator()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);

		list<int>::iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		list<int>::reverse_iterator rit = lt.rbegin();
		while (rit != lt.rend())
		{
			cout << *rit << " ";
			++rit;
		}
		cout << endl;
	}

	void Test_reverse_iterator2()
	{
		vector<int> lt;//为了方便lt的名字都不改成常用的v了
		lt.push_back(10);
		lt.push_back(20);
		lt.push_back(30);
		lt.push_back(40);
		lt.push_back(50);

		vector<int>::iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		vector<int>::reverse_iterator rit = lt.rbegin();
		while (rit != lt.rend())
		{
			cout << *rit << " ";
			++rit;
		}
		cout << endl;
	}
}

int main()
{
	//rtx::Test_reverse_iterator();
	rtx::Test_reverse_iterator2();

	return 0;
}

 刑,反向迭代器就讲到这里了,讲了这么多属实有点麻烦,但了解原理后就很方便了。

所有正向迭代器++和--的容器都能用这个反向迭代器了。

3.  迭代器的功能分类

迭代器从功能可以分为这三类:单向迭代器,双向迭代器,随机迭代器,(文档有说明)

其中只有单向迭代器不支持上面实现的反向迭代器,

对应的是没讲的,forward_list单链表和蓝线的下面两个哈希表

下面的迭代器可以看作是上面的迭代器的特殊形式,所以算法库里的传参就有这样的命名:

本章完。

到这里我们已经讲了容器适配器和迭代器适配器,体会了适配器封装和复用的特点。

软件开发提倡的就是能复用就复用,尽量不要自己写。

到这里我们基本已经把C++(简单的学了),后面继续融合地讲,先讲讲模板没讲的东西。

再讲C++的三大特性里的继承和多态。还有数据结构关于树的内容,map和set容器。

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

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

相关文章

性能测试基础知识(一)性能测试策略

性能测试策略 一、什么是性能测试&#xff1f;二、性能测试的目的三、性能测试策略1、基准测试2、并发测试3、负载测试4、压力测试5、其他测试 一、什么是性能测试&#xff1f; 性能测试是在一定的负载1条件下&#xff0c;系统的响应时间等特性是否满足特定的性能需求。需要有…

软考A计划-系统集成项目管理工程师-信息化知识(二)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

【高危】Apache Nifi JMS组件存在JNDI反序列化漏洞

漏洞描述 Apache NiFi 是一个开源的数据流处理和自动化工具&#xff0c; JndiJmsConnectionFactoryProvider 控制器组件用于配置 JMS 连接地址。 Apache NiFi 1.8.0 至 1.21.0 版本中&#xff0c;由于 JndiJmsConnectionFactoryProvider 控制器服务允许已授权的用户配置 URL…

NUCLEO-F411RE RT-Thread 体验 (3) - GCC环境 uart驱动的移植以及console的使用

NUCLEO-F411RE RT-Thread 体验 (3) - GCC环境 uart驱动的移植以及console的使用 1、准备工作 在第一节里&#xff0c;我们用stm32cubemx将pa2 pa3管脚配置成usart2&#xff0c;用于跟st-link虚拟串口的打印用&#xff0c;那么我们先重定向printf函数&#xff0c;看这条通道是…

创建 Python 脚本以在 Linux 中打开新终端并运行命令

文章目录 创建在 Linux 中打开新终端并运行命令的 Python 脚本在 Linux 中创建 Python 脚本来检查 Python 版本使打开的终端保持活动状态并在其中运行命令的 Python 脚本在 Linux 中使用 Python 子模块 subprocess() 将命令传递到新终端总结 Linux 操作系统以其程序员广泛使用的…

PB9如何实现datawindow打印导出PDF,PB导出PDF

PB9如何实现datawindow打印导出PDF&#xff0c;PB导出PDF&#xff1f; 之前的saveas导出pdf&#xff0c;设置非常麻烦。需要 1. 安装gs705w32.exe 2. 安装虚拟打印机 Sybase\Shared\PowerBuilder\drivers\ADIST5.INF 手动添加打印机 这个方法现在对于win64不支持。 今天客…

window11 + python3.7+NVDIA11.7 +pytorch GPU 加速环境配置

window11 python3.7NVDIA11.7 pytorchGPU 加速环境配置 关于pytorch配置GPU环境我在网上看了很多&#xff0c;其实现在基本上没有windows 11 版本环境的配置但是其实没必要担心这个&#xff0c;这没有影响。 对于博主呢&#xff0c;其实不太像配置GPU的&#xff0c;因为其实…

【MYSQL篇】一文弄懂mysql索引原理

文章目录 索引是什么&#xff1f;索引定义索引类型 索引存储模型推演二分查找二叉查找树&#xff08; Binary Search Tree&#xff09;平衡二叉树&#xff08;AVL Tree&#xff09;多路平衡查找树&#xff08;B Tree&#xff09;B树&#xff08;加强版B Tree&#xff09; 小结 …

PyQt中数据库的访问(一)

访问数据库的第一步是确保ODBC数据源配置成功&#xff0c;我接下来会写数据源配置的文章&#xff0c;请继续关注本栏&#xff01; &#xff08;一&#xff09;数据库连接 self.DBQSqlDatabase.addDatabase("QODBC") self.DB.setDatabaseName("Driver{sqlServer…

shell 实现子进程多任务,进程高并发

多进程的作用 提高程序的效率&#xff1a;一些CPU密集型的任务&#xff0c;如数据处理、解压、加密等&#xff0c;使用多进程可以提高程序的执行效率&#xff0c;更快地完成计算任务&#xff1b; 实现更复杂的功能&#xff1a;多进程可以在同一时间向不同的方向处理不同的任务…

SpringBoot创建和运行

1、什么是SpringBoot1.1、优点 2、项目创建2.1、使用Idea创建2.2、使用网页创建 3、项目目录介绍4、项目运行5、包路径错误 1、什么是SpringBoot Spring是为了简化Java程序开发的。Spring Boot是一种用于快速构建独立、生产级别的Java应用程序的开源框架&#xff0c;是为了简化…

MySQL优化--undo log和redo log的区别

首先我们需要知道两个概念 缓冲池&#xff08;buffer pool&#xff09;:主内存中的一个区域&#xff0c;里面可以缓存磁盘上经常操作的真实数据&#xff0c;在执行增删改查操作时&#xff0c;先操作缓冲池中的数据&#xff08;若缓冲池没有数据&#xff0c;则从磁盘加载并缓存…

MySQL数据库基础 13

第十三章 约束 1. 约束(constraint)概述1.1 为什么需要约束1.2 什么是约束1.3 约束的分类 2. 非空约束2.1 作用2.2 关键字2.3 特点2.4 添加非空约束2.5 删除非空约束 3. 唯一性约束3.1 作用3.2 关键字3.3 特点3.4 添加唯一约束3.5 关于复合唯一约束3.5 删除唯一约束 4. PRIMARY…

燃气管网监测系统助力天燃气管道安全运行

随着城市化的进程&#xff0c;燃气管道网络在各个城市中越来越密集&#xff0c;一旦发生燃气泄漏等安全事故&#xff0c;后果将不堪设想。因此&#xff0c;城市燃气管网的建设发展有赖于制定一个安全可靠的监控方案&#xff0c;以保障供气管道与用户安全。物联网技术的发展为城…

北邮国院物联网RFID课程笔记

PDF 获取&#xff1a;微信公众号&#xff1a;灰海宽松&#xff0c;后台回复 “RFID” 获取。 文章目录 RFID1. IntroductionComparison of different automatic identification technologiesThe main features of RFIDConstraints of RFID technologyCore technologies of RFI…

一个cad绘图图型的过程

cad绘图步骤 &#xff1a; 1.设置绘图环境。 选择菜单栏中的“格式”→“图层”命令&#xff0c;新建 3 个图层&#xff1a; 第一图层命名为“粗实线”&#xff0c;线宽为 0.3mm&#xff0c;其余属性默认。 第二图层命名为“细实线”&#xff0c;线宽为 0.15mm&#xff0c;其…

计算机网络开荒5-数据链路层

文章目录 一、 数据链路层服务二、链路层具体实现三、差错检测3.1 差错编码3.2 奇偶校验吗3.3 Internet校验和checksum3.4 循环冗余校验码CRC 四、多路访问控制MAC协议4.1 理想的MAC协议4.2 MAC协议分类4.2.1 TDMA4.2.2 FDMA 4.3 随机访问控制协议4.3.1 时隙ALOHA协议4.3.2 ALO…

chatgpt赋能python:Python拆数指南:如何使用Python快速拆解数字

Python拆数指南&#xff1a;如何使用Python快速拆解数字 如果您正在开发一个关于数字的应用程序&#xff0c;那么您会发现Python可以非常方便地拆解数字。Python的拆数功能可以快速拆解数字并将其转换为可读的形式&#xff0c;这对于数据处理和编程任务来说非常有用。 Python…

JavaScript的数学计算库:decimal.js

An arbitrary-precision Decimal type for JavaScript. 功能 整数和浮点数简单但功能齐全的 API复制 JavaScript 和对象的许多方法Number.prototype Math还处理十六进制、二进制和八进制值比 Java 的 BigDecimal JavaScript 版本更快&#xff0c;更小&#xff0c;也许更容易使…

chatgpt赋能python:Python代码实现查找重复字符串

Python代码实现查找重复字符串 Python 是一种高级程序设计语言&#xff0c;被广泛用于网络编程、web开发、数据分析等领域。在 SEO 操作中&#xff0c;经常需要进行关键词分析&#xff0c;以确定哪些词汇是最重要的。本文将介绍如何使用 Python 实现查找重复字符串的代码。 什…