leetcode 99:恢复二叉搜索树

2024-09-02 15:18
文章标签 leetcode 恢复 搜索 二叉 99

本文主要是介绍leetcode 99:恢复二叉搜索树,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

方法一:首先使用中序遍历将所有的节点和节点的值存起来,如果是搜索二叉树节点值的数组应该是升序的,找到不是升序的点,交换节点的值,空间复杂度为O(n)

void inorder(TreeNode*root,std::vector<TreeNode*>&list,std::vector<int> &vals){if(root==NULL)return;inorder(root->left,list,vals);list.push_back(root);vals.push_back(root->val);inorder(root->right,list,vals);
}void recoverTree(TreeNode*root){std::vector<TreeNode*>list;std::vector<int>vals;inorder(root,list,vals);std::vector<int> node;for(int i=0;i<vals.size()-1;){if(vals[i]>vals[i+1]){node.push_back(i);i=i+2;if(node.size()==2)break;}elsei=i+1;}if(node.size()==1){int c=node.back();node.clear();list[c]->val=vals[c+1];list[c+1]->val=vals[c];}else if(node.size()==2){int c1=node[0];int c2=node[1];node.clear();list[c1]->val=vals[c2+1];list[c2+1]->val=vals[c1];}sort(vals.begin(),vals.end());for(int i=0;i<list.size();i++)list[i]->val=vals[i];
}

方法二:使用Morris算法进行遍历,空间复杂度为O(1)

首先介绍Morris算法

1. 当前子树的头结点为head,空闲指针由head的左子树中最右结点的右指针指向head结点。对head的左子树重复该步骤1,直到遍历至某个结点没有左子树,将该结点记    为node。进入步骤2。

2. 从node结点开始通过每个结点的右指针进行移动,并打印结点的值。

  假设遍历到的当前结点为curNode,做如下判断:

curNode结点的左子树中最右结点(记为lastRNode)是否指向curNode。

A. 是 让lastRNode结点的右指针指向null,打印curNode的值。接着通过curNode的右指针遍历下一个结点,重复步骤2。

B. 否 将curNode为头结点的子树重复步骤1。

 

下面举例说明上述步骤(先以中序遍历为例),二叉树结构如下图所示:

遍历至结点1 发现其没有左子树 记为Node。

curNode : 1 打印1

curNode : 2 满足A 空闲指针由1的右指针指向2 将该空闲指针取消掉 打印2。通过2的右指针遍历到3。

curNode : 3 满足B 进行步骤1 最终打印3。

通过空闲指针遍历至4。

curNode : 4 满足A 空闲指针由3的右指针指向4 将该空闲指针取消掉 打印4。通过4的右指针遍历到6。

至此 左子树和根结点遍历完毕。

curNode : 6 满足B 进行步骤1 之后二叉树变为右图。

遍历至结点5 其没有左子树 记为Node。

curNode : 5 满足B 进行步骤1 最终打印5。

通过空闲指针遍历至6。

curNode : 6 满足A 空闲指针由5的右指针指向6 将该空闲指针取消掉 打印6。

通过6的右指针遍历到7。

curNode : 7 满足B 进行步骤1 最终打印7。

3. 步骤2最终移动到null结点 整个过程结束。

1) 二叉树的中序遍历

1. 初始化指针cur指向root

2. 当cur不为空时

  - 如果cur没有左子结点

      a) 打印出cur的值

    b) 将cur指针指向其右子节点

  - 反之

     将pre指针指向cur的左子树中的最右子节点 

     * 若pre不存在右子节点

          a) 将其右子节点指回cur

        b) cur指向其左子节点

     * 反之

      a) 将pre的右子节点置空

      b) 打印cur的值

      c) 将cur指针指向其右子节点

void middle_visit(TreeNode *root) {TreeNode*pre;TreeNode*cur;cur=root;while(cur){if(cur->left==NULL){std::cout<<cur->val<<std::endl;cur=cur->right;}else{pre=cur->left;while(pre->right!=NULL&&pre->right!=cur){pre=pre->right;}if(pre->right==NULL){pre->right=cur;cur=cur->left;}else{pre->right=NULL;std::cout<<cur->val<<std::endl;cur=cur->right;}}}
}

2)二叉树的前序遍历  前序遍历与中序遍历的差别只是打印的位置不同

1. 初始化指针cur指向root

2. 当cur不为空时

  - 如果cur没有左子结点

      a) 打印出cur的值

    b) 将cur指针指向其右子节点

  - 反之

     将pre指针指向cur的左子树中的最右子节点 

     * 若pre不存在右子节点

          a) 将其右子节点指回cur

                   b) 打印cur的值

        c) cur指向其左子节点

     * 反之

      a) 将pre的右子节点置空

      b) 将cur指针指向其右子节点

