C++万字总结(中篇)

本文是C++程序员的万字总结,深入探讨STL中的vector、string、list、stack、queue等容器的模拟实现及迭代器失效问题。特别讨论了在list删除操作时如何避免迭代器失效,并解释为何在stack和queue中常选用deque作为底层容器。同时,文章还介绍了二叉搜索树和AVL树的模拟实现。
摘要由CSDN通过智能技术生成

菜狗程序员 C++ 万字总结,从入门到入土(中篇)

STL

vector

模拟实现

#include <iostream>

namespace Schuyler
{
	// 因为 vector 容器可以存放任意类型,所以要用模板
	template <class T>
	class vector
	{
	public:
		// 用迭代器包装任意类型的指针
		typedef T* iterator;
		typedef const T* const_iterator;
		// begin 返回空间起始地址
		iterator begin()
		{
			return _start;
		}
		// end 返回最后一个有效数据的下个空间的地址
		iterator end()
		{
			return _finish;
		}
		// const 对象调用,只有读权限,不能写
		const_iterator begin() const
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}
		// 扩容
		void reserve(int n)
		{
			if (capacity() < n)
			{
				int sz = size();
				// 开辟新空间
				T* p = new T[n];
				if (_start)
				{
					// 旧空间数据拷贝到新空间
					for (int i = 0; i < sz;i++)
					{
						p[i] = _start[i];
					}					
				}
				// 释放旧空间
				delete[] _start;
				// 指针指向新空间
				_start = p;
				_finish = p + sz;
				_endOfStorage = _start + n;
			}
		}
		// 调整 size
		void resize(int n, T val = T())
		{
			// 要求调整的 size 小于 有效数据个数,则直接移动 _finish
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				// 要求的 size 大于 容量,则另外开辟一块空间
				if (n > capacity())
				{
					reserve(n);
				}
				// 从数据末尾存放新的数据 val
				while (_finish < _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
		}
		// 数据尾插就是往 _finish 处添数据
		void push_back(const T& val)
		{
			if (capacity() == size())
			{
				const int newCapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newCapacity);
			}
			*_finish = val;
			++_finish;
		}
		// 尾删
		void pop_back()
		{
			if (empty())
				throw new _exception{ 1, "容器为空" };

			--_finish;
		}
		// 任意位置插入
		void insert(iterator pos, const T& val)
		{
			// 判断是否需要扩容
			if (_finish == _endOfStorage)
			{
				// 计算 pos 离起始位置的距离,方便扩容后找到新空间的 pos
				int len = pos - _start;
				
				int newCapacity = capacity() == 0 ? 4 : capacity * 2;
				reserve(newCapacity);
			}
			pos = _start + len;

			// 向后挪动数据,插入新输入
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = val;
			++_finish;
		}
		// 删除任意位置元素
		iterator erase(iterator pos)
		{
			iterator it = pos + 1;
			while (it != _finish)
			{
				*(it - 1) = *it;
				++it;
			}
			--_finish;
			return pos;
		}
		
		// 构造函数给三个成员变量赋初值
		vector()
		{
			_start = nullptr;
			_finish = nullptr;
			_endOfStorage = nullptr;
		}

		// 使用迭代器构造 vector
		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		// 拷贝构造
		vector(const vector<T>& v)
		{
			_start = new T[v.capacity()];
			for (int i = 0; i < v.size(); i++)
			{
				_start[i] = v._start[i];
			}
			_finish = _start + v.size();
			_endOfStorage = _start + v.capacity();
		}
	private:
		void swap(vector<T>& v)
		{
			_start = v._start;
			_finish = v._finish;
			_endOfStorage = v._endOfStorage;
		}
	public:
		// 赋值运算符
		vector<T>& operator=(vector<T> v)
		{
			swap(v);
			
			return *this;
		}
		// 析构函数
		~vector()
		{
			if (_start)
			{
				delete[] _start;
			}
			_start = _finish = _endOfStorage = nullptr;
		}
		// vector 索引
		T& operator[](int i)
		{
			if (i >= size())
				throw new _exception{ 0, "索引错误" };

			return _start[i];
		}
		
