接着第四課的內容,加入部分第五課的內容,主要介紹樹形dp和LRUnode
給定一棵二叉樹的頭節點head,請返回最大搜索二叉子樹的大小數組
二叉樹的套路緩存
統一處理邏輯:假設以每一個節點爲頭的這棵樹,他的最大搜索二叉子樹是什麼。答案必定在其中數據結構
第一步,列出可能性(最難部分)app
一、可能來自左子樹上的某課子樹this
二、可能來自右子樹上的某課子樹spa
三、整顆都是(左右子樹都是搜索二叉樹而且左子樹最大小於該節點,右子樹最小大於該節點)設計
第二步,收集信息:3d
一、左樹最大搜索子樹大小指針
二、右樹最大搜索子樹大小
三、左樹最大二叉搜索子樹的頭部(經過查看這個頭部是否等於節點的左孩子,來判斷整個左子樹是否都是二叉搜索樹)
四、右樹最大二叉搜索子樹的頭部
五、左樹最大值
六、右樹最小值
化簡爲一個信息體:
一、左/右搜大小
二、左/右搜頭
三、左max
四、右min
無論左樹仍是右樹都存儲
一、最大搜索子樹大小
二、最大搜索子樹的頭部
三、這棵樹上的最大值和最小值
若是不理解能夠看引子題(很簡單的)
一棵樹中找最大最小
第三步,改遞歸(比較複雜)
先假設左和右都給我這樣的信息了,而後怎麼利用左邊和右邊的信息,組出來我該返回的信息。最後baseKey填什麼,搞定!
public class Code_04_BiggestSubBSTInTree { public static class Node { public int value; public Node left; public Node right; public Node(int data) { this.value = data; } } public static Node biggestSubBST(Node head) { int[] record = new int[3]; // 0->size, 1->min, 2->max return posOrder(head, record); } public static class ReturnType{ public int size; public Node head; public int min; public int max; public ReturnType(int a, Node b,int c,int d) { this.size =a; this.head = b; this.min = c; this.max = d; } } public static ReturnType process(Node head) { if(head == null) { //設置系統最大最小爲了避免干擾判斷最大最小的決策 return new ReturnType(0,null,Integer.MAX_VALUE, Integer.MIN_VALUE); } Node left = head.left; ReturnType leftSubTressInfo = process(left);//當成一個黑盒 Node right = head.right; ReturnType rightSubTressInfo = process(right); int includeItSelf = 0; if(leftSubTressInfo.head == left &&rightSubTressInfo.head == right && head.value > leftSubTressInfo.max && head.value < rightSubTressInfo.min ) { includeItSelf = leftSubTressInfo.size + 1 + rightSubTressInfo.size; } int p1 = leftSubTressInfo.size; int p2 = rightSubTressInfo.size; //解黑盒的過程 int maxSize = Math.max(Math.max(p1, p2), includeItSelf); Node maxHead = p1 > p2 ? leftSubTressInfo.head : rightSubTressInfo.head; if(maxSize == includeItSelf) { maxHead = head; } return new ReturnType(maxSize, maxHead, Math.min(Math.min(leftSubTressInfo.min,rightSubTressInfo.min),head.value), Math.max(Math.max(leftSubTressInfo.max,rightSubTressInfo.max),head.value)); } //數組實現版本 public static Node posOrder(Node head, int[] record) { if (head == null) { record[0] = 0; record[1] = Integer.MAX_VALUE; record[2] = Integer.MIN_VALUE; return null; } int value = head.value; Node left = head.left; Node right = head.right; Node lBST = posOrder(left, record); int lSize = record[0]; int lMin = record[1]; int lMax = record[2]; Node rBST = posOrder(right, record); int rSize = record[0]; int rMin = record[1]; int rMax = record[2]; record[1] = Math.min(rMin, Math.min(lMin, value)); // lmin, value, rmin -> min record[2] = Math.max(lMax, Math.max(rMax, value)); // lmax, value, rmax -> max if (left == lBST && right == rBST && lMax < value && value < rMin) { record[0] = lSize + rSize + 1; return head; } record[0] = Math.max(lSize, rSize); return lSize > rSize ? lBST : rBST; } // 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(6); head.left = new Node(1); head.left.left = new Node(0); head.left.right = new Node(3); head.right = new Node(12); head.right.left = new Node(10); head.right.left.left = new Node(4); head.right.left.left.left = new Node(2); head.right.left.left.right = new Node(5); head.right.left.right = new Node(14); head.right.left.right.left = new Node(11); head.right.left.right.right = new Node(15); head.right.right = new Node(13); head.right.right.left = new Node(20); head.right.right.right = new Node(16); printTree(head); Node bst = biggestSubBST(head); printTree(bst); } }
二叉樹中,一個節點能夠往上走和往下走,那麼從節點A總能走到節點B。
節點A走到節點B的距離爲:A走到B最短路徑上的節點個數。
求一棵二叉樹上的最遠距離
列可能性:
一、來自左子樹最長距離
二、來自右子樹最長距離
三、通過X的狀況下的最遠距離,左樹最深+右樹最深+1
收集信息:
一、最長距離
二、深度
public class Code_03_MaxDistanceInTree { public static class Node { public int value; public Node left; public Node right; public Node(int data) { this.value = data; } } public static int maxDistance(Node head) { int[] record = new int[1]; return posOrder(head, record); } public static class ReturnType{ public int maxDistance; public int h; public ReturnType(int m, int h) { this.maxDistance = m; this.h = h; } } public static ReturnType process(Node head) { if(head == null) { return new ReturnType(0,0); } ReturnType leftReturnType = process(head.left); ReturnType rightReturnType = process(head.right); int includeHeadDistance = leftReturnType.h + 1 + rightReturnType.h; int p1 = leftReturnType.maxDistance; int p2 = rightReturnType.maxDistance; int resultDistance = Math.max(Math.max(p1, p2), includeHeadDistance); int hitSelf = Math.max(leftReturnType.h, leftReturnType.h) + 1; return new ReturnType(resultDistance, hitSelf); } public static int posOrder(Node head, int[] record) { if (head == null) { record[0] = 0; return 0; } int lMax = posOrder(head.left, record); int maxFromLeft = record[0]; int rMax = posOrder(head.right, record); int maxFromRight = record[0]; int curNodeMax = maxFromLeft + maxFromRight + 1; record[0] = Math.max(maxFromLeft, maxFromRight) + 1; return Math.max(Math.max(lMax, rMax), curNodeMax); } public static void main(String[] args) { Node head1 = new Node(1); head1.left = new Node(2); head1.right = new Node(3); head1.left.left = new Node(4); head1.left.right = new Node(5); head1.right.left = new Node(6); head1.right.right = new Node(7); head1.left.left.left = new Node(8); head1.right.left.right = new Node(9); System.out.println(maxDistance(head1)); Node head2 = new Node(1); head2.left = new Node(2); head2.right = new Node(3); head2.right.left = new Node(4); head2.right.right = new Node(5); head2.right.left.left = new Node(6); head2.right.right.right = new Node(7); head2.right.left.left.left = new Node(8); head2.right.right.right.right = new Node(9); System.out.println(maxDistance(head2)); } }
擴充:若是是計算兩個固定節點a~b的距離,須要找出他們的最近公共祖先,而後計算a~公共祖先+b~公共祖先。
一個公司的上下節關係是一棵多叉樹,這個公司要舉辦晚會,你做爲組織者已經摸清了你們的心理:一個員工的直接上級若是到場,這個員工確定不會來。每一個員工都有一個活躍度的值,決定誰來你會給這個員工發邀請函,怎麼讓舞會的氣氛最活躍?返回最大的活躍值。
舉例:
給定一個矩陣來表述這種關係
matrix =
{
1,6
1,5
1,4
}
這個矩陣的含義是:
matrix[0] = {1 , 6},表示0這個員工的直接上級爲1,0這個員工本身的活躍度爲6
matrix[1] = {1 , 5},表示1這個員工的直接上級爲1(他本身是這個公司的最大boss),1這個員工本身的活躍度爲5
matrix[2] = {1 , 4},表示2這個員工的直接上級爲1,2這個員工本身的活躍度爲4
爲了讓晚會活躍度最大,應該讓1不來,0和2來。最後返回活躍度爲10
可能性
一、X來,活躍度就是x活躍度+x1不來+x2不來+x3不來的總和。
二、X不來,活躍度就是x1/x2/x3來和不來中選最大的總和。
收集信息:
一、一棵樹在頭結點來的活躍度
二、一棵樹在頭結點不來的活躍度
public class Code_04_MaxHappy { public static class Node{ public int happy; public ArrayList<Node> nexts; public Node(int happy){ this.happy = happy; nexts = new ArrayList<Node>(); } } public static class ReturnData{ public int comeHappy; public int notComeHappy; public ReturnData(int c,int nc){ comeHappy = c; notComeHappy = nc; } } public static ReturnData process(Node head){ int comeHappy = head.happy; int notComeHappy = 0; for (int i = 0;i!=head.nexts.size();i++){ ReturnData data = process(head.nexts.get(i)); comeHappy += data.notComeHappy; notComeHappy += Math.max(data.notComeHappy,data.comeHappy); } return new ReturnData(comeHappy,notComeHappy); } public static int calcMaxHappy(Node head){ ReturnData data = process(head); return Math.max(data.comeHappy, data.notComeHappy); } //下面是用數組結構去求 public static int maxHappy(int[][] matrix) { int[][] dp = new int[matrix.length][2]; boolean[] visited = new boolean[matrix.length]; int root = 0; for (int i = 0; i < matrix.length; i++) { if (i == matrix[i][0]) { root = i; } } process(matrix, dp, visited, root); return Math.max(dp[root][0], dp[root][1]); } public static void process(int[][] matrix, int[][] dp, boolean[] visited, int root) { visited[root] = true; dp[root][1] = matrix[root][1]; for (int i = 0; i < matrix.length; i++) { if (matrix[i][0] == root && !visited[i]) { process(matrix, dp, visited, i); dp[root][1] += dp[i][0]; dp[root][0] += Math.max(dp[i][1], dp[i][0]); } } } public static void main(String[] args) { int[][] matrix = { { 1, 8 }, { 1, 9 }, { 1, 10 } }; System.out.println(maxHappy(matrix)); } }
上述全部題目都叫樹形dp。(列可能性)
思路:小樹計算完,再算父親樹。
summary(總結)
一、分析可能性(先計算小樹,再計算大樹)
二、列信息全集,定下返回值結構。
三、編寫代碼的時候,默認每顆子樹都給你這樣的信息,而後看拿到這些子樹信息後怎麼加工出父的信息。
四、basekey要單獨考慮一下,做爲最簡單的狀況,要給父返回啥,不至於讓他干擾。
判斷一棵樹是不是平衡二叉樹
public class c04_04IsBalancedTree { public static class Node { public int value; public Node left; public Node right; public Node(int data) { this.value = data; } } public static class ReturnData{ public boolean isBalance; public int level; public ReturnData(boolean isBalance, int level) { this.isBalance = isBalance; this.level = level; } } public static ReturnData process(Node head){ if(head == null){ return new ReturnData(true,0); } //若是左子樹或者右子樹返回了他們不是平衡的,那整體也不會是平衡的 ReturnData lRData = process(head.left); if(!lRData.isBalance){ return new ReturnData(true,0); } ReturnData rRData = process(head.right); if(!rRData.isBalance){ return new ReturnData(true,0); } if(Math.abs(lRData.level - rRData.level) > 1){ return new ReturnData(true,0); } return new ReturnData(true,Math.max(lRData.level,rRData.level)+1); } 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); System.out.println(process(head).isBalance); } }
數據結構設計題(LeetCode中等難度)難在code上
設計能夠變動的緩存結構(LRU)(常用的留下)
【題目】
設計一種緩存結構,該結構在構造時肯定大小,假設大小爲K,並有兩個功能:
set(key,value):將記錄(key,value)插入該結構。
get(key):返回key對應的value值。
【要求】
1.set和get方法的時間複雜度爲O(1)。
2.某個key的set或get操做一旦發生,認爲這個key的記錄成了最常用的。
3.當緩存的大小超過K時,移除最不常用的記錄,即set或get最久遠的。
【舉例】
假設緩存結構的實例是cache,大小爲3,並依次發生以下行爲:
1.cache.set("A",1)。最常用的記錄爲("A",1)。
2.cache.set("B",2)。最常用的記錄爲("B",2),("A",1)變爲最不常常的。
3.cache.set("C",3)。最常用的記錄爲("C",2),("A",1)仍是最不常常的。
4.cache.get("A")。最常用的記錄爲("A",1),("B",2)變爲最不常常的。
5.cache.set("D",4)。大小超過了3,因此移除此時最不常用的記錄("B",2),加入記錄 ("D",4),而且爲最常用的記錄,而後("C",2)變爲最不常用的記錄
思路:hash表(key,Node<key,value>內存地址)+定製的雙向鏈表(尾加頭出)
加入的時候先把節點從環境分離,掛到最後,再重連其餘節點。
有一個size記錄大小,在刪的時候能夠經過head指針把優先級最低的刪除,再根據key到hash裏面尋找並完全刪除。
public class Code_02_LRU { public static class Node<K,V> { public K key; public V value; public Node<K,V> last; public Node<K,V> next; public Node(K key,V value) { this.key = key; this.value = value; } } //定製的雙向鏈表 public static class NodeDoubleLinkedList<K,V> { private Node<K,V> head; private Node<K,V> tail; public NodeDoubleLinkedList() { this.head = null; this.tail = null; } public void addNode(Node<K,V> newNode) { if (newNode == null) { return; } if (this.head == null) { this.head = newNode; this.tail = newNode; } else {//最新的添加到尾部 this.tail.next = newNode; newNode.last = this.tail;//新節點的前一個是以前的尾部 this.tail = newNode; } } //操做節點後把結點調整在尾部 public void moveNodeToTail(Node<K,V> node) { if (this.tail == node) { return; } //先把節點從環境分離 if (this.head == node) { this.head = node.next; this.head.last = null; } else {//中間的廣泛節點 node.last.next = node.next; node.next.last = node.last; } node.last = this.tail; node.next = null; this.tail.next = node; this.tail = node; } //容量滿了刪除最不常常操做的數 public Node<K,V> removeHead() { if (this.head == null) { return null; } Node<K,V> res = this.head; if (this.head == this.tail) {//只有一個節點 this.head = null; this.tail = null; } else { this.head = res.next; res.next = null; this.head.last = null; } return res; } } public static class MyCache<K, V> { //經過key能夠找到Node private HashMap<K, Node<K,V>> keyNodeMap; private NodeDoubleLinkedList<K,V> nodeList; private int capacity; public MyCache(int capacity) { if (capacity < 1) { throw new RuntimeException("should be more than 0."); } this.keyNodeMap = new HashMap<K, Node<K,V>>(); this.nodeList = new NodeDoubleLinkedList<K,V>(); this.capacity = capacity; } public V get(K key) { if (this.keyNodeMap.containsKey(key)) { Node<K,V> res = this.keyNodeMap.get(key); this.nodeList.moveNodeToTail(res); return res.value; } return null; } public void set(K key, V value) { if (this.keyNodeMap.containsKey(key)) { Node<K,V> node = this.keyNodeMap.get(key); node.value = value; this.nodeList.moveNodeToTail(node); } else {//沒有就新增 Node<K,V> newNode = new Node<K,V>(key,value); this.keyNodeMap.put(key, newNode); this.nodeList.addNode(newNode); if (this.keyNodeMap.size() == this.capacity + 1) { this.removeMostUnusedCache(); } } } private void removeMostUnusedCache() { Node<K,V> removeNode = this.nodeList.removeHead();//取出優先級最低的 K removeKey = removeNode.key; this.keyNodeMap.remove(removeKey); } } public static void main(String[] args) { MyCache<String, Integer> testCache = new MyCache<String, Integer>(3); testCache.set("A", 1); testCache.set("B", 2); testCache.set("C", 3); System.out.println(testCache.get("B")); System.out.println(testCache.get("A")); testCache.set("D", 4); System.out.println(testCache.get("D")); System.out.println(testCache.get("C")); } }
就是有限的幾個結構組成出來。(鏈表、hash)
自定義的Node,Map會存內存地址(8字節)。
回去看一下LFU。