二叉树Morris Traversal

Morris遍历是一种高效的二叉树遍历算法,空间复杂度为O(1)。通过巧妙利用二叉树中叶子节点的空指针,Morris遍历避免了额外的数据结构开销。本文详细介绍了Morris前序、中序和后序遍历的具体实现,并提供了代码示例。

常见的二叉树遍历有递归和栈循环两种方式,其实还有另一种更为巧妙的遍历方式Morris Traversal。

Morris Traversal的空间复杂度为O(1),时间复杂度为O(n)

我们知道,在深度搜索遍历的过程中,之所以要用递归或者是用非递归的栈方式,都是因为其他的方式没法记录当前节点的parent,而如果在每个节点的结构里面加个parent 分量显然是不现实的,那么Morris是怎么解决这一问题的呢?好吧,他用得很巧妙,实际上是用叶子节点的空指针来记录当前节点的位置,然后一旦遍历到了叶子节点,发现叶子节点的右指针指向的是当前节点,那么就认为以当前节点的左子树已经遍历完成。

以inorder为例,初始化当前节点为root,它的遍历规则如下:

  • 如果当前节点为空,程序退出。
  • 如果当前节点非空,
    • 如果当前节点的左儿子为空,那么输出当前节点,当前节点重置为当前节点的右儿子。
    • 如果当前节点的左儿子非空,找到当前节点左子树的最右叶子节点(此时最右节点的右儿子有两种情况,一种是指向当前节点,一种是为空,你也许感到奇怪,右节点的右儿子怎么可能非空,注意,这里的最右叶子节点只带的是原树中的最右叶子节点。),若其最右叶子节点为空,令其指向当前节点,将当前节点重置为其左儿子,若其最右节点指向当前节点,输出当前节点,将当前节点重置为当前节点的右儿子,并恢复树结构,即将最右节点的右节点再次设置为NULL

morris 中序遍历

[cpp]  view plain  copy
  1. void bst_morris_inorder(struct bst_node *root)  
  2. {  
  3.     struct bst_node *p = root, *tmp;  
  4.   
  5.     while (p) {  
  6.         if (p->left == NULL) {  
  7.             printf("%d ", p->key);  
  8.             p = p->right;  
  9.         }  
  10.         else {  
  11.             tmp = p->left;  
  12.             while (tmp->right != NULL && tmp->right != p)  
  13.                 tmp = tmp->right;  
  14.             if (tmp->right == NULL) {  
  15.                 tmp->right = p;  
  16.                 p = p->left;  
  17.             }  
  18.             else {  
  19.                 printf("%d ", p->key);  
  20.                 tmp->right = NULL;  
  21.                 p = p->right;  
  22.             }  
  23.         }  
  24.     }  
  25. }  
代码相当简单,局部变量也只需要两个。以下简单讲述其实现方式。


morris traversal 原理很简单,利用所有叶子结点的right 指针,指向其后继结点,组成一个环,在第二次遍历到这个结点时,由于其左子树已经遍历完了,则访问该结点

如下图为morris 的一个示例



morris 前序遍历

[cpp]  view plain  copy
  1. void bst_morris_preorder(struct bst_node *root)  
  2. {  
  3.     struct bst_node *p = root, *tmp;  
  4.   
  5.     while (p) {  
  6.         if (p->left == NULL) {  
  7.             printf("%d ", p->key);  
  8.             p = p->right;  
  9.         }  
  10.         else {  
  11.             tmp = p->left;  
  12.             while (tmp->right != NULL && tmp->right != p)  
  13.                 tmp = tmp->right;  
  14.             if (tmp->right == NULL) {  
  15.                 printf("%d ", p->key);                  
  16.                 tmp->right = p;  
  17.                 p = p->left;  
  18.             }  
  19.             else {  
  20.                 tmp->right = NULL;  
  21.                 p = p->right;  
  22.             }  
  23.         }  
  24.     }  
  25. }  
比较简单,只要注意访问结点的顺序即可

morris 后序遍历