		const T& operator[](int i)const
		{
			if (i >= size())
				throw new _exception{ 0, "索引错误" };

			return _start[i];
		}
		
		
	private:
		// 指向数组空间起始位置
		iterator _start;
		// 指向数组空间中有效数据末尾的下一个位置
		iterator _finish;
		// 指向数组空间的末尾
		iterator _endOfStorage;
	public:
		// 有效数据个数
		int size() const
		{
			return _finish - _start;
		}
		// 容器空间大小
		int capacity() const
		{
			return _endOfStorage - _start;
		}
		// 判断容器是否为空
		bool empty()
		{
			return _finish == _start;
		}
	};
}


vector 迭代器失效问题:

引起迭代器失效的情况有两种,

  1. 一种是像 resize、reserve、insert、push_back 这些能改变数据空间的操作,扩容操作会使原先空间销毁,从而产生野指针,因此需要对迭代器重新赋值。
  2. 即使不扩容,只要发生数据挪动,迭代器原先指向的空间可能也会发生变化,从而迭代器失效,比如 erase 操作。

另一种

string

模拟实现

# include <iostream>

namespace Schuyler
{
	class string
	{
	public:
		// 四个默认成员函数
		// 构造函数
		string(const char* str = "")
		{
			if (nullptr == str)
				throw new _exception{0,"字符串指针为 nullptr"};
			int size = strlen(str);
			// +1 是为了保留 ‘\0’
			_str = new char[size + 1];
			strcpy(_str, str);
			
			_size = size;
			_capacity = size;
		}
		// 析构函数
		~string()
		{
			if (_str != nullptr)
			{
				delete[] _str;
				_str = nullptr;
				_size = _capacity = 0;
			}		
		}
		// 拷贝构造
		string(const string& str)
		{
			_str = nullptr;
			_size = _capacity = 0;
			// 开辟一块和 str 一样的空间
			string tmp(str._str);
			// 将新空间和 _str 交换,新空间的字符指针变为 nullptr,tmp 出作用域空间释放
			swap(tmp);
		}
		// 赋值运算符号
		string& operator=(string str)
		{
			// 因为 str 传值类型,会拷贝构造一个临时对象,可以借临时对象来赋值,出作用域,临时对象销毁
			swap(str);
			return *this;
		}
		// 对字符串进行索引,const 对象索引返回值不能修改
		const char& operator[](int i) const
		{
			if (i < 0)
				throw new _exception{ 1, "字符串索引错误" };
			return _str[i];
		}
		char& operator[](int i)
		{
			if (i < 0)
				throw new _exception{ 1, "字符串索引错误" };
			return _str[i];
		}
		// 扩容
		void reserve(int n)
		{
			if (n > _capacity)
			{
				// 创建一块新空间,将原本空间中的内容拷贝下来
				char* newStr = new char[n + 1];
				strncpy(newStr, _str, _size + 1);
				// 释放原本空间,更新 _capacity
				delete[] _str;
				_str = newStr;
				_capacity = n;
			}
		}
		void resize(int n, char val = '\0')
		{
			// 修改的空间小于 size,将 _size 更新,并在末尾添 '\0'
			if (n < _size)
			{
				_size = n;
				_str[n] = '\0';
			}
			else
			{
				// n > _capacity 需要扩容,当 _size < n < _capacity 时,不需要扩容,只需从 _size 处开始添 '\0',更新 _size
				if (n > _capacity)
				{
					reserve(n);
				}
				for (int i = _size; i < n; i++)
				{
					_str[i] = val;
				}
				_str[n] = '\0';
				_size = n;
			}
		}
		// 增删查
		// 增
		string& insert(int pos, char val)
		{
			if (pos > _size)
				throw new _exception{ 2, "插入位置错误" };
			// 判断是否需要增容
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			char* end = _str + _size;
			// 从有效字符空间的末尾开始挪动,将 pos 及其之后的数据往后移动
			while (end >= _str + pos)
			{
				*(end + 1) = *end;
				end--;
			}
			// 在 pos 位置插入数据
			_str[pos] = val;
			_size++;
			
			return *this;
		}