void pre_visit(TreeNode *root) {TreeNode*pre;TreeNode*cur;cur=root;while(cur){if(cur->left==NULL){std::cout<<cur->val<<std::endl;cur=cur->right;}else{pre=cur->left;while(pre->right!=NULL&&pre->right!=cur){pre=pre->right;}if(pre->right==NULL){pre->right=cur;std::cout<<cur->val<<std::endl;cur=cur->left;}else{pre->right=NULL;cur=cur->right;}}}
}

3)二叉树的后序遍历 也是打印的的方式不同

1. 初始化指针cur指向root

2. 当cur不为空时

  - 如果cur没有左子结点

      a) 打印出cur的值

    b) 将cur指针指向其右子节点

  - 反之

     将pre指针指向cur的左子树中的最右子节点 

     * 若pre不存在右子节点

          a) 将其右子节点指回cur

        b) cur指向其左子节点

     * 反之

      a) 将pre的右子节点置空

      b) 倒序输出从当前节点的左孩子到该前驱节点这条路径上的所有节点。

      c) 将cur指针指向其右子节点

 

void reverse(TreeNode*from,TreeNode*to){if(from==to)return;TreeNode *x=from;TreeNode*y=from->right;TreeNode*z;while(true){z=y->right;y->right=x;x=y;y=z;if(x==to)break;}
}void printReverse(TreeNode* from, TreeNode *to) // print the reversed tree nodes 'from' -> 'to'.
{reverse(from,to);TreeNode*p=to;while(true){printf("%d",p->val);if(p==from)break;p=p->right;}reverse(to,from);
}void post_visit(TreeNode *root) {TreeNode dump(0);dump.left = root;TreeNode *cur = &dump, *prev = NULL;while(cur) {if (cur->left == NULL) {cur = cur->right;} else {prev = cur->left;while (prev->right != NULL && prev->right != cur) {prev = prev->right;}if (prev->right == NULL) {prev->right = cur;cur = cur->left;} else {printReverse(cur->left, prev);prev->right = NULL;cur = cur->right;}}}
}

 

下面是本题方法二的代码,也是使用中序遍历找到两个节点 之后进行交换

void recoverTree(TreeNode *root) {TreeNode *first = NULL;//第一个逆序的节点TreeNode*second = NULL;//第二个逆序的节点TreeNode*parent = NULL;//中序遍历的前一个节点TreeNode *cur, *pre;cur = root;while(cur){if(cur->left==NULL){if(parent&&parent->val>cur->val){if(first==NULL)first=parent;second=cur;}parent=cur;cur=cur->right;}else{pre=cur->left;while(pre->right&&pre->right!=cur)pre=pre->right;if(pre->right==NULL){pre->right=cur;cur=cur->right;}else{pre->right=NULL;if(parent->val>cur->val){if(first==NULL)first=parent;second=cur;}parent=cur;cur=cur->right;}}}if (first && second) swap(first->val, second->val);
}

参考:http://www.cnblogs.com/AnnieKim/archive/2013/06/15/morristraversal.html

https://www.cnblogs.com/ariel-dreamland/p/9159781.html

 

这篇关于leetcode 99:恢复二叉搜索树的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1130324

相关文章

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

hdu1240、hdu1253(三维搜索题)

1、从后往前输入,(x,y,z); 2、从下往上输入,(y , z, x); 3、从左往右输入,(z,x,y); hdu1240代码如下: #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#inc

电脑桌面文件删除了怎么找回来?别急,快速恢复攻略在此

在日常使用电脑的过程中,我们经常会遇到这样的情况:一不小心,桌面上的某个重要文件被删除了。这时,大多数人可能会感到惊慌失措,不知所措。 其实,不必过于担心,因为有很多方法可以帮助我们找回被删除的桌面文件。下面,就让我们一起来了解一下这些恢复桌面文件的方法吧。 一、使用撤销操作 如果我们刚刚删除了桌面上的文件,并且还没有进行其他操作,那么可以尝试使用撤销操作来恢复文件。在键盘上同时按下“C

hdu 4517 floyd+记忆化搜索

题意: 有n(100)个景点,m(1000)条路,时间限制为t(300),起点s,终点e。 访问每个景点需要时间cost_i,每个景点的访问价值为value_i。 点与点之间行走需要花费的时间为g[ i ] [ j ] 。注意点间可能有多条边。 走到一个点时可以选择访问或者不访问,并且当前点的访问价值应该严格大于前一个访问的点。 现在求,从起点出发,到达终点,在时间限制内,能得到的最大

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

hdu4277搜索

给你n个有长度的线段,问如果用上所有的线段来拼1个三角形,最多能拼出多少种不同的? import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;

leetcode-24Swap Nodes in Pairs

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode swapPairs(L

leetcode-23Merge k Sorted Lists

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode mergeKLists