[cpp]  view plain  copy
  1. static void bst_morris_reverse(struct bst_node *node, struct bst_node *last)  
  2. {  
  3.     struct bst_node *p = node, *x, *y;  
  4.     if (p == last) {  
  5.         printf("%d ", last->key);  
  6.         return;  
  7.     }  
  8.   
  9.     /* change right to parent pointer */  
  10.     x = p->right;  
  11.     for (;;) {  
  12.         if (x == last) {  
  13.             x->right = p;  
  14.             break;  
  15.         }  
  16.         y = x->right;  
  17.         x->right = p;  
  18.         p = x;  
  19.         x = y;  
  20.     }  
  21.   
  22.     /* visit each */  
  23.     x = last;  
  24.     for (;;) {  
  25.         printf("%d ", x->key);  
  26.         if (x == node)  
  27.             break;  
  28.         x = x->right;  
  29.     }   
  30.   
  31.     /* revert right pointer */  
  32.     p = last;  
  33.     x = last->right;  
  34.     for (;;) {  
  35.         if (x == node) {  
  36.             x->right = p;  
  37.             break;  
  38.         }  
  39.         y = x->right;  
  40.         x->right = p;  
  41.         p = x;  
  42.         x = y;  
  43.     }  
  44. }  
  45.   
  46.   
  47. void bst_morris_postorder(struct bst_node *root)  
  48. {  
  49.     struct bst_node dummy;  
  50.     struct bst_node *p, *tmp;  
  51.   
  52.     dummy.left = root;  
  53.     dummy.right = NULL;  
  54.     p = &dummy;  
  55.   
  56.     while (p) {  
  57.         if (p->left == NULL) {  
  58.             p = p->right;  
  59.         }  
  60.         else {  
  61.             tmp = p->left;  
  62.             while (tmp->right != NULL && tmp->right != p)  
  63.                 tmp = tmp->right;  
  64.             if (tmp->right == NULL) {  
  65.                 tmp->right = p;  
  66.                 p = p->left;  
  67.             }  
  68.             else {  
  69.                 bst_morris_reverse(p->left, tmp);  
  70.                 tmp->right = NULL;  
  71.                 p = p->right;  
  72.             }  
  73.         }  
  74.     }  
  75. }  
后序遍历比较复杂,它的思路是利用中序遍历,所以首先产生了一个假的根结点,其左子树为原来的二叉树,从假的根结点开始中序遍历

它和中序遍历有所不同,在发现当前结点左子树为空时,不访问此结点(后序遍历需要保证访问完右子树后才能访问根结点),直接访问右子树。

第二次遍历到某个结点时,将该结点左子树的最右路径反序输出即可,对应函数为bst_morris_reverse



概要:   DevCon 实用工具是一种命令行实用工具,可以替代设备管理器。使用 DevCon,您可以启用禁用、重新启动、更新、删除查询单个设备或一组设备。DevCon 提供与开发人员有关但无法在设备管理器中看到的信息。   您可以将 DevCon 用于 Windows 2000 、Windows XPWindows vista。不能将 Devcon 用于 Microsoft Windows 95、Windows 98、或 Windows Millennium Edition。   下载:http://download.microsoft.com/download/1/1/f/11f7dd10-272d-4cd2-896f-9ce67f3e0240/devcon.exe 用法及参数说明:   devcon.exe [-r] [-m:\\] [...]   -r 如果指定它,在命令完成后若需要则重新启动计算机。    是目标计算机的名称。    是将要执行的命令(如下所示)。   ... 是命令需要的一个或多个参数。   要获取关于某一特定命令的帮助,请键入:devcon.exe help   classfilter 允许修改类别筛选程序。   classes 列出所有设备安装类别。   disable 禁用与指定的硬件或实例 ID 匹配的设备。   driverfiles 列出针对设备安装的驱动程序文件。   drivernodes 列出设备的所有驱动程序节点。   enable 启用与指定的硬件或 实例 ID 匹配的设备。   find 查找与指定的硬件或 实例 ID 匹配的设备。   findall 查找设备,包括那些未显示的设备。   help 显示此信息。   hwids 列出设备的硬件 ID。   install 手动安装设备。   listclass 列出某一安装类别的所有设备。   reboot 重新启动本地计算机。   remove 删除与特定的硬件或 实例 ID 匹配的设备。   rescan 扫描以发现新的硬件。   resources 列出设备的硬件资源。   restart 重新启动与特定的硬件或 实例 ID 匹配的设备。   stack 列出预期的设备驱动程序堆栈。   status 列出设备的运行状态。   update 手动更新设备。   UpdateNI 手动更新设备,无用户提示   SetHwID 添加、删除更改根枚举设备的硬件 ID 的顺序。 示例:   devcon -m:\\test find pci\* 列出计算机 test 上的所有已知 PCI 设备。(通过使用 -m,您可以指定一个目标计算机。您必须使用“进程间通信”(IPC) 访问此计算机。)   devcon -r install Windows directory\Inf\Netloop.inf *MSLOOP 安装一个新的 Microsoft 环回适配器实例。这将创建一个新的根枚举设备节点,使用此节点您可以安装“虚拟设备”,如环回适配器。如果需要重新启动计算机,此命令还将以安静模式重启计算机。   devcon classes 列出所有已知的安装类别。输出结果包含短的未本地化的名称(例如,“USB”)描述性名称(例如,“通用串行总线控制器”)。 禁用启用网卡的步骤:   1.用devcon hwids PCI*命令得到所有以PCI开头的设备。这时会列出很多设备,那么哪个才是网卡对应的呢?   2.打开设备管理器,展开网络适配器,找到网卡的名称,然后记住到刚才得到的列表中找对应的Name,然后你会在下面看到好几个ID,随便挑一个就行   3.用devcon disable "PCI\VEN_11AB&DEV_4380&SUBSYS_301B17AA&REV_10"禁用网卡(启用的话讲disable换成enable就行了)   4.其实用PCI开头得到的几组设备中一般第一个就是网卡设备 sysdzw 16:01 2010-11-16
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值