		void push_back(char ch)
		{
			insert(_size, ch);
		}
		string& insert(int pos, const char* str)
		{
			if (pos > _size)
				throw new _exception{ 2, "插入位置错误" };
			
			int len = strlen(str);
			if (_size +len > _capacity)
				reserve(_size + len);
			// 移动 pos 及其之后的数据
			char* end = _str + _size;
			while (end >= _str + pos)
			{
				*(end + len) = *end;
				end--;
			}
			strncpy(_str + pos,  str, len);
			_size += len;

			return *this;
		}
		void append(const char* str)
		{
			insert(_size, str);
		}

		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}
		// 删除 pos 及其之后的 len 个数据,若不指定 len,则删除 pos 及其之后的全部数据
		string& erase(int pos, int len = npos)
		{
			if (pos >= _size)
				throw new _exception{ 3, "删除的位置错误" };

			// 计算从 pos 位置开始到字符串结尾的长度
			int leftLen = _size - pos;
			// 如果 leftLen <= len,则全部删除,否则删除 len 个字符
			if (leftLen <= len)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}

		void clear() 
		{
			_size = 0;
			_str[0] = '\0';
		}
		// 查
		int find(char ch, int pos = 0)
		{
			if (pos >= _size)
				throw new _exception{ 4, "查询位置出错" };
			for (int i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
				{
					return i;
				}
			}
			return npos;

		}
		int find(const char* str, int pos = 0)
		{
			if (pos >= _size)
				throw new _exception{ 4, "查询位置出错" };

			char* ret = strstr(_str, str);
			if (ret)
			{
				return ret - _str;
			}
			else
			{
				return npos;
			}
		}
		char* c_str() const
		{
			return _str;
		}
		
		// 迭代器
		typedef char* iterator;
		typedef const char* const_iterator;

		// 返回字符串起始位置
		iterator begin()
		{
			return _str;
		}
		// 返回字符串最后一个有效字符的下一位置
		iterator end()
		{
			return _str + _size;
		}
		const_iterator begin() const
		{
			return _str;
		}
		const_iterator end() const
		{
			return _str + _size;
		}
		
		
	private:
		void swap(string& str)
		{
			std::swap(_str, str._str);
			std::swap(_size, str._size);
			std::swap(_capacity, str._capacity);
		}
	private:
		// 管理字符空间的指针
		char* _str;
		// 字符空间有效字符大小
		int _size;
		// 字符空间大小这里定义成总大小减去'\0'的大小
		int _capacity;
		// 初始化 -1,表示"字符串末尾",因为 size_t 类型,所以 npos 为无限大
		static size_t npos;
	};
	size_t string::npos = -1;
	
	inline bool operator<(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}
	inline bool operator>(const string& s1, const string& s2)
	{
		return !(s1 < s2);
	}
	inline bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}
	inline bool operator<=(const string& s1, const string& s2)
	{
		return s1 == s2 || s1 < s2;
	}
	inline bool operator>=(const string& s1, const string& s2)
	{
		return s1 == s2 || s1 > s2;
	}
	
	inline bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}
	std::ostream& operator<<(std::ostream& out, const string& s)
	{
		for (auto ch : s)
		{
			out << ch;
		}

		return out;
	}
	std::istream& operator>>(std::istream& in,  string& s)
	{
		s.clear();

		char ch;
		ch = in.get();
		while (ch != ' ' || ch != '\n')
		{
			s += ch;
			ch = in.get();
		}

		return in;
	}
	std::istream& getline(std::istream& in, string& s)
	{
		s.clear();

		char ch;
		ch = in.get();
		while (ch != '\n')
		{
			s += ch;
			ch = in.get();
		}

		return in;
	}
}


若想写一个简洁版的 string,只需包含四个默认成员函数即可,可以是传统写法,也可以是现代写法。
list

模拟实现


namespace Schuyler
{
	// list 节点类型
	template<class T>
	struct ListNode
	{
		ListNode(const T& val = T())
		{
			_val = val;
			_pre = nullptr;
			_next = nullptr;
		}
		T _val;
		ListNode<T>* _pre;
		ListNode<T>* _next;
	};

	// list 的迭代器是对原生指针的封装
	// 这里的模板使用 3 种类型,是为了可以同时用一份代码,实例化出 const 和 非 const 的迭代器
	template<class T, class Ref, class Ptr>
	class ListIterator
	{
		typedef ListIterator<T, Ref, Ptr> self;
		typedef ListNode<T>* node;
	public:
		// 通过一个节点指针去初始化迭代器指针
		ListIterator(node* pNode)
		{
			_node = pNode;
		}

