剑指offer之树

Posted dzy521

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指offer之树相关的知识,希望对你有一定的参考价值。

//Tree in 剑指offer
/*
面试题7:重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建
该二叉树。
*/
typedef struct BinaryTreeNode{
  int m_nValue;
  BinaryTreeNode* m_pLeft;
  BinaryTreeNode* m_pRight;
}BinaryTreeNode;

BinaryTreeNode* Construct(int *preorder, int *inorder, int length)
{
  if(preorder == NULL || inorder == NULL || (length <= 0))
     return NULL;

  return ConstructCore(preorder,preorder+length-1,inorder,inorder+length-1);
}

BinaryTreeNode* ConstructCore(int *sPreorder, int* ePreorder, int* sInorder, int* eInorder)
{
  int rootValue = sPreorder[0];
  BinaryTreeNode* root = (BinaryTreeNode*)malloc(sizeof(struct BinaryTreeNode));
  root->m_nValue = rootValue;
  root->m_pLeft = root->m_pRight = NULL;

  if(sPreorder = eInorder)
  {
    if(sInorder == eInorder && *sPreorder == *sInorder) return NULL;
    else goto;
  }

  //find the address in Inorder array
  int *rootInorder = sInorder;
  while(rootInorder <= eInorder && *rootInorder != rootValue)
    rootInorder++;
  //may the root don‘t have right subtree
  //找遍整个中序序列都无法找到根节点值 error
  if(rootInorder == eInorder && *rootInorder != rootValue)
     goto;

   int leftLength = rootInorder - sInorder;
   int *leftPreorderEnd = sPreorder + leftLength;

   //if there is a left subtree
   if(leftLength > 0)
   {
     root->left = ConstructCore(sPreorder+1, leftPreorderEnd, sInorder, rootInorder-1);
   }
   //if there is a right subtree
   if(leftLength < ePreorder - sPreorder)
   {
     root->right = ConstructCore(leftPreorderEnd+1, ePreorder, rootInorder+1, eInorder);
   }

   return root;
}



/*
面试题8: 二叉树的下一个节点
给定一颗二叉树和其中一个节点,如何找出中序遍历序列的
下一个节点?树中的节点除了有两个分别指向左、右子节点的指针
还有一个指向父节点的指针
*/
/*
1)该节点有右子树 返回右子树最左边的节点
2)该节点无右子树 但该节点是其父节点的左子树
               3)但该节点是其父节点的右子树
*/
BinaryTreeNode* GetNext(BinaryTreeNode* pNode)
{
  if(pNode == NULL) return NULL;
  if(pNode->right)
  {
    BinaryTreeNode *pRighgt = pNode->right;
    while(pRighgt->left)
    {
      pRighgt = pRighgt->left;
    }
     return pRight;
  }
  else if(pNode->m_nParent)
  {
    BinaryTreeNode *pParent = pNode->m_nParent;
    BinaryTreeNode *pCurrent = pNode;
    while(pParent != NULL && pCurrent != pParent->right)
    {
      pCurrent = pParent;
      pParent = pParent->m_nParent;
    }

    return pParent;
  }
}



/*
面试题26:树的子结构
输入两棵二叉树A和B,判断B是不是A的子结构。二叉树的节点定义如下:
struct BinaryTreeNode
{
  double m_dbvalue;
  BinaryTreeNode* m_pLeft;
  BinaryTreeNode* m_pRight;
};
*/
bool HasSubtree(BinaryTreeNode* pRoot1,BinaryTreeNode* pRoot2)
{
  bool flag = false;
  if(pRoot1 != NULL && pRoot2 != NULL)
  {
    if(Equal(pRoot1->m_dbvalue,pRoot2->m_dbvalue))
      flag = DoesTree1HaveTree2(pRoot1,pRoot2);
    if(!flag)
      flag = HasSubtree(pRoot1->m_pLeft,pRoot2);
    if(!flag)
      flag = HasSubtree(pRoot1->m_pRight,pRoot2);
  }
  return flag;
}


bool DoesTree1HaveTree2(BinaryTreeNode* pRoot1,BinaryTreeNode* pRoot2)
{
  if(pRoot1 == NULL) return false;
  if(pRoot2 == NULL) return true;

  if(!Equal(pRoot1->m_dbvalue,pRoot2->m_dbvalue)) return false;

  return DoesTree1HaveTree2(pRoot1->m_pLeft,pRoot2->m_pLeft) && DoesTree1HaveTree2(pRoot1->m_pRight,pRoot2->m_pRight);
}


