C++ [stack & queue (3 types) & reverse iterator]

1. Container Adapter

Adapter is a design pattern (a design pattern is a set of repeated use, known to most people, classified and cataloged, and a summary of code design experience), which converts the interface of a class into another that customers want. interface.
The stacks, queues, and reverse iterators described later are all adapters.
Although elements can also be stored in stack and queue, they are not divided into the ranks of containers in STL, but are called container adapters, because stacks and queues only wrap the interfaces of other containers, STL The underlying implementation of stack and queue in the middle layer uses deque by default. deque double-ended queue. As shown below:
insert image description here
insert image description here

Two, the stack

(1) Stack definition

stack is a container adapter, which is specially used in the context environment with last-in-first-out operation, and its deletion can only insert and extract elements from one end of the container. The stack is implemented as a container adapter, which encapsulates a specific class as its underlying container, and provides a set of specific member functions to access its elements, using a specific class as its underlying, element-specific container tail ( i.e. the top of the stack) is pushed and popped. The underlying container of the stack can be any standard container class template or some other specific container classes . These container classes should support the following operations:
empty: null operation
back: get tail element operation
push_back: tail insert element operation
pop_back: tail delete element Operation
The standard containers vector, deque, and list all meet these requirements. By default, if no specific underlying container is specified for stack, deque is used by default.

(2) Stack usage interface

stack(): construct an empty stack
empty(): check whether the stack is empty
size(): return the number of elements in the stack
top(): return the reference to the top element of the stack
push(): push the element val into the stack
pop () : Pop the element at the end of the stack

(3) Implementation of stack simulation

Here, the underlying container of my stack is implemented with vector.

(1) Analysis of stack simulation implementation

#pragma once

namespace nza
{
    
    
	template<class T, class Container=vector<int>>
	class stack
	{
    
    
	public:
		void push(const T& x)
		{
    
    
			_co.push_back(x);
		}
		void pop()
		{
    
    
			_co.pop_back();
		}
		const T& top()
		{
    
    
			return _co.back();
		}
		size_t size()
		{
    
    
			return _co.size();
		}
		bool empty()
		{
    
    
			return _co.empty();
		}
	private:
		Container _co;
	};
}

First, replace the template class container deque in the template with vector, and instantiate the object in the private domain in the stack class. Because the stack can only be operated at one end, you have to insert and delete the top element of the stack, which corresponds to tail insertion and For tail deletion, you can directly call the vector tail insertion and tail deletion interfaces. To get the top of the stack, you can directly call the vector's queue tail interface. The size and empty judgment are also the interfaces of vector.

(2) Stack simulation implementation code


#pragma once

namespace nza
{
    
    
	template<class T, class Container=vector<int>>
	class stack
	{
    
    
	public:
		void push(const T& x)
		{
    
    
			_co.push_back(x);
		}
		void pop()
		{
    
    
			_co.pop_back();
		}
		const T& top()
		{
    
    
			return _co.back();
		}
		size_t size()
		{
    
    
			return _co.size();
		}
		bool empty()
		{
    
    
			return _co.empty();
		}
	private:
		Container _co;
	};
}

#include"simulate_stack.h"
#include<iostream>
#include<vector>
#include<list>
using namespace std;

void test1()
{
    
    
	nza::stack<int> s;
	s.push(6);
	s.push(1);
	s.push(8);
	s.push(3);
	s.push(7);
	while (!s.empty())
	{
    
    
		cout << s.top() << " ";
		s.pop();
	}
	cout << endl;
}
int main()
{
    
    
	test1();
}

(3) Stack simulation results

insert image description here

3. Queue

(1) Ordinary queue

(1) Ordinary queue definition

A queue is a container adapter designed to operate in a FIFO context first-in-first-out , where elements are inserted from one end of the container and extracted from the other. A queue is implemented as a container adapter, which encapsulates a specific container class as its underlying container class, and queue provides a specific set of member functions to access its elements. Elements enter the queue from the tail and dequeue from the head. The underlying container can be one of the standard container class templates, or another specially designed container class. The underlying container should at least support the following operations:
empty: check whether the queue is empty
size: return the number of valid elements in the queue
front: return the reference of the element at the head of the queue
back: return the reference of the element
at the end of the queue push_back: enter the queue at the end of the queue
pop_front : Dequeue at the head of the queue
The standard container classes deque and list meet these requirements. By default, if no container class is specified for queue instantiation, the standard container deque is used.

