您的位置:首页 > 其它

算法学习--二叉树

2016-02-06 20:27 316 查看


二分查找树转化为排序的循环双链表

输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。

要求不能创建任何新的结点,只调整指针的指向。

例如对于下面的二分查找树:



small pointer 其实也就是指向左孩子,large pointer指向右孩子,转化为双链表之后 small pointer应该指向前一个元素,large pointer应该指向后一个元素。转化为排序的循环双链表则为:



中序遍历改变指针方向

void inOrderBSTree(BSTreeNode* pBSTree)
{

if (NULL==pBSTree)
{
return;
}
if (NULL!=pBSTree->m_pLeft)
{
inOrderBSTree(pBSTree->m_pLeft);
}

//  if (NULL!=pBSTree)
//  {
//      cout<<pBSTree->m_nValue;
//  }
convertToDoubleList(pBSTree);

if (NULL!=pBSTree->m_pRight)
{
inOrderBSTree(pBSTree->m_pRight);
}

}
/************************************************************************/

/************************************************************************/
/* 调整结点指针                                                                   */
void convertToDoubleList(BSTreeNode* pCurrent)
{
pCurrent->m_pLeft=pIndex;//使当前结点的左指针指向双向链表中最后一个结点
if (NULL==pIndex)//若最后一个元素不存在,此时双向链表尚未建立,因此将当前结点设为双向链表头结点
{
pHead=pCurrent;
}
else//使双向链表中最后一个结点的右指针指向当前结点
{
pIndex->m_pRight=pCurrent;
}

pIndex=pCurrent;//将当前结点设为双向链表中最后一个结点

cout<<pCurrent->m_nValue<<" ";

}

LCA:
解法1:下面是一个简单的复杂度为 O(n) 的算法,解决LCA问题

1) 找到从根到n1的路径,并存储在一个向量或数组中。

2)找到从根到n2的路径,并存储在一个向量或数组中。

3) 遍历这两条路径,直到遇到一个不同的节点,则前面的那个即为最低公共祖先.
bool findpath(Node * root,vector<int> &path,int key){
if(root == NULL) return false;
path.push_back(root->key);
if(root->key == key) return true;
//左子树或右子树 是否找到,找到的话当前节点就在路径中了
bool find =  ( findpath(root->left, path, key) || findpath(root->right,path ,key) );
if(find) return true;
//该节点下未找到就弹出
path.pop_back();
return false;
}

int findLCA(Node * root,int key1,int key2){
vector<int> path1,path2;
bool find1 = findpath(root, path1, key1);
bool find2 = findpath(root, path2, key2);
if(find1 && find2){
int ans ;
for(int i=0; i<path1.size(); i++){
if(path1[i] != path2[i]){
break;
}else
ans = path1[i];
}
return ans;
}
return -1;
}


解法2:

上面的方法虽然是O(n),但是操作依然繁琐了一点,并且需要额外的空间来存储路径。其实可以只遍历一次,利用递归的巧妙之处。学好二叉树,其实就是学好递归。

从root开始遍历,如果n1和n2中的任一个和root匹配,那么root就是LCA。 如果都不匹配,则分别递归左、右子树,如果有一个 key(n1或n2)出现在左子树,并且另一个key(n1或n2)出现在右子树,则root就是LCA.  如果两个key都出现在左子树,则说明LCA在左子树中,否则在右子树。
struct Node *findLCA(struct Node* root, int n1, int n2)
{
if (root == NULL) return NULL;
// 只要n1 或 n2 的任一个匹配即可
//  (注意:如果 一个节点是另一个祖先,则返回的是祖先节点。因为递归是要返回到祖先的 )
if (root->key == n1 || root->key == n2)
return root;
// 分别在左右子树查找
Node *left_lca  = findLCA(root->left, n1, n2);
Node *right_lca = findLCA(root->right, n1, n2);
// 如果都返回非空指针 Non-NULL, 则说明两个节点分别出现了在两个子树中,则当前节点肯定为LCA
if (left_lca && right_lca)  return root;
// 如果一个为空,在说明LCA在另一个子树
return (left_lca != NULL)? left_lca: right_lca;
}



不使用递归和栈中序遍历二叉树

我们知道,在深度搜索遍历的过程中,之所以要用递归或者是用非递归的栈方式,参考二叉树非递归中序遍历,都是因为其他的方式没法记录当前节点的parent,而如果在每个节点的结构里面加个parent 分量显然是不现实的,那么Morris是怎么解决这一问题的呢?好吧,他用得很巧妙,实际上是用叶子节点的空指针来记录当前节点的位置,然后一旦遍历到了叶子节点,发现叶子节点的右指针指向的是当前节点,那么就认为以当前节点的左子树已经遍历完成。Morris
遍历正是利用了线索二叉树 的思想。

以inorder为例,初始化当前节点为root,它的遍历规则如下:

如果当前节点为空,程序退出。

如果当前节点非空,

如果当前节点的左儿子为空,那么输出当前节点,当前节点重置为当前节点的右儿子。

如果当前节点的左儿子非空,找到当前节点左子树的最右叶子节点(此时最右节点的右儿子有两种情况,一种是指向当前节点,一种是为空,你也许感到奇怪,右节点的右儿子怎么可能非空,注意,这里的最右叶子节点只带的是原树中的最右叶子节点。),若其最右叶子节点为空,令其指向当前节点,将当前节点重置为其左儿子,若其最右节点指向当前节点,输出当前节点,将当前节点重置为当前节点的右儿子,并恢复树结构,即将最右节点的右节点再次设置为NULL

void MorrisTraversal(struct tNode *root)
{
struct tNode *current,*pre;

if(root == NULL)
return;

current = root;
while(current != NULL)
{
if(current->left == NULL)
{
printf(" %d ", current->data);
current = current->right;
}
else
{
/* 找到current的前驱节点 */
pre = current->left;
while(pre->right != NULL && pre->right != current)
pre = pre->right;

/* 将current节点作为其前驱节点的右孩子 */
if(pre->right == NULL)
{
pre->right = current;
current = current->left;
}

/* 恢复树的原有结构,更改right 指针 */
else
{
pre->right = NULL;
printf(" %d ",current->data);
current = current->right;
} /* End of if condition pre->right == NULL */
} /* End of if condition current->left == NULL*/
} /* End of while */
}


有序链表转化为平衡的二分查找树

比较直观的解法是自顶向下的递归解决,先找到中间节点作为根节点,然后递归左右两部分。所有我们需要先找到中间节点,对于单链表来说,必须要遍历一边,可以使用快慢指针加快查找速度。

public class Solution {
public TreeNode sortedListToBST(ListNode head) {
if(head == null) return null;
if(head.next == null){
return new TreeNode(head.val);
}
//用快慢指针找到中间节点
ListNode slow = head;
ListNode fast = head;
ListNode preSlow = null;
while(fast.next != null && fast.next.next != null){

be6f
preSlow = slow;
slow = slow.next;
fast = fast.next.next;
}
//分别递归左右两部分
TreeNode mid = new TreeNode(slow.val);
if(preSlow != null){
preSlow.next = null;
mid.left = sortedListToBST(head);
}
if(slow.next != null){
mid.right = sortedListToBST(slow.next);
}
return mid;
}
}


找出二叉树中某个节点的所有祖先节点

对于一颗普通的二叉树和一个节点key,找出该节点的所有祖先节点。

例如树结构如下:给定的key为节点7,则应该打印 4,2,1.

bool printAncestors(struct node *root, int target)
{
if (root == NULL)
return false;

if (root->data == target)
return true;
//子树可以找到,当前节点肯定为祖先节点
if ( printAncestors(root->left, target) ||
printAncestors(root->right, target) )
{
cout << root->data << " ";
return true;
}
/* Else return false */
return false;
}



二叉树非递归中序遍历

void InOrderTraverse(BiTree T)//非递归中序遍历
{

stack<BiTree> Stack;
if(!T)
{
printf("空树!\n");
return;
}

while(T || !Stack.empty())
{
while(T)
{
Stack.push(T);
T=T->lchild;
}
T=Stack.top();
Stack.pop();
printf("%c",T->data);
T=T->rchild;
}
}



二叉查找树的后序遍历结果

题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果。如果是返回true,否则返回false。

例如输入5、7、6、9、11、10、8,由于这一整数序列是如下树的后序遍历结果:

8

/  \

6    10

/ \    / \

5   7   9  11

因此返回true。

如果输入7、4、6、5,没有哪棵树的后序遍历的结果是这个序列,因此返回false。

分析:这是一道trilogy的笔试题,主要考查对二元查找树的理解。

在后续遍历得到的序列中,最后一个元素为树的根结点。从头开始扫描这个序列,比根结点小的元素都应该位于序列的左半部分;从第一个大于跟结点开始到跟结点前面的一个元素为止,所有元素都应该大于跟结点,因为这部分元素对应的是树的右子树。根据这样的划分,把序列划分为左右两部分,我们递归地确认序列的左、右两部分是不是都是二元查找树。
bool verifySquenceOfBST(int squence[], int length)
{
if(squence == NULL || length <= 0)
return false;

// root of a BST is at the end of post order traversal squence
int root = squence[length - 1];

// the nodes in left sub-tree are less than the root
int i = 0;
for(; i < length - 1; ++ i)
{
if(squence[i] > root)
break;
}

// the nodes in the right sub-tree are greater than the root
int j = i;
for(; j < length - 1; ++ j)
{
if(squence[j] < root)
return false;
}

// verify whether the left sub-tree is a BST
bool left = true;
if(i > 0)
left = verifySquenceOfBST(squence, i);

// verify whether the right sub-tree is a BST
bool right = true;
if(i < length - 1)
right = verifySquenceOfBST(squence + i, length - i - 1);
return (left && right);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: