一.树的概念
树是一种非线性结构它是由n个结点组成的一个具有层次结构的集合。它具有以下的特点:1.每个结点有零个或者多个子结点。2.每一个非根结点只有一个父结点。3.没有父结点的结点为根结点。4.除了根结点外,每个结点都可以分为多个不相交的结点。
结点的度:一个结点含有的子树的个数。
叶结点(终端结点):度为0的结点。
分支结点(非终端结点):度不为0的结点。
父结点(双亲结点):若一个结点含有子结点,则这个结点称为该结点字节点的父结点。
子结点:一个根结点所含有的子树的结点。
兄弟结点:具有相同父结点的的结点互为兄弟结点。
树的度:一颗树中最大的结点的度。
树的深度:一棵树的层数。
二.二叉树的概念及结构
二叉树是树的特殊的一种。它具有以下特点:1.每个结点最多只有俩个子结点。2.左右子树是有顺序的,次序不能颠倒。
1.特殊的二叉树:
1.1满二叉树:一个二叉树,如果每一层的结点都达到最大值,则称其为满二叉树。
1.2完全二叉树:对于深度为k的,有n个结点的二叉树,当且仅当每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时称为完全二叉树。要注意的是满二叉树是一种特殊的完全二叉树。但完全二叉树不一定是满二叉树。
2.二叉树的存储结构
二叉树一般可以使用顺序结构和链式结构进存储
顺序结构存储:使用数组来存储,一般使用数组只适合表示完全二叉树。在现实使用中一般只有堆才会使用数组存储。
链式结构存储:使用链表来表示一颗二叉树。通常的方法是链表中每个结点由三个域组成,数据域和左右指针域。左右指针分别给出该节点的左孩子和右孩子。
struct BinaryTreeNode
{
struct BinaryTreeNode* m_left; //左孩子
struct BinaryTreeNode* m_right; //右孩子
BTDataType m_data; //值域
}
三.二叉树的性质
性质1:二叉树第i层上的结点数目最多为2^(i-1) (i≥1)。
性质2:深度为k的二叉树至多有2^k-1个结点(k>=1)。
性质3:包含n个结点的二叉树的高度至少为log2(n+1)。
性质4:在任意一颗二叉树中,若叶结点的个数为n0,度为2的结点数为n2,则n0=n2+1。
四.二叉树的实现
1.二叉树的顺序结构实现
普通的二叉树不适合用数组来储存,因为可能会造成大量的空间浪费,而完全二叉树则不会。而堆又是一种特殊的完全二叉树,因此我们来实现堆。
1.1堆的概念
如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
1.堆中某个结点的值总是不大于或者不小于其父结点的值。
2.堆总是一颗完全二叉树。
1.2.堆的实现
1.2.1向下调整算法
现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。
void Heapify(vector<HPDataType>& m_nums, int i, int low, int high)
{
int l = 2 * i + 1;
int r = 2 * i + 2;
int _max; //保存最大值下标
if (l <= high && m_nums[l] > m_nums[i])
{
_max = l;
}
else
{
_max = i;
}
if (r <= high && m_nums[r] > m_nums[_max])
{
_max = r;
}
if (_max != i)
{
swap(m_nums[i], m_nums[_max]);
Heapify(m_nums, _max, low, high);
}
}
1.2.2构建堆
void HeapInit(vector<HPDataType>& m_nums) //初始化为大堆
{
int len = m_nums.size();
for (int i = len / 2 - 1; i >= 0; i--) //从第一个非叶子结点开始
{
Heapify(m_nums, i, 0, len - 1);
}
}
1.2.3堆排序
交换最后一个元素与第一个元素然后进行向下调整算法,直到排序完成
void HeapSort(vector<HPDataType>& m_nums)
{
int len = m_nums.size();
for (int i = len - 1; i >= 0; i--)
{
swap(m_nums[i], m_nums[0]);
Heapify(m_nums, 0, 0, i - 1);
}
}
2.二叉树的链式结构实现
template < class T >
class TreeNode
{
T m_val;
TreeNode<T> * m_left;
TreeNode<T> * m_right;
public:
TreeNode(const T &val) :
m_val(val)
{
}
template <class T>
friend class Tree;
};
template <class T>
class Tree
{
TreeNode<T> * m_root;
static TreeNode<T> * maketree(const T * val, const T & end, int &i)
{
if (val[i] == end)
{
i++;
return nullptr;
}
TreeNode<T> * root = new TreeNode<T>(val[i]);
i++;
root->m_left = maketree(val, end, i);
root->m_right = maketree(val, end, i);
return root;
}
public:
Tree() :
m_root(nullptr)
{
}
Tree(const T * val, const T & end)
{
int i = 0;
m_root = maketree(val, end, i);
}
void pre_order()
{
TreeNode<T> * cur = m_root;
stack<TreeNode<T> *> st;
while (cur)
{
cout << cur->m_val << ' ';
if (cur->m_right)
{
st.push(cur->m_right);
}
if (cur->m_left)
{
cur = cur->m_left;
}
else
{
if (st.empty())
{
cur = nullptr;
}
else
{
cur = st.top();
st.pop();
}
}
}
}
void in_order()
{
TreeNode<T> * cur = m_root;
stack<TreeNode<T> *> st;
while (cur || !st.empty())
{
for (; cur; cur = cur->m_left)
{
st.push(cur);
}
if (!st.empty())
{
cur = st.top();
st.pop();
cout << cur->m_val << ' ';
cur = cur->m_right;
}
}
}
void post_order()
{
TreeNode<T> * cur = m_root;
stack<TreeNode<T> *> st;
stack<bool> tag;
while (cur || !st.empty())
{
for (; cur; cur = cur->m_left)
{
st.push(cur);
tag.push(false);
}
while (!st.empty() && tag.top())
{
cur = st.top();
cout << cur->m_val << ' ';
st.pop();
tag.pop();
cur = nullptr;
}
if (!st.empty())
{
tag.top() = true;
cur = st.top();
cur = cur->m_right;
}
}
}
};