(2) Queue usage interface

queue(): Construct an empty queue
empty(): Detect whether the queue is empty, return true if yes, otherwise return false
size(): return the number of valid elements in the queue
front(): return the reference of the element at the head of the queue
back() : Return the reference of the element at the end of the queue
push(): Put the element val into the queue at the end of the queue
pop(): Dequeue the element at the head of the queue

(3) Ordinary queue simulation implementation

(1) Analysis of ordinary queue simulation implementation


#pragma once

namespace nza
{
    
    
	template<class T, class Container = list<int>>
	class queue
	{
    
    
	public:
		void push(const T& x)
		{
    
    
			_co.push_back(x);
		}
		void pop()
		{
    
    
			_co.pop_front();
		}
		const T& front()
		{
    
    
			return _co.front();
		}
		const T& back()
		{
    
    
			return _co.back();
		}
		size_t size()
		{
    
    
			return _co.size();
		}
		bool empty()
		{
    
    
			return _co.empty();
		}
	private:
		Container _co;
	};
}

Similarly, the queue here is to first replace the template class container deque in the template with vector, and instantiate the object in the private field in the queue class. Because the stack queue is first in first out, there must be insertion and deletion of head elements, corresponding to For tail insertion and head deletion, you can directly call the vector tail insertion and head deletion interfaces. To fetch the head and tail of the queue, you can directly call the vector’s head fetch interface and tail fetch interface. Size and empty judgment are also the vector’s interfaces .

(2) Ordinary queue simulation implementation code


#pragma once

namespace nza
{
    
    
	template<class T, class Container = list<int>>
	class queue
	{
    
    
	public:
		void push(const T& x)
		{
    
    
			_co.push_back(x);
		}
		void pop()
		{
    
    
			_co.pop_front();
		}
		const T& front()
		{
    
    
			return _co.front();
		}
		const T& back()
		{
    
    
			return _co.back();
		}
		size_t size()
		{
    
    
			return _co.size();
		}
		bool empty()
		{
    
    
			return _co.empty();
		}
	private:
		Container _co;
	};
}

#include"simulate_queue.h"
#include<iostream>
#include<vector>
#include<list>

using namespace std;

void test1()
{
    
    
	nza::queue<int> s;
	s.push(9);
	s.push(7);
	s.push(5);
	s.push(3);
	while (!s.empty())
	{
    
    
		cout << s.front() << " ";
		s.pop();
	}
	cout << endl;

}
int main()
{
    
    
	test1();
}

(3) Common queue simulation results

insert image description here

(2) Priority queue

(1) Priority queue definition

A priority queue is also a container adapter whose first element is always the largest among the elements it contains according to strict weak ordering criteria. This context is similar to a heap, where elements can be inserted at any time, and only the largest heap element (the one at the top in the priority queue) can be retrieved. The priority queue is implemented as a container adapter, which encapsulates a specific container class as its underlying container class, and queue provides a set of specific member functions to access its elements. Elements are popped from the "tail" of a particular container, which is called the top of the priority queue. The underlying container can be any standard container class template, or any other container class of a specific design. The container should be accessible via a random access iterator and support the following operations:
empty(): check if the container is empty
size(): return the number of valid elements in the container
front(): return a reference to the first element in the container
push_back( ): Insert elements at the end of the container
The standard container classes vector and deque satisfy these requirements. By default, vector is used if no container class is specified for a particular priority_queue class instantiation. Random access iterators need to be supported so that the heap structure is always maintained internally. The container adapter does this automatically by automatically calling the algorithmic functions make_heap, push_heap, and pop_heap when needed.

(2) Priority queue interface

priority_queue()/priority_queue(first,last): construct an empty priority queue
empty( ): detect whether the priority queue is empty, return true if it is, otherwise return false
top( ): return the largest (smallest) element in the priority queue ), that is, the top element
push(x): insert element x in the priority queue
pop(): delete the largest (smallest) element in the priority queue, that is, the top element