		// 通过迭代器去初始化一个迭代器指针
		ListIterator(const self& it)
		{
			_node = it._node;
		}

		// 为了使迭代器可以像指针一样去使用,需要重载 operator*() 和 operator->()
		// 重载解引用运算符,这里就体现了模板的好处:Ref 可以是 const(只读) ,也可以是可读可写
		Ref operator*()
		{
			return _node->_val;
		}
		// 重载->运算符
		Ptr operator->()
		{
			return &_node->_val;
		}
		// == 和 != 运算符
		bool operator==(const self& s) const
		{
			return s._node == _node;
		}

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

		// 重载++,实现指针的移动
		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		// 后置++
		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;
			
			return tmp;
		}

		// 重载--
		self& operator--()
		{
			_node = _node->_pre;
			return *this;
		}
		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_pre;

			return tmp;
		}
	private:
		// 迭代器是对指针的封装
		node _node;
	};

	template<class T>
	class list
	{
		typedef ListNode<T> node;
		// 体现模板的好处,不用再声明一份 const 的迭代器
		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<const T, const T&, const T*> const_iterator;
	private:
		// 哨兵节点
		node* _head;

	public:
		// 返回第一个有效元素的位置
		iterator begin()
		{
			return iterator(_head->_next);
		}
		// 返回最后一个有效元素的下一个位置
		iterator end()
		{
			return iterator(_head);
		}
		// const 对象调用
		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}

		const_iterator end() const
		{
			return const_iterator(_head);
		}
		// 判断 list 是否为空
		bool empty()
		{
			return begin() == end();
		}
		// 插入一个节点
		void insert(iterator pos, const T& x)
		{
			if (pos._node == nullptr)
				throw new _exception{ 0, "索引错误" };

			node* newNode  = new node(x);
			node* cur = pos._node;
			node* pre = cur->_pre;

			pre->_next = newNode;;
			newNode->_pre = pre;
			newNode->_next = cur;
			cur->_pre = newNode;
		}

		void push_back(const T& x)
		{
			insert(end(), x);
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_back()
		{
			erase(--end());
		}

		void pop_front()
		{
			erase(begin());
		}
		// 删除节点
		iterator erase(iterator pos)
		{
			if (pos == nullptr || pos == end())
				throw new _exception{ 0, "索引错误" };
			node* pre = pos._node->_pre;
			node* next = pos._node->_next;

			pre->_next = next;
			next->_pre = pre;

            delete pos._node
			return iterator(next);
		}

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				erase(it++);
			}
		}
		
		// 构造函数,构造双向带头链表
		list()
		{
			_head = new node;
			_head->_next = _head;
			_head->_pre = _head;
		}

		// 拷贝构造函数
		list(const list<T> _list)
		{
			_head = new node;
			_head->_next = _head;
			_head->_pre = _head;

			for (auto& e : _list)
			{
				push_back(e);
			}
		}
		// 析构函数
		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}
		// 拷贝构造
		list<T>& operator=(list<T> _list)
		{
			swap(_head, _list._head);
			return *this;
		}
		int size()
		{
			int sz = 0;
			iterator it = begin();
			while (it != end())
			{
				sz++;
				++it;
			}
			return sz;
		}
	};
}


list 同样存在迭代器失效问题,与 vector 不同的是,首先进行插入时,list 不会扩容,不会产生野指针,因此插入操作不会导致迭代器失效;
其次,进行删除时 vector 需要挪动元素,导致迭代器指向的元素发生改变,list 进行删除时,则会返回删除元素的下一个位置的迭代器,只会使删除位置的迭代器失效。

stack

stack 是一种容器适配器,底层容器可以是 vector、list等(只要支持 push_back 尾插、pop_back、top 获取栈顶元素和 empty 判空即可),
默认底层容器使用 deque。

模拟实现

#include <iostream>
#include <deque>

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

queue

队列也是一种容器适配器,底层容器应该支持:front 返回队头元素引用、back 返回队尾元素引用、push_back 队尾入队列、pop_back 队头出队列
默认情况下底层容器使用 deque。
模拟实现

