数据归纳法 用于证明断言对所有自然数成立
证明对于N=1成立 
证明N>1时:如果对于N-1成立,那么对于N成立 
 
递归控制 如何证明递归函数正确执行?
递归书写方法
严格定义递归函数作用,包括函数,返回值,Side-effect(副作用) 
先写一般 ,再后特殊(如,值=0的时候 return null) 到前面 
每次调用必须缩小问题规模 
每次规模缩小程度必须为1 ,因为数据归纳法中本来就是1 
 
例1:链表创建 1 2 3 4 5 6 7 8 9 10 11 public  Node createLinkedList (List<Integer> data)           if  (data.isEmpty()) {         return  null ;     }          Node firstNode = new  Node(data.get(0 ));     Node headOfSublist = createLinkedList(data.subList(1 , data.size()));     firstNode.setNext(headOfSublist);     return  firstNode; } 
例2:链表反转 
1 2 3 4 5 6 7 8 9 10 11 public  Node reverseLinkedList (Node head)           if  (head == null  || head.getNext() == null ) {         return  head;     }     Node newHead = reverseLinkedList(head.getNext());     head.getNext().setNext(head);     head.setNext(null );     return  newHead; } 
例3:列出所有组合 
combinations([1,2,3,4],2): 4C2 = 6个
选1 -> combinations([2,3,4],1) 
不选1 -> combinations([2,3,4],2) 
 
 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public  void  combinations (List<Integer> selected, List<Integer> data, int  n)           if  (n == 0 ) {                  for  (Integer integer : selected) {             System.out.print(integer);             System.out.print(" " );         }         System.out.println();         return ;     }     if  (data.isEmpty()) {         return ;     }          selected.add(data.get(0 ));     combinations(selected, data.subList(1 , data.size()), n - 1 );          selected.remove(selected.size() - 1 );     combinations(selected, data.subList(1 , data.size()), n); } 
递归的缺点 Stack
函数调用开销 
Stack Overflow! 
问题规模:n million 栈大小? 
 
循环控制 不要尝试递归 -> 非递归 
循环不变式(loop invariant) 
循环书写方法 
定义循环不变式,并在循环体每次结束后保持 循环不变 
先写一般 ,后特殊  
每次必须向前推进循环不变式中设计的变量值,从而缩小问题规模 
每次规模缩小程度必须为1  
 
例1:链表反转 递归的时候是以第一个元素为例;思考循环时先拿出中间元素
这句话就是循环不变式,在循环的过程中一直成立.
满足循环不变式的初始值,和最后的值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  Node reverseLinkedList (Node head)      Node newHead = null ;     Node curHead = head;                    while  (curHead != null ) {         Node next = curHead.getNext();         curHead.setNext(newHead);         newHead = curHead;         curHead = next;     }     return  newHead; } 
循环创建链表 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  Node createLargeLinkedList (int  size)     Node prev = null ;     Node head = null ;     for  (int  i = 1  ; i <=size ; i++) {         Node node = new  Node(i);         if  (prev!=null ){             prev.setNext(node);         }else  {             head = node;         }         prev = node;     }     return  head; } 
例2:链表中delete_if 数值为2的节点删除
还是从中间思考
头节点没有previous怎么办?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public  Node deleteIfEquals (Node head, int  value)      while  ((head != null  && head.getValue() == value)) {         head = head.getNext();     }     if  (head == null ) {         return  null ;     }     Node prev = head;          while  (prev.getNext() != null ) {         if  (prev.getNext().getValue() == value) {                          prev.setNext(prev.getNext().getNext());         } else  {             prev = prev.getNext();         }     }     return  head; } 
边界控制 例:二分查找 
在有序数组中查找元素k,返回k所在下标 
binarySearch([ 15) == 3 
 
二分查找思路:
规定要查找的值k可能在的数组arr内下表区间 a,b 
计算区间a,b的中间点m 
若k < arr[m],将区间缩小为a,m , 继续二分查找 
若k > arr[m],将区间缩小为m,b , 继续二分查找 
若k == arr[m],则找到元素位置m 
 
二分查找很难写! 边界变量很多
  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public  int  binarySearch (int [] arr, int  k)      int  a = 0 ;     int  b = arr.length;               while  (a < b) {                  int  m = a + (b - a) / 2 ;                                    if  (k < arr[m]) {              b = m;         } else  if  (k > arr[m]) {             a = m + 1 ;         } else  {             return  m;         }     }     return  -1 ; } 
数据结构 
列表 
数组 
链表 
队列,栈 
 