(3) Priority queue simulation implementation

(1) Analysis of priority queue simulation implementation


#pragma once
namespace nza
{
    
    
	template<class T>
	struct less
	{
    
    
		bool operator()(const T& x, const T& y)
		{
    
    
			return x < y;
		}

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

	template<class T ,class container= vector<T>,class compare=less<T>>
	class priority_queue
	{
    
    
	public:
		void AjustDown(size_t parent)
		{
    
    
			compare comp;
			size_t child = 2 * parent + 1;
			while (child<_con.size())
			{
    
    
				if (child + 1 < _con.size() && comp(_con[child] ,_con[child + 1]))
				{
    
    
					++child;
				}
				if (comp(_con[parent], _con[child]))
				{
    
    
					swap(_con[child], _con[parent]);
					parent = child;
					child = 2 * parent + 1;
				}
				else
				{
    
    
					break;
				}
			}
		}
		void AjustUp(int child)
		{
    
    
			compare comp;
			int parent = (child-1)/ 2;
			while (child>0)
			{
    
    
				if (comp(_con[parent], _con[child]))
				{
    
    
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
    
    
					break;
				}

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

		}
		void pop_back()
		{
    
    
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			AjustDown(0);

		}
		const T& top()
		{
    
    
			return _con[0];
		}
		size_t size()
		{
    
    
			return _con.size();
		}
		bool empty()
		{
    
    
			return _con.empty();
		}
	private:
		container _con;
	};
}

Less and greater are functors. Less builds a large root heap, and the final sorting result is from large to small. Greater builds a small root heap, and the final sorting result is from small to large.
1. Functor : It defines an object with a member function of operator(), which can be regarded as a general function, but the function of this function is implemented in the operator operator() in a class, and it is a function object. It uses the function as a way of passing parameters. Two of the advantages are used here : the execution speed of the functor is faster than that of the function pointer, and the function pointer is called by the address, while the functor overloads the operator operator. Improves the efficiency of calls and functors can be used as template parameters, because each functor has its own type.

For example, the Compare com here instantiates an object, and Compare here controls the comparison method, passing greater, _con is the object of greater, and it calls operator(), which is the comparison method of greater than.

The above Compare is a generic type, if you pass less, it has nothing to do with greater, com is a less object. I can use whatever is passed. I am a less object and I call the less oparator. The template passes the type, and the functor is a class, so it can be passed as a template parameter, and the entire class can be used. If it is a function parameter, it is used to pass a certain function somewhere. It can pass the entire class, and the whole can be used.

2. Insertion: , insert at the tail, and then adjust upward, from the last position of _con.size() - 1. At this time, we need to write an upward adjustment algorithm.
Upward adjustment algorithm :
What I implement here is a small root heap. The upward adjustment algorithm idea is to start from the child node, and compare the size of the child node and the father node from bottom to top. If it is smaller than the father node, the parent-child node is exchanged. , and update the child node, that is, let the child node go to the position of the father, and find out the size of the father at this time, and continue to compare until the child node is 0, that is, to the root; otherwise, if it is larger than the father node Just stop the cycle, no need to adjust.

3. Deletion : If you cannot move the data directly, the time complexity will increase. You should first exchange the first and last data, then delete the data at the end, and finally adjust downward. At this time, you need to write a downward adjustment algorithm.
Downward adjustment algorithm :
starting from the root node, which is the parent node, and the largest comparison with his two child nodes, adjust from top to bottom. I first assume that the left child child is the largest, and then use an if function to compare. If child + 1 < n, ensure that there is a right child, and if child+1 is less than child, it means that the left child is small, so ++. Afterwards, if the smallest child is smaller than the parent node, then adjust and update the parent node, let the father go to the position of the child node, and find the subscript of its next child node, and stop if it is larger.
4. Get the top element of the heap : that is, directly return the data corresponding to the subscript 0.
5. Size and space judgment : Size and space judgment are also interfaces for directly calling vector.

(2) Priority queue simulation implementation interface


#pragma once
namespace nza
{
    
    
	template<class T>
	struct less
	{
    
    
		bool operator()(const T& x, const T& y)
		{
    
    
			return x < y;
		}

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

