算法与数据结构之查找二叉树

1 MAP类模板

建立一个MAP类模板,其中有两个参数

template<typename T1,typename T2>
class MAP
{


//源代码


};

T1表示键类型,T2表示值类型

2 Node结构体

template<typename T1, typename T2>
class Node
{
	Node(T1 key, T2 val)
	{
		_Key = key;
		_Val = val;
		_Left = nullptr;
		_Right = nullptr;
		_N = 1;
	}
	T1 _Key;
	T2 _Val;
	Node<T1, T2>*_Left;
	Node<T1, T2>*_Right;
	int _N;//该节点所含节点个数
};

3 得到总节点数的方法实现

公有调用:

	int size() { return size(_root); }

私有实现:

template<typename T1, typename T2>
int MAP<T1, T2>::size(Node<T1, T2>* x)
{
	if (x == nullptr)
		return 0;
	return size(x->_Left) + size(x->_Right) + 1;
}

如果节点为空,默认为0

4 根据键取值的方法

公有调用

	T2 get(T1 key) { return get(_root, key); }

私有实现

template<typename T1,typename T2>
T2 MAP<T1,T2>::get(Node<T1, T2>*x, T1 key)
{
	if (x == nullptr)
		return T2();
	if (x->_Key < key)
		return get(x->_Right, key);
	else if (x->_Key > key)
		return get(x->_Left, key);
	else
		return x->_Val;
}

5 改变键对应的值的方法

