目录
堆排序的实现基础是树,在此之前我们需要先了解堆,在了解堆之前先来了解下树。
树的概念
跟我们生活中的树一样,都是由根和节点构成的。像这样的,最下面的是根部,然后向上依次是枝条和叶子。在程序中的树,也是如此,有根、有枝条、由叶子。不同的是程序中的数是根部在上,叶子在下。
树的基本术语
如图,是一棵树,凡是在下一层有分支的节点都叫做根节点,例如A是B/C/D的根节点,B是E/F的根节点,根节点也叫做父节点,父节点下面连接的那一行节点叫做孩子节点,同一层的节点称为兄弟节点,当一个节点的下面没有分支的时候,叫做叶子结点。树的节点在第几层,高度就是几,度数就是子节点的个数。
树的存储结构
树的存储结构有两种,分别是数组储存和链表储存,数组储存时孩子节点和父亲节点的下标之间存在一定关系,可以相互表示;链表实现就是使用指针指孩子节点了。
1.双亲表示法
typedef int DataType;
typedef struct TreeNode
{
struct TreeNode* left;
struct TreeNode* right;
DataType val;
}BTNode;
像这样的,左指针指向的就是左孩子,右指针指向的就是右孩子,当然孩子可能有多个,定义对应数量的节点指针即可。
这样的存储结构,我们可以根据结点的parent 指针很容易找到它的双亲结点,所用的时间复杂度为0(1),直到parent为-1时,表示找到了树结点的根。可如果我们要知道结点的孩子是什么,对不起,请遍历整个结构才行。
2.孩子兄弟表示法
像这样的,每个节点的指针只有指向孩子的和兄弟的同样也可以实现树的表示方法,代码实现如下;
typedef int DataType;
typedef struct TreeNode
{
struct TreeNode* firstchild;
struct TreeNode* borther;
DataType val;
}BTNode;
我们一般使用的是双亲表示法;
二叉树
完全二叉树
像这样的出最后一层之外都是满的,最后一层节点都是紧挨着的叫做完全二叉树,
满二叉树
顾名思义,满二叉树就是满的,除了最后一层其余节点度数都是2,最后一层的节点的度数是1;
手搓二叉树
实现二叉树的各项功能前我们现在创建一个二叉树;
//创造节点
BTNode* BuyBTNode(int val)
{
BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
if (newnode == nullptr)
{
perror("malloc fail");
exit(1);
}
newnode->val = val;
newnode->left = nullptr;
newnode->right = nullptr;
return newnode;
}
//手搓一棵树
BTNode* CreateTree()
{
BTNode* n1 = BuyBTNode(1);
BTNode* n2 = BuyBTNode(2);
BTNode* n3 = BuyBTNode(3);
BTNode* n4 = BuyBTNode(4);
BTNode* n5 = BuyBTNode(5);
BTNode* n6 = BuyBTNode(6);
BTNode* n7 = BuyBTNode(7);
n1->left = n2;
n1->right = n4;
n2->left = n3;
n2->right = n6;
n4->left = n5;
n4->right = n7;
return n1;
}
二叉树的功能函数
二叉树的遍历
二叉树的遍历分为4种,前序遍历,中序遍历,后序遍历,层序遍历。前中后其实是根节点的遍历顺序。比如前序遍历就是先遍历根节点然后再遍历左右节点。中序遍历就是遍历左节点然后是根节点最后是右节点,
前序遍历
//前序遍历
void PreOrder(BTNode* root)
{
if (root == nullptr)
{
cout << "N ";
return;
}
cout << root->val << " ";
PreOrder(root->left);
PreOrder(root->right);
}
前序遍历的顺序是1236457 ,首先将236看成1的左子树,457看成1的右子树,前序遍历前遍历根,然后依次是左右子树节点,在左子树中236又是一个二叉树,按照同样的规则进行遍历。
中序遍历
//中序遍历
void InOrder(BTNode* root)
{
if (root == nullptr)
{
cout << "N ";
return;
}
InOrder(root->left);
cout << root->val << " ";
InOrder(root->right);
}
中序遍历的顺序是 3261547,先遍历左子树节点在遍历在遍历右子树节点。
后序遍历
//后序遍历
void PostOrder(BTNode* root)
{
if (root == nullptr)
{
cout << "N ";
return;
}
PostOrder(root->left);
PostOrder(root->right);
cout << root->val << " ";
}
后序遍历的顺序是3625741,先遍历左右子树节点在遍历根节点。
层序遍历
// 层序遍历
void LevelOrder(BTNode* root)
{
queue<BTNode*> q;
if (!root) return;
q.push(root);
while (!q.empty())
{
BTNode* front = q.front();
q.pop();
if (front)//如果该节点不是空的,那就把孩子节点插入队列中
{
cout << front->val << " ";
//if (front->left)
q.push(front->left);//插入的孩子可能为空
//if (front->right)
q.push(front->right);
}
else
cout << "N ";
}
}
层序遍历并没有使用递归来实现,而是使用队列来实现,先把根节点插入队列然后取出把孩子插入队列,以此类推,便可层层访问树。
求树的节点个数
//求树的节点个数
int TreeSize(BTNode* root)
{
return root==nullptr?0: TreeSize(root->left) + TreeSize(root->right) + 1;
}
求第K层的节点的个数
//求树的第k层的节点的个数;
//树的第K层节点的个数=k-1层节点的左树+k-1节点的右树;
int TreeLevel(BTNode* root,int k)//k理解为走的步数
{
if (root == nullptr)return 0;
if (k == 1)return 1;
return TreeLevel(root->left , k - 1) + TreeLevel(root->right, k - 1);
//走到最后一步判断最后一层的节点是空还是1,加载一块就是答案
}
求树的高度
//求树的高度
int TreeHeight(BTNode* root)
{
return root == 0 ? 0 : max(TreeHeight(root->left), TreeHeight(root->right))+1;
}
树的高度等于子树中的较大的高度+1;
判断是否是完全二叉树
// 判断二叉树是否是完全二叉树
bool TreeComplete(BTNode* root)
{
queue<BTNode*>s;
if (!root)return false;
s.push(root);
while (!s.empty())
{
BTNode* front = s.front();
s.pop();
if (front)
{
s.push(front->left);
s.push(front->right);
}
if (!front)
{
break;
}
}
while (!s.empty())
{
BTNode* front = s.front();
s.pop();
if (front)
{
return false;
}
}
s.emplace();
return true;
}
采用层序遍历的方式将每层的节点放入队列中,如果出现了空的节点就要判断空节点后是否有不为空的节点,如果有空的节点那么就不是完全二叉树,如果没有就是完全二叉树。
查找节点并返回
//查找节点并返回
BTNode* TreeFind(BTNode* root, int x)
{
//处理区域
if (root == nullptr)return nullptr;
if (root->val == x)
{
return root;
}
//前序遍历(遍历区域)
BTNode* ret1=TreeFind(root->left,x);
if (ret1)return ret1;
return TreeFind(root->right,x);
}
前序遍历查找目标节点,查找的关键在于要有变量接收返回值,否则返回值不能传递到最外层的函数中;