	template<class T ,class container= vector<T>,class compare=less<T>>
	class priority_queue
	{
    
    
	public:
		void AjustDown(size_t parent)
		{
    
    
			compare comp;
			size_t child = 2 * parent + 1;
			while (child<_con.size())
			{
    
    
				if (child + 1 < _con.size() && comp(_con[child] ,_con[child + 1]))
				{
    
    
					++child;
				}
				if (comp(_con[parent], _con[child]))
				{
    
    
					swap(_con[child], _con[parent]);
					parent = child;
					child = 2 * parent + 1;
				}
				else
				{
    
    
					break;
				}
			}
		}
		void AjustUp(int child)
		{
    
    
			compare comp;
			int parent = (child-1)/ 2;
			while (child>0)
			{
    
    
				if (comp(_con[parent], _con[child]))
				{
    
    
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
    
    
					break;
				}

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

		}
		void pop_back()
		{
    
    
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			AjustDown(0);

		}
		const T& top()
		{
    
    
			return _con[0];
		}
		size_t size()
		{
    
    
			return _con.size();
		}
		bool empty()
		{
    
    
			return _con.empty();
		}
	private:
		container _con;
	};
}
#include"Simulate_PriorityQueue.h"
#include<iostream>
#include<vector>
using namespace std;
void test()
{
    
    
	nza::priority_queue<int, vector<int>,nza::greater<int>> q;
	/*nza::priority_queue<int> q;*/
	q.push_back(8);
	q.push_back(4);
	q.push_back(2);
	q.push_back(9);
	q.push_back(6);
	q.push_back(1);
	while (!q.empty())
	{
    
    
		cout << q.top() << " ";
		q.pop_back();
	}
	cout << endl;
}
int main()
{
    
    
	test();
	return 0;

}

(3) Simulation results of priority queues

insert image description here

(3) Double-ended queue

Introduction:
deque is a double-ended queue, which is a double-opened "continuous" space data structure. The meaning of double-opened is: insertion and deletion can be performed at both ends of the head and tail, and the time complexity is O(1). Compared with vector, it has high header insertion efficiency and does not need to move elements; compared with list, it has higher space utilization. Deque is not a real continuous space, but is spliced ​​by segments of continuous small spaces. The actual deque is similar to a dynamic two-dimensional array.

Structural features:
The double-ended queue is actually a central controller, which is actually an array of pointers, and the insertion is performed from the middle. When the central control is full, it will be expanded, but the expansion cost is low. Its bottom layer is an illusion of continuous space, which is actually segmented and continuous. In order to maintain its "overall continuity" and the illusion of random access, it falls on the deque iterator, so the design of the deque iterator is more complicated. Four pointers are encapsulated inside, the first is cur pointing to the current data position, the second is finish pointing to the first position of the buffer, the third is last pointing to the end of the buffer, and the fourth is node pointing to the central control buffer pointer on the device.

Advantages:
Compared with vector, the cost of capacity expansion is low, and the efficiency of head-plug deletion and tail-plug deletion is high, and random access is also supported.

Disadvantages:
Compared with vector, the advantage of deque is that when the head is inserted and deleted, there is no need to move elements, and the efficiency is particularly high, and when expanding, there is no need to move a large number of elements, so its efficiency must be high. Compared with list, its bottom layer is a continuous space, the space utilization rate is relatively high, and no additional fields need to be stored. However, deque has a fatal flaw: it is not suitable for traversal, because when traversing, the iterator of deque needs to frequently check whether it has moved to the boundary of a small space, resulting in low efficiency. In sequential scenarios, it may be necessary to frequently Traversal, so in practice, when a linear structure is required, vector and list are given priority in most cases. There are not many applications of deque, and one application that can be seen so far is that STL uses it as the underlying data structure of stack and queue . Insertion and deletion in the middle is tricky. There are no vector and list extremes.

Reasons for choosing deque as the underlying default container of stack and queue:
stack is a special last-in-first-out linear data structure, so as long as it has a linear structure with push_back() and pop_back() operations, it can be used as the underlying container of stack, such as Both vector and list are acceptable; queue is a special linear data structure of first-in-first-out, as long as it has a linear structure with push_back and pop_front operations, it can be used as the underlying container of queue, such as list. However, in STL, deque is selected as the underlying container for stack and queue by default, mainly because:
1. Stack and queue do not need to be traversed (because stack and queue do not have iterators), and only need to be operated at one or both fixed ends.
2. When the elements in the stack grow, deque is more efficient than vector (no need to move a large amount of data when expanding); when the elements in the queue grow, deque not only has high efficiency, but also has high memory usage. Combining the advantages of deque, it perfectly avoids its defects.

Fourth, the reverse iterator

(1) Reverse iterator implementation ideas and analysis

A reverse iterator is an iterator that traverses a container in reverse, and is an adapter for a normal iterator. By redefining the self-increment and self-decrement operations, the purpose of traversing elements in reverse order is achieved.

#pragma once
namespace nza
{
    
    
	template<class iterator, class re, class p>
	struct Reverseiterator
	{
    
    
		typedef Reverseiterator<iterator,re, p> self;
		iterator _cur;
		Reverseiterator(iterator it)
			:_cur(it)
		{
    
    }
		re operator*()
		{
    
    
			iterator tmp(_cur);
			--tmp;
			return *tmp;
		}
		self&  operator++()
		{
    
    
			--_cur;
			return *this;
		}
		self& operator--()
		{
    
    
			++_cur;
			return *this;

		}
		bool operator!=(const self& s)
		{
    
    
			return _cur!= s._cur;

		}
	};
}

Implementation idea:
The normal idea is to change the forward iterator implemented by the original list to reverse, and then make corresponding modifications to other class functions.
But this can only achieve the reverse iterator of the container we are currently implementing. If we change the vector container, it will not work, because its forward iterator is a built-in type, and we cannot directly copy cv and change it directly, unless it does not use native pointers. Encapsulate iterators like lists. At the beginning, it was said that the reverse iterator is a kind of adapter. Now we need to make this reverse iterator adapt not only to vector, but also to list, so as to achieve real reuse.

The above code refers to the idea of ​​hand-written code in the stl source code, mainly to get rid of the problem of redundancy and repetition, and to become more generic, that is, to adapt to various containers more widely.
The idea here is to use forward iterators to encapsulate reverse iterators, so we don’t need to care whether our forward iterators are native or encapsulated. In this way, a reverse iterator is implemented, and the reverse iterators of all containers come out. The premise is a bidirectional iterator that supports subtraction. Passing the forward direction of the list adapts to the reverse direction of the list, passing the forward direction of the vector adapts to the reverse direction of the vector, followed by deque, etc., to solve the problem once and for all, when we still think about realizing the reverse of the list When dealing with iterators, the master's thinking is to solve the reverse iterator with containers.
Function analysis:
1. The first parameter of the template is an iterator of any type, the second parameter re is the return value type in the iterator, the third parameter is the return value type of the overloaded arrow symbol, and then use the forward iterator Instantiates an object _cur. The initialization constructor initializes _cur with a forward iterator.
2. Its dereferencing does not take the current position, it is the previous position, because the original rend is at the sentinel position, rbegin is at the end, at this time the rend is at the next evil node of the sentinel position, and rbegin is at the sentinel position node Above, of course this is a list, and the vector is the same, except that it is an array without a head node , so when traversing, you need to subtract and subtract first if you want to start from the last position. Here, in order to traverse the next bit of data smoothly, it is necessary to copy a temporary variable first and then subtract it, and because the temporary variable is constant, there must be a reference return (re is useless here, because it has been passed in the implementation of the container class) As shown in Figure 2). The above design features are actually to pursue a symmetry, as shown in Figure 1 below:
insert image description here
insert image description here

3. Similarly, the reverse iterator supports movement. The ++ that implements the reverse iterator is - -, and the implementation - - is ++, which also supports comparison.

(2) Reverse iterator implementation code (take vector as an example)

#pragma once
namespace nza
{
    
    
	template<class iterator, class re, class p>
	struct Reverseiterator
	{
    
    
		typedef Reverseiterator<iterator,re, p> self;
		iterator _cur;
		Reverseiterator(iterator it)
			:_cur(it)
		{
    
    }
		re operator*()
		{
    
    
			iterator tmp(_cur);
			--tmp;
			return *tmp;
		}
		self&  operator++()
		{
    
    
			--_cur;
			return *this;
		}
		self& operator--()
		{
    
    
			++_cur;
			return *this;

		}
		bool operator!=(const self& s)
		{
    
    
			return _cur!= s._cur;

		}
	};
}
# pragma once
#include<assert.h>
#include<algorithm>
//#include<vector>
#include<string>
#include"ReverseIterator.h"
namespace nza
{
    
    

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

