【C++之容器篇】造轮子:list的模拟实现与使用

前言

前面我们已经学习了string和vector的模拟实现和使用,相信对于容器的模拟实现和使用的能力已经上升一定的水平,今天我们要学习的是list的模拟实现,List的模拟实现和string和vector其实没有本质的区别,只是在list的模拟实现过程中,list的迭代器和string和vector有所不同,这是我们实现List的模拟实现中需要重点掌握的,今天学习的List本质就是一个带头双向循环链表。

一、关于list

1. 简介

在这里插入图片描述
list本质就是一个带头双向循环链表,支持在任何位置以O(1)的时间进行插入和删除。

2. 成员类型

在这里插入图片描述
看到上图,我们一定要知道迭代器的类型:list中的迭代器的类型是双向迭代器,其他的迭代器类型好还有:单向迭代器,随机迭代器。

  • 单向迭代器:只支持单向遍历访问的迭代器,只支持++,不支持–
  • 双向迭代器:支持双向访问容器的迭代器,同时支持++和–
  • 随机迭代器:支持随机访问容器的迭代器,同时支持++,–,+,-

3. 结构

list的底层本质是一个带头双向循环链表,支持在任何位置以O(1)的时间复杂度进行插入和删除,因为相比于前面学习的string和vector,string和vector的底层是一个连续存储的数组,所以在插入和删除数据的时候需要挪动数据,时间复杂度为O(N),List插入和删除数据只需要改变插入和删除位置的前后结点的指针关系,不需要挪动数据,所以插入和删除的时间复杂度为O(1)。
在这里插入图片描述

4. 代码表示底层结构

  • 代码:
namespace hjt
{
	// 结点类型
	template <class T>
	struct ListNode
	{
		// 成员函数
		// 构造函数:
		ListNode(const T& data)
			:_next(nullptr)
			,_prev(nullptr)
			,_data(data)
		{}

		// 基本的成员变量
		ListNode* _next;
		ListNode* _prev;
		T _data;
	};

	// 实现list
	template <class T>
	class list
	{
		typedef ListNode<T> Node;
	public:
		// 基本的成员函数
		

		// 基本成员变量
	private:
		Node* _head;
	};
}

list是一个带头双向循环链表,由很多的结点链接而成的,一个List中最本质的就是其头结点,所以,list的成员变量就是头结点的指针,所以我们首先需要实现结点类型,实现结点类型,结点类型中需要存储数据(数据具有不同的数据类型,所以需要模板来支撑),还需要两个指针分别指向前一个结点和后一个结点,从而实现双向的功能。

二、默认成员函数

1. 构造函数

在这里插入图片描述

1. list()
  • 代码:
// 构造函数
		list(const T& val = T())
		{
			_head = new Node(val);
			_head->_prev = _head;
			_head->_next = _head;
		}
  • 使用代码(std::list)
void test_list1()
{
	// 无参构造函数
	list<int> lt1;// 创建一个存储int的list对象
	list<char> lt2;// 创建一个存储char的list对象
	list<double> lt3;// 创建一个存储double的list对象
	list<string> lt4;// 创建一个存储string的list对象
}

2. list(size_t n,const T& val = T())和list(InputIterator first,InputIterator last)
  • 代码:构造函数:支持使用一段迭代器区间的数据进行构造
// 构造函数:支持使用一段迭代器区间的数据进行构造
		template <class InputIterator>
		list(InputIterator first, InputIterator last)
		{
			empty_init();
			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}

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

		for (auto& e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		list<int> lt1(lt.begin(), lt.end());
		for (auto& e : lt1)
		{
			cout << e << " ";
		}
		cout << endl;


	}

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

  • 使用代码(std::list):