#include <deque>
namespace Schuyler
{
	template<class T, class Container = std::deque<T>>
	class queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}

		void pop()
		{
			_con.pop_front();
		}

		T& front()
		{
			return _con.front();
		}

		const T& front() const
		{
			return _con.front();
		}

		T& back()
		{
			return _con.back();
		}

		const T& back() const
		{
			return _con.back();
		}

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

		bool empty()
		{
			return _con.empty();
		}
	private:
		Container _con;
	};
}

优先队列

// 声明仿函数
	template<class T>
	struct less
	{
		bool operator()(const T& x1, const T& x2)
		{
			return x1 < x2;
		}
	};

	template<class T>
	struct greater
	{
		bool operator()(const T& x1, const T& x2)
		{
			return x1 > x2;
		}
	};
	// 优先队列:底层用堆实现,堆就是用数组存储的完全二叉树
	template<class T, class Container = std::vector<T>, class Compare = less<T>>
	class priority_queue
	{
	public:
		bool empty() const
		{
			return _con.empty();
		}
		int size() const
		{
			return _con.size();
		}
		T& top()
		{
			return _con[0];
		}

		// 堆中任意子节点向上调整
		void AjustUp(int child)
		{
			Compare com;

			int parent = child / 2;

			while (child > 0)
			{
				if (com(_con[parent], _con[child]))
				{
					// 仿函数比较条件为 true,调整父节点和子节点位置
					swap(_con[parent], _con[child]);
					child = parent;
					parent = parent / 2;
				}
				else
				{
					// 找到 child 的正确位置,退出循环
					break;
				}
			}
		}
		
		void push(const T& x)
		{
			// 将 x 插入堆的尾部
			_con.push_back(x);
			// 再向上调整到正确位置
			AjustUp(_con.size() - 1);
		}
		// 用于删除操作的向下调整,将任意一个元素向下调整
		void AjustDown(int parent)
		{
			Compare com;
			int child = parent * 2 + 1;

			while (child < _con.size())
			{
				//找到较大的孩子节点
				if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
				{
					++child;
				}
				// 比较父子节点并调整位置
				if (com(_con[parent], _con[child]))
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = child * 2 + 1;
				}
				else
				{
					break;
				}
				
			}
		}
		// 删除堆顶元素
		void pop()
		{
			// 堆顶元素和堆的最后一个元素交换位置
			swap(_con[0], _con[_con.size() - 1]);
			// 删除交换后的堆顶元素
			_con.pop_back();
			// 调整此时堆顶元素
			AjustDown(0);
		}
	private:
		Container _con;
	};

额外知识点:堆的建立首先按顺序建立二叉树,其次从最后一个有孩子的节点开始,一层一层往上调。时间复杂度为 O(N)。

为什么使用 deque 作为 stack 和 queue 的底层容器:

  1. stack 和 queue 不需要遍历,只需要在固定一端或两端进行操作
  2. stack 和 queue 在元素增长时,效率高
二叉搜索树

模拟实现

#include <iostream>

// K 模型:只用 Key 作为关键码,关键码就是要查找的值,每个节点只包含 Key
namespace K
{
	// 二叉搜索树节点
	template<class K>
	struct BSTreeNode
	{
		BSTreeNode<K>* _left;
		BSTreeNode<K>* _right;

		K _key;

