zoukankan      html  css  js  c++  java
  • Morris遍历-如何用空间复杂度O(1)来遍历二叉树

    参照和学习:

    https://www.cnblogs.com/AnnieKim/archive/2013/06/15/morristraversal.html

    解决的问题:如何使用空间复杂度O(1),来遍历二叉树。

    我们通常的办法:是递归或者利用栈的迭代,空间复杂度都为O(logN),虽然已经很完美,但是还有更加美丽和充满艺术感的Morris。

    Morris解法:首先要面临的问题是,O(1)的空间,遍历的时候怎么回去(常规方法,是利用栈,和递归存储先前信息的能力),我们所知道的二叉树的叶节点,都有两个空间浪费了,指向NULL。Morris就把这些空间给利用了起来,达到回去的效果。

    流程:我们记来到的当前节点为cur。大致分为两步,第二步再分两步,一共三步。

    (1)如果cur无左孩子,则cur向右移动。即(cur = cur.right

    (2)如果cur有左孩子,则找到cur左子树上最右的节点,记为mostRigth

                   1、如果mostRight的右指针指向NULL,则让其指向cur,然后cur向左移动。即(cur = cur.left

                   2、如果mostRight的右指针指向cur,则让其指向NULL,然后cur向右移动。即(cur = cur.right

    代码:

    package advanced_class_03;
    
    public class Code_01_MorrisTraversal {
    
    	
    	
    	public static void process(Node head) {
    		if(head == null) {
    			return;
    		}
    		
    		// 1
    		//System.out.println(head.value);
    		
    		
    		process(head.left);
    		
    		// 2
    		//System.out.println(head.value);
    		
    		
    		process(head.right);
    		
    		// 3
    		//System.out.println(head.value);
    	}
    	
    	
    	public static class Node {
    		public int value;
    		Node left;
    		Node right;
    
    		public Node(int data) {
    			this.value = data;
    		}
    	}
    
    	public static void morrisIn(Node head) {
    		if (head == null) {
    			return;
    		}
    		Node cur = head;
    		Node mostRight = null;
    		while (cur != null) {
    			mostRight = cur.left;
    			if (mostRight != null) {  // 如果cur有左孩子
    				while (mostRight.right != null && mostRight.right != cur) { // 找到cur左子树最右的节点
    					mostRight = mostRight.right;
    				}
    				if (mostRight.right == null) {  // 如果mostRight的右指针为空,则让其指向cur,然后cur向左移动
    					mostRight.right = cur;
    					cur = cur.left;
    					continue;
    				} else {   
    					mostRight.right = null;  // 恢复mostRight的右指针
    				}
    			}
    			System.out.print(cur.value + " "); // 打印当前节点
    			cur = cur.right;     // 注意代码的逻辑性,如果cur没有左孩子和mostRight的右指针不为空,都向右走。
    		}
    		System.out.println();
    	}
    
    	public static void morrisPre(Node head) {
    		if (head == null) {
    			return;
    		}
    		Node cur = head;
    		Node mostRight = null;
    		while (cur != null) {
    			mostRight = cur.left;
    			if (mostRight != null) {
    				while (mostRight.right != null && mostRight.right != cur) {
    					mostRight = mostRight.right;
    				}
    				if (mostRight.right == null) {
    					mostRight.right = cur;
    					System.out.print(cur.value + " ");
    					cur = cur.left;
    					continue;
    				} else {
    					mostRight.right = null;
    				}
    			} else {
    				System.out.print(cur.value + " ");
    			}
    			cur = cur.right;
    		}
    		System.out.println();
    	}
    
    	public static void morrisPos(Node head) {
    		if (head == null) {
    			return;
    		}
    		Node cur = head;
    		Node mostRight = null;
    		while (cur != null) {
    			mostRight = cur.left;
    			if (mostRight != null) {
    				while (mostRight.right != null && mostRight.right != cur) {
    					mostRight = mostRight.right;
    				}
    				if (mostRight.right == null) {
    					mostRight.right = cur;
    					cur = cur.left;
    					continue;
    				} else {
    					mostRight.right = null;
    					printEdge(cur.left);
    				}
    			}
    			cur = cur.right;
    		}
    		printEdge(head);
    		System.out.println();
    	}
    
    	public static void printEdge(Node head) {
    		Node tail = reverseEdge(head);
    		Node cur = tail;
    		while (cur != null) {
    			System.out.print(cur.value + " ");
    			cur = cur.right;
    		}
    		reverseEdge(tail);
    	}
    
    	public static Node reverseEdge(Node from) {
    		Node pre = null;
    		Node next = null;
    		while (from != null) {
    			next = from.right;
    			from.right = pre;
    			pre = from;
    			from = next;
    		}
    		return pre;
    	}
    
    	// for test -- print tree
    	public static void printTree(Node head) {
    		System.out.println("Binary Tree:");
    		printInOrder(head, 0, "H", 17);
    		System.out.println();
    	}
    
    	public static void printInOrder(Node head, int height, String to, int len) {
    		if (head == null) {
    			return;
    		}
    		printInOrder(head.right, height + 1, "v", len);
    		String val = to + head.value + to;
    		int lenM = val.length();
    		int lenL = (len - lenM) / 2;
    		int lenR = len - lenM - lenL;
    		val = getSpace(lenL) + val + getSpace(lenR);
    		System.out.println(getSpace(height * len) + val);
    		printInOrder(head.left, height + 1, "^", len);
    	}
    
    	public static String getSpace(int num) {
    		String space = " ";
    		StringBuffer buf = new StringBuffer("");
    		for (int i = 0; i < num; i++) {
    			buf.append(space);
    		}
    		return buf.toString();
    	}
    
    	public static void main(String[] args) {
    		Node head = new Node(4);
    		head.left = new Node(2);
    		head.right = new Node(6);
    		head.left.left = new Node(1);
    		head.left.right = new Node(3);
    		head.right.left = new Node(5);
    		head.right.right = new Node(7);
    		printTree(head);
    		morrisIn(head);
    		morrisPre(head);
    		morrisPos(head);
    		printTree(head);
    
    	}
    
    }
    
  • 相关阅读:
    《Android源码设计模式》--装饰模式
    弹出对话框输入框
    顶部搜索框背景色渐变
    《Android源码设计模式》--模板方法模式
    《Android源码设计模式》--状态模式--责任链模式--解释器模式--命令模式--观察者模式--备忘录模式--迭代器模式
    《Android源码设计模式》--策略模式
    《Android源码设计模式》--抽象工厂模式
    《Android源码设计模式》--工厂方法模式
    《Android源码设计模式》--原型模式
    《Android源码设计模式》--Builder模式
  • 原文地址:https://www.cnblogs.com/horken/p/10706115.html
Copyright © 2011-2022 走看看