void test_list2()
{
	
	// 用n个值来构造List
	list<int> lt1(3, 6);// 用3个6来构造一个list对象

	// 使用一段迭代器区间来构造
	string s2("hello list::list(InputIterator first,InputIterator last)");
	vector<char> v2(s2.begin(), s2.end());
	list<char> lt2(v2.begin(), v2.end());

	// 遍历
	

	// 使用迭代器进行遍历
	// 遍历lt1
	cout << "lt1:" << endl;
	list<int>::iterator lit1 = lt1.begin();
	while (lit1 != lt1.end())
	{
		cout << *lit1 << " ";
		lit1++;
	}
	cout << endl;

	// 遍历lt2
	cout << "lt2:" << endl;
	list<char>::iterator lit2 = lt2.begin();
	while (lit2 != lt2.end())
	{
		cout << *lit2 << " ";
		lit2++;
	}
	cout << endl;

	// 使用范围for进行遍历
	cout << "lt1:" << endl;
	for (auto& e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;

	cout << "lt2:" << endl;
	for (auto& e : lt2)
	{
		cout << e << " ";
	}
	cout << endl;



}

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

2. 拷贝构造函数

在这里插入图片描述
拷贝构造函数和前面的容器样子还是差不多

  • 代码:
// 拷贝构造函数
		list(const list<T>& lt)
		{
			empty_init();
			list<T> tmp(lt.begin(), lt.end());
			swap(tmp);
		}

void test_list8()
	{
		list<int> 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);

		list<int> lt1(lt);// 调用拷贝构造函数
		cout << "lt:" << endl;
		for (auto& e: lt)
		{
			cout << e << " ";
		}
		cout << endl;
		cout << "lt1:" << endl;
		for (auto& e : lt1)
		{
			cout << e << " ";
		}
		cout << endl;
	}

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

  • 使用代码(std::list)
void test_list3()
{
	string s("hello list(const list<char>& lt)");
	list<char> lt1(s.begin(), s.end());
	list<char> lt2(lt1);

	cout << "lt1:" << endl;
	for (auto& e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;

	cout << "lt2" << endl;
	for (auto& e : lt2)
	{
		cout << e << " ";
	}
	cout << endl;
}

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

3. 析构函数

在这里插入图片描述

  • 代码:

		// 析构函数
		~list()
		{
			clear();
			_head = nullptr;
		}

4. 赋值运算符重载函数

在这里插入图片描述

  • 代码:
// 赋值运算符重载函数
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}


	void test_list9()
	{
		list<int> 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);

		list<int> lt1;
		lt1 = lt;// 调用赋值运算符重载函数
		cout << "lt:" << endl;
		for (auto& e : lt)
		{
			cout << e << " ";
		}
		cout << endl;
		cout << "lt1:" << endl;
		for (auto& e : lt1)
		{
			cout << e << " ";
		}
		cout << endl;
	}

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

  • 使用代码(std::list):