		BSTreeNode(const K& key)
		{
			_left = nullptr;
			_right = nullptr;
			_key = key;
		}
	};
	// 二叉搜索树
	template <class  K>
	class BSTree
	{
		typedef BSTreeNode<K> Node;
	private:
		// 释放树中的节点,销毁树
		void _Destroy(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_Destroy(root->_left);
			_Destroy(root->_right);

			delete root;
		}
		// 查找 key
		Node* _Search(Node* root, const K& key)
		{
			// 根节点为空或者根节点恰好是要找的,返回 root
			if (root == nullptr || root->_key == key)
			{
				return root;
			}
			// 根节点大于 key,到左子树中查找
			if (root->_key > key)
			{
				return _Search(root->_left, key);
			}
			// 根节点小于 key,到右子树中查找
			else if (root->_key < key)
			{
				return _Search(root->_right, key);
			}
		}
		// 拷贝一棵树
		Node* _Copy(Node* root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			Node* newNode = new Node(root->_key);
			// 直到叶子节点开始返回,将节点之间相“连接”
			newNode->_left = _Copy(root->_left);
			newNode->_right = _Copy(root->_right);

			return root;
		}
		// 插入节点,成功插入返回 true,否则返回 false
		bool _Insert(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				root = new Node(key);
				return true;
			}
			// 插入的节点大于 root,往 root 的右子树插
			if (root->_key < key)
			{
				return _Insert(root->_right, key);
			}
			// 插入的节点小于 root,往 root 的左子树插
			else if (root->_key > key)
			{
				return _Insert(root->_left, key);
			}
			else
			{
				return false;
			}
		}
		// 删除节点,成功删除返回 true,否则返回 false
		bool _Erase(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}
			// root 大于要删的元素 ,从 root 的左子树中删除
			if (root->_key > key)
			{
				return _Erase(root->_left, key);
			}
			// root 小于要删的元素 ,从 root 的右子树中删除
			else if (root->_key < key)
			{
				return _Erase(root->_right, key);
			}
			// 找到要删除的元素
			else
			{				
				if (root->_left == nullptr)
				{
					Node* del = root;
					// 父节点指向删除节点的右孩子节点
					root = root->_right;
					// 删除节点
					delete del;
				}
				else if (root->_right == nullptr)
				{
					Node* del = root;
					// 父节点指向删除节点的左孩子节点
					root = root->_left;
					// 删除节点
					delete del;
				}
				else
				{
					// 要删除节点有两个孩子的情况
					// 对删除元素的右子树遍历得到最小值
					Node* minRight = root->_right;
					while (minRight->_left)
					{
						minRight = minRight->_left;
					}
					K min = minRight->_key;
					// 删除 root 右子树最小元素
					_Erase(root->_left, min);
					// 并用最小元素替换删除的元素
					root->_key = min;
				}
				return true;
			}		
		}
	public:
		// 构造搜索二叉树
		BSTree():_root(nullptr)
		{}
		// 拷贝构造搜索二叉树
		BSTree(const BSTree<K>& t)
		{
			_root = _Copy(t._root);
		}
		// 赋值重载搜索二叉树
		BSTree<K> operator=(BSTree<K> t)
		{
			swap(_root, t._root);
			return *this;
		}
		
		// 销毁搜索二叉树
		~BSTree()
		{
			_Destroy(_root);
			_root = nullptr;
		}
		// 查找二叉树中的节点,只对外提供 key 查找,不提供 root
		Node* Search(const K& key)
		{
			_Search(_root, key);
		}
		// 插入节点,只对外提供 key 查找,不提供 root
		bool Insert(const K& key)
		{
			return _Insert(_root, key);
		}
		// 删除节点,只对外提供 key 查找,不提供 root
		bool Erase(const K& key)
		{
			return _Erase(_root, key);
		}
	private:
		// 中序遍历,实现按大小顺序打印
		void _InOrder(Node* root)
		{
			if (root == nullptr)
				return;
			_InOrder(root->_left);
			std::cout << root->_key << " ";
			_InOrder(root->_right);
		}
	public:
		void _InOrder()
		{
			_InOrder(_root);
			std::cout << std::endl;
		}
	private:
		Node* _root;
	};
}


// KV 模型:二叉树的每个节点是 <Key, Value> 类型
namespace KV
{
	template<class K, class V>
	struct BSTreeNode
	{
		BSTreeNode<K, V>* _left;
		BSTreeNode<K, V>* _right;

		K _key;
		V _value;

		BSTreeNode(const K& key, const V& value)
			:_left(nullptr)
			, _right(nullptr)
			,_key(key)
			,_value(value)
		{}
		
	};

	template<class K, class V>
	class BSTree
	{
		typedef BSTreeNode<K, V> Node;
	public:
		// 插入节点,成功插入返回 true,否则返回 false
		bool _Insert(Node*& root, const K& key, const V& value)
		{
			if (root == nullptr)
			{
				root = new Node(key, value);
				return true;
			}
			// 插入的节点大于 root,往 root 的右子树插
			if (root->_key < key)
			{
				return _Insert(root->_right, key, value);
			}
			// 插入的节点小于 root,往 root 的左子树插
			else if (root->_key > key)
			{
				return _Insert(root->_left, key, value);
			}
			else
			{
				return false;
			}
		}

