对于每个父节点,其左子树中所有节点的值小于等于父结点的值,其右子树中所有节点的值大于等于父结点的值,因此对于一个二叉查找树,我们可以在 O ( n l o g n ) O(nlogn) O(nlogn) 的时间内查找一个值是否存在:从根节点开始,若查找值小于当前节点的值则向左下走,若查找值大于当前节点的值则向右下走。
二叉查找树的中序遍历结果即为排序好的数组
template <class T>
class BST {
struct Node {
T data;
Node* left;
Node* right;
};
Node* root;
Node* makeEmpty(Node* t) { // 清空操作
if (t == NULL)
return NULL;
makeEmpty(t->left);
makeEmpty(t->right);
delete t;
return NULL;
}
Node* insert(Node* t, T x) { // 插入操作
if (t == NULL) {
t = new Node;
t->data = x;
t->left = t->right = NULL;
} else if (x < t->data) {
t->left = insert(t->left, x);
} else if (x > t->data) {
t->right = insert(t->right, x);
}
return t;
}
Node* find(Node* t, T x) { // 查找操作
if (t == NULL)
return NULL;
if (x < t->data)
return find(t->left, x);
if (x > t->data)
return find(t->right, x);
return t;
}
Node* findMin(Node* t) {
if (t == NULL || t->left == NULL)
return t;
return findMin(t->left);
}
Node* findMax(Node* t) {
if (t == NULL || t->right == NULL)
return t;
return findMax(t->right);
}
Node* remove(Node* t, T x) { // 删除操作
Node* temp;
if (t == NULL)
return NULL;
else if (x < t->data)
t->left = remove(t->left, x);
else if (x > t->data)
t->right = remove(t->right, x);
else if (t->left && t->right) {
temp = findMin(t->right);
t->data = temp->data;
t->right = remove(t->right, t->data);
} else {
temp = t;
if (t->left == NULL)
t = t->right;
else if (t->right == NULL)
t = t->left;
delete temp;
}
return t;
}
public:
BST()
: root(NULL) {}
~BST() {
root = makeEmpty(root);
}
void insert(T x) {
insert(root, x);
}
void remove(T x) {
remove(root, x);
}
};
给定一棵树的前序遍历 preorder 与中序遍历 inorder。请构造二叉树并返回其根节点。
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (preorder.empty()) {
return nullptr;
}
unordered_map<int, int> hash;
// 建立中序遍历结果中节点值与索引的哈希映射
for (int i = 0; i < preorder.size(); i++) {
hash[inorder[i]] = i;
}
return helper(hash, preorder, 0, preorder.size() - 1, 0);
}
TreeNode* helper(unordered_map<int, int>& hash, vector<int>& preorder, int s0, int e0, int s1) {
if (s0 > e0) {
return nullptr;
}
int mid = preorder[s1], index = hash[mid], leftLen = index - 1 - s0 + 1;
TreeNode *node = new TreeNode(mid);
node->left = helper(hash, preorder, s0, index - 1, s1 + 1); // 构建左子树
node->right = helper(hash, preorder, index + 1, e0, s1 + leftLen + 1); // 构建右子树
return node;
}
};
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
this->preorder = preorder;
for (int i = 0; i < inorder.size(); i++) {
hash[inorder[i]] = i;
}
return helper(0, 0, inorder.size() - 1);
}
private:
vector<int> preorder;
unordered_map<int, int> hash;
TreeNode* helper(int root, int left, int right) {
if (left > right) {
return nullptr;
}
int rootval = preorder[root];
int m = hash[rootval]; // 根节点在中序遍历的索引
TreeNode* node = new TreeNode(rootval);
node->left = helper(root + 1, left, m - 1); // 左子树的左边界一直为 left = 0
node->right = helper(root + m - left + 1, m + 1, right); // 右子树的右边界一直味 right = size() - 1
return node;
}
};
路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中至多出现一次 。该路径至少包含一个节点,且不一定经过根节点。路径和 是路径中各节点值的总和。Link
class Solution {
private:
int maxSum = INT_MIN;
public:
int maxPathSum(TreeNode* root) {
maxGain(root);
return maxSum;
}
int maxGain(TreeNode* root) {
if (!root) return 0;
int leftGain = max(maxGain(root->left), 0);
int rightGain = max(maxGain(root->right), 0);
int innerMax = root->val + leftGain + rightGain; // 以当前节点为根节点的最大值
maxSum = max(maxSum, innerMax);
return root->val + max(leftGain, rightGain); // 经过当前节点的最大增益
}
};
给你二叉搜索树的根节点 root ,该树中的两个节点被错误地交换。请在不改变其结构的情况下,恢复这棵树。
class Solution {
public:
void recoverTree(TreeNode* root) {
TreeNode *mistake1 = nullptr, *mistake2 = nullptr, *prev = nullptr;
inorder(root, mistake1, mistake2, prev);
if (mistake1 && mistake2) {
// 将错误节点的值暂存
int temp = mistake1->val;
mistake1->val = mistake2->val;
mistake2->val = temp;
}
}
void inorder(TreeNode* root, TreeNode* &mistake1, TreeNode* &mistake2, TreeNode* &prev) {
if (!root) {
return;
}
if (root->left) {
inorder(root->left, mistake1, mistake2, prev); // 中序遍历左子节点
}
if (prev && root->val < prev->val) {
if (!mistake1) {
mistake1 = prev; // 一次排序错误
mistake2 = root;
}else {
mistake2 = root; // 两次排序错误
}
}
prev = root; // 访问根节点
if (root->right) {
inorder(root->right, mistake1, mistake2, prev); // 中序遍历右子节点
}
}
};
给你二叉搜索树的根节点 root ,同时给定最小边界 low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树不应该改变保留在树中的元素的相对结构(即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在唯一的答案。
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
// 终止条件
if (!root) {
return root;
}
// 当前节点值大于 high,则右子树均不满足条件
if (root->val > high) {
return trimBST(root->left, low, high);
}
if (root->val < low) {
return trimBST(root->right, low, high);
}
// 当前节点值在 [low, high]中
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
return root;
}
};
尝试建立一个字典树,支持快速插入单词、查找单词、查找单词前缀的功能。
class Trie {
public:
bool isEnd;
vector<Trie *> next;
Trie() {
isEnd = false;
for(int i = 0; i < 26; i++) {
next.push_back(nullptr);
}
}
void insert(string word) {
Trie *cur = this; // 重要位置
for (char &c : word) {
if (cur->next[c - 'a'] == nullptr) {
cur->next[c - 'a'] = new Trie();
}
cur = cur->next[c - 'a'];
}
cur->isEnd = true;
}
bool search(string word) {
Trie *cur = this;
for (char &c : word) {
if (cur->next[c - 'a'] == nullptr) {
return false;
}
cur = cur->next[c - 'a'];
}
return cur->isEnd;
}
bool startsWith(string prefix) {
Trie *cur = this;
for (char &c : prefix) {
if (cur->next[c - 'a'] == nullptr) {
return false;
}
cur = cur->next[c - 'a'];
}
return true;
}
};