void test_list4()
{
	string s("hello list<char>& operator=(const list<char>& lt)");
	list<char> lt(s.begin(), s.end());
	list<char> lt1;
	lt1 = lt;// 调用赋值运算符重载函数

	cout << "lt:" << endl;
	for (auto& e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	cout << "lt1:" << endl;
	for (auto& e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;

}

运行结果:

在这里插入图片描述

三、迭代器(重难点)

list中的迭代器的实现是本节学习的重难点,我们要深刻理解list中的迭代器的实现方法,因为list是一个链表,链表中的结点不是顺序存储的,所以不能直接通过结点的指针的++或者–就直接找到下一个或者上一个结点,所以我们可以通过将迭代器封装成一个自定义类型,从而通过运算符重载来实现其功能。

  • 代码1:最原始的版本(封装迭代器)
	// 迭代器
	template <class T>
	struct __list_iterator
	{
		typedef ListNode<T> Node;

		// 构造函数
		__list_iterator(Node* node)
			:_node(node)
		{
		}

		// 运算符重载
		// * ->
		T& operator*()
		{
			return _node->_data;
		}

		T* operator->()
		{
			return &_node->_data;
		}

		// 前置++前置--后置++后置--

		__list_iterator<T>& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		__list_iterator<T>& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		__list_iterator<T> operator++(int)
		{
			__list_iterator<T> tmp(*this);
			_node = _node->_next;
			return tmp;
		}

		__list_iterator<T> operator--(int)
		{
			__list_iterator<T> tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

		// 比较大小
		bool operator==(const __list_iterator<T>& it)
		{
			return _node == it._node;
		}

		bool operator!=(const __list_iterator<T>& it)
		{
			return _node == it._node;
		}

		// 成员变量
		Node* _node;
	};
// 普通迭代器
	template <class T>
	struct __list_const_iterator
	{
		typedef ListNode<T> Node;

		// 构造函数
		__list_const_iterator(Node* node)
			:_node(node)
		{
		}

		// 运算符重载
		// * ->
		const T& operator*()
		{
			return _node->_data;
		}

		const T* operator->()
		{
			return &_node->_data;
		}

		// 前置++前置--后置++后置--

		__list_const_iterator<T>& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		__list_const_iterator<T>& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		__list_const_iterator<T> operator++(int)
		{
			__list_const_iterator<T> tmp(*this);
			_node = _node->_next;
			return tmp;
		}

		__list_const_iterator<T> operator--(int)
		{
			__list_const_iterator<T> tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

		// 比较大小
		bool operator==(const __list_const_iterator<T>& it)
		{
			return _node == it._node;
		}

		bool operator!=(const __list_const_iterator<T>& it)
		{
			return _node == it._node;
		}

		// 成员变量
		Node* _node;
	};
  • 代码:在list 中定义迭代器
typedef __list_iterator<T> iterator;
typedef __list_const_iterator<T> const_iterator;
// 迭代器
		iterator begin()
		{
			return iterator(_head->_next);
		}

		iterator end()
		{
			return iterator(_head);
		}

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

		const_iterator end() const
		{
			return const_iterator(_head);
		}
  • 代码2:迭代器实现的优化
// 迭代器实现的优化
template <class T,class Ref,class Ptr>
struct __list_iterator
{
	typedef ListNode<T> Node;

	typedef __list_iterator<T, Ref, Ptr> Self;

	Node* _node;

	// 构造函数
	__list_iterator(Node* node)
		:_node(node)
	{

	}

	// 运算符重载
	Ref operator*()
	{
		return _node->_data;
	}

	Ptr operator->()
	{
		return &_node->_data;
	}

	Self& operator++()
	{
		_node = _node->_next;
		return *this;
	}

	Self operator++(int)
	{
		Self tmp(*this);
		_node = _node->_next;
		return tmp;
	}

	Self& operator--()
	{
		_node = _node->_prev;
		return *this;
	}

	Self& operator--(int)
	{
		Self tmp(*this);
		_node = _node->_prev;
	}

	// 比较大小
	bool operator==(const Self& it)
	{
		return _node == it._node;
	}

	bool operator!=(const Self& it)
	{
		return _node != it._node;
	}
};
  • 代码:list中定义普通迭代器和const对象的迭代器
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;
// 迭代器
		iterator begin()
		{
			return iterator(_head->_next);
		}

		iterator end()
		{
			return iterator(_head);
		}

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

		const_iterator end() const
		{
			return const_iterator(_head);
		}
  • 测试代码:
void test_list1()
	{
		list<int> 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);

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

	}

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

1. 普通对象的正向迭代器

  • 使用代码(std::list):
void test_list5()
{
	string s("hello list<char>::iterator begin() and end()");
	list<char> lt(s.begin(), s.end());

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

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

2. const对象的正向迭代器

  • 使用代码(std::list):
void test_list6()
{
	string s("hello list<char>::const_iterator begin() and end()");
	const list<char> lt(s.begin(), s.end());

	list<char>::const_iterator lit = lt.begin();
	while (lit != lt.end())
	{
		cout << *lit << " ";
		lit++;
	}
	cout << endl;
}

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

3. 普通对象的反向迭代器

使用代码(std::list):

void test_list7()
{
	string s("hello list<char>::reverse_iterator begin() and end()");
	list<char> lt(s.begin(), s.end());

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

4. const对象的反向迭代器

四、容量接口

1. empty()

这个函数的功能是判断链表是否为空
在这里插入图片描述

  • 代码:
// 判断链表是否为空
		bool empty()
		{
			return _head->_next == _head;
		}

void test_list10()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(1);
		lt.push_back(1);

		if (lt.empty())
		{
			cout << "链表为空" << endl;
		}
		else
		{
			cout << "链表不为空" << endl;
		}

		lt.clear();
		if (lt.empty())
		{
			cout << "链表为空" << endl;
		}
		else
		{
			cout << "链表不为空" << endl;
		}
	}

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

2. size()

返回list结点个数
在这里插入图片描述

  • 代码:
// 结点个数
		size_t size()
		{
			iterator pos = begin();
			int count = 0;
			while (pos != end())
			{
				count++;
				pos++;
			}
			return count;
		}

void test_list11()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(1);
		lt.push_back(1);
		lt.push_back(1);
		lt.push_back(1);
		lt.push_back(1);

		for (auto& e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		cout << "结点个数:" << lt.size() << endl;
	}

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

五、元素访问接口

1. front()

2. back()

六、修改接口

1. push_front()

在这里插入图片描述

  • 代码:
void push_front(const T& val)
		{
			Node* front = _head->_next;
			Node* newnode = new Node(val);
			// _head newnode front
			_head->_next = newnode;
			newnode->_prev = _head;
			newnode->_next = front;
			front->_prev = newnode;
		}

2. pop_front()

在这里插入图片描述

  • 代码:
void pop_front()
		{
			Node* front = _head->_next;
			Node* frontNext = front->_next;
			// _head  front  frontNext
			_head->_next = frontNext;
			frontNext->_prev = _head;
			delete front;
		}

3. push_back()

在这里插入图片描述

  • 代码:
void push_back(const T& val)
		{
			Node* tail = _head->_prev;
			// _head  tail newnode
			Node* newnode = new Node(val);
			tail->_next = newnode;
			newnode->_prev = tail;
			newnode->_next = _head;
			_head->_prev = newnode;
		}

4. pop_back()

在这里插入图片描述

  • 代码:
void pop_back()
		{
			Node* tail = _head->_prev;
			Node* tailPrev = tail->_prev;
			// tailPrev tail _head
			tailPrev->_next = _head;
			_head->_prev = tailPrev;
			delete tail;
		}

综合测试尾插尾删,头插,头删

  • 代码:
void test_list3()
	{
		list<int> 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);

		for (auto& e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		lt.push_front(1);
		lt.push_front(2);
		lt.push_front(3);
		lt.push_front(4);
		lt.push_front(5);
		lt.push_front(6);

		for (auto& e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		lt.pop_back();
		lt.pop_back();

		for (auto& e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		lt.pop_front();
		lt.pop_front();

		for (auto& e : lt)
		{
			cout << e << " ";
		}
		cout << endl;


	}

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

5. insert()在这里插入图片描述

在这里插入图片描述

insert()函数主要是使用迭代器进行插入,就是通过迭代器找到对应的插入位置,注意,首先我们应该先通过迭代器找到这个迭代器对应的指针,然后通过这个指针找到插入的位置,insert(pos,val)就是在pos位置的前面插入val,插入的过程中,在插入前和插入后,pos迭代器指向的都是同一个结点,所以list的插入不会导致迭代器失效,insert函数最终返回新插入结点的迭代器。

  • 代码:
iterator insert(iterator pos, const T& val)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;

			// prev newnode cur
			Node* newnode = new Node(val);
			// 链接指针
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			return iterator(newnode);
		}

void test_list4()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);
		for (auto& e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		list<int>::iterator lit = lt.begin();
		while (lit != lt.end())
		{
			if (*lit == 3)
			{
				lit = lt.insert(lit, *lit * 10);
				lit++;
			}
			lit++;
		}
		
		for (auto& e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		// 在所有偶数前面插入这个偶数的10倍
		list<int>::iterator lit1 = lt.begin();
		while (lit1 != lt.end())
		{
			if (*lit1 % 2 == 0)
			{
				lit1 = lt.insert(lit1, *lit1 * 10);
				lit1++;
			}
			lit1++;
		}
		
		for (auto& e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

	}

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

6.erase()

在这里插入图片描述
erase()函数中会删除pos迭代器指向的那个结点,所以调用erase函数之后会产生迭代器失效的问题,所以在使用的时候我们需要注意迭代器失效的问题,erase函数调用之后会返回删除的结点的下一个结点的迭代器。

  • 代码:
iterator erase(iterator pos)
		{
			assert(pos != end());
			// end()位置是头结点,不能将头结点删除
			// 找到删除的结点
			Node* cur = pos._node;
			// 找到前一个结点
			Node* prev = cur->_prev;
			// 找到下一个结点
			Node* next = cur->_next;

			// prev cur next
			prev->_next = next;
			next->_prev = prev;
			delete cur;
			return iterator(next);
		}

void test_list5()
	{
		list<int> 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);

		for (auto& e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		lt.erase(lt.begin());
		lt.erase(--lt.end());

		for (auto& e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		// 删除所有奇数
		list<int>::iterator lit = lt.begin();
		while (lit != lt.end())
		{
			if (*lit % 2)
			{
				lit = lt.erase(lit);
			}
			lit++;
		}

		for (auto& e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

	}
		

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

7. clear()

在这里插入图片描述
这个函数负责将list中的有效结点清空

  • 代码:
void clear()
		{
			list<int>::iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}

void test_list6()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(1);
		lt.push_back(1);
		lt.push_back(1);

		for (auto& e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		lt.clear();
		for (auto& e : lt)
		{
			cout << e << " ";
		}
		cout << endl;
	}

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

七、list经典题目

  1. reverse的使用
  • 题目:
    在这里插入图片描述
  • 选项:
    在这里插入图片描述

分析:
本题主要考察list中的构造函数(使用一段迭代器区间进行初始化),还有reverse(),参数是一段迭代器区间,注意迭代器区间都是左闭右开的,所以一定要注意逆置的边界,第一次逆置传的参数是:(mylist.begin(),pos),所以实际逆置的范围是:(mylist.begin(),pos-1),所以此时会将0-4进行逆置,变成4321056789,接下来的逆置传的是(pos,mtlist.end()),所以实际逆置的范围:(pos,--mylist.end()),也就是对56789进行逆置,所以最终的结果是:4321098765,然后再反向输出:5678901234。故本题选:C

自己实现的代码:

void test_list2()
{
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	int n = sizeof(arr) / sizeof(int);
	list<int> mylist(arr, arr + n);

	list<int>::iterator pos = find(mylist.begin(), mylist.end(), 5);
	if (pos != mylist.end())
	{
		reverse(mylist.begin(), pos);
		reverse(pos, mylist.end());
	}
	list<int>::const_reverse_iterator crit = mylist.rbegin();
	while (crit != mylist.rend())
	{
		cout << *crit << " ";
		crit++;
	}
	cout << endl;
	
}

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

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

  1. 删除list中的重复项
  • 题目:
    在这里插入图片描述
  • 选项
    在这里插入图片描述
    分析:
    本题的思路是:首先使用cur遍历整个链表,刚开始先保存第一个结点的值,然后使cur和p都从第二个开始,然后不断遍历后面的结点,如果此时p指向的结点和保存的值是相等,那么此时就是重复的值,接下来可能出现两种情况:cur和p指向同一个结点,cur和p指向不同的结点,如果cur和p指向同一个结点,删除当前p指向的结点,同时更新cur和p指向下一个结点。如果cur和p指向不同的结点,删除当前p指向的结点,更新p指向下一个结点。更新cue和p是通过list.erase()的返回值来实现的。
    自己实现的代码:
// 删除链表中的重复项
template <typename T>
void RemoveDuplicate(list<T>& mylist)
{
	// 保存每一次不重复值的变量
	T value;

	// 迭代器遍历链表的
	typename list<T>::iterator cur, p;
	cur = mylist.begin();
	while (cur != mylist.end())
	{
		value = *cur;
		p = ++cur;
		while (p != mylist.end())
		{
			if (*p == value)
			{
				if (p == cur)
				{
					p = cur = mylist.erase(p);
				}
				else
				{
					p = mylist.erase(p);
				}
			}
			else
			{
				p++;
			}
		}
	}
}

void test_list1()
{
	list<int> mylist;
	mylist.push_back(1);
	mylist.push_back(2);
	mylist.push_back(1);
	mylist.push_back(1);
	mylist.push_back(3);
	mylist.push_back(5);
	mylist.push_back(5);

	list<int>::iterator lit = mylist.begin();
	while (lit != mylist.end())
	{
		cout << *lit << " ";
		lit++;
	}
	cout << endl;
	RemoveDuplicate(mylist);
	for (auto& e : mylist)
	{
		cout << e << " ";
	}
	cout << endl;
}

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

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

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值