目录
13.将二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
1.前中后序遍历(递归与非递归)
1.前序遍历
1.递归
void preOrder1(BinTree *root)
{
if (root != NULL)
{
cout << root->data << " ";
preOrder1(root->lchild);
preOrder1(root->rchild);
}
}
2.非递归
对于任一结点P:
1)访问结点P,并将结点P入栈;
2)判断结点P的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P,循环至1);若不为空,则将P的左孩子置为当前的结点P;
3)直到P为NULL并且栈为空,则遍历结束。
void preOrder2(BinTree *root)
{
stack<BinTree*> s;
BinTree *p = root;
while (p != NULL || !s.empty())
{
while (p != NULL)
{
cout << p->data << " ";
s.push(p);
p = p->lchild;
}
if (!s.empty())
{
p = s.top();
s.pop();
p = p->rchild;
}
}
}
2.中序遍历
1.递归
void inOrder1(BinTree *root)
{
if (root != NULL)
{
inOrder1(root->lchild);
cout << root->data << " ";
inOrder1(root->rchild);
}
}
2.非递归
对于任一结点P,
1)若其左孩子不为空,则将P入栈并将P的左孩子置为当前的P,然后对当前结点P再进行相同的处理;
2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子;
3)直到P为NULL并且栈为空则遍历结束。
void inOrder2(BinTree *root)
{
stack<BinTree*> s;
BinTree *p = root;
while (p != NULL || !s.empty())
{
while (p != NULL)
{
s.push(p);
p = p->lchild;
}
if (!s.empty())
{
p = s.top();
cout << p->data << " ";
s.pop();
p = p->rchild;
}
}
}
3.后序遍历
1.递归
void postOrder1(BinTree *root)
{
if (root != NULL)
{
postOrder1(root->lchild);
postOrder1(root->rchild);
cout << root->data << " ";
}
}
2.非递归
要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存 在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了 每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。
void postOrder3(BinTree *root)
{
stack<BinTree*> s;
BinTree *cur; //当前结点
BinTree *pre = NULL; //前一次访问的结点
s.push(root);
while (!s.empty())
{
cur = s.top();
if ((cur->lchild == NULL&&cur->rchild == NULL) ||
(pre != NULL && (pre == cur->lchild || pre == cur->rchild)))
{
cout << cur->data << " "; //如果当前结点没有孩子结点或者孩子节点都已被访问过
s.pop();
pre = cur;
}
else
{
if (cur->rchild != NULL)
s.push(cur->rchild);
if (cur->lchild != NULL)
s.push(cur->lchild);
}
}
}
2.层序遍历
void printTree(BinTree *root)//层序遍历
{
queue<BinTree *> s;
s.push(root);
while (!s.empty())
{
BinTree *p = s.front();
cout << p->data << "";
s.pop();
if (p->lchild != NULL)
s.push(p->lchild);
if (p->rchild != NULL)
s.push(p->rchild);
}
}
3.求二叉树高度(深度)
int Depth(BinTree* root)
{
if (root == NULL)
return 0;
int left = Depth(root->lchild);
int right = Depth(root->rchild);
return left > right ? left + 1 : right + 1;
}
4.求叶子节点个数
size_t GetLeafSize(BinTree* root)//求叶子节点数
{
if (root == NULL)
return 0;
if (root->lchild == NULL
&& root->rchild == NULL)
{
return 1;
}
return GetLeafSize(root->lchild)
+ GetLeafSize(root->rchild);
}
5.求二叉树第K层节点个数
1.求二叉树第K层节点个数(递归)
int get_k_level_number(BinTree *root, int k)
{
if (root == NULL || k <= 0)
return 0;
if (root != NULL && k == 1)
return 1;
return (get_k_level_number(root->lchild, k - 1) +
get_k_level_number(root->rchild,k - 1));
}
2.非递归实现求二叉树第k层的节点数
6.判断一个节点是否在一棵二叉树上
bool IsInTree(BinTree* root, int x)
{
if (root == NULL)
return false;
if (root->data == x)
return true;
return IsInTree(root->lchild, x)
|| IsInTree(root->rchild, x);
}
7.求两个节点的最近公共祖先
1.搜索树
1 某一个节点为根节点,那么公共节点就是根节点了。
2 这俩个节点在不同子树,那么公共节点就是根节点。
3 这俩个节点在同一子树。
所以根据以上分析,可得我们可以通过不断的递归直到找到一个子树,在该子树中俩节点分别位于它的左子树和右子树,那么该子树节点就是公共节点。
BinTree* findCom_ancestor(BinTree *root, BinTree *x, BinTree *x2)
{
if (root == NULL) return NULL;
if (x == root || x2 == root) return root;
if (x->data > root->data&&x2->data < root->data)
{
return root;
}
if (x->data < root->data&&x2->data > root->data)
{
return root;
}
if (x->data < root->data)
{
return findCom_ancestor(root->lchild, x, x2);
}
else
{
return findCom_ancestor(root->rchild, x, x2);
}
}
2.三叉链
1.给定的两个节点都含有父节点,因此,可将这两个节点看做是两个链表的头结点,将求两个节点的最近公共祖先节点转化为求两链表的交点,这两个链表的尾节点都是根节点。(O(n))
int Hight(BinTree* root, BinTree* node)
{
int len = 0;
while (node != NULL)
{
len++;
node = node->parent;
}
return len;
}
BinTree* GetLastCommonAncestor(BinTree* root, BinTree* node1, BinTree* node2)
{
if (root == NULL || node1 == NULL || node2 == NULL)
return NULL;
int len1 = Hight(root, node1);
int len2 = Hight(root, node2);
for (; len1 > len2; len1--)
node1 = node1->parent;
for (; len2 > len1; len2--)
node2 = node2->parent;
while (node1 && node2 && node1 != node2)
{
node1 = node1->parent;
node2 = node2->parent;
}
if (node1 == node2)
return node1;
else
return NULL;
}
2.首先给出node1的父节点node1->_parent,然后将node1的所有父节点依次和node2->parent作比较,如果发现两个节点相等,则该节点就是最近公共祖先,直接将其返回。如果没找到相等节点,则将node2的所有父节点依次和node1->_parent->_parent作比较......直到node1->_parent==NULL。O(n^2)
BinTree * GetLastCommonAncestor(BinTree * root, BinTree * node1, BinTree * node2)
{
BinTree * temp;
while (node1 != NULL)
{
node1 = node1->parent;
temp = node2;
while (temp != NULL)
{
if (node1 == temp->parent)
return node1;
temp = temp->parent;
}
}
}
3.一般二叉树
从根节点开始遍历,如果node1和node2中的任一个和root匹配,那么root就是最低公共祖先。
如果都不匹配,则分别递归左、右子树,
如果有一个 节点出现在左子树,并且另一个节点出现在右子树,则root就是最低公共祖先.
如果两个节点都出现在左子树,则说明最低公共祖先在左子树中,否则在右子树。
该函数当一个节点是另一个节点的祖先时,返回的是离根节点最近的那个节点,要想返回最近公共祖先节点需进行判断两节点是否有祖孙关系,
若两节点为F,D,则判断出B的左子树中的D节点后,继续判断判断节点D的左右子树中是否含有节点F,若有,则返回B,若没有则继续判断右子树中的另一个节点。
BinTree* GetLastCommonAncestor(BinTree* root, BinTree* node1, BinTree* node2)
{
if (root == NULL || node1 == NULL || node2 == NULL)
return NULL;
if (node1 == root || node2 == root)
return root;
BinTree* cur = NULL;
BinTree* left_lca = GetLastCommonAncestor(root->lchild, node1, node2);
if (NULL != left_lca)
{
cur = GetLastCommonAncestor(left_lca->lchild, node1, node2);
if (cur == NULL)
cur = GetLastCommonAncestor(left_lca->rchild, node1, node2);
if ((cur == node1) && (left_lca == node2) || (cur == node2) && (left_lca == node1))
return root;
}
BinTree* right_lca = GetLastCommonAncestor(root->rchild, node1, node2);
if (NULL != right_lca)
{
cur = GetLastCommonAncestor(right_lca->rchild, node1, node2);
if (cur == NULL)
cur = GetLastCommonAncestor(right_lca->rchild, node1, node2);
if ((cur == node1) && (right_lca == node2) || (cur == node2) && (right_lca == node1))
return root;
}
if (left_lca && right_lca)
return root;
if (left_lca == NULL)
return right_lca;
else
return left_lca;
}
8.判断一棵二叉树是否是平衡二叉树
//1
bool IsBalanceTree(BinTree* root)//(时间复杂度是N^2)
{
if (root == NULL)
return true;
int left = Depth(root->lchild);
int right = Depth(root->rchild);
return abs(left - right)<2
&& IsBalanceTree(root->lchild)
&& IsBalanceTree(root->rchild);
}
//2
bool IsBalance(BinTree* root, int& depth)//(时间复杂度是N)
{
if (root == NULL)
{
depth = 0;
return true;
}
int leftDepth = 0;
if (IsBalance(root->lchild, leftDepth) == false)
return false;
int rightDepth = 0;
if (IsBalance(root->rchild, rightDepth) == false)
return false;
depth = leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
return abs(leftDepth - rightDepth) < 2;
}
9.求二叉树中最远两个节点的距离
int _FindMaxLen(BinTree* root, int& maxLen)
{
if (root == NULL)
return 0;
int left = _FindMaxLen(root->lchild, maxLen);
int right = _FindMaxLen(root->rchild, maxLen);
if (left + right > maxLen)
{
maxLen = left + right;
}
return left > right ? left + 1 : right + 1;
}
10. 由前序遍历和中序遍历重建二叉树
BinTree* _RebuildBinaryTree(int* startPre, int* endPre, int* startIn, int* endIn)
{
//根据前序遍历保存根节点
BinTree *Root;
Root = new BinTree();
Root->data = startPre[0];
Root->lchild = NULL;
Root->rchild = NULL;
//判断是否找完了此次中序遍历,若是找完了,则返回Root;
if (startIn == endIn&&*startIn == *endIn)
return Root;
//在中序遍历中找根节点所在的位置
int* RootIn = startIn;
while (*startPre != *RootIn)
RootIn++;
//根据根节点在中遍历中的位置,递归还原左子树
int leftlen = RootIn - startIn;
if (leftlen > 0)
{
Root->lchild = _RebuildBinaryTree(startPre + 1, startPre + leftlen, startIn, RootIn-1);
}
//左子树长度加上中序的起始位置后若仍小于整个中序长度,则说明该节点右子树存在,递归还原右子树
if (leftlen + startIn < endIn)
{
Root->rchild = _RebuildBinaryTree(startPre + leftlen + 1, endPre, startIn + leftlen + 1, endIn);
}
return Root;
}
11. 判断一棵树是否是完全二叉树
//->关键:找第一个度不为2的结点->后序结点:如果有孩子则不是完全二叉树,否则是
bool IsCompleteBinaryTree(BinTree* root)
{
if (NULL == root) //
return false;
bool isOnlyLeft = false; //标记仅有左节点的结点
queue<BinTree*> q;
q.push(root);
while (!q.empty())
{
BinTree* pCur = q.front();
q.pop();
if (isOnlyLeft)
{
if (pCur->lchild || pCur->rchild)
return false;
}
else
{
if (NULL == pCur->lchild && NULL != pCur->rchild
) //存在右孩子没有左孩子,一定不为完全二叉树
return false;
else if (NULL != pCur->lchild && NULL == pCur->rchild) //存在左孩子没有右孩子可能不是,记录标记结点看后续结点
{
q.push(pCur->lchild);
isOnlyLeft = true; //只有左孩子是非满结点
}
else if (NULL != pCur->lchild && NULL != pCur->rchild) // 左右孩子都存在,入队列继续循环判断
{
q.push(pCur->lchild);
q.push(pCur->rchild);
}
else
isOnlyLeft = true; //左右孩子都存在,为非满结点,看后续结点
}
}
return true;
}
12.求二叉树的镜像(即左右子树交换)
BinTree * Mirror(BinTree * Root)
{
if (Root == NULL) // 返回NULL
return NULL;
BinTree * pLeft = Mirror(Root->lchild); // 求左子树镜像
BinTree * pRight = Mirror(Root->rchild); // 求右子树镜像
// 交换左子树和右子树
Root->lchild = pRight;
Root->rchild = pLeft;
return Root;
}
13.将二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
BinTree* Convert(BinTree* root)
{
if (root == NULL){//假如根节点为空,返回空
return NULL;
}
if (root->lchild == NULL&&root->rchild == NULL){//假如只有一个根节点,则返回根节点
return root;
}
//1、将左子树构造成双链表,并返回该链表头结点left
BinTree* left = Convert(root->lchild);
//2、定位到左子树链表的最后一个节点(左子树最右边的节点)
BinTree* p = left;//创建一个临时节点P,用来遍历找到左链表的最后一个节点(左子树最右边的节点),p初始化指向做左子树的根节点,
while (p != NULL&&p->rchild != NULL)
{
p = p->lchild;//最终p为左子树最右边的节点
}
//3、如果左子树链表不为空,将当前root追加到左子树链表后
if (left != NULL){//左子树链表不为空
p->rchild = root;//左子树链表的最后一个节点p(左子树最右边节点)的右指针指向当前root节点
root->lchild = p;//当前root节点的左指针指向左子树链表的最后一个节点p(左子树最右边节点)
}
//4、将右子树构造成双链表,并返回该链表的头结点right
BinTree* right = Convert(root->rchild);
//5、如果右子树链表不为空,将右子树链表追加到当前root后
if (right != NULL)
{//右子树链表不为空
right->lchild = root;//右子树链表的头结点right的左指针指向当前root
root->rchild = right;//当前root的右指针指向右子树链表的头结点right
}
return left != NULL ? left : root;//根据左子树链表是否为空返回整个双向链表的头指针。
}