vlambda博客
学习文章列表

二叉树基本算法(上)

1、链表附加题

给定两个可能有环也可能无环的单链表,头节点head1和head2。请实现一个函数,如果两个链表相交,请返回相交的 第一个节点。如果不相交,返回null
【要求】如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度 请达到O(1)。

package class10;public class Code01_FindFirstIntersectNode { public static class Node { public int value; public Node next; public Node(int data) { this.value = data; } } public static Node getIntersectNode(Node head1, Node head2) { if (head1 == null || head2 == null) { return null; } Node loop1 = getLoopNode(head1); Node loop2 = getLoopNode(head2); if (loop1 == null && loop2 == null) { return noLoop(head1, head2); } if (loop1 != null && loop2 != null) { return bothLoop(head1, loop1, head2, loop2); } return null; } // 找到链表第一个入环节点,如果无环,返回null public static Node getLoopNode(Node head) { if (head == null || head.next == null || head.next.next == null) { return null; } // n1 慢 n2 快 Node slow = head.next; // n1 -> slow Node fast = head.next.next; // n2 -> fast while (slow != fast) { if (fast.next == null || fast.next.next == null) { return null; } fast = fast.next.next; slow = slow.next; } // slow fast 相遇 fast = head; // n2 -> walk again from head while (slow != fast) { slow = slow.next; fast = fast.next; } return slow; } // 如果两个链表都无环,返回第一个相交节点,如果不想交,返回null public static Node noLoop(Node head1, Node head2) { if (head1 == null || head2 == null) { return null; } Node cur1 = head1; Node cur2 = head2; int n = 0; while (cur1.next != null) { n++; cur1 = cur1.next; } while (cur2.next != null) { n--; cur2 = cur2.next; } if (cur1 != cur2) { return null; } // n : 链表1长度减去链表2长度的值 cur1 = n > 0 ? head1 : head2; // 谁长,谁的头变成cur1 cur2 = cur1 == head1 ? head2 : head1; // 谁短,谁的头变成cur2 n = Math.abs(n); while (n != 0) { n--; cur1 = cur1.next; } while (cur1 != cur2) { cur1 = cur1.next; cur2 = cur2.next; } return cur1; }
// 两个有环链表,返回第一个相交节点,如果不想交返回null public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) { Node cur1 = null; Node cur2 = null; if (loop1 == loop2) { cur1 = head1; cur2 = head2; int n = 0; while (cur1 != loop1) { n++; cur1 = cur1.next; } while (cur2 != loop2) { n--; cur2 = cur2.next; } cur1 = n > 0 ? head1 : head2; cur2 = cur1 == head1 ? head2 : head1; n = Math.abs(n); while (n != 0) { n--; cur1 = cur1.next; } while (cur1 != cur2) { cur1 = cur1.next; cur2 = cur2.next; } return cur1; } else { cur1 = loop1.next; while (cur1 != loop1) { if (cur1 == loop2) { return loop1; } cur1 = cur1.next; } return null; } } public static void main(String[] args) { // 1->2->3->4->5->6->7->null Node head1 = new Node(1); head1.next = new Node(2); head1.next.next = new Node(3); head1.next.next.next = new Node(4); head1.next.next.next.next = new Node(5); head1.next.next.next.next.next = new Node(6); head1.next.next.next.next.next.next = new Node(7); // 0->9->8->6->7->null Node head2 = new Node(0); head2.next = new Node(9); head2.next.next = new Node(8); head2.next.next.next = head1.next.next.next.next.next; // 8->6 System.out.println(getIntersectNode(head1, head2).value); // 1->2->3->4->5->6->7->4... head1 = new Node(1); head1.next = new Node(2); head1.next.next = new Node(3); head1.next.next.next = new Node(4); head1.next.next.next.next = new Node(5); head1.next.next.next.next.next = new Node(6); head1.next.next.next.next.next.next = new Node(7); head1.next.next.next.next.next.next = head1.next.next.next; // 7->4 // 0->9->8->2... head2 = new Node(0); head2.next = new Node(9); head2.next.next = new Node(8); head2.next.next.next = head1.next; // 8->2 System.out.println(getIntersectNode(head1, head2).value); // 0->9->8->6->4->5->6.. head2 = new Node(0); head2.next = new Node(9); head2.next.next = new Node(8); head2.next.next.next = head1.next.next.next.next.next; // 8->6 System.out.println(getIntersectNode(head1, head2).value); }}


2、递归遍历二叉树

package class10;public class Code02_RecursiveTraversalBT {
public static class Node { public int value; public Node left; public Node right; public Node(int v) { value = v; } }
public static void f(Node head) { if (head == null) { return; } // 1 f(head.left); // 2 f(head.right); // 3 }
// 先序打印所有节点 public static void pre(Node head) { if (head == null) { return; } System.out.println(head.value); pre(head.left); pre(head.right); }
public static void in(Node head) { if (head == null) { return; } in(head.left); System.out.println(head.value); in(head.right); }
public static void pos(Node head) { if (head == null) { return; } pos(head.left); pos(head.right); System.out.println(head.value); }
public static void main(String[] args) { Node head = new Node(1); head.left = new Node(2); head.right = new Node(3); head.left.left = new Node(4); head.left.right = new Node(5); head.right.left = new Node(6); head.right.right = new Node(7); pre(head); System.out.println("========"); in(head); System.out.println("========"); pos(head); System.out.println("========"); }}

3、非递归遍历二叉树

先序遍历:

(1)头结点入栈。

(2)在一个循环中,栈顶元素出栈,记为cur,输出该节点。

(3)cur存在右孩子则右孩子入栈,存在左孩子则左孩子入栈,再进行下一次循环。

public static void pre(Node head) { System.out.print("pre-order: "); if (head != null) { Stack<Node> stack = new Stack<Node>(); stack.add(head); while (!stack.isEmpty()) { head = stack.pop(); System.out.print(head.value + " "); if (head.right != null) { stack.push(head.right); } if (head.left != null) { stack.push(head.left); } } } System.out.println(); }

后序遍历:

先序遍历的出栈顺序为:头左右,而头右左的逆置为:左右头,即为后序遍历顺序。因此,可将先序遍历稍作修改,然后逆置即可得到后序遍历。

(1)设置两个栈,头结点入A栈。

(2)在一个循环中,A栈栈顶元素出栈,再入B栈。

(3)存在左孩子则左孩子入A栈,存在右孩子则右孩子入A栈,再执行下一次循环。

(4)B栈元素全部出栈。

public static void pos(Node head) { System.out.print("pos-order: "); if (head != null) { Stack<Node> s1 = new Stack<Node>(); Stack<Node> s2 = new Stack<Node>(); s1.push(head); while (!s1.isEmpty()) { head = s1.pop(); // 头 右 左 s2.push(head); if (head.left != null) { s1.push(head.left); } if (head.right != null) { s1.push(head.right); } } // 左 右 头 while (!s2.isEmpty()) { System.out.print(s2.pop().value + " "); } } System.out.println(); }

中序遍历:

(1)设二叉树当前节点为cur,cur的整条左边界入栈。

(2)栈依次弹出节点,并打印,若弹出的该节点存在右孩子,则右孩子设为cur重复(1),若不存在右孩子,则栈继续弹出元素。

public static void in(Node cur) { System.out.print("in-order: "); if (cur != null) { Stack<Node> stack = new Stack <Node>(); while (!stack.isEmpty() || cur != null) { if (cur != null) { stack.push(cur); cur = cur.left; } else { cur = stack.pop(); System.out.print(cur.value + " "); cur = cur.right; } } } System.out.println(); }