图与树
图结构:节点、边
分类:有向、无向
特殊的图:二叉树、普通树、堆
树的定义
树是由一个集合以及在该集合上定义的一种关系构成的,集合中的元素称为树的结点,所定义的关系称为父子关系。父子关系在树的结点之间建立了一个层次结构,在这种层次结构中有一个结点具有特殊的地位,这个结点称为该树的根结点。
数据结构中有很多树的结构,其中包括二叉树、二叉搜索树、2-3树、红黑树等等,本文着重介绍二叉树。
树的基本术语
节点的度:一个节点含有的子树的个数称为该节点的度;
叶节点或终端节点:度为0的节点称为叶节点;
非终端节点或分支节点:度不为0的节点;
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
兄弟节点:具有相同父节点的节点互称为兄弟节点;
树的度:一棵树中,最大的节点的度称为树的度;
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度或深度:树中节点的最大层次;
堂兄弟节点:双亲在同一层的节点互为堂兄弟;
节点的祖先:从根到该节点所经分支上的所有节点;
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。
森林:由m(m>=0)棵互不相交的树的集合称为森林;
二叉树
二叉树是数据结构中一种重要的数据结构,也是树表家族最为基础的结构。
二叉树的定义:二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。
二叉树的性质
二叉树的第i层至多有 2 i − 1 2^i-1 2i−1 个结点;
深度为k 的二叉树至多有 2 k − 1 2^{k-1} 2k−1 个结点;
对任何一棵二叉树TT ,如果其终端结点数为n0n0 ,度为2的结点数为n2n2 ,则n0=n2+1n0=n2+1 。
完全二叉树:只在最后一层右边缺少若干节点;
对于完全二叉树,其编号为i的节点,左孩子为2i,右孩子为2i+1,双亲为i/2
二叉树框架
遍历约定:先左后又
DLR:先序遍历 ABDHIEJCFG
LDR:中序遍历 HDIBJEAFCG
LRD:后序遍历 HIDJEBFGCA
template <class DataType>//任何类型的数据
struct BiNode
{
DataType data; //节点数据
BiNode<DataType> * lchild,*rchild;//左孩子右孩子
};
template <class DataType>
class BiTree
{
public:
BiTree(){
root = Create(root);}
~BiTree(){
Release(root);}
void PreOrder(){
PreOrder(root);} //前序遍历
void InOrder(){
InOrder(root);} //中序遍历
void PostOrder(){
PostOrder(root);} //后序遍历
private:
BiNode<DataType> * root;
BiNode<DataType> * Create(BiNode<DataType> *bt);
void Release(BiNode<DataType> *bt);
void PreOrder(BiNode<DataType> *bt);
void InOrder(BiNode<DataType> *bt);
void PostOrder(BiNode<DataType> *bt);
};
创造
template <class DataType>
BiNode<DataType> *BiTree<DataType>::Create(BiNode<DataType> *bt)
{
DataType ch;
cin>>ch;
if(ch == '#') bt = NULL;
else{
bt = new BiNode<DataType>;
bt->data = ch;
bt->lchild = Create(bt->lchild);//递归创建,先输入的是跟节点,之后输入左孩子,当不输入左孩子时输入#再输入其右节点
bt->rchild = Create(bt->rchild);
}
return bt;
}
释放
template <class DataType>
void BiTree<DataType>::Release(BiNode<DataType> *bt)
{
if(bt != NULL){
Release(bt->lchild);//递归释放
Release(bt->rchild);
delete bt;
}
}
前序遍历
template <class DataType>
void BiTree<DataType>::PreOrder(BiNode<DataType> *bt)
{
if(bt == NULL) return;
else{
cout<<bt->data;
PreOrder(bt->lchild);
PreOrder(bt->rchild);
}
}
中序遍历
template <class DataType>
void BiTree<DataType>::InOrder(BiNode<DataType> *bt)
{
if(bt == NULL) return;
else{
InOrder(bt->lchild);
cout<<bt->data;
InOrder(bt->rchild);
}
}
后序遍历
template <class DataType>
void BiTree<DataType>::PostOrder(BiNode<DataType> *bt)
{
if(bt == NULL) return;
else{
PostOrder(bt->lchild);
PostOrder(bt->rchild);
cout<<bt->data;
}
}
树与图算法题
左子树叶节点之和 (深度优先+广度优先+递归)
3
/
9 20
/
15 7
比如上面 9 和15是左子树上的子节点, 那么求和得 24.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {
}
};
我们需要用一个队列来实现广度优先. 每次循环的时候从队首取出一个节点, 然后把它的子节点(也就是左右子节点, 如果有的话)添加到队列中, 直到队列为空. 我们还需要把左右子节点的状态(也就是左边还是右边)记录到节点中, 所以我们用了一个 std::pair 这么一个数据结构.
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
if (root == NULL) return 0;
queue< pair<TreeNode*, bool> > q;
q.push(make_pair(root, false));
auto r = 0;
while (!q.empty()) {
auto p = q.front();
q.pop();
if (p.first->left == NULL && p.first->right == NULL && p.second) {
r += p.first->val; // sum the value if it comes from left
}
if (p.first->left) {
q.push(make_pair(p.first->left, true));
}
if (p.first->right) {
q.push(make_pair(p.first->right, false));
}
}
return r;
}
};
深度优先 DFS (DEPTH FIRST SEARCH)
深度优先访问树的方式也很直观, 就是一条路能往左下走就往左下走, 直到叶子节点, 然后再回溯到右节点. 我们可以通过递归的方式 来实现, 当然也可以自己创建和维护堆栈来实现DFS.
class Solution {
public:
void walk(TreeNode* root, bool left) {
if (root == NULL) return;
// leaf
if (root->left == NULL && root->right == NULL) {
if (left) {
// sum only left branches
sum += root->val;
}
}
if (root->left != NULL) {
// DFS left sub tree
walk(root->left, true);
}
if (root->right != NULL) {
// DFS right sub tree
walk(root->right, false);
}
}
int sumOfLeftLeaves(TreeNode* root) {
if (root) {
walk(root->left, true);
walk(root->right, false);
}
return sum;
}
private:
int sum = 0;
};
给定二叉树前、中序遍历,构造二叉树
前序遍历第一个节点是根节点x;
从中序遍历序列中找到根节点x;
中序遍历中的x的左边序列对应等长的前序遍历序列——左子树;
中序遍历中的x的右边序列对应等长的前序遍历序列——右子树;
二叉树的下一个节点
二叉树的下一个节点
给定一颗二叉树和其中一个节点,找到中序遍历的下一个节点,树中有指向左、右两个子树的指针,还有指向父节点的指针
关键是怎么就能知道只有这两种情况了,可能只有试了:
8的下一节点在4,因为其是左节点,直接找其父节点,这是最简单的情况;
9的下一节点在2,因为是右节点,而且是左子树,所以找其左子树父节点;
10的下一节点在5;
11的下一节点在12,因为是右节点,而且是右子树,所以找其更大的父节点;
4的节点是9,找其右子节点;
5的节点是11;
6的节点是16,找其右子节点的最左节点;
/*
二叉树的下一个节点
给定一颗二叉树和其中一个节点,找到中序遍历的下一个节点,树中有指向左、右两个子树的指针,还有
指向父节点的指针
*/
Struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(NULL), rihgt(NULL){
}
};
class Solution{
public:
TreeNode* getNext(TreeNode* node){
if(node == NULL){
return NULL;
}
if(node->right != NULL){
//如果节点的右子节点不为空,如4,5,2,1,6,3,7,13
TreeNode* next = node->right; //右子节点
while(next->left != NULL){
//右子节点的左子节点一直求左子节点,例如6的下一个节点是16
next = next->left;
}
return next;
} //右子节点为空
while(node->parent != NULL){
//只要父节点是不为空
TreeNode* next = node->parent