公有调用

	void put(T1 key, T2 val) { _root = put(_root, key, val); 

私有实现

template<typename T1,typename T2>
Node<T1, T2>*MAP<T1,T2>::put(Node<T1, T2> *x, T1 key, T2 val)
{
	if (x == nullptr)
	{
		Node<T1, T2>*temp = new Node<T1, T2>(key, val);
		return temp;
	}
	if (x->_Key < key)
		x->_Right = put(x->_Right, key, val);
	else if (x->_Key > key)
		x->_Left = put(x->_Left, key, val);
	else
		x->_Val = val;
	x->_N = size(x->_Left) + size(x->_Right) + 1;
	return x;
}

6 得到最大值与最小值的方法

公有调用

	T2 GetMax() { return GetMax(_root); }
	T2 GetMin() { return GetMin(_root); }

私有实现

template<typename T1,typename T2>
T2 MAP<T1, T2>::GetMax(Node<T1, T2>*x)
{
	if (x->_Right == nullptr)
		return x->_Val;
	return GetMax(x->_Right);
}
template<typename T1,typename T2>
T2 MAP<T1,T2>::GetMin(Node<T1, T2>*x)
{
	if (x->_Left == nullptr)
		return x->_Val;
	return GetMin(x->_Left);
}

7 向上取整与向下取整

公有调用

	T1 Floor(T1 key) 
	{
		Node<T1,T2>*temp = Floor(_root, key);
		if (temp == nullptr)
			return T1();
		return temp->_Key;
	}
	T1 Ceiling(T1 key)
	{
		Node<T1, T2>*temp = Ceiling(_root, key);
		if (temp == nullptr)
			return T1();
		return temp->_Key;
	}

私有实现

template<typename T1, typename T2>
Node<T1,T2>* MAP<T1, T2>::Floor(Node<T1, T2>* x, T1 key)
{
	if (x == nullptr)
		return nullptr;
	if (x->_Key > key)
		return Floor(x->_Left, key);
	else if (x->_Key == key)
		return x;
	Node<T1, T2>*temp = Floor(x->_Right, key);
	if (temp != nullptr)
		return temp;
	return x;
}

template<typename T1, typename T2>
Node<T1, T2>* MAP<T1, T2>::Ceiling(Node<T1, T2>* x, T1 key)
{
	if (x == nullptr)
		return nullptr;
	if (x->_Key < key)
			return Floor(x->_Right, key);
	else if (x->_Key == key)
			return x;
	Node<T1, T2>*temp = Ceiling(x->_Left, key);
	if (temp != nullptr)
		return temp;
	return x;
}

8.排名

公有调用

	int Rank(T1 key) { return Rank(_root, key); }
	T1 Select(int k)
	{
		Node<T1, T2>*temp = Select(_root, k);
		if (temp == nullptr)
			return T1();
		return temp->_Key;
	}

私有实现

template<typename T1, typename T2>
Node<T1, T2>* MAP<T1, T2>::Select(Node<T1, T2>* x, int k)
{
	if (x == nullptr)
		return nullptr;
	int t = size(x->_Left);
	if (t > k)
		return Select(x->_Left, k);
	else if (t < k)
		return Select(x->_Right, k - t - 1);
	else
		return x;
}
template<typename T1, typename T2>
int MAP<T1, T2>::Rank(Node<T1, T2>* x, T1 key)
{
	if (x == nullptr)
		return 0;
	if (x->_Key < key)
		return 1 + size(x->_Left) + Rank(x->_Right, key);
	else if (x->_Key > key)
		return Rank(x->_Left, key);
	else
		return size(x->_Left);
}

9.删除

9.1 删除最大值与最小值

私有调用:

	void DeleteMin() {  DeleteMin(_root); }
	void DeleteMax() {  DeleteMax(_root); }

共有实现:

template<typename T1, typename T2>
void MAP<T1, T2>::DeleteMin(Node<T1, T2>* x)
{
	if (x == nullptr)
		return;
	Node<T1, T2>*temp1 = x;
	Node<T1, T2>*temp2 = nullptr;
	while (temp1->_Left)
	{
		temp2 = temp1;
		temp1 = temp1->_Left;
	}
	if (temp2 == nullptr)
		x = x->_Right;
	else
		temp2->_Left = temp1->_Right;
	delete temp1;
	std::cout << "delete" << std::endl;
	x->_N -= 1;
}
template<typename T1, typename T2>
void MAP<T1, T2>::DeleteMax(Node<T1, T2>* x)
{
	if (x == nullptr)
		return;
	Node<T1, T2>*temp1 = x;
	Node<T1, T2>*temp2 = nullptr;
	while (temp1->_Right)
	{
		temp2 = temp1;
		temp1 = temp1->_Right;
	}
	if (temp2 == nullptr)
		x = x->_Left;
	else
		temp2->_Right = temp1->_Left;
	delete temp1;
	std::cout << "delete" << std::endl;
	x->_N -= 1;
}

9.2删除某键

template<typename T1, typename T2>
Node<T1,T2>* MAP<T1,T2>::DeleteKey(Node<T1,T2>*x,T1 key)
{
	Node<T1, T2>*temp1 = nullptr;
	Node<T1, T2>*temp2 = nullptr;
	if (x == nullptr)
		return nullptr;
	if (x->_Key > key)
		x->_Left = DeleteKey(x->_Left, key);
	else if (x->_Key < key)
		x->_Right = DeleteKey(x->_Right, key);
	else
	{
		if (x->_Left == nullptr)
		{
			temp1 = x->_Right;
			delete x;
			std::cout << "delete" << std::endl;
			return temp1;
		}
		else if (x->_Right == nullptr)
		{
			temp1 = x->_Left;
			delete x;
			std::cout << "delete" << std::endl;
			return temp1;
		}
		else
		{
			temp2 = x;
			temp1 = GetMin(x->_Right);
			x = new Node<T1, T2>(temp1->_Key, temp1->_Val);
			DeleteMin(temp2->_Right);
			x->_Left = temp2->_Left;
			x->_Right = temp2->_Right;
			std::cout << "delete" << std::endl;
			delete temp2;
			x->_N = size(x->_Left) + size(x->_Right) + 1;
		}
	}
	return x;
}

找到某键后,判断左右是否为空,若左右一方为空,则选择另一方直接连上,随后用delete删除

若不为空,则在右支中找到最小值,用temp2保存节点位置,随后用temp1找寻最小键

找到之后,将temp1的节点信息新建节点,这是由于删除最小键方法局限导致的已经delete掉节点

随后将找到的键的左右支给予新节点,随后返回节点x,看似复杂,其实结构还好

10.遍历

10.1 递归遍历

我们的任务是将元素放置于一个容器之中

先来一个中序遍历,中序遍历意思是先访问左节点再访问父节点再访问右节点,该遍历方式可以按照升序方式排列二叉树

前序遍历与后序遍历顾名思义

只以中序遍历作为代码例子

私有调用

	void keys() { vector<T1>Keys; T1 lo = GetMin(); T1 hi = GetMax(); keys(_root,Keys, lo,hi); Show(Keys); }

公有实现

template<typename T1, typename T2>
inline void MAP<T1, T2>::Show(vector<T1>&Keys)
{
	for (int i = 0; i < Keys.size(); i++)
		std::cout << Keys[i] << "   ";
}
template<typename T1, typename T2>
void MAP<T1, T2>::keys(Node<T1,T2>*x,vector<T1>& Keys, T1 lo, T1 hi)
{
	if (x == nullptr)
		return;
	if (lo < x->_Key)
		keys(x->_Left, Keys, lo, hi);
	if (lo <= x->_Key &&hi >= x->_Key)
		Keys.push_back(x->_Key);
	if (hi > x->_Key)
		keys(x->_Right, Keys, lo, hi);
}

该遍历可变形,若遍历没有范围,则可以去除lo与hi的条件限制

若遍历有范围,则需要按照上述条件进行筛选,其中不取等于号的意思是可以节省迭代次数提高效率

10.2 非递归中序遍历

template<typename T1, typename T2>
void MAP<T1, T2>::InOrder(Node<T1, T2>* x, vector<T1>& result)
{
	Node<T1, T2>*p = x;
	stack<Node<T1, T2>*>s;
	while (!s.empty() || p)
	{
		if (p)
		{
			s.push(p);
			p = p->_Left;
		}
		else
		{
			p = s.top();
			s.pop();
			result.push_back(p->_Key);
			p = p->_Right;
		}
	}
}

10.3 非递归前序遍历

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*>s;
        vector<int>result;
        while(!s.empty()||root)
        {
            if(root)
            {
                s.push(root);
                result.push_back(root->val);
                root=root->left;
            }
            else
            {
                root = s.top();
                s.pop();
                root=root->right;
            }
        }
        return result;
    }
};