		 typedef Reverseiterator<iterator, T&,const T*> reverse_iterator;
		 typedef Reverseiterator<iterator, const T&, const T* > const_reverse_iterator;

		 vector()//构造函数
		 {
    
    }
		 ~vector()
		 {
    
    
			 delete[] _start;
			 _start = _finish = _end_capacity = nullptr;
		 }
		 iterator begin()
		 {
    
    
			 return _start;
		 }
		 iterator end()
		 {
    
    
			 return _finish;

		 }
		 reverse_iterator rbegin()
		 {
    
    
			 return  reverse_iterator(end());
		 }
		 reverse_iterator rend()
		 {
    
    
			 return  reverse_iterator(begin());
		 }
		 const_iterator begin() const
		 {
    
    
			 return _start;

		 }
		 const_iterator end() const
		 {
    
    
			 return _finish;
		 }
		size_t size() const
		 {
    
    
			 return _finish - _start;
		 }
		size_t capacity() const
		{
    
    
			return _end_capacity - _start;

		}
		 
		
		 vector(const vector<T>& v)
		 {
    
    
			 /*_start = new T[v.capacity()];
			 for (size_t i = 0; i < v.size(); ++i)
			 {
				 _start[i] = v._start[i];
			 }
			 _finish = _start + v.size();
			 _end_capacity = _start + v.capacity();*/
			 vector<T> tmp(v.begin(), v.end());
			 swap(tmp);
		 }
		void swap(vector<T>& v)
		 {
    
    
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_capacity, v._end_capacity);
		 }
		 vector<T>& operator = (vector<T> v)
		 {
    
    
			/* if (this != &v)
			 {
				 T* tmp = new T[v.capacity()];
				 memcpy(tmp, v._start, sizeof(T)*v.size());
				 delete[] _start;
				 _start = tmp;
				 _finish = _start + v.size();
				 _end_capacity = _start + v.capacity();
			 }
			 return *this; */ //这是常规思路,可以复用swap函数
			 swap(v);
			 return *this;
		 } 

