2012,淘宝笔试第一题,我被无情地鄙视了,自己算法基础真是不好,在当时没能看出这道题的解法。
题目要求:一颗完全二叉树,每一个节点除了pLeft和pRight之外还有一个pNext指针用来指示自己的兄弟,形如下图。要求是写个代码搞定他,分析时间复杂度和空间复杂度?
我来说下解法吧。
首先,一个完全二叉树可以用数组来表示(我就是2X的没有想起来这个有木有!!!!!)
然后,你要做的工作就是找到整棵树的所有最左边的左子树节点,例如A B 和 D。F虽然是左子树,但是不是最左边的,所以不行。再说白一点就是找到你要链表化的那个头节点!找这个头节点可以用数组下标来定位,第一层的下标是1,第二层是3,第三层是7....于是就是2^n - 1,其中n是层数(假设根节点A是第0层)。
找到这个头节点后,根据完全二叉树的定义,它的所有兄弟姐妹都和它在地址上连续,例如B的兄弟C就在B的下一个下标,同理D的兄弟 E,F(堂兄弟也是兄弟)也跟D是地址邻接的关系。那么第i层(如果不是最后一层的话)必然有2^i个兄弟。于是你就把这2^i个兄弟都用链表串在一起就行了。
for(int nLevel = 0;;nLevel++) //从第0层,也就是根节点开始
{
int nIndex = power(2,nLevel)-1; //计算链表头的下标
for(int nNextSibling = 1;nNextSibling<power(2,nLevel);nNextSibling++)//循环2^i - 1个节点(链表头节点已经排除出了循环范围)。这样根节点也就不会进入循环。
{
CompelteBinaryTree[nIndex].pNext = CompelteBinaryTree[nIndex+1];
nIndex++;
if(nIndex >= CompelteBinaryTreeNodeCount -1) return;
}
}
/×在这样的代码下,我们看看它是怎么对上图的树进行链表化的
从根节点进入(第0层)
nIndex = 0,power(2,nLevel) = 1, 于是不进入循环
第1层
nIndex = 1,power(2,nLevel) = 2,nNextSibling = 1 于是CompelteBinaryTree[1]的pNext就是CompelteBinaryTree[2]。
随后nNextSibling增长为2,进入下一层
第2层
nIndex = 3, power(2,nLevel) = 4,nNextSibling = 1 于是:CompelteBinaryTree[3]的pNext就是CompelteBinaryTree[4]
CompelteBinaryTree[4].pNext = CompelteBinaryTree[5],当nIndex曾长到5时,nIndex == CompelteBinaryTreeNodeCount -1于是函数退出
*/
时间复杂度(LogN)的平方,空间的话,如果一上来树就是数组存储的话,不需要额外空间,否则需要一个n的空间
----------------------------------------我是分割线---------------------------------------------------------
这里在补上一个我同学实现的算法(也是O(Log^2 N))。对于完全二叉树来说,把任何一层和其子树全部去掉之后,剩下的部分肯定是一个满二叉树。于是,如果对第i层:
若i+1层存在,那么第i层肯定满二叉树的状态。
若第i+1层不存在,那么第i层肯定是最后一层。
要判断i+1层是否能存在,实质上是判断第i层的第一个节点(最左方的节点)是否有左孩子。记录第i层的第一个节点的下标是j,于是它的左孩子就是2j+1。于是j=0开始,若2j+1存在,则将从j - > 2j下标的所有节点都串联起来并更新j=2j+1(开始判断下一层)。否则将从j位置开始的,到数组结束处的所有节点都串联起来(即最后一层)。
----------------------------------------我也是分割线---------------------------------------------------------
最后是我另一个同学的解法,O(n)的量级,无论是否是数组表示的完全二叉树,都需要一个n的队列作为辅助。
首先有一个计数器从1开始计数,前面说过了,每一层最左端的节点标号都是可以计算出来的。他的方法就是,把根节点先入队,然后执行下面的序列
while(队列不空)
{
出队;
计数器+1 //代表下一个节点的标号
左右孩子入队;
if(队首是否是下一层的开头) //如果下一个节点的标号恰是2的次方,那么下一个节点代表新的一层,同时出队节点是本层的最后一个节点
{
更换下一层的开始标号;
continue
}
else
{
出队的元素指向队首。
}
}