230. 二叉搜索树中第K小的元素
这一题比较简单,就是复习中序遍历,中左右进行遍历之后,直接获取到二叉树的第k个参数即可,对于Rust来说,空间比较难继续优化了
pub fn kth_smallest(root: Option<Rc<RefCell<TreeNode>>>, k: i32) -> i32 {let mut list = Vec::<i32>::new();Self::dfs_search_tree(root, &mut list);list[k as usize]}/// 经典的二叉树中序遍历的实现,注意,我们传入的list虽然是vec,是一个指向一块内存的指针/// 但是实际上还是会被move进去,和所有的struct相同,就算是指针,在我们传递函数的时候是/// move或者clone的,因此需要使用mut指针来进行传入fn dfs_search_tree(root: Option<Rc<RefCell<TreeNode>>>, list: &mut Vec<i32>) {match root {None => { return; }Some(node) => {// 获取到对于当前节点的RC引用let next = Rc::clone(&node);// 将树的子结点深拷贝了一份出来,不知道会不会有额外空间开销let left = next.borrow().left.clone();let right = node.borrow().right.clone();Solution::dfs_search_tree(left, list);list.push(node.borrow().val);Solution::dfs_search_tree(right, list);}}}
有一种优化空间的思路,但是奇怪的是,没有优化到空间,时间也多了去了,至少在Rust,Vec的优化是真好
/// 230. 二叉搜索树中第K小的元素pub fn kth_smallest(root: Option<Rc<RefCell<TreeNode>>>, k: i32) -> i32 {let mut ans = 0;let mut k= k;Self::dfs_search_tree(root, &mut k, &mut ans);ans}fn dfs_search_tree(root: Option<Rc<RefCell<TreeNode>>>, k: &mut i32, ans: &mut i32) {match root {None => { return; }Some(node) => {// 获取到对于当前节点的RC引用let next = Rc::clone(&node);// 将树的子结点深拷贝了一份出来,不知道会不会有额外空间开销let left = next.borrow().left.clone();let right = node.borrow().right.clone();Solution::dfs_search_tree(left, k, ans);// 优化在这里,通过k来进行计数*k -= 1;if *k == 0 {*ans = node.borrow().val;}Solution::dfs_search_tree(right, k, ans);}}}
java可以这么优化:
class Solution {int k, ans;public int kthSmallest(TreeNode root, int _k) {k = _k;dfs(root);return ans;}void dfs(TreeNode root) {if (root == null || k <= 0) return ;dfs(root.left);if (--k == 0) ans = root.val;dfs(root.right);}}
关于从RC以及REF中拿取值
性能最好的方法不是clone一个节点出来,确实会造成空间损失,使用borrow_mut直接拿出来value可以得到最小的空间利用率let left = next.borrow_mut().left.take();
pub fn kth_smallest(root: Option<Rc<RefCell<TreeNode>>>, k: i32) -> i32 {let mut list = Vec::<i32>::new();Self::dfs_search_tree(root, &mut list);list[(k-1) as usize]}fn dfs_search_tree(root: Option<Rc<RefCell<TreeNode>>>, list: &mut Vec<i32>) {match root {None => { return; }Some(node) => {// 获取到对于当前节点的RC引用let next = Rc::clone(&node);// 将树的子结点深拷贝了一份出来,不知道会不会有额外空间开销let left = next.borrow_mut().left.take();let right = node.borrow_mut().right.take();Solution::dfs_search_tree(left, list);list.push(node.borrow().val);Solution::dfs_search_tree(right, list);}}}
中序遍历的栈实现
思路就是一直存左,然后左中右
pub fn kth_smallest(root: Option<Rc<RefCell<TreeNode>>>, mut k: i32) -> i32 {// 使用一个队列来迭代处理let mut queue = Vec::new();// 为了能够直接对root进行修改作出的牺牲,但是既然进函数了,所有权就已经不是外面的了let mut curr = root;while curr.is_some() || !queue.is_empty() {// 这里会一直找到最左边元素while curr.is_some() {queue.push(curr.clone());curr = curr.unwrap().borrow().left.clone();}// 中的处理,这里注意思考,哪怕是最左边的元素其实也是正确的// 就算中节点是空,获取不到右节点,但是后面queue正常pop了curr = queue.pop().unwrap();k -= 1;if k == 0 {return curr.unwrap().borrow().val;}// 走向右节点,注意都是clone,不破坏树curr = curr.unwrap().borrow().right.clone();}k}
783. 二叉搜索树节点最小距离
1305. 两棵二叉搜索树中的所有元素
这一题的思路比较简单,就是利用二叉搜索树的有序性质直接得到线性表,然后进行合并即可,关键在于进行dfs的时候,需要注意一些用法
use std::rc::Rc;use std::cell::RefCell;impl Solution {pub fn get_all_elements(root1: Option<Rc<RefCell<TreeNode>>>, root2: Option<Rc<RefCell<TreeNode>>>) -> Vec<i32> {// 中序遍历获取所有数据fn dfs(root: Option<Rc<RefCell<TreeNode>>>, list: &mut Vec<i32>) {match root {None => { return; }// 左中右Some(mut node) => {dfs(node.borrow_mut().left.take(), list);list.push(node.borrow().val);dfs(node.borrow_mut().right.take(), list);}}}let mut ret = Vec::new();dfs(root1, &mut ret);dfs(root2, &mut ret);ret.sort();ret}}
另一个就是不用push的思路,直接写有序数组合并的
class Solution {int INF = 0x3f3f3f3f;public List<Integer> getAllElements(TreeNode root1, TreeNode root2) {List<Integer> ans = new ArrayList<>();List<Integer> l1 = new ArrayList<>(), l2 = new ArrayList<>();dfs(root1, l1); dfs(root2, l2);int n = l1.size(), m = l2.size(), i = 0, j = 0;// 注意三叶写的这里,就很简洁while (i < n || j < m) {int a = i < n ? l1.get(i) : INF, b = j < m ? l2.get(j) : INF;if (a <= b) {ans.add(a); i++;} else {ans.add(b); j++;}}return ans;}void dfs(TreeNode root, List<Integer> list) {if (root == null) return ;dfs(root.left, list);list.add(root.val);dfs(root.right, list);}}
449. 序列化和反序列化二叉搜索树
简单来说就是前序遍历直接序列化,反序列化的话,需要考虑到BST的性质,左子树小于root小于右子树,因此就在序列化的数组上面查找刚好大于和小于的位置,作为分界点,使用二分查找优化,但是二分的细节太多了,看代码:
impl Codec {fn new() -> Self {Self {}}/// 序列化,简单地前序遍历即可fn serialize(&self, root: Option<Rc<RefCell<TreeNode>>>) -> String {let mut list = Vec::new();self.pre_order(root, &mut list);list.into_iter().fold(String::from(""), |acc, x| {if acc.len() == 0 {format!("{},", x)} else {format!("{},{}", acc, x)}}).to_string()}fn pre_order(&self, root: Option<Rc<RefCell<TreeNode>>>, list: &mut Vec<i32>) {match root {None => { return; }Some(mut node) => {let temp = node.borrow_mut().val;list.push(temp);self.pre_order(node.borrow_mut().left.clone(), list);self.pre_order(node.borrow_mut().right.clone(), list);}}}fn deserialize(&self, data: String) -> Option<Rc<RefCell<TreeNode>>> {if data.is_empty() { return None; }let x: Vec<i32> = data.split(",").map(|x: &str| {x.parse::<i32>().unwrap()}).collect();println!("{:?}", x);self.construct(0, x.len()-1, &x)}/// 利用二叉搜索树的特性进行重构,就是左子树小于叶子节点,小于右子树,因此就只需要找到每一个子树fn construct(&self, l: usize, r: usize, list: &Vec<i32>) -> Option<Rc<RefCell<TreeNode>>> {if l > r { return None; }let (mut ll, mut rr) = (l + 1, r);// root节点let root_num = list[l];let ans = Rc::new(RefCell::new(TreeNode::new(root_num)));// 二分查找分界点while ll < rr {let mid = ll + ((rr - ll) >> 1);// 二分这么来分析,大于root的时候右子树,往左找没问题,左二分是l=mid+1// 等于的时候,有可能左子树也有可能右子树,关键就在于我们想把这个值放在哪// 如果是假设这个值为左子树的话,那么ll=mid+1,使得往右找,相反rr=mid往左// 继续if list[mid] > root_num { rr = mid; }else { ll = mid + 1; }}// 上面看为了左子树,最后我们rr和ll都是中间结点的位置,还需要继续把相同值左挪if list[rr] <= root_num { rr += 1; }ans.borrow_mut().left = self.construct(l + 1, rr - 1, list);ans.borrow_mut().right = self.construct(rr, r, list);Some(ans)}}