bool Equal(double num1, double num2)
{
  if((num1 - num2 > -0.0000001) && (num1 - num2 < 0.0000001))
    return true;
  else
    return false;
}



/*
面试题27:二叉树的镜像
完成一个函数,输入一棵二叉树,该函数输出它的镜像。二叉树
节点定义如下:
struct BinaryTreeNode
{
  int m_nValue;
  ...
  ...
};
*/

//writer
void MirrorRecursively(BinaryTreeNode* pNode)
{
  if(pNode == NULL)
    return;
  if(pNode->m_pLeft == NULL && pNode->m_pRight == NULL) return;

  BinaryTreeNode *pTemp = pNode->m_pLeft;
  pNode->m_pLeft = pNode->m_pRight;
  pNode->m_pRight = pTemp;

  if(pNode->m_pLeft) MirrorRecursively(pNode->m_pLeft);
  if(pNode->m_pRight) MirrorRecursively(pNode->m_pRight);
}

//mine
//but didn‘t be tested
BinaryTreeNode* mine_Mirror(BinaryTreeNode* T)
{
  if(!T) return T;

  BinaryTreeNode *r = mine_Mirror(T->m_pLeft);
  BinaryTreeNode *l = mine_Mirror(T->m_pRight);

  T->m_pLeft = r;
  T->m_pRight = l;

  return T;
}



/*
面试题28:对称的二叉树
实现一个函数,判断它是不是对称的
如果一个二叉树和他的镜像一样,则它是对称的
*/
bool isSymmetrical(BinaryTreeNode* pRoot)
{
  return isSymmetrical(pRoot,pRoot);
}

bool isSymmetrical(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2)
{
  if(pRoot1 == NULL && pRoot2 == NULL) return true;
  if(pRoot1 == NULL || pRoot2 == NULL) return false;

  if(pRoot1->m_nValue != pRoot2->m_nValue) return false;
  return isSymmetrical(pRoot1->m_pLeft, pRoot2->m_pRight) && isSymmetrical(pRoot1->m_pRight,pRoot2->m_pLeft);
}



/*
面试题32:从上到下打印二叉树
思路:层次遍历
*/

