接着第三課的內容和講了第四課的部份內容java
在二叉樹上,何爲一個節點的後繼節點?node
何爲搜索二叉樹?算法
如何實現搜索二叉樹的查找?插入?刪除?數組
二叉樹的概念上衍生出的。dom
任何一個節點,左比他小,右比他大。標準搜索二叉樹是沒有重複值的。ide
TreeMap就是搜索二叉樹,key是有序組織起來的,組織方式是搜索二叉樹,具體就是紅黑樹(具備某一種平衡性的搜索二叉樹),和HashMap的無序分佈不一樣。ui
有序的話,能完成更多的事情,找剛剛小的、剛剛大的。this
查數很方便,若是左子樹和右子樹高度差不超過一,每次搜索都篩選掉一半了。若是不平衡,會退變爲O(n)的算法。spa
從搜索二叉樹開始講3d
樹和插入樹的順序相關。
因此衍生了幾種類型。
一、AVL,平衡性高度嚴苛,任何節點高度差都不超過一。O(logn),極可能調整頻率高。
二、紅黑樹,閹割了平衡性,每一個節點染上色,頭節點黑,葉節點黑,相鄰不能出現紅色節點。(這幾年工程上愈來愈少用)
任何一條鏈,要求黑色的數量不能超過一。
若是盡最大能力插入紅,最長和最短鏈的長度差也不會超過兩倍。
三、SB樹(Size Balanced Tree),平衡性來自,任何一個叔叔節點的節點個數,不能少於任何一個侄子節點的節點個數。Y不能少於Z1或者Z2整顆樹的節點數。
不必都掌握,要理解爲何須要這些樹,就是在修改最嚴苛的平衡性,作到每一個節點來講,左右子樹節點個數差很少的。
只是平衡性標準不同。
目的爲了調整不用那麼頻繁。
增刪改查都是O(logn)。
package advanced_class_03; /** * Not implemented by zuochengyun * * Abstract binary search tree implementation. Its basically fully implemented * binary search tree, just template method is provided for creating Node (other * trees can have slightly different nodes with more info). This way some code * from standart binary search tree can be reused for other kinds of binary * trees. * * @author Ignas Lelys * @created Jun 29, 2011 * */ public class AbstractBinarySearchTree {//搜索二叉樹 /** Root node where whole tree starts. */ public Node root; /** Tree size. */ protected int size; /** * Because this is abstract class and various trees have different * additional information on different nodes subclasses uses this abstract * method to create nodes (maybe of class {@link Node} or maybe some * different node sub class). * * @param value * Value that node will have. * @param parent * Node's parent. * @param left * Node's left child. * @param right * Node's right child. * @return Created node instance. */ protected Node createNode(int value, Node parent, Node left, Node right) { return new Node(value, parent, left, right); } /** * Finds a node with concrete value. If it is not found then null is * returned. * * @param element * Element value. * @return Node with value provided, or null if not found. */ public Node search(int element) { Node node = root; while (node != null && node.value != null && node.value != element) { if (element < node.value) { node = node.left; } else { node = node.right; } } return node; } /** * Insert new element to tree. * * @param element * Element to insert. */ public Node insert(int element) { if (root == null) { root = createNode(element, null, null, null); size++; return root; } //查找插入的父節點,找到不能再找 Node insertParentNode = null; Node searchTempNode = root; while (searchTempNode != null && searchTempNode.value != null) { insertParentNode = searchTempNode; if (element < searchTempNode.value) { searchTempNode = searchTempNode.left; } else { searchTempNode = searchTempNode.right; } } Node newNode = createNode(element, insertParentNode, null, null); if (insertParentNode.value > newNode.value) { insertParentNode.left = newNode; } else { insertParentNode.right = newNode; } size++; return newNode; } /** * Removes element if node with such value exists. * * @param element * Element value to remove. * * @return New node that is in place of deleted node. Or null if element for * delete was not found. */ public Node delete(int element) { Node deleteNode = search(element); if (deleteNode != null) { return delete(deleteNode); } else { return null; } } /** * Delete logic when node is already found. * * @param deleteNode * Node that needs to be deleted. * * @return New node that is in place of deleted node. Or null if element for * delete was not found. */ protected Node delete(Node deleteNode) { if (deleteNode != null) { Node nodeToReturn = null; if (deleteNode != null) { if (deleteNode.left == null) { nodeToReturn = transplant(deleteNode, deleteNode.right); } else if (deleteNode.right == null) { nodeToReturn = transplant(deleteNode, deleteNode.left); } else { Node successorNode = getMinimum(deleteNode.right); if (successorNode.parent != deleteNode) { transplant(successorNode, successorNode.right); successorNode.right = deleteNode.right; successorNode.right.parent = successorNode; } transplant(deleteNode, successorNode); successorNode.left = deleteNode.left; successorNode.left.parent = successorNode; nodeToReturn = successorNode; } size--; } return nodeToReturn; } return null; } /** * Put one node from tree (newNode) to the place of another (nodeToReplace). * * @param nodeToReplace * Node which is replaced by newNode and removed from tree. * @param newNode * New node. * * @return New replaced node. */ //相應的環境移交給newNode private Node transplant(Node nodeToReplace, Node newNode) { if (nodeToReplace.parent == null) { this.root = newNode; } else if (nodeToReplace == nodeToReplace.parent.left) { nodeToReplace.parent.left = newNode; } else { nodeToReplace.parent.right = newNode; } if (newNode != null) { newNode.parent = nodeToReplace.parent; } return newNode; } /** * @param element * @return true if tree contains element. */ public boolean contains(int element) { return search(element) != null; } /** * @return Minimum element in tree. */ public int getMinimum() { return getMinimum(root).value; } /** * @return Maximum element in tree. */ public int getMaximum() { return getMaximum(root).value; } /** * Get next element element who is bigger than provided element. * * @param element * Element for whom descendand element is searched * @return Successor value. */ // TODO Predecessor public int getSuccessor(int element) { return getSuccessor(search(element)).value; } /** * @return Number of elements in the tree. */ public int getSize() { return size; } /** * Tree traversal with printing element values. In order method. */ public void printTreeInOrder() { printTreeInOrder(root); } /** * Tree traversal with printing element values. Pre order method. */ public void printTreePreOrder() { printTreePreOrder(root); } /** * Tree traversal with printing element values. Post order method. */ public void printTreePostOrder() { printTreePostOrder(root); } /*-------------------PRIVATE HELPER METHODS-------------------*/ private void printTreeInOrder(Node entry) { if (entry != null) { printTreeInOrder(entry.left); if (entry.value != null) { System.out.println(entry.value); } printTreeInOrder(entry.right); } } private void printTreePreOrder(Node entry) { if (entry != null) { if (entry.value != null) { System.out.println(entry.value); } printTreeInOrder(entry.left); printTreeInOrder(entry.right); } } private void printTreePostOrder(Node entry) { if (entry != null) { printTreeInOrder(entry.left); printTreeInOrder(entry.right); if (entry.value != null) { System.out.println(entry.value); } } } protected Node getMinimum(Node node) { while (node.left != null) { node = node.left; } return node; } protected Node getMaximum(Node node) { while (node.right != null) { node = node.right; } return node; } protected Node getSuccessor(Node node) { // if there is right branch, then successor is leftmost node of that // subtree if (node.right != null) { return getMinimum(node.right); } else { // otherwise it is a lowest ancestor whose left child is also // ancestor of node Node currentNode = node; Node parentNode = node.parent; while (parentNode != null && currentNode == parentNode.right) { // go up until we find parent that currentNode is not in right // subtree. currentNode = parentNode; parentNode = parentNode.parent; } return parentNode; } } // -------------------------------- TREE PRINTING // ------------------------------------ public void printTree() { printSubtree(root); } public void printSubtree(Node node) { if (node.right != null) { printTree(node.right, true, ""); } printNodeValue(node); if (node.left != null) { printTree(node.left, false, ""); } } private void printNodeValue(Node node) { if (node.value == null) { System.out.print("<null>"); } else { System.out.print(node.value.toString()); } System.out.println(); } private void printTree(Node node, boolean isRight, String indent) { if (node.right != null) { printTree(node.right, true, indent + (isRight ? " " : " | ")); } System.out.print(indent); if (isRight) { System.out.print(" /"); } else { System.out.print(" \\"); } System.out.print("----- "); printNodeValue(node); if (node.left != null) { printTree(node.left, false, indent + (isRight ? " | " : " ")); } } public static class Node { public Node(Integer value, Node parent, Node left, Node right) { super(); this.value = value; this.parent = parent; this.left = left; this.right = right; } public Integer value; public Node parent; public Node left; public Node right; public boolean isLeaf() { return left == null && right == null; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((value == null) ? 0 : value.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Node other = (Node) obj; if (value == null) { if (other.value != null) return false; } else if (!value.equals(other.value)) return false; return true; } } }
刪除邏輯:
若是刪除的節點沒有左孩子,直接讓右孩子頂替上去。
若是沒有右孩子,左孩子變成x的右孩子,把5從環境分離,將4頂替上去。
左右雙全怎麼辦?
右子樹最左的節點6去頂。(最小的比他大的數)(後繼節點)
左接管三、右接管9
6的右子樹移交給7(父的左邊)。
拿前驅節點去頂替也能夠。(中序遍歷是有順序的)
全部類型的搜索二叉樹,調整的基本動做都是同樣的。
左旋右旋(順/逆時針旋)
只是動做的組合不同。4種組合。
右旋。順時針旋
左旋,逆時針選
怎麼使用這些動做?
AVL怎麼發現不平衡,節點存儲當前層數,經過查看左右子樹,得到他們分別能到達的層數,每插入一個數,都會回溯到以前的節點,更改能達到的層數,若是發現不平衡天然會調整,逐個向上檢查。
改的過程當中,能知道不平衡
調整組合有四種,LL,RR,LR,RL
LL
RR,左旋一下就行。
LL和RR形出現單一動做就夠了。
LR的狀況,先對5的左子樹進行左旋轉,而後在進行右旋
而後進行LL操做。
LR形也同理。
具體實現看代碼。
package advanced_class_03; /** * Not implemented by zuochengyun * * Abstract class for self balancing binary search trees. Contains some methods * that is used for self balancing trees. * * @author Ignas Lelys * @created Jul 24, 2011 * */ public abstract class AbstractSelfBalancingBinarySearchTree extends AbstractBinarySearchTree { /** * Rotate to the left. * * @param node Node on which to rotate. * @return Node that is in place of provided node after rotation. */ protected Node rotateLeft(Node node) { Node temp = node.right; temp.parent = node.parent; node.right = temp.left; if (node.right != null) { node.right.parent = node; } temp.left = node; node.parent = temp; // temp took over node's place so now its parent should point to temp if (temp.parent != null) { if (node == temp.parent.left) { temp.parent.left = temp; } else { temp.parent.right = temp; } } else { root = temp; } return temp; } /** * Rotate to the right. * * @param node Node on which to rotate. * @return Node that is in place of provided node after rotation. */ protected Node rotateRight(Node node) { Node temp = node.left; temp.parent = node.parent; node.left = temp.right; if (node.left != null) { node.left.parent = node; } temp.right = node; node.parent = temp; // temp took over node's place so now its parent should point to temp if (temp.parent != null) { if (node == temp.parent.left) { temp.parent.left = temp; } else { temp.parent.right = temp; } } else { root = temp; } return temp; } }
package advanced_class_03; /** * Not implemented by zuochengyun * * AVL tree implementation. * * In computer science, an AVL tree is a self-balancing binary search tree, and * it was the first such data structure to be invented.[1] In an AVL tree, the * heights of the two child subtrees of any node differ by at most one. Lookup, * insertion, and deletion all take O(log n) time in both the average and worst * cases, where n is the number of nodes in the tree prior to the operation. * Insertions and deletions may require the tree to be rebalanced by one or more * tree rotations. * * @author Ignas Lelys * @created Jun 28, 2011 * */ public class AVLTree extends AbstractSelfBalancingBinarySearchTree { /** * @see trees.AbstractBinarySearchTree#insert(int) * * AVL tree insert method also balances tree if needed. Additional * height parameter on node is used to track if one subtree is higher * than other by more than one, if so AVL tree rotations is performed * to regain balance of the tree. */ @Override public Node insert(int element) { Node newNode = super.insert(element); rebalance((AVLNode)newNode); return newNode; } /** * @see trees.AbstractBinarySearchTree#delete(int) */ @Override public Node delete(int element) { Node deleteNode = super.search(element); if (deleteNode != null) { Node successorNode = super.delete(deleteNode); if (successorNode != null) { // if replaced from getMinimum(deleteNode.right) then come back there and update heights AVLNode minimum = successorNode.right != null ? (AVLNode)getMinimum(successorNode.right) : (AVLNode)successorNode; recomputeHeight(minimum); rebalance((AVLNode)minimum); } else { recomputeHeight((AVLNode)deleteNode.parent); rebalance((AVLNode)deleteNode.parent); } return successorNode; } return null; } /** * @see trees.AbstractBinarySearchTree#createNode(int, trees.AbstractBinarySearchTree.Node, trees.AbstractBinarySearchTree.Node, trees.AbstractBinarySearchTree.Node) */ @Override protected Node createNode(int value, Node parent, Node left, Node right) { return new AVLNode(value, parent, left, right); } /** * Go up from inserted node, and update height and balance informations if needed. * If some node balance reaches 2 or -2 that means that subtree must be rebalanced. * * @param node Inserted Node. */ private void rebalance(AVLNode node) { while (node != null) { Node parent = node.parent; int leftHeight = (node.left == null) ? -1 : ((AVLNode) node.left).height; int rightHeight = (node.right == null) ? -1 : ((AVLNode) node.right).height; int nodeBalance = rightHeight - leftHeight; // rebalance (-2 means left subtree outgrow, 2 means right subtree) if (nodeBalance == 2) { if (node.right.right != null) { node = (AVLNode)avlRotateLeft(node); break; } else { node = (AVLNode)doubleRotateRightLeft(node); break; } } else if (nodeBalance == -2) {//左樹超了 if (node.left.left != null) { node = (AVLNode)avlRotateRight(node); break; } else { node = (AVLNode)doubleRotateLeftRight(node); break; } } else { updateHeight(node); } node = (AVLNode)parent; } } /** * Rotates to left side. */ private Node avlRotateLeft(Node node) { Node temp = super.rotateLeft(node); updateHeight((AVLNode)temp.left); updateHeight((AVLNode)temp); return temp; } /** * Rotates to right side. */ private Node avlRotateRight(Node node) { Node temp = super.rotateRight(node); updateHeight((AVLNode)temp.right); updateHeight((AVLNode)temp); return temp; } /** * Take right child and rotate it to the right side first and then rotate * node to the left side. */ protected Node doubleRotateRightLeft(Node node) { node.right = avlRotateRight(node.right); return avlRotateLeft(node); } /** * Take right child and rotate it to the right side first and then rotate * node to the left side. */ protected Node doubleRotateLeftRight(Node node) { node.left = avlRotateLeft(node.left); return avlRotateRight(node); } /** * Recomputes height information from the node and up for all of parents. It needs to be done after delete. */ private void recomputeHeight(AVLNode node) { while (node != null) { node.height = maxHeight((AVLNode)node.left, (AVLNode)node.right) + 1; node = (AVLNode)node.parent; } } /** * Returns higher height of 2 nodes. */ private int maxHeight(AVLNode node1, AVLNode node2) { if (node1 != null && node2 != null) { return node1.height > node2.height ? node1.height : node2.height; } else if (node1 == null) { return node2 != null ? node2.height : -1; } else if (node2 == null) { return node1 != null ? node1.height : -1; } return -1; } /** * Updates height and balance of the node. * * @param node Node for which height and balance must be updated. */ private static final void updateHeight(AVLNode node) { int leftHeight = (node.left == null) ? -1 : ((AVLNode) node.left).height; int rightHeight = (node.right == null) ? -1 : ((AVLNode) node.right).height; node.height = 1 + Math.max(leftHeight, rightHeight); } /** * Node of AVL tree has height and balance additional properties. If balance * equals 2 (or -2) that node needs to be re balanced. (Height is height of * the subtree starting with this node, and balance is difference between * left and right nodes heights). * * @author Ignas Lelys * @created Jun 30, 2011 * */ protected static class AVLNode extends Node { public int height; public AVLNode(int value, Node parent, Node left, Node right) { super(value, parent, left, right); } } }
若是是紅黑樹,插入組合5種,刪除8種。
只需會用便可,只要理解平衡性來自左右子樹規模差很少(爲了每次效率都是logn),瞭解左右旋動做。
怎麼用?
能夠查最大值,都是o(logn),hashMap只能逐個找。
找到恰好比某數大/小的值。
package advanced_class_03; import java.util.TreeMap; public class TestTreeMap { public static void main(String[] args) { TreeMap<Integer,String> treeMap = new TreeMap<>(); treeMap.put(5,"xie"); treeMap.put(10,"yu"); treeMap.put(25,"peng"); treeMap.put(15,"ni"); treeMap.put(20,"hao"); System.out.println(treeMap.get(10)); System.out.println(treeMap.lastKey()); System.out.println(treeMap.firstKey()); System.out.println(treeMap.ceilingKey(12)); System.out.println(treeMap.floorKey(12)); } }
須要數組按序組織,就用TreeMap,平衡搜索二叉樹。
LeetCode三大頂級難題之一(頻度兩年出一次)
給定一個N行3列二維數組,每一行表示有一座大樓,一共有N座大樓。 全部大樓的底部都坐落在X軸上,每一行的三個值(a,b,c)表明每座大樓的從(a,0)點開始,到 (b,0)點結束,高度爲c。 輸入的數據能夠保證a<b,且a,b,c均爲正數。大樓之間能夠有重合。 請輸出總體的輪廓線。
例子:給定一個二維數組 [ [1, 3, 3], [2, 4, 4], [5, 6,1] ]
輸出爲輪廓線 [ [1, 2, 3], [2, 4, 4], [5, 6, 1] ]
處理方式:
每一個大樓拆成兩個信息,(1,3,上)、(3,3,下),1這個地方有高度3上去了,3這個地方有高度3下去了。
而後按位置進行排序,(1,3,上)(2,4,上)(3,3,下)....
一個輪廓產生必定是最大高度發生變化了。
位置排好序後,創建一張TreeMap,
遍歷排序的同時,利用TreeMap(<高度,次數>)爲輔助,加入當前值的高度信息後,改變TreeMap高度信息,查看當前的最大高度是否有改變。
得出1,5,8,10會產生輪廓。
最大高度的變化來描述輪廓的產生行爲,怎麼在不少高度存在的時候,最快的找到最大高度,因而就想到用TreeMap結構,用搜索二叉樹找是最快的。
package advanced_class_04; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Map.Entry; import java.util.TreeMap; public class Code_01_Building_Outline { public static class Node {//高度 位置 上/下 public boolean isUp; public int posi; public int h; public Node(boolean bORe, int position, int height) { isUp = bORe; posi = position; h = height; } } public static class NodeComparator implements Comparator<Node> { @Override public int compare(Node o1, Node o2) { if (o1.posi != o2.posi) { return o1.posi - o2.posi; } // if (o1.isUp != o2.isUp) { // return o1.isUp ? -1 : 1; // } return 0; } } public static List<List<Integer>> buildingOutline(int[][] buildings) { Node[] nodes = new Node[buildings.length * 2]; for (int i = 0; i < buildings.length; i++) { nodes[i * 2] = new Node(true, buildings[i][0], buildings[i][2]); nodes[i * 2 + 1] = new Node(false, buildings[i][1], buildings[i][2]); } Arrays.sort(nodes, new NodeComparator());//按照位置排序 //存高度,次數 TreeMap<Integer, Integer> htMap = new TreeMap<>();//高度信息 TreeMap<Integer, Integer> pmMap = new TreeMap<>();//位置信息 for (int i = 0; i < nodes.length; i++) { if (nodes[i].isUp) {//向上,加一 if (!htMap.containsKey(nodes[i].h)) { htMap.put(nodes[i].h, 1); } else { htMap.put(nodes[i].h, htMap.get(nodes[i].h) + 1); } } else {//向下,減一 if (htMap.containsKey(nodes[i].h)) { if (htMap.get(nodes[i].h) == 1) { htMap.remove(nodes[i].h); } else { htMap.put(nodes[i].h, htMap.get(nodes[i].h) - 1); } } } //收集每一個位置的最大高度 if (htMap.isEmpty()) { pmMap.put(nodes[i].posi, 0); } else { pmMap.put(nodes[i].posi, htMap.lastKey()); } } List<List<Integer>> res = new ArrayList<>(); int start = 0; int height = 0; for (Entry<Integer, Integer> entry : pmMap.entrySet()) { int curPosition = entry.getKey(); int curMaxHeight = entry.getValue(); //當前高度和以前的不一樣,開始產生輪廓線 if (height != curMaxHeight) { //不等於0證實以前已經記錄了開始,要收尾了 if (height != 0) { List<Integer> newRecord = new ArrayList<Integer>(); newRecord.add(start); newRecord.add(curPosition); newRecord.add(height); res.add(newRecord); } //等於0的話,纔剛開始生成輪廓線 start = curPosition; height = curMaxHeight; } } return res; } public static void main(String[] args) { int[][] test = new int[][]{ {1, 3, 3}, {2, 4, 4}, {5, 6, 1} }; List<List<Integer>> buildingOutLine = buildingOutline(test); for (List<Integer> list : buildingOutLine) { for (Integer i : list) { System.out.printf(i + " "); } System.out.println(); } } }
若是相同的位置下,上的高度排在前面(也能夠不這麼排),先加入低高度的,再減去高高度的,輪廓會被低高度的頂一下。
pmMap,記錄每一個位置的最大高度,由於3位置有個4落下來,因此會是2。
利用pmMap重建輪廓信息
高度不變的不用管,高度改變了證實以前的輪廓線結束,新的輪廓線開始。
中間有0的狀況
給定一個數組arr(有0、正、負),和一個整數num,求在arr中,累加和等於num的最長子數組的長度
例子:
arr = {7,3,2,1,1,7,7,7} num = 7
其中有不少的子數組累加和等於7,可是最長的子數組是{3,2,1,1},因此返回其長度4
廣泛思路:必須以每一個位置結尾的狀況下,若是求出答案,那答案必定在其中。
以100位結尾,從0~1000sum爲2000,若是發現0~17位1200,那麼18~1000就是咱們要求的以1000結尾最長數組。
操做以下:
準備一個sum每走一步就累加,準備一個map記錄第一次出現累加數據的位置,默認加入0,-1由於累加0不須要任何數據參與。
而後每走一步都把sum-aim,而後去map中找,第一次出現sum-aim的值的位置
若是有就是這個位置+1~當前位置能夠出現aim,記錄長度
若是沒有就加入到map中(注意只加第一次出現的位置,後面再碰見用樣的數都不更新)而後繼續下一步...
默認加入0,-1,是由於不錯過從0開始出現答案的機會,由於咱們的結論是查出一個位置是從這個位置開始的下一個位置開始算的。
package advanced_class_04; import java.util.HashMap; public class Code_05_LongestSumSubArrayLength { public static int maxLength(int[] arr, int aim) { if (arr == null || arr.length == 0) { return 0; } HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); map.put(0, -1); // important int len = 0; int sum = 0; for (int i = 0; i < arr.length; i++) { sum += arr[i]; if (map.containsKey(sum - aim)) { len = Math.max(i - map.get(sum - aim), len); } if (!map.containsKey(sum)) { map.put(sum, i); } } return len; } public static int[] generateArray(int size) { int[] result = new int[size]; for (int i = 0; i != size; i++) { result[i] = (int) (Math.random() * 11) - 5; } return result; } public static void printArray(int[] arr) { for (int i = 0; i != arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); } public static void main(String[] args) { int[] arr = generateArray(20); printArray(arr); System.out.println(maxLength(arr, 10)); } }
擴展:
一個數組中,只有整數,有奇數偶數,求奇數和偶數個數相等的最長子數組。
作法:把奇數改成1,偶數改成-1,把累加和爲0的最長子數組查出便可。
數組中只有1和0的話,也同理把0改成-1,計算出累加和爲0的最長子數組便可。
數組中只有0、一、2的話,求一、2數量相等的最長子數組,把2變-1,技巧同理。
思考這題,算法原型是最長累加和問題:(拆分比較麻煩)
定義數組的異或和的概念:
數組中全部的數異或起來,獲得的結果叫作數組的異或和,
好比數組{3,2,1}的異或和是,3^2^1 = 0
給定一個數組arr,你能夠任意把arr分紅不少不相容的子數組,你的目的是:
分出來的子數組中,異或和爲0的子數組最多。
請返回:分出來的子數組中,異或和爲0的子數組最可能是多少?
首先要理解下異或運算,
一、異或運算知足交換律和結合律,一組數異或結果和異或順序無關。
二、0和任何數異或都是那個數。0^n=n、n^n=0
咱們求0~i範圍最多能切出幾個異或和爲0的子數組。
而後求0~i+1的範圍。
依次求到0~n-1。
0~i,必定存在一個最優劃分,那麼i做爲最後一個部分的最後一個數,有兩種可能性。
一、i所在的部分,不是爲0的子數組
0~i最多能劃多少塊和0~i-1最多能劃多少塊,數量必定相等。(說白了就是要你和不要你有什麼區別呢)
一個dp問題,決定的時候用到了以前的算法原型。
二、i所在的部分,是爲0的子數組
k必定是左邊離i最近的異或和爲0的位置。中間不可能存在一個j~i是異或和0。
0~i異或結果是sum,那就是在找0~i-1中間,異或仍是sum的最晚的位置。那個最晚的位置的下一個位置就是k的位置。
k~i這個部分在哪呢?
就是以前有沒有出現過相同的異或和,他最晚的位置在哪裏,那個位置就是k位置。
dp在兩種決策中選最大的,就是答案。
這個變成了,找一個異或和最晚出現的位置。
出處2018年滴滴校招的原題。
理清思路:
DP,假設數組最後一個數的下標是 i,而且數組存在一個最優劃分,使得劃分的子數組個數最多,那麼 i 有兩種狀況,第一,i 所在的劃分區間異或爲 0;第二,i 所在的劃分區間,異或不爲 0。對於第一種狀況 dp[i] = dp[i-1] 的,對於第二種狀況,假設 i 的最優劃分區間是 [k,i],0 到 i 的連續異或爲 sum,只要求出一個最大的下標 k-1,使得 0 到 k-1 的異或也爲 sum 就好了
package advanced_class_04; import java.util.HashMap; public class Code_06_Most_EOR { public static int mostEOR(int[] arr) { int ans = 0; int xor = 0;//異或,英文爲exclusive OR,縮寫成xor int[] dp = new int[arr.length]; HashMap<Integer, Integer> map = new HashMap<>(); map.put(0, -1);//一個數都沒有的時候,異或和爲0 for (int i = 0; i < arr.length; i++) { xor ^= arr[i]; if (map.containsKey(xor)) {//存在證實異或和區域+1 int pre = map.get(xor); dp[i] = pre == -1 ? 1 : (dp[pre] + 1); } if (i > 0) {//拿最優狀況,相似貪心 dp[i] = Math.max(dp[i - 1], dp[i]); } //求最晚的位置,因此每次都更新 map.put(xor, i); ans = Math.max(ans, dp[i]); } return ans; } // for test public static int comparator(int[] arr) { if (arr == null || arr.length == 0) { return 0; } int[] eors = new int[arr.length]; int eor = 0; for (int i = 0; i < arr.length; i++) { eor ^= arr[i]; eors[i] = eor; } int[] mosts = new int[arr.length]; mosts[0] = arr[0] == 0 ? 1 : 0; for (int i = 1; i < arr.length; i++) { mosts[i] = eors[i] == 0 ? 1 : 0; for (int j = 0; j < i; j++) { if ((eors[i] ^ eors[j]) == 0) { mosts[i] = Math.max(mosts[i], mosts[j] + 1); } } mosts[i] = Math.max(mosts[i], mosts[i - 1]); } return mosts[mosts.length - 1]; } // for test public static int[] generateRandomArray(int maxSize, int maxValue) { int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; for (int i = 0; i < arr.length; i++) { arr[i] = (int) ((maxValue + 1) * Math.random()); } return arr; } // for test public static void printArray(int[] arr) { if (arr == null) { return; } for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); } // for test public static void main(String[] args) { int testTime = 500000; int maxSize = 300; int maxValue = 100; boolean succeed = true; for (int i = 0; i < testTime; i++) { int[] arr = generateRandomArray(maxSize, maxValue); int res = mostEOR(arr); int comp = comparator(arr); if (res != comp) { succeed = false; printArray(arr); System.out.println(res); System.out.println(comp); break; } } System.out.println(succeed ? "Nice!" : "Fucking fucked!"); } }