二叉树通用遍历法

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*>s;
        vector<int>result;
        if(root==nullptr)
            return result;
        s.push(root);
        while(!s.empty())
        {
            root = s.top();
            result.push_back(root->val);
            s.pop();
            if(root->right)
                s.push(root->right);
            if(root->left)
                s.push(root->left);
        }
        return result;
    }
};

10.4 非递归后序遍历

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int>result;
        stack<TreeNode*>s;
        TreeNode* pre = nullptr;
        while(!s.empty()||root)
        {
            if(root)
            {
                s.push(root);
                root = root->left;
            }
            else
            {
                TreeNode* top = s.top();
                if(top->right && top->right != pre)
                    root = top->right;
                else
                {
                    result.push_back(top->val);
                    pre = top;
                    s.pop();
                }
            }
        }
        return result;
    }
};

后序遍历无疑是最难的,我们需要判断之前保存的节点为左节点还是右节点

我们首先要遍历至左下角,边遍历边入栈,直到为空,随后我们取出top,如果top还有右节点的话

我们继续将二叉树遍历至最左下角,如果这时右节点为空了,我们就可以考虑保存数据

保存完数据后,我们需要出栈并且保存该遍历过的节点,因为root继续为空,所以我们继续进行else内的代码块

随后进行到根节点,根节点的右节点若存在,则进入,进入后假设没有节点了,那么我们保存完数据回到根节点

根节点虽然还有右节点,但是我们上一个保存的节点pre与他的节点相同,所以我们这时可以保存根节点的数据了

就这样一个思路进行下去

posted @ 2017-08-08 22:05  vhyz  阅读(223)  评论(0编辑  收藏  举报