		// 查找 key
		Node* _Search(Node* root, const K& key)
		{
			// 根节点为空或者根节点恰好是要找的,返回 root
			if (root == nullptr || root->_key == key)
			{
				return root;
			}
			// 根节点大于 key,到左子树中查找
			if (root->_key > key)
			{
				return _Search(root->_left, key);
			}
			// 根节点小于 key,到右子树中查找
			else if (root->_key < key)
			{
				return _Search(root->_right, key);
			}
		}
		// 删除节点,成功删除返回 true,否则返回 false
		bool _Erase(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}
			// root 大于要删的元素 ,从 root 的左子树中删除
			if (root->_key > key)
			{
				return _Erase(root->_left, key);
			}
			// root 小于要删的元素 ,从 root 的右子树中删除
			else if (root->_key < key)
			{
				return _Erase(root->_right, key);
			}
			// 找到要删除的元素
			else
			{
				if (root->_left == nullptr)
				{
					Node* del = root;
					// 父节点指向删除节点的右孩子节点
					root = root->_right;
					// 删除节点
					delete del;
				}
				else if (root->_right == nullptr)
				{
					Node* del = root;
					// 父节点指向删除节点的左孩子节点
					root = root->_left;
					// 删除节点
					delete del;
				}
				else
				{
					// 对删除元素的右子树遍历得到最小值
					Node* minRight = root->_right;
					while (minRight->_left)
					{
						minRight = minRight->_left;
					}
					K min_key = minRight->_key;
					V min_value = minRight->_value;
					// 删除 root 右子树最小元素
					_Erase(root->_left, min_key);
					// 并用最小元素替换删除的元素
					root->_key = min_key;
					root->_value = min_value;
				}
				return true;
			}
		}
		// 拷贝一棵树
		Node* _Copy(Node* root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			Node* newNode = new Node(root->_key, root->_value);
			// 直到叶子节点开始返回,将节点之间相“连接”
			newNode->_left = _Copy(root->_left);
			newNode->_right = _Copy(root->_right);

			return newNode;
		}
		// 释放树中的节点,销毁树
		void _Destroy(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_Destroy(root->_left);
			_Destroy(root->_right);

			delete root;
		}
	public:
		BSTree():_root(nullptr)
		{}
		BSTree(const BSTree<K, V>& t)
		{
			_root = _Copy(t._root);
		}
		BSTree<K, V>& operator=(BSTree<K,V> t)
		{
			swap(_root, t._root);
			return *this;
		}
		~BSTree()
		{
			_Destroy(_root);
			_root = nullptr;
		}
	private:
		Node* _root;
	};
}

AVL 树

模拟实现

#include <iostream>
#include <assert.h>

// AVL 树是一棵特殊的二叉搜索树
namespace Schuyler
{
	template<class K, class V>
	struct AVLTreeNode
	{
		AVLTreeNode<K, V>* _left;
		AVLTreeNode<K, V>* _right;
		AVLTreeNode<K, V>* _parent;
		// 平衡因子
		int _bf;
		// 键值对
		std::pair<K, V> _kv;

