• C++:搜索二叉树


    目录

    一.二叉搜索数介绍

    1.介绍

    2.时间复杂度:

            个遍历结果可以确定树的结构,其一必须是中序遍历

    二.模拟实现

    第一阶段:增,查 

    阶段二:非递归版本-增,删,赋值运算符重载,拷贝构造,析构

    三.二叉搜索树的应用

    K模型:找对应单词的中文

    KV模型:记录水果的次数

    补充:cin中的operator bool

     operator int也是可以的

    四.搜素二叉树题目

    1.606. 根据二叉树创建字符串

    个人思路写法:

    官方写法:

    2. 102. 二叉树的层序遍历

    个人手写:

    管方写法: 

    3.236. 二叉树的最近公共祖先

    方法一:利用p和q一定在最近公共祖先的两侧来找

    个人写法:非递归版本

    官方写法:递归版本

    方法二:找路径交点

    个人手写:

    官方写法:

    4.二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com) 

    个人手写:

    官方写法: 

    5.105. 从前序与中序遍历序列构造二叉树

    个人手写:

    官方写法:

    6. 106. 从中序与后序遍历序列构造二叉树


    一.二叉搜索数介绍

    1.介绍

    二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
    ①若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
    ②若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
    ③它的左右子树也分别为二叉搜索树
    即:
    任意一个子树都需要满足,左子树的值<根<右子树的值 就是 二叉搜索数
    对二叉搜索树进行中序遍历,一定能够得到一个有序序列

    2.时间复杂度:

    二叉搜索树最差情况下会退化为单支树,因此:其查找的效率为O(N),O(N)这个时间复杂度是线性的

            个遍历结果可以确定树的结构,其一必须是中序遍历

    根据前序遍历和中序遍历,是可以确定一棵树的结构,使用两个遍历结果确定树的结构, 其中有一个遍历结果必须要是中序遍历结果。

    解释:

    前序:根、左子树、右子树--依次确定根
    中序:左子树、根、右子树--划分左右子树区间

    二.模拟实现

    第一阶段:增,查 

    易错:insert返回类型bool

    1. #pragma once
    2. template<class K>
    3. //struct BinarySearchTreeNode
    4. struct BSTreeNode
    5. {
    6. BSTreeNode* _left;
    7. BSTreeNode* _right;
    8. K _key;
    9. BSTreeNode(const K& key)
    10. :_left(nullptr)
    11. , _right(nullptr)
    12. , _key(key)
    13. {}
    14. };
    15. template<class K>
    16. class BSTree
    17. {
    18. typedef BSTreeNode Node;
    19. public:
    20. bool Insert(const K& key)
    21. {
    22. if (_root == nullptr)
    23. {
    24. _root = new Node(key);
    25. return true;
    26. }
    27. Node* parent = nullptr;
    28. Node* cur = _root;
    29. while (cur)
    30. {
    31. if (cur->_key < key)
    32. {
    33. parent = cur;
    34. cur = cur->_right;
    35. }
    36. else if (cur->_key > key)
    37. {
    38. parent = cur;
    39. cur = cur->_left;
    40. }
    41. else
    42. {
    43. return false;
    44. }
    45. }
    46. cur = new Node(key);
    47. if (parent->_key < key)
    48. {
    49. parent->_right = cur;
    50. }
    51. else
    52. {
    53. parent->_left = cur;
    54. }
    55. return true;
    56. }
    57. //const Node* Find(const K& key)
    58. bool Find(const K& key)
    59. {
    60. Node* cur = _root;
    61. while (cur)
    62. {
    63. if (cur->_key < key)
    64. {
    65. cur = cur->_right;
    66. }
    67. else if (cur->_key > key)
    68. {
    69. cur = cur->_left;
    70. }
    71. else
    72. {
    73. return true;
    74. }
    75. }
    76. return false;
    77. }
    78. bool Erase(const K& key);
    79. void InOrder()
    80. {
    81. _InOrder(_root);
    82. }
    83. private:
    84. void _InOrder(Node* root)
    85. {
    86. if (root == nullptr)
    87. return;
    88. _InOrder(root->_left);
    89. cout << root->_key << " ";
    90. _InOrder(root->_right);
    91. }
    92. private:
    93. Node* _root = nullptr;
    94. };
    95. void TestBSTree()
    96. {
    97. BSTree<int> t;
    98. int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
    99. for (auto e : a)
    100. {
    101. t.Insert(e);
    102. }
    103. t.InOrder();
    104. t.Insert(16);
    105. t.Insert(9);
    106. t.InOrder();
    107. }

    依次删除7、14、 3、8
    7和14属于直接删除的场景
    3、8属于需要替换法进行删除的场景
    两个孩子没办法给父亲,父亲养不了。找个人替代我养孩子
    这个人是:左子树的最大值节点,或者右子树的最小值节点

    阶段二:非递归版本-增,删,赋值运算符重载,拷贝构造,析构

    问题:FindR为什么要套一层

    易错:非递归Erase最后一个if条件if (minParent->_left == minRight)写错,拷贝构造不会写,拷贝函数CopyTree是递归不好写

    1. #pragma once
    2. template<class K>
    3. //struct BinarySearchTreeNode
    4. struct BSTreeNode
    5. {
    6. BSTreeNode* _left;
    7. BSTreeNode* _right;
    8. K _key;
    9. BSTreeNode(const K& key)
    10. :_left(nullptr)
    11. , _right(nullptr)
    12. , _key(key)
    13. {}
    14. };
    15. template<class K>
    16. class BSTree
    17. {
    18. typedef BSTreeNode Node;
    19. private:
    20. void DestoryTree(Node* root)
    21. {
    22. if (root == nullptr)
    23. return;
    24. DestoryTree(root->_left);
    25. DestoryTree(root->_right);
    26. delete root;
    27. }
    28. Node* CopyTree(Node* root)
    29. {
    30. if (root == nullptr)
    31. return nullptr;
    32. Node* copyNode = new Node(root->_key);
    33. copyNode->_left = CopyTree(root->_left);
    34. copyNode->_right = CopyTree(root->_right);
    35. return copyNode;
    36. }
    37. public:
    38. // 强制编译器自己生成构造
    39. // C++11
    40. BSTree() = default; //默认构造很好用,但是拷贝构造也是构造函数,就不会自动生成构造函数
    41. BSTree(const BSTree& t) //拷贝构造
    42. {
    43. _root = CopyTree(t._root);
    44. }
    45. // t1 = t2
    46. BSTree& operator=(BSTree t)
    47. {
    48. swap(_root, t._root);
    49. return *this;
    50. }
    51. ~BSTree()
    52. {
    53. DestoryTree(_root);
    54. _root = nullptr;
    55. }
    56. bool Insert(const K& key)
    57. {
    58. if (_root == nullptr)
    59. {
    60. _root = new Node(key);
    61. return true;
    62. }
    63. Node* parent = nullptr;
    64. Node* cur = _root;
    65. while (cur)
    66. {
    67. if (cur->_key < key)
    68. {
    69. parent = cur;
    70. cur = cur->_right;
    71. }
    72. else if (cur->_key > key)
    73. {
    74. parent = cur;
    75. cur = cur->_left;
    76. }
    77. else
    78. {
    79. return false;
    80. }
    81. }
    82. cur = new Node(key);
    83. if (parent->_key < key)
    84. {
    85. parent->_right = cur;
    86. }
    87. else
    88. {
    89. parent->_left = cur;
    90. }
    91. return true;
    92. }
    93. //const Node* Find(const K& key)
    94. bool Find(const K& key)
    95. {
    96. Node* cur = _root;
    97. while (cur)
    98. {
    99. if (cur->_key < key)
    100. {
    101. cur = cur->_right;
    102. }
    103. else if (cur->_key > key)
    104. {
    105. cur = cur->_left;
    106. }
    107. else
    108. {
    109. return true;
    110. }
    111. }
    112. return false;
    113. }
    114. bool Erase(const K& key)
    115. {
    116. Node* parent = nullptr;
    117. Node* cur = _root;
    118. while (cur)
    119. {
    120. if (cur->_key < key)
    121. {
    122. parent = cur;
    123. cur = cur->_right;
    124. }
    125. else if (cur->_key > key)
    126. {
    127. parent = cur;
    128. cur = cur->_left;
    129. }
    130. else
    131. {
    132. // 一个孩子--左为空 or 右为空
    133. // 两个孩子 -- 替换法
    134. if (cur->_left == nullptr)
    135. {
    136. //if (parent == nullptr)
    137. if (cur == _root)
    138. {
    139. _root = cur->_right;
    140. }
    141. else
    142. {
    143. if (cur == parent->_left)
    144. {
    145. parent->_left = cur->_right;
    146. }
    147. else
    148. {
    149. parent->_right = cur->_right;
    150. }
    151. }
    152. delete cur;
    153. }
    154. else if (cur->_right == nullptr)
    155. {
    156. //if (parent == nullptr)
    157. if (cur == _root)
    158. {
    159. _root = cur->_left;
    160. }
    161. else
    162. {
    163. if (cur == parent->_left)
    164. {
    165. parent->_left = cur->_left;
    166. }
    167. else
    168. {
    169. parent->_right = cur->_left;
    170. }
    171. }
    172. delete cur;
    173. }
    174. else // 两个孩子都不为空
    175. {
    176. // 右子树的最小节点替代
    177. Node* minParent = cur;
    178. Node* minRight = cur->_right;
    179. while (minRight->_left)
    180. {
    181. minParent = minRight;
    182. minRight = minRight->_left;
    183. }
    184. swap(minRight->_key, cur->_key);
    185. //cur->_key = minRight->_key;
    186. if (minParent->_left == minRight)
    187. {
    188. minParent->_left = minRight->_right;
    189. }
    190. else
    191. {
    192. minParent->_right = minRight->_right;
    193. }
    194. delete minRight;
    195. }
    196. return true;
    197. }
    198. }
    199. return false;
    200. }
    201. void InOrder()
    202. {
    203. _InOrder(_root);
    204. cout << endl;
    205. }
    206. ///
    207. bool FindR(const K& key)
    208. {
    209. return _FindR(_root, key);
    210. }
    211. bool InsertR(const K& key)
    212. {
    213. return _InsertR(_root, key);
    214. }
    215. bool EraseR(const K& key)
    216. {
    217. return _EraseR(_root, key);
    218. }
    219. private:
    220. bool _EraseR(Node*& root, const K& key)
    221. //key是10时,root是8的右孩子这个指针的别名,同时指向10
    222. {
    223. if (root == nullptr)
    224. return false;
    225. if (root->_key < key)
    226. {
    227. return _EraseR(root->_right, key);
    228. }
    229. else if (root->_key > key)
    230. {
    231. return _EraseR(root->_left, key);
    232. }
    233. else
    234. {
    235. Node* del = root;
    236. // 删除
    237. if (root->_left == nullptr)
    238. {
    239. root = root->_right;
    240. }
    241. else if (root->_right == nullptr)
    242. {
    243. root = root->_left;
    244. }
    245. else
    246. {
    247. Node* minRight = root->_right;
    248. while (minRight->_left)
    249. {
    250. minRight = minRight->_left;
    251. }
    252. swap(root->_key, minRight->_key);
    253. return _EraseR(root->_right, key);
    254. }
    255. delete del;
    256. return true;
    257. }
    258. }
    259. bool _InsertR(Node*& root, const K& key)
    260. {
    261. if (root == nullptr)
    262. {
    263. root = new Node(key);
    264. return true;
    265. }
    266. if (root->_key < key)
    267. return _InsertR(root->_right, key);
    268. else if (root->_key > key)
    269. return _InsertR(root->_left, key);
    270. else
    271. return false;
    272. }
    273. bool _FindR(Node* root, const K& key)
    274. {
    275. if (root == nullptr)
    276. return false;
    277. if (root->_key < key)
    278. {
    279. return _FindR(root->_right, key);
    280. }
    281. else if (root->_key > key)
    282. {
    283. return _FindR(root->_left, key);
    284. }
    285. else
    286. {
    287. return true;
    288. }
    289. }
    290. void _InOrder(Node* root)
    291. {
    292. if (root == nullptr)
    293. return;
    294. _InOrder(root->_left);
    295. cout << root->_key << " ";
    296. _InOrder(root->_right);
    297. }
    298. private:
    299. Node* _root = nullptr;
    300. };
    301. void TestBSTree1()
    302. {
    303. BSTree<int> t;
    304. int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
    305. for (auto e : a)
    306. {
    307. t.Insert(e);
    308. }
    309. t.InOrder();
    310. t.Insert(16);
    311. t.Insert(9);
    312. t.InOrder();
    313. }
    314. void TestBSTree2()
    315. {
    316. BSTree<int> t;
    317. int a[] = { 8, 7, 9, 12, 5, 19, 20, 30,7,12 };
    318. for (auto e : a)
    319. {
    320. t.Insert(e);
    321. }
    322. t.InOrder();
    323. }
    324. void TestBSTree3()
    325. {
    326. BSTree<int> t;
    327. int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
    328. for (auto e : a)
    329. {
    330. t.Insert(e);
    331. }
    332. t.InOrder();
    333. t.Erase(3);
    334. t.Erase(8);
    335. t.InOrder();
    336. t.Erase(14);
    337. t.Erase(7);
    338. t.InOrder();
    339. for (auto e : a)
    340. {
    341. t.Erase(e);
    342. }
    343. t.InOrder();
    344. }
    345. void TestBSTree4()
    346. {
    347. BSTree<int> t;
    348. int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
    349. for (auto e : a)
    350. {
    351. t.Insert(e);
    352. }
    353. t.InOrder();
    354. BSTree<int> copy = t;
    355. copy.InOrder();
    356. }
    357. void TestBSTree5()
    358. {
    359. BSTree<int> t;
    360. int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
    361. for (auto e : a)
    362. {
    363. t.InsertR(e);
    364. }
    365. t.InOrder();
    366. t.InsertR(9);
    367. BSTree<int> copy = t;
    368. copy.InOrder();
    369. }
    370. void TestBSTree6()
    371. {
    372. BSTree<int> t;
    373. int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
    374. for (auto e : a)
    375. {
    376. t.InsertR(e);
    377. }
    378. t.InOrder();
    379. t.EraseR(3);
    380. t.EraseR(8);
    381. t.InOrder();
    382. t.EraseR(14);
    383. t.EraseR(7);
    384. t.InOrder();
    385. for (auto e : a)
    386. {
    387. t.EraseR(e);
    388. }
    389. t.InOrder();
    390. }

    三.二叉搜索树的应用

    1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到
    的值。(K-set)
    比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
    以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
    在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
    2. KV模型:每一个关键码key,都有与之对应的值Value,即的键值对。该种方
    式在现实生活中非常常见:
    比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英
    文单词与其对应的中文就构成一种键值对;
    再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出
    现次数就是就构成一种键值对

    KV-map

    1. namespace key_value
    2. {
    3. #pragma once
    4. template<class K, class V>
    5. struct BSTreeNode
    6. {
    7. BSTreeNode* _left;
    8. BSTreeNode* _right;
    9. const K _key;
    10. V _value;
    11. BSTreeNode(const K& key, const V& value)
    12. :_left(nullptr)
    13. , _right(nullptr)
    14. , _key(key)
    15. , _value(value)
    16. {}
    17. };
    18. template<class K, class V>
    19. class BSTree
    20. {
    21. typedef BSTreeNode Node;
    22. public:
    23. void InOrder()
    24. {
    25. _InOrder(_root);
    26. cout << endl;
    27. }
    28. ///
    29. Node* FindR(const K& key)
    30. {
    31. return _FindR(_root, key);
    32. }
    33. bool InsertR(const K& key, const V& value)
    34. {
    35. return _InsertR(_root, key, value);
    36. }
    37. bool EraseR(const K& key)
    38. {
    39. return _EraseR(_root, key);
    40. }
    41. private:
    42. bool _EraseR(Node*& root, const K& key)
    43. {
    44. if (root == nullptr)
    45. return false;
    46. if (root->_key < key)
    47. {
    48. return _EraseR(root->_right, key);
    49. }
    50. else if (root->_key > key)
    51. {
    52. return _EraseR(root->_left, key);
    53. }
    54. else
    55. {
    56. Node* del = root;
    57. // 删除
    58. if (root->_left == nullptr)
    59. {
    60. root = root->_right;
    61. }
    62. else if (root->_right == nullptr)
    63. {
    64. root = root->_left;
    65. }
    66. else
    67. {
    68. Node* minRight = root->_right;
    69. while (minRight->_left)
    70. {
    71. minRight = minRight->_left;
    72. }
    73. swap(root->_key, minRight->_key);
    74. return _EraseR(root->_right, key);
    75. }
    76. delete del;
    77. return true;
    78. }
    79. }
    80. bool _InsertR(Node*& root, const K& key, const V& value)
    81. {
    82. if (root == nullptr)
    83. {
    84. root = new Node(key, value);
    85. return true;
    86. }
    87. if (root->_key < key)
    88. return _InsertR(root->_right, key, value);
    89. else if (root->_key > key)
    90. return _InsertR(root->_left, key, value);
    91. else
    92. return false;
    93. }
    94. Node* _FindR(Node* root, const K& key)
    95. {
    96. if (root == nullptr)
    97. return nullptr;
    98. if (root->_key < key)
    99. {
    100. return _FindR(root->_right, key);
    101. }
    102. else if (root->_key > key)
    103. {
    104. return _FindR(root->_left, key);
    105. }
    106. else
    107. {
    108. return root;
    109. }
    110. }
    111. void _InOrder(Node* root)
    112. {
    113. if (root == nullptr)
    114. return;
    115. _InOrder(root->_left);
    116. cout << root->_key << ":" << root->_value << endl;
    117. _InOrder(root->_right);
    118. }
    119. private:
    120. Node* _root = nullptr;
    121. };

    K模型:找对应单词的中文

    1. void TestBSTree1()
    2. {
    3. BSTree ECDict;
    4. ECDict.InsertR("root", "根");
    5. ECDict.InsertR("string", "字符串");
    6. ECDict.InsertR("left", "左边");
    7. ECDict.InsertR("insert", "插入");
    8. //...
    9. string str;
    10. while (cin >> str) //while (scanf() != EOF)
    11. {
    12. //BSTreeNode* ret = ECDict.FindR(str);
    13. auto ret = ECDict.FindR(str);
    14. if (ret != nullptr)
    15. {
    16. cout << "对应的中文:" << ret->_value << endl;
    17. //ret->_key = "";
    18. ret->_value = "";
    19. }
    20. else
    21. {
    22. cout << "无此单词,请重新输入" << endl;
    23. }
    24. }
    25. }

    KV模型:记录水果的次数

    1. void TestBSTree2()
    2. {
    3. string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
    4. // 水果出现的次数
    5. BSTreeint> countTree;
    6. for (const auto& str : arr)
    7. {
    8. auto ret = countTree.FindR(str);
    9. if (ret == nullptr)
    10. {
    11. countTree.InsertR(str, 1);
    12. }
    13. else
    14. {
    15. ret->_value++; // 修改value
    16. }
    17. }
    18. countTree.InOrder();
    19. }

    补充:cin中的operator bool

    operator bool

    上面K模型的 while (cin >> str)  //while (scanf() != EOF) 能做多组输入原因是因为 cin的istream类型重载了 operator bool

    1. #include
    2. using namespace std;
    3. class A
    4. {
    5. public:
    6. explicit A(int a = 0)
    7. :_a(a)
    8. {}
    9. operator bool()
    10. {
    11. if (_a < 10)
    12. {
    13. return true;
    14. }
    15. else
    16. {
    17. return false;
    18. }
    19. }
    20. void Print()
    21. {
    22. cout << _a << endl;
    23. }
    24. void Set(int a)
    25. {
    26. _a = a;
    27. }
    28. private:
    29. int _a;
    30. };
    31. void test()
    32. {
    33. A a;
    34. bool ret = a;
    35. int x;
    36. while (a) //while (a.operator bool())
    37. {
    38. a.Print();
    39. cin >> x;
    40. a.Set(x);
    41. }
    42. }
    43. int main()
    44. {
    45. test();
    46. return 0;
    47. }

     operator int也是可以的

    内置类型<--自定义类型

    explicit operator int() 会导致隐式类型转换 int y = a;不支持

    1. #include
    2. using namespace std;
    3. class A
    4. {
    5. public:
    6. explicit A(int a = 0)
    7. :_a(a)
    8. {}
    9. operator int() //explicit operator int() 会导致隐式类型转换 int y = a;不支持
    10. {
    11. if (_a < 10)
    12. {
    13. return 1;
    14. }
    15. else
    16. {
    17. return 0;
    18. }
    19. }
    20. void Print()
    21. {
    22. cout << _a << endl;
    23. }
    24. void Set(int a)
    25. {
    26. _a = a;
    27. }
    28. private:
    29. int _a;
    30. };
    31. void test()
    32. {
    33. A a;
    34. //int y = a;
    35. int x;
    36. //while (a.operator bool())
    37. while (a)
    38. {
    39. a.Print();
    40. cin >> x;
    41. a.Set(x);
    42. }
    43. }
    44. int main()
    45. {
    46. test();
    47. return 0;
    48. }

    四.搜素二叉树题目

    1.606. 根据二叉树创建字符串

    个人思路写法:

    1. class Solution {
    2. public:
    3. string tree2str(TreeNode* root) {
    4. string str;
    5. if(root==nullptr)
    6. return str;
    7. str+=to_string(root->val);
    8. if(root->left||root->right)
    9. {
    10. str+='(';
    11. str+=tree2str(root->left);
    12. str+=')';
    13. }
    14. if(root->right)
    15. {
    16. str+='(';
    17. str+=tree2str(root->right);
    18. str+=')';
    19. }
    20. return str;
    21. }
    22. };

    官方写法:

    2. 102. 二叉树的层序遍历

    利用levelSize 一层一层出

    个人手写:

    1. class Solution {
    2. public:
    3. vectorint>> levelOrder(TreeNode* root) {
    4. vectorint>> vv;
    5. if(root==nullptr)
    6. return vv;
    7. queue q;
    8. int levelSize=1;
    9. q.push(root);
    10. while(levelSize)
    11. {
    12. vector<int> levelV;
    13. while(levelSize--)
    14. {
    15. TreeNode* front=q.front();
    16. levelV.push_back(front->val);
    17. if(front->left)
    18. q.push(front->left);
    19. if(front->right)
    20. q.push(front->right);
    21. q.pop();
    22. }
    23. vv.push_back(levelV);
    24. levelSize=q.size();
    25. }
    26. return vv;
    27. }
    28. };

    管方写法: 

    3.236. 二叉树的最近公共祖先

     

    方法一:利用p和q一定在最近公共祖先的两侧来找

    个人写法:非递归版本

    1. class Solution {
    2. public:
    3. bool IsInSubTree(TreeNode* tree, TreeNode* x) //看是否在此根节点下
    4. {
    5. if(tree==nullptr)
    6. return false;
    7. if(tree==x) //要比较地址!!
    8. return true;
    9. return IsInSubTree(tree->left, x)||IsInSubTree(tree->right, x);
    10. }
    11. TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    12. TreeNode* cur=root;
    13. while(cur)
    14. {
    15. if(p==cur||q==cur)
    16. return cur;
    17. bool pInLeft=IsInSubTree(cur->left, p);
    18. bool pInRight= !pInLeft;
    19. bool qInLeft=IsInSubTree(cur->left, q);
    20. bool qInRight= !qInLeft;
    21. if((pInLeft && qInRight)||(qInLeft && pInRight))
    22. return cur;
    23. else if(pInLeft && qInLeft)
    24. cur=cur->left;
    25. else if(pInRight && qInRight)
    26. cur=cur->right;
    27. }
    28. return nullptr;
    29. }
    30. };

    官方写法:递归版本

     因为这种写法每走一个节点就要在他的左右子树找p和q节点,N个节点,每个节点最坏找N/2,时间复杂度是O(N²),所以要用时间复杂度低的方法二。

    方法二:找路径交点

    根据三叉链(每个节点有左,右,父亲三个指针),可以利用栈记录节点p,q到根的路径,然后找相交路径(先让长的走到和短的一样长,再同时走,相等就是交点,交点就是最近公共祖先)

    易错点:FindPath 路径函数(把路径放进栈)递归写法不好写

    个人手写:

    1. class Solution {
    2. public:
    3. bool FindPath(TreeNode* root,TreeNode* x,stack& Path)
    4. {
    5. if(root==nullptr)
    6. return false;
    7. Path.push(root);
    8. if(root==x)
    9. return true;
    10. if(FindPath(root->left,x,Path))
    11. return true;
    12. if(FindPath(root->right,x,Path))
    13. return true;
    14. // root不是要找的节点x,左子树和右子树都没有找到,那么root不是x的的路径中节点,
    15. Path.pop();
    16. return false;
    17. }
    18. TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    19. stack pPath,qPath;
    20. FindPath(root,p,pPath);
    21. FindPath(root,q,qPath);
    22. while(pPath.size()size())
    23. {
    24. qPath.pop();
    25. }
    26. while(pPath.size()>qPath.size())
    27. {
    28. pPath.pop();
    29. }
    30. while(qPath.top() != pPath.top())
    31. {
    32. qPath.pop();
    33. pPath.pop();
    34. }
    35. return pPath.top();
    36. }
    37. };

    官方写法:

    4.二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com) 

    中序遍历时,把每一个节点的left和right指向改变成head和tail双向链表的形式,用prev记录上一个节点, 让 pRootOfTree 的left指向prev,并让prev的right指向自己,以此类推

    个人手写:

    1. class Solution {
    2. public:
    3. void InOrderConvert(TreeNode* pRootOfTree,TreeNode*& prev)
    4. {
    5. if(pRootOfTree==nullptr)
    6. {
    7. return ;
    8. }
    9. InOrderConvert( pRootOfTree->left,prev);
    10. pRootOfTree->left=prev;
    11. if(prev)
    12. {
    13. prev->right= pRootOfTree;
    14. }
    15. prev=pRootOfTree;
    16. InOrderConvert( pRootOfTree->right,prev);
    17. }
    18. TreeNode* Convert(TreeNode* pRootOfTree) {
    19. if(pRootOfTree==nullptr)
    20. return nullptr;
    21. TreeNode* prev=nullptr;
    22. InOrderConvert(pRootOfTree,prev);
    23. while(pRootOfTree->left)
    24. {
    25. pRootOfTree=pRootOfTree->left;
    26. }
    27. return pRootOfTree;
    28. }
    29. };

    官方写法: 

    5.105. 从前序与中序遍历序列构造二叉树

    思路:

    前序:根、左子树、右子树--依次确定根
    中序:左子树、根、右子树--划分左右子树区间

    个人手写:

    1. class Solution {
    2. public:
    3. TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder,
    4. int& prei,int inbegin,int inend)
    5. {
    6. if(inbegin>inend)
    7. {
    8. return nullptr;
    9. }
    10. TreeNode* root=new TreeNode(preorder[prei]);
    11. int rooti=0;
    12. while(inorder[rooti]!=preorder[prei])
    13. {
    14. ++rooti;
    15. }
    16. ++prei;
    17. root->left=_buildTree(preorder,inorder,prei,inbegin,rooti-1);
    18. root->right=_buildTree(preorder,inorder,prei,rooti+1,inend);
    19. return root;
    20. }
    21. TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
    22. int prei=0,inbegin=0,inend=inorder.size()-1;
    23. return _buildTree(preorder,inorder,prei,inbegin,inend);
    24. }
    25. };

    官方写法:

    6. 106. 从中序与后序遍历序列构造二叉树

    与5类似

    1. class Solution {
    2. public:
    3. TreeNode* _buildTree(vector<int>& postorder, vector<int>& inorder,
    4. int& backi,int inbegin,int inend)
    5. {
    6. if(inbegin>inend)
    7. {
    8. return nullptr;
    9. }
    10. TreeNode* root=new TreeNode(postorder[backi]);
    11. int rooti=0;
    12. while(inorder[rooti]!=postorder[backi])
    13. {
    14. ++rooti;
    15. }
    16. --backi;
    17. root->right=_buildTree(postorder,inorder,backi,rooti+1,inend);
    18. root->left=_buildTree(postorder,inorder,backi,inbegin,rooti-1);
    19. return root;
    20. }
    21. TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
    22. int backi=postorder.size()-1,inbegin=0,inend=inorder.size()-1;
    23. //cout<
    24. return _buildTree(postorder,inorder,backi,inbegin,inend);
    25. }
    26. };

      _buildTree 子函数注意顺序,当时调试了很久才知道是顺序错了。

     

  • 相关阅读:
    动态规划学习1
    ant design vue 的getPopupContainer
    Apache POI 解析和处理Excel
    docker 安装卸载及常用命令
    dji uav建图导航系列()move_base
    22.10.31补卡 22CCPC桂林C题
    安卓面经_Android面试题解析大全(2/30)之Service全解析
    软文为什么成为企业降本增效的营销利器?
    linux开启交换空间
    【C++学习第六讲】第一章练习题(含源代码)
  • 原文地址:https://blog.csdn.net/zhang_si_hang/article/details/126237630