/*
扩展一:分行从上到下打印字符串
eg:
8
6 10
5 7 9 11
*/
void Print(BinaryTreeNode* pRoot)
{
  if(pRoot == NULL)
  {
    return;
  }

  q = CreateQueue();
  q.push(pRoot);
  int ToBeDeleted = 1;
  int nextlevel = 0;

  while(!isEmpty(q))
  {
    BinaryTreeNode *pNode = q.front();
    printf("%d ",pNode->m_nValue);
    

    if(pNode->m_pLeft)
    {
      nextlevel++;
      q.push(pNode->m_pLeft);
    }

    if(pNode->m_pRight)
    {
      nextlevel++;
      q.push(pNode->m_pRight);
    }

    q.pop();
    --ToBeDeleted;
    //当这一层节点都printf以后
    if(ToBeDeleted == 0)
    {
      printf("
");
      ToBeDeleted = nextlevel;
      nextlevel = 0;
    }
  }
}


/*
扩展2:之字形打印二叉树
eg:(扩展1)
8
10 6
5 7 9 11
*/


//mine
void Print(BinaryTreeNode* pRoot)
{
  if(pRoot == NULL)
    return;

  s1 = CreateStack();  //奇数层所用的栈
  s2 = CreateStack();  //偶数层所用的栈
  int k = 1;           //当前打印的层数
  s1.push(pRoot);

  int nextlevel = 0;
  int ToBeDeleted = 1;

  while(!isEmpty(s1) || !isEmpty(s2))
  {
    //当前在打印奇数层
    //应当把子节点压入偶数栈
    if(k % 2 == 1)
    {
      BinaryTreeNode *pNode = s1.top();
      printf("%d ",pNode->m_nValue);

      if(pNode->left)
      {
        s2.push(pNode->left);
        nextlevel++;
      }

      if(pNode->right)
      {
        s2.push(pNode->right);
        nextlevel++;
      }

      s1.pop();
      ToBeDeleted--;
      if(ToBeDeleted == 0)
      {
        k++;
        ToBeDeleted = nextlevel;
        nextlevel = 0;
      }
    }

    if(k % 2 == 0)
    {
      BinaryTreeNode *pNode = s2.top();
      printf("%d ",pNode->m_nValue);

      if(pNode->right)
      {
        s1.push(pNode->right);
        nextlevel++;
      }

      if(pNode->left)
      {
        s1.push(pNode->left);
        nextlevel++;
      }

      s2.pop();
      ToBeDeleted--;
      if(ToBeDeleted == 0)
      {
        k++;
        ToBeDeleted = nextlevel;
        nextlevel = 0;
      }
    }
  }
}



//writer
void Print(BinaryTreeNode* pRoot)
{
  if(pRoot == NULL)
    return;

  Stack levels[2];
  levels[0] = CreateStack();
  levels[1] = CreateStack();
  int current = 0;
  int next = 1;

  levels[current].push(pRoot);
  while(!levels[0].empty() || !levels[1].empty())
  {
    BinaryTreeNode *pNode = levels[current].top();
    levels[current].pop();

    printf("%d ",pNode->m_nValue);

    if(current == 0)
    {
      if(pNode->m_pLeft)
        levels[next].push(pNode->m_pLeft);
      if(pNode->m_pRight)
        levels[next].push(pNode->m_pRight);
    }
    else
    {
      if(pNode->m_pRight)
        levels[next].push(pNode->m_pRight);
      if(pNode->m_pLeft)
        levels[next].push(pNode->m_pLeft);
    }

    if(levels[current].empty())
    {
      printf("
");
      current = 1 - current;
      next = 1 - next;
    }
  }
}



/*
面试题33:二叉搜索树的后序遍历
输入一个整数数组,判断该数组是不是某二叉搜索树的
后序遍历结果。如果是则返回true,否则返回false。
假设输入的数组中的任意两个数字都互不相同
*/
bool VerifySquenceOfBST(int sequence[], int length)
{
  if(sequence == NULL || length <= 0) return false;

  int root = sequence[length-1];

  int i=0;
  for(;i<length-1;i++)
  {
    if(sequence[i] > root) break;
  }

  int j = i;
  for(;j<length-1;j++)
  {
    if(sequence[j] < root)
      return false;
  }

  bool left = true;
  if(i>0) left = VerifySquenceOfBST(sequence,i);
  bool right = true;
  if(length - i - 1 >0) right = VerifySquenceOfBST(sequence+i,length-i-1);

  return (left&&right);
}



/*
面试题34:二叉树中和为某一值的路径
题目:输入一棵二叉树和整数,打印出二叉树中节点值
的和为输入整数的所有路径。从树的根节点开始往下一直到
叶节点所经过的节点形成一条路径。
*/
void FindPath(BinaryTreeNode* pRoot, int expectedSum)
{
  if(pRoot == NULL) return;

  std::vector<int> path;
  int currentSum = 0;
  FindPath(pRoot, expectedSum, path, currentSum);
}

void FindPath(BinaryTreeNode* pRoot, int expectedSum, std::vector<int>& path, int currentSum)
{
  currentSum += pRoot->m_nValue;
  path.push_back(pRoot->m_nValue);

  bool isLeaf = (pRoot->m_pLeft == NULL && pRoot->m_pRight == NULL);
  if(currentSum == expectedSum && isLeaf)
  {
    printf("a path is found:");
    std::vector<int>::iterator iter = path.begin();
    for(;iter!=path.end();++iter)
      printf("%d	",*iter);

    printf("
");
  }

  if(pRoot->m_pLeft != NULL)
    FindPath(pRoot->m_pLeft,expectedSum,path,currentSum);
  if(pRoot->m_pRight != NULL)
    FindPath(pRoot->m_pRight,expectedSum,path,currentSum);

  path.pop_back();
}



/*
面试题36:二叉搜索树和双链表
*/
BinaryTreeNode* Convert(BinaryTreeNode* pRootOfTree)
{
  BinaryTreeNode* pLastNodeInList = NULL;
  Convert(pRootOfTree,&pLastNodeInList);

  //返回头节点
  BinaryTreeNode* pHeadOfList = pLastNodeInList;
  while(pHeadOfList != NULL && pHeadOfList->m_pLeft != NULL)
     pHeadOfList = pHeadOfList->m_pLeft;

  return pHeadOfList;
}

void ConvertNode(BinaryTreeNode* pNode, BinaryTreeNode** pLastNodeInList)
{
  if(pNode == NULL) return;
  BinaryTreeNode *pCurrent = pNode;

  if(pCurrent->m_pLeft != NULL)
    ConvertNode(pCurrent->m_pLeft,pLastNodeInList);

  pCurrent->m_pLeft = *pLastNodeInList;
  if(*pLastNodeInList != NULL)
    (*pLastNodeInList)->m_pRight = pCurrent;

  *pLastNodeInList = pCurrent;

  if(pCurrent->m_pRight != NULL)
    ConvertNode(pCurrent->m_pRight,pLastNodeInList);
}



/*
面试题37:序列化二叉树
请实现两个函数,分别用来序列化和反序列化二叉树
*/
void Serialize(BinaryTreeNode* pRoot, ostream& stream)
{
  if(pRoot == NULL)
  {
    stream<<"%,";
    return;
  }

  stream<<pRoot->m_nValue<<,;
  Serialize(pRoot->m_pLeft,stream);
  Serialize(pRoot->m_pRight,stream);
}

void Deserialize(BinaryTreeNode* pRoot, istream& stream)
{
  int number;
  if(ReadStream(stream,&number))   //函数ReadStream每次从流中读取一个数字或其他字符
                                   //读到数字返回true,否则返回false
  {
    *pRoot = new BinaryTreeNode();
    (*pRoot)->m_nValue = number;
    (*pRoot)->m_pLeft = NULL;
    (*pRoot)->m_pRight = NULL;

    Deserialize(&((*pRoot)->m_pLeft),stream);
    Deserialize(&((*pRoot)->m_pRight),stream);
  }
}



/*
面试题54: 二叉搜索树的第k大节点
给定一棵二叉搜索树,找出其中第k大的节点
*/
BinaryTreeNode* KthNode(BinaryTreeNode* pRoot, unsigned int &k)  //注意,此处为k的引用
{
  if(pRoot == NULL || k<= 0)
    return NULL;

  BinaryTreeNode* res = NULL;
  //中序遍历
  if(pRoot->m_pLeft != NULL)
    res = KthNode(pRoot->m_pLeft,k);
  /*************************************///
  if(res == NULL)
  {
    if(k == 1)  //找到了
      return pRoot;

    k--;
  }
  /************************************///
  if(res == NULL && pRoot->m_pRight != NULL)
    res = KthNode(pRoot->m_pRight,k);

  return res;
}



/*
面试题55:二叉树的深度
输入一棵二叉树的根节点,求该树的深度
深度:最长路径的长度
*/
int TreeDepth(BinaryTreeNode* pRoot)
{
  if(pRoot == NULL)
    return 0;

  int nLeft = TreeDepth(pRoot->m_pLeft);
  int nRight = TreeDepth(pRoot->m_pRight);

  return (nLeft > nRight)?(nLeft+1):(nRight+1);
}

/*
扩展:平衡二叉树
输入一棵二叉树的根节点,判断是不是AVL树
*/
bool isBalanced(BinaryTreeNode* pRoot)
{
  if(pRoot == NULL) return true;
  
  int left = TreeDepth(pRoot->m_pLeft);
  int right = TreeDepth(pRoot->m_pRight);
  int diff = left - right;
  if(diff > 1 || diff < -1) return false;

  return isBalanced(pRoot->m_pLeft) && isBalanced(pRoot->m_pRight);
}

//better solution
bool isBalanced(BinaryTreeNode* pRoot, int *pDepth)
{
  if(pRoot == NULL)
  {
    *pDepth = 0;
    return true;
  }

  int left,right;
  if(isBalanced(pRoot->left,&left) && isBalanced(pRoot->right,&right))
  {
    int diff = left - right;
    if(diff <= 1 && diff >= -1)
    {
      *pDepth = 1+(left>right?left:right);
      return true;
    }
  }

  return false;
}

 

以上是关于剑指offer之树的主要内容,如果未能解决你的问题,请参考以下文章

剑指Offer数据结构之树[Python版]

剑指Offer数据结构之树[Python版]

剑指offer之树的子结构

LeetCode810. 黑板异或游戏/455. 分发饼干/剑指Offer 53 - I. 在排序数组中查找数字 I/53 - II. 0~n-1中缺失的数字/54. 二叉搜索树的第k大节点(代码片段

剑指 Offer(第 2 版)完整题解笔记 & C++代码实现(LeetCode版)

剑指 Offer(第 2 版)完整题解笔记 & C++代码实现(LeetCode版)