		AVLTreeNode(const std::pair<K, V>& kv)
				:_left(nullptr)
				,_right(nullptr)
				,_parent(nullptr)
				,_bf(0)
				, _kv(kv)	
		{}
	};
	template<class K, class V>
	class AVLTree
	{
		typedef  AVLTreeNode<K, V> Node;
	public:
		AVLTree()
			:_root(nullptr)
		{}
		// 析构函数、拷贝构造和赋值运算与二叉搜索树一样
		~AVLTree()
		{
			_Destroy(_root);
			_root = nullptr;
		}
		AVLTree(const AVLTree<K, V>& t)
		{
			_root = _Copy(t._root);
		}
		AVLTree<K, V>& operator=(const AVLTree<K, V> t)
		{
			std::swap(t._root, _root);
			return *this;
		}
		// 查找,非递归形式
		Node* Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_kv.first < key)
				{
					cur = cur->_right;
				}
				else if (cur->_kv.first > key)
				{
					cur = cur->_left;
				}
				else
				{
					return cur;
				}
			}
			return nullptr;
		}
		// AVL 树是左右子树高度绝对值不能超过 1 的二叉搜索树,
		// 每插入一个结点,都要检查平衡性,如果出现不平衡,
		// 如果出现不平衡,那么首先要找出最小不平衡子树,然后进行调整
		
		
		//  LL 调整(左孩子的左子树上插入结点导致):右旋
		void RotateR(Node* parent)
		{
			// 左孩子
			Node*	subL = parent->_left;
			// 左孩子的右子树
			Node* subLR = subL->_right;
			
			// 1. 调整最小不平衡子树根节点的左孩子
			parent->_left = subLR;
			// 调整左孩子的右子树的父节点
			if (subLR)
				subLR->_parent = parent;

			// 2. 此时发生右旋
			subL->_right = parent;
			// 在调整父节点前先保存父节点的父节点
			Node* parentParent = parent->_parent;
			parent->_parent = subL;

			// 3. 如果父节点为根节点,调整根节点
			if (_root == parent)
			{
				_root = subL;
				_root->_parent = nullptr;
			}
			else
			{
				// 调整最小不平衡子树的根节点的父节点
				if (parentParent->_left == parent)
					parentParent->_left = subL;
				else
					parentParent->_right = subL;

				subL->_parent = parentParent;
			}
			// 更新最小不平衡子树的平衡因子
			subL->_bf = parent->_bf = 0;
		}

		//  RR 调整(右孩子的右子树上插入结点导致):左旋
		void RotateL(Node* parent)
		{
			// 最小不平衡子树的右孩子
			Node* subR = parent->_right;
			// 右孩子的左子树
			Node* subRL = subR->_left;

			// 1.调整最小不平衡子树的右孩子的左子树

			// 右边的结点大于根节点,所以“要接在parent->_right”
			parent->_right = subRL;
			if (subRL)
				subRL->_parent = parent;

			// 2.左旋
			subR->_left = parent;
			Node* parentParent = parent->_parent;
			parent->_parent = subR;

			//3. 调整 parentParent
			if (_root == parent)
			{
				_root = subR;
				_root->_parent = nullptr;
			}
			else
			{
				if (parentParent->_left == parent)
					parentParent->_left = subR;
				else
					parentParent->_right = subR;
				subR->_parent = parentParent;
			}
			parent->_bf = subR->_bf = 0;
		}
		//  LR 调整(左孩子的右子树上插入结点导致)
		void RotateLR(Node* parent)
		{
			Node* subL = parent->_left;
			Node* subLR = subL->_right;
			int bf = subLR->_bf;
			// 最小不平衡子树的左孩子作为根节点整体左旋
			RotateL(parent->_left);
			// 最小不平衡子树整体右旋
			RotateR(parent);

			// 更新平衡因子
			if (bf == -1)
			{
				parent->_bf = 1;
				subL->_bf = 0;
				subLR->_bf = 0;
			}
			else if (bf == 1)
			{
				parent->_bf = 0;
				subL->_bf = -1;
				subLR->_bf = 0;
			}
			else if (bf == 0)
			{
				parent->_bf = 0;
				subL->_bf = 0;
				subLR->_bf = 0;
			}
			else
			{
				assert(false);
			}

		}
		//  RL 调整()右孩子的左子树上插入结点导致
		void RotateRL(Node* parent)
		{
			Node* subR = parent->_right;
			Node* subRL = subR->_left;
			int bf = subRL->_bf;
			// 最小不平衡子树的右孩子作为根节点整体左旋
			RotateR(parent->_right);
			// 最小不平衡子树整体左旋
			RotateL(parent);

			// 更新平衡因子
			if (bf == -1)
			{
				parent->_bf = 0;
				subR->_bf = 1;
				subRL->_bf = 0;
			}
			else if (bf == 1)
			{
				parent->_bf = -1;
				subR->_bf = 0;
				subRL->_bf = 0;
			}
			else if (bf == 0)
			{
				parent->_bf = 0;
				subR->_bf = 0;
				subRL->_bf = 0;
			}
			else
			{
				assert(false);
			}
		}
	private:
		void _Destroy(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_Destroy(root->_left);
			_Destroy(root->_right);
			delete root;
		}
		Node* _Copy(Node* root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			
			Node* node = new Node(root->_kv);
			node->left = _Copy(root->_left);
			node->_right = _Copy(root->_right);
			
			return node;
		}
	private:
		Node* _root;
	};
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Schuyler Hu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值