		 vector(size_t n, const T& val = T())
		 {
    
    
			 reserve(n);
			 for (size_t i = 0; i<n; ++i)
			 {
    
    
				 push_back(val);
			 }
		 }
		 vector(int n, const T& val = T())
		 {
    
    
			 reserve(n);
			 for (int i = 0; i<n; ++i)
			 {
    
    
				 push_back(val);
			 }
		 }
		 template<class InputIterator>
		 vector(InputIterator first, InputIterator last)
		 {
    
    
			 while (first != last)
			 {
    
    
				 push_back(*first);
				 ++first;
			 }
		 }


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

		 }
		 void reserve(size_t n)
		 {
    
    
			 if (n > capacity())
			 {
    
    
				 T* tmp = new T[n];
				 size_t size1 = size();
				 if (_start)
				 {
    
    
					 for (size_t i = 0; i < size1; ++i)
					 {
    
    
						 tmp[i] = _start[i];
					 }
				     delete[] _start;
				 }
				 _start = tmp;
				 _finish = _start + size1;
				 _end_capacity = _start + n;
			 }
		 }
		 void push_back(const T& x)
		 {
    
    
			 if (_finish == _end_capacity)
			 {
    
    
				 reserve(capacity() == 0 ? 4 : capacity() * 2);
			 }
			 *_finish = x;
			 ++_finish;
		 }
		 void pop_back()
		 {
    
    
			 assert(!empty());
			 --_finish;
		 }
		 iterator insert(iterator pos, const T& val)
		 {
    
    
			 assert(pos <= _finish);
			 assert(pos >= _start);
			 if (_finish == _end_capacity)
			 {
    
    
				 int len = pos - _start;
				 reserve(capacity() == 0 ? 4 : capacity() * 2);
				 pos = _start + pos;
			 }
			 iterator end = _finish - 1;
			 while(end>=pos)
			 {
    
    
				 *(end+1) = *end;
				 --end;
			 }
			 *pos = val;
			 ++_finish;
			 return pos;
		 }
		iterator erase(iterator pos)
		 {
    
    
			 assert(pos <= _finish);
			 assert(pos >= _start);
			 itetator first = pos + 1;
			 while (first!=_finish )
			 {
    
    
				 *(first - 1) = *first;
				 ++first;
			 }
			 --_finish;
			 return pos;
		 }
		 bool empty()
		 {
    
    
			 return _start == _finish;

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

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

		 }
	 private:
		 iterator _start=nullptr;
		 iterator _finish=nullptr;
		 iterator _end_capacity=nullptr;
	 };



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



class Solution {
    
    
public:
	nza::vector<nza::vector<int>> generate(int numRows) {
    
    
		nza::vector<nza::vector<int>> vv;
		vv.resize(numRows, nza::vector<int>());
		for (size_t i = 0; i < vv.size(); ++i)
		{
    
    
			vv[i].resize(i + 1, 0);
			vv[i][0] = vv[i][vv[i].size() - 1] = 1;
		}

		for (size_t i = 0; i < vv.size(); ++i)
		{
    
    
			for (size_t j = 0; j < vv[i].size(); ++j)
			{
    
    
				if (vv[i][j] == 0)
				{
    
    
					vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
				}
			}
		}

		return vv;
	}
};

void test1()
{
    
    
	nza::vector<int> v1;
	v1.push_back(14);
	v1.push_back(15);
	v1.push_back(16);
	v1.push_back(17);
	v1.push_back(18);


	for (size_t i = 0; i < v1.size(); ++i)
	{
    
    
		cout << v1[i] << " ";
	}
	cout << endl;

	v1.pop_back();
	v1.pop_back();

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

	v1.pop_back();
	v1.pop_back();

	for (auto v : v1)
	{
    
    
		cout << v << " ";
	}
	cout << endl;

}
void test2()
{
    
    
	std::string s1("hello");
	nza::vector<int> v3(s1.begin(), s1.end());
	for (auto e : v3)
	{
    
    
		cout << e << " ";
	}
	cout << endl;
}
void test3()
{
    
    
	nza::vector<std::string> v3(3, "dddddddddd");
	for (auto e : v3)
	{
    
    
		cout<<e<<" ";
	}
	cout << endl;
	nza::vector<std::string> v4(v3);
	for (auto e : v4)
	{
    
    
		std::cout << e << " ";
	}
	cout << endl;

	v4.push_back("kkkkkkkkkkk");
	v4.push_back("kkkkkkkkkkk");
	v4.push_back("kkkkkkkkkkk");
	for (auto e : v4)
	{
    
    
		cout << e << " ";
	}
	cout << endl;
}
void test4()
{
    
    

	nza::vector<nza::vector<int>> ret =Solution().generate(5);
	for (size_t i = 0; i < ret.size(); ++i)
	{
    
    
		for (size_t j = 0; j < ret[i].size(); ++j)
		{
    
    
			cout << ret[i][j] << " ";
		}
		cout << endl;
	}
	cout << endl;

}
void test5()
{
    
    
	nza::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	nza::vector<int>::reverse_iterator  s= v.rbegin();
	while (s != v.rend())
	{
    
    
		cout << *s << " ";
		++s;
	}
	cout << endl;

}
int main()
{
    
    
	test1();
	test2();
	test3();
	test4();
	test5();
	return 0;
}

(3) The reverse iterator realizes the result

insert image description here

Guess you like

Origin blog.csdn.net/m0_59292239/article/details/129974627