#include <iostream>
#include <stdio.h>
#include <vector>
#include <stack>
using namespace std;
/*
问题:
Two elements of a binary search tree (BST) are swapped by mistake.
Recover the tree without changing its structure.
Note:
A solution using O(n) space is pretty straight forward. Could you devise a constant space solution?
分析:题目说二叉查找树中某两个元素被错误的交换了,需要在不改变结构的情况下回复这棵树。
举例:
8
4 12
2 6 14 10
首先要明白这里说的两个元素弄错了,是不是指一个结点下的两个元素被错误交换了,
还是任意两个元素被交换了。因为错误地交换,此时必定不是一颗二叉查找树了。
我们可以中序遍历一把,得到不正确的位置
比如,对下面这棵树中序遍历得到: 2 4 6 8 14 12 10,发现从14 12 10这一段不是升序,而是降序
说明
8
4 12
2 6 14 10
找到第一个逆序的数14,然后不看14,继续寻找下一个逆序的数是12,找到第二个逆序的数,
但是发现12起始不是逆序的,因为如果
则就是这两个数逆序的。
又比如对下面这棵树中序遍历: 2 10 6 8 4 12 14,第一个逆序的数是10,第二个逆序的数是4
8
10 12
2 6 4 14
8
4 14
2 6 10 12
2 4 6 8 10 14 12
8
4 12
14 6 10 2
14 4 6 8 19 12 2
总结规律:从前向后,找到第一个逆序的数位置i,满足A[i] > A[i+1];
从后向前,找到第一个逆序的数位置j,满足A[j] < A[j-1]; 或者替换成 从前向后,找到最后一组逆序数j,A[j] < A[j-1]
再交换这两个结点
题目说:可以使用O(n)的空间,明显是使用一个数组
找到两个结点后,如何交换结点呢,直接的交换使可以的,但是需要找到这两个结点的各自父节点,各自孩子结点,进行重新指向。
这样太麻烦,不如直接交换这两个结点内部的数据
第一个结点是当前结点 < 上一个结点中的上一个结点
第二个结点是当前结点 < 上一个结点中的当前结点,如果有多次这样的,保留最后一个
输入:
3
1 2 3
2
0 1
输出:
1 2 3
1 0
关键:
1 //第一个结点是当前结点 < 上一个结点中的上一个结点
//第二个结点是当前结点 < 上一个结点中的当前结点,如果有多次这样的,保留最后一个这样的当前结点
//因此,需要记录上一个结点
2 采用迭代遍历中序来寻找,递归无法确定上一次结点,因此不可以
//递归寻找中序的上一个结点很难弄,必须用非递归方式,用一个栈来做
//使得当前结点为根节点,然后寻找最左边的结点,并把最左边的结点压入栈中
//当找不到最左边的结点时,弹出栈中保存的结点即为最左边结点,然后令当前结点=最左边结点的右孩子,重复上述循环
//那么上一个结点就是当前结点即可
void inOrder_iteration(TreeNode* root)
{
stack<TreeNode*> nodes;
//nodes.push(root);//根节点无需先压入,因为后续令当前结点为根节点,会压入
TreeNode* current = root;
TreeNode* node;
TreeNode* previous = NULL;
TreeNode* resultNode1 = NULL;
TreeNode* resultNode2 = NULL;
while(!nodes.empty() || current)
{
//当前遍历的结点都是寻找最左边结点的沿途结点,都需要先压入,再寻找
if(current)
{
nodes.push(current);
current = current->left;//这里current表示的就是当前遍历结点,要先压入,而不是
}
//找不到最左边的结点,弹出栈中的结点
else
{
node = nodes.top();//这个是最左边结点
//比较
if(previous)
{
if(node->val < previous->val)
{
//只需要比较一次
if(!resultNode1)
{
resultNode1= previous;
}
resultNode2 = node;
}
}
//更新前一个结点
previous = node;
nodes.pop();
//不需要直接压入,因为压入的判断放在前面了,这里只需要时当前结点=结点的右孩子
current = node->right;
}
}
//交换结点值
int temp = resultNode1->val;
resultNode1->val = resultNode2->val;
resultNode2->val = temp;
}
*/
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
TreeNode():left(NULL), right(NULL){}
};
class Solution {
public:
//递归寻找中序的上一个结点很难弄,必须用非递归方式,用一个栈来做
//使得当前结点为根节点,然后寻找最左边的结点,并把最左边的结点压入栈中
//当找不到最左边的结点时,弹出栈中保存的结点即为最左边结点,然后令当前结点=最左边结点的右孩子,重复上述循环
//那么上一个结点就是当前结点即可
void inOrder_iteration(TreeNode* root)
{
stack<TreeNode*> nodes;
//nodes.push(root);//根节点无需先压入,因为后续令当前结点为根节点,会压入
TreeNode* current = root;
TreeNode* node;
TreeNode* previous = NULL;
TreeNode* resultNode1 = NULL;
TreeNode* resultNode2 = NULL;
while(!nodes.empty() || current)
{
//当前遍历的结点都是寻找最左边结点的沿途结点,都需要先压入,再寻找
if(current)
{
nodes.push(current);
current = current->left;//这里current表示的就是当前遍历结点,要先压入,而不是
}
//找不到最左边的结点,弹出栈中的结点
else
{
node = nodes.top();//这个是最左边结点
//比较
if(previous)
{
if(node->val < previous->val)
{
//只需要比较一次
if(!resultNode1)
{
resultNode1= previous;
}
resultNode2 = node;
}
}
//更新前一个结点
previous = node;
nodes.pop();
//不需要直接压入,因为压入的判断放在前面了,这里只需要时当前结点=结点的右孩子
current = node->right;
}
}
//交换结点值
int temp = resultNode1->val;
resultNode1->val = resultNode2->val;
resultNode2->val = temp;
}
void inOrder(TreeNode* root, TreeNode* previousNode, TreeNode* resultNode1 , TreeNode* resultNode2)
{
//空指针就不需要处理,直接返回
if(!root)
{
return;
}
//没有上一个结点,自然无需比较
//第一个结点是当前结点 < 上一个结点中的上一个结点
//第二个结点是当前结点 < 上一个结点中的当前结点,如果有多次这样的,保留最后一个这样的当前结点
//因此,需要记录上一个结点
//对根节点进行处理,如果上一个结点为空,说明当前结点是根节点,则无需处理
if(previousNode)
{
//比较当前结点和上一个结点
if(root->val < previousNode->val)
{
if(!resultNode1)
{
resultNode1 = previousNode;
}
resultNode2 = root;
}
}
previousNode = root;//更新上一个结点为当前结点,这里的更新好像是错误的,上一个结点并不是父节点
inOrder(root->left , previousNode , resultNode1 , resultNode2);
inOrder(root->right, previousNode , resultNode1 , resultNode2);
}
void recoverTree(TreeNode* root) {
if(!root)
{
return ;
}
inOrder_iteration(root);
//初始的时候,记录上一个结点为空
/*
TreeNode* previousNode = NULL;
TreeNode* resultNode1 = NULL;
TreeNode* resultNode2 = NULL;
inOrder(root , previousNode , resultNode1 , resultNode2);
//找到两个结点后,下面开始交换结点内数值
if(resultNode1 && resultNode2)
{
int temp = resultNode1->val;
resultNode1->val = resultNode2->val;
resultNode2->val = temp;
}
*/
}
};
const int MAXSIZE = 1000;
TreeNode gNodeArr[MAXSIZE];
int gIndex;
TreeNode* createNode(int value)
{
++gIndex;
gNodeArr[gIndex].val = value;
return &gNodeArr[gIndex];
}
//构建二叉树,这里默认首个元素为二叉树根节点,然后接下来按照作为每个结点的左右孩子的顺序遍历
TreeNode* buildBinaryTree(vector<int>& nums)
{
if(nums.empty())
{
return NULL;
}
TreeNode* root;
int size = nums.size();
int j = 0;
//结点i的孩子结点是2i,2i+1
for(int i = 0 ; i < size ; i++)
{
if(i)
{
createNode(nums.at(i));
}
else
{
root = createNode(nums.at(i));
}
}
//设定孩子结点指向,
for(int i = 1 ; i <= size ; i++)
{
if(2 * i <= size)
{
gNodeArr[i].left = &gNodeArr[2*i];
}
if(2*i + 1 <= size)
{
gNodeArr[i].right = &gNodeArr[2*i + 1];
}
}
//设定完了之后,返回根节点
return root;
}
void inOrder(TreeNode* root, vector<int>& result)
{
//空指针就不需要处理,直接返回
if(!root)
{
return;
}
//第一个结点是当前结点 < 上一个结点中的上一个结点
//第二个结点是当前结点 < 上一个结点中的当前结点,如果有多次这样的,保留最后一个这样的当前结点
//因此,需要记录上一个结点
inOrder(root->left , result);
if(root)
{
result.push_back(root->val);
}
inOrder(root->right , result);
}
void print(vector<int>& result)
{
if(result.empty())
{
cout << "no result" << endl;
return;
}
int size = result.size();
for(int i = 0 ; i < size ; i++)
{
cout << result.at(i) << " " ;
}
cout << endl;
}
void process()
{
vector<int> nums;
int value;
int num;
Solution solution;
vector<int> result;
while(cin >> num )
{
nums.clear();
gIndex = 0;//设定下标
for(int i = 0 ; i < num ; i++)
{
cin >> value;
nums.push_back(value);
}
TreeNode* root = buildBinaryTree(nums);
solution.recoverTree(root);
//打印二叉树中序结点值
inOrder(root , result);
print(result);
}
}
int main(int argc , char* argv[])
{
process();
getchar();
return 0;
}
//第一个结点满足:A[i] > A[i+1]的i,同理是A[i-1] > A[i]中的上一个结点A[i-1]
//第二个结点是满足:A[i] < A[i-1]中的最后一组的i,在比较的过程中我们总是记录上一个遍历结点的值,然后与当前值比较
leecode 解题总结:99. Recover Binary Search Tree
最新推荐文章于 2019-02-07 12:44:39 发布