树 
二叉树 
搜索树 
堆/优先队列 
 
栈/队列/优先队列 
push(1);push(3);push(2);pop();pop();pop(); 
栈:2,3,1 
队列:1,3,2 
优先队列:1,2,3 
 
Map<K,V>/Set<K>
HashMap/HashSet -> K.hashCode() 
TreeMap/TreeSet -> K implement Comparable 
 
图 
无向图 
有向图 
有向无环图 
 
图的算法 
深度优先遍历 
广度优先遍历 
拓扑排序 
最短路径/最小生成树 
 
树 二叉树的遍历 
前序遍历 : 根->左->右 
中序遍历 : 左->根->右 
后序遍历 :左->右->根 
 
后序遍历:DGEBFCA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  void  preOrder (TreeNode root)      if  (root == null ) return ;     System.out.print(root.getValue());     preOrder(root.getLeft());     preOrder(root.getRight()); } public  void  inOrder (TreeNode root)      if  (root == null ) return ;     inOrder(root.getLeft());     System.out.print(root.getValue());     inOrder(root.getRight()); } public  void  postOrder (TreeNode root)      if  (root == null ) return ;     postOrder(root.getLeft());     postOrder(root.getRight());     System.out.print(root.getValue()); } 
例1:根据前序中序构造二叉树 前序:ABDEGCF
求后序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  TreeNode createTree (String preOrder, String inOrder)      if  (preOrder.isEmpty()){         return  null ;     }     char  rootValue = preOrder.charAt(0 );     int  rootIndex = inOrder.indexOf(rootValue);     TreeNode root = new  TreeNode(rootValue);     TreeNode left = createTree(preOrder.substring(1 , rootIndex+1 ), inOrder.substring(0 , rootIndex));     root.setLeft(left);     TreeNode right = createTree(preOrder.substring(rootIndex+1 ), inOrder.substring(rootIndex + 1 ));     root.setRight(right);     return  root; } 
那么如果想不不创建树,直接输出postOrder的结果呢?
1 2 3 4 5 6 7 8 9 10 public  String postOrder (String preOrder, String inOrder)      if  (preOrder.isEmpty()) {         return  "" ;     }     char  rootValue = preOrder.charAt(0 );     int  rootIndex = inOrder.indexOf(rootValue);     String left = postOrder(preOrder.substring(1 , rootIndex + 1 ), inOrder.substring(0 , rootIndex));     String right = postOrder(preOrder.substring(rootIndex + 1 ), inOrder.substring(rootIndex + 1 ));     return  left + right + rootValue; } 
例2:寻找中序遍历时的下一个节点 
注意箭头是双向.实际应用于搜索树 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public  TreeNode next (TreeNode node)      if  (node == null ) {         return  null ;     }     if  (node.getRight() != null ) {         return  first(node.getRight());     } else  {         while  (node.getParent() != null  && node.getParent().getRight() == node) {             node = node.getParent();         }         return  node.getParent();     } } public  TreeNode first (TreeNode node)      if  (node == null ) return  null ;     TreeNode curNode = node;     while  (curNode.getLeft() != null ) {         curNode = curNode.getLeft();     }     return  curNode; } 
算法复杂度 复杂度从大到小
1 O(N!) > O(2 ^ N) > O(N ^ 2) > O(NlogN) > O(N) > O(longN)... 
什么样的代码会分别产生这些复杂度?
O(N^2) 
1 2 3 4 5 for  (int  i = 0 ; i < n; i++) {    for  (int  j = i; j < n; j++) { 	...     } } 
for里面还有一个for
O(NlongN) 
1 f([...]) -> f([..]) + f([..]) 
归并排序,快速排序(平均,因为是从中间选,看运气) 
 
O(logN) 
算法的组合 例:区间合并 
一个算法做完处理后,继续用另一个算法
区间合并问题
给定一些列区间,合并他们 
输入:[1,3],[4,7],[2,6],[9,10],[8,9] 
输出:[1,7],[8,10] 
解法
对区间进行排序,左端点从小到大排 
从左到右扫描,循环不变式,记录之前看到的所有最大右端点
现在看到的区间,比之前看到的最大区间小 
现在看到的区间,比之前看到的最大区间中 
现在看到的区间,比之前看到的最大区间大 
 
 
 
 
复杂度
排序 O(NlogN) 
扫描已排序的列表 O(N) 
总复杂度 ?  O(NlogN)+O(N) =  O(NlogN) 
 
 
 
递归的算法复杂度 
每个节点都访问一次吗? 
输出多少东西? 
每个节点访问的时间是常数吗?