算法(1)

  1. 單例

    定義:保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。

    1. 餓漢模式

      public class Singleton {
          private static Singleton instance = new Singleton();
          private Singleton() {}
          public static Singleton getInstance() {
              return instance;
          }
      }  
      

      這種方式在類加載時(shí)就完成了初始化,所以類加載較慢,但獲取對(duì)象的速度快。 這種方式基于類加載機(jī)制避免了多線程的同步問題,但是也不能確定有其他的方式(或者其他的靜態(tài)方法)導(dǎo)致類裝載,這時(shí)候初始化instance顯然沒有達(dá)到懶加載的效果。

    2. 懶漢模式(線程不安全)

      public class Singleton {  
            private static Singleton instance;  
            private Singleton (){
            }   
            public static Singleton getInstance() {  
            if (instance == null) {  
                instance = new Singleton();  
            }  
            return instance;  
            }  
      }  
      

      懶漢模式申明了一個(gè)靜態(tài)對(duì)象,在用戶第一次調(diào)用時(shí)初始化,雖然節(jié)約了資源,但第一次加載時(shí)需要實(shí)例化,反映稍慢一些,而且在多線程不能正常工作。

      多線程不能正常工作?

      instance = new Singleton();//這一步驟出現(xiàn)問題,可以分解為3個(gè)步驟
      1. memory = allocate(); //1.分配對(duì)象的內(nèi)存空間
      2. ctorInstance(memory);//2.初始化對(duì)象
      3. instance = memory;//3.設(shè)置instance指向剛才分配的內(nèi)存地址
      //注意,上面2和3是可能發(fā)生指令重排序的,指向地址的時(shí)候可能還沒初始化,要加synchronized,但是這是錯(cuò)誤的優(yōu)化,還要加上volatile
      
    3. 懶漢模式(線程安全)

      public class Singleton {  
            private static Singleton instance;  
            private Singleton (){
            }
            public static synchronized Singleton getInstance() {  
            if (instance == null) {  
                instance = new Singleton();  
            }  
            return instance;  
            }  
       }  
      

      這種寫法能夠在多線程中很好的工作,但是每次調(diào)用getInstance方法時(shí)都需要進(jìn)行同步,造成不必要的同步開銷,而且大部分時(shí)候我們是用不到同步的,所以不建議用這種模式。

    4. 雙重檢查模式 (DCL)

      public class Singleton {  
            private volatile static Singleton instance;  
            private Singleton (){
            }   
            public static Singleton getInstance() {  
            if (instance== null) {  
                synchronized (Singleton.class) {  
                if (instance== null) {  
                    instance= new Singleton();  
                }  
               }  
           }  
           return singleton;  
           }  
      }  
      

      這種寫法在getSingleton方法中對(duì)singleton進(jìn)行了兩次判空,第一次是為了不必要的同步,第二次是在singleton等于null的情況下才創(chuàng)建實(shí)例。在這里用到了volatile關(guān)鍵字,防止指令重排序。在這里使用volatile會(huì)或多或少的影響性能,但考慮到程序的正確性,犧牲這點(diǎn)性能還是值得的。 DCL優(yōu)點(diǎn)是資源利用率高,第一次執(zhí)行g(shù)etInstance時(shí)單例對(duì)象才被實(shí)例化,效率高。缺點(diǎn)是第一次加載時(shí)反應(yīng)稍慢一些,在高并發(fā)環(huán)境下也有一定的缺陷,雖然發(fā)生的概率很小。DCL雖然在一定程度解決了資源的消耗和多余的同步,線程安全等問題,但是他還是在某些情況會(huì)出現(xiàn)失效的問題,也就是DCL失效,在《java并發(fā)編程實(shí)踐》一書建議用靜態(tài)內(nèi)部類單例模式來替代DCL。

    5. 靜態(tài)內(nèi)部類單例模式

      public class Singleton { 
          private Singleton(){
          }
          public static Singleton getInstance(){  
              return SingletonHolder.sInstance;  
          }  
          private static class SingletonHolder {  
              private static final Singleton sInstance = new Singleton();  
          }  
      } 
      

      第一次加載Singleton類時(shí)并不會(huì)初始化sInstance,只有第一次調(diào)用getInstance方法時(shí)虛擬機(jī)加載SingletonHolder 并初始化sInstance ,這樣不僅能確保線程安全也能保證Singleton類的唯一性,所以推薦使用靜態(tài)內(nèi)部類單例模式。

    6. 枚舉單例

      public enum Singleton {  
           INSTANCE;  
           public void doSomeThing() {  
           }  
       }  
      
      

      默認(rèn)枚舉實(shí)例的創(chuàng)建是線程安全的,并且在任何情況下都是單例,上述講的幾種單例模式實(shí)現(xiàn)中,有一種情況下他們會(huì)重新創(chuàng)建對(duì)象,那就是反序列化,將一個(gè)單例實(shí)例對(duì)象寫到磁盤再讀回來,從而獲得了一個(gè)實(shí)例。反序列化操作提供了readResolve方法,這個(gè)方法可以讓開發(fā)人員控制對(duì)象的反序列化。在上述的幾個(gè)方法示例中如果要杜絕單例對(duì)象被反序列化是重新生成對(duì)象,就必須加入如下方法:

      private Object readResolve() throws ObjectStreamException{
      return singleton;
      

      枚舉單例的優(yōu)點(diǎn)就是簡(jiǎn)單,但是大部分應(yīng)用開發(fā)很少用枚舉,可讀性并不是很高,不建議用。

    7. 使用容器實(shí)現(xiàn)單例模式

      public class SingleTon {//系統(tǒng)服務(wù)就用的這種 getSystemService()
          private static Map<String , Object> mSingleMap = new HashMap<>() ;
          private SingleTon(){
          }
      
          /**
           * 用靜態(tài)的方式把 SingleTon對(duì)象初始化好了
           */
          static {
              mSingleMap.put("activity_manager" , new SingleTon()) ;
          }
      
          public static Object getService(String serviceName){
              return mSingleMap.get(serviceName) ;
          }
      }
      

      用SingletonManager 將多種的單例類統(tǒng)一管理,在使用時(shí)根據(jù)key獲取對(duì)象對(duì)應(yīng)類型的對(duì)象。這種方式使得我們可以管理多種類型的單例,并且在使用時(shí)可以通過統(tǒng)一的接口進(jìn)行獲取操作,降低了用戶的使用成本,也對(duì)用戶隱藏了具體實(shí)現(xiàn),降低了耦合度。

  2. 反轉(zhuǎn)鏈表

    package leetcode;
    
    public class ReverseLinkedList {
        /**
         * 206. Reverse Linked List
         time : O(n);
         space : O(1);
         * @param head
         * @return
         * 思路是在原鏈表之前建立一個(gè)空的newHead(pre),因?yàn)槭坠?jié)點(diǎn)會(huì)變,然后從head開始,將之后的一個(gè)節(jié)點(diǎn)移到       * newHead(pre)之后,重復(fù)此操作直到head成為末節(jié)點(diǎn)為止
         */
        //看這個(gè)
        public static ListNode reverseList(ListNode head) {
            //if(head==null) return head;這樣寫也行,下面一行注釋掉也行
            if (head == null || head.next == null) return head;
            ListNode pre = null;
            while (head != null) {
                //臨時(shí)節(jié)點(diǎn)暫存當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)
                ListNode temp = head.next;
                //當(dāng)前節(jié)點(diǎn)指向他前面的節(jié)點(diǎn)
                head.next = pre;
                //然后變換節(jié)點(diǎn)
                pre = head;
                head = temp;
            }
            return pre;
        }
      //遞歸  
     class Solution {
        public ListNode reverseList(ListNode head) {
            if(head==null||head.next==null) return head;
            ListNode last=reverseList(head.next);//最后last是5  head是1   last指向前一個(gè)last
            head.next.next=head;
            head.next=null;
            return last;
        }
    }
    
  3. 字符串轉(zhuǎn)整數(shù)

    package leetcode;
    public class StringtoInteger {
        /**
         * time : O(n)
         * space : O(1)
         * @param str
         * @return
         */
        public int myAtoi(String str) {
            if (str == null || str.length() == 0) return 0;
            //去掉前尾的空白符
            str = str.trim();
            if(str.length()==0) return 0;
            char firstChar = str.charAt(0);
            int sign = 1;//注意是1不是0 不然若沒有‘+’‘-’,那么就res*sign=0
            int start = 0;
            long res = 0;
            if (firstChar == '+') {
                //判斷+ —性
                sign = 1;
                //從下一位符號(hào)開始判斷
                start++;
            } else if (firstChar == '-') {
                sign = -1;
                start++;
            }
            for (int i = start; i < str.length(); i++) {
                //Character.isDigit()判斷是不是一個(gè)數(shù)字
                if (!Character.isDigit(str.charAt(i))) {
                    //不是數(shù)字
                    return (int) res * sign;
                }
                //-143
                //0*10+1=1
                //1*10+4=14
                //14*10+3=143
    //charAt(i)是字符串中的第i個(gè)字符,s.charAt(i)就是S中的第i個(gè)字符,因?yàn)樽址际怯肁SCII碼存儲(chǔ)的,存儲(chǔ)
    //的事ASCII碼值,用s.charAt(i)減去字符‘0’,就是用s.charAt(i)的碼值減去‘0’的碼值,得到的值干好就是s
    //中第i個(gè)字符的十進(jìn)制值。
                res = res * 10 + str.charAt(i) - '0';
                //越界
                if (sign == 1 && res > Integer.MAX_VALUE) return  Integer.MAX_VALUE;
                if (sign == -1 && res > Integer.MAX_VALUE) return Integer.MIN_VALUE;
            }
           return (int) (res*sign);//注意要加括號(hào),不加括號(hào)的話,res要放在前面
        }
    }
    
  4. 整數(shù)翻轉(zhuǎn)

    package leetcode;
    
    public class ReverseInteger {
        /**
         * 7. Reverse Integer
         * Reverse digits of an integer.
         Example1: x = 123, return 321
         Example2: x = -123, return -321
         int :
         -2147483648 ~ 2147483647
         corner case : 越界
         time : O(n);
         space : O(1);
         * @param x
         * @return
         */
        
    // 1.取模運(yùn)算多見于計(jì)算機(jī)領(lǐng)域,取余運(yùn)算一般用于數(shù)學(xué)領(lǐng)域。
    // 2.取模運(yùn)算(取余運(yùn)算)計(jì)算步驟
    //  a、求整數(shù)商
    //  c=a/b
    //  b、求模(余數(shù))
    //  r=a-c*b
    //兩者不同點(diǎn):取模運(yùn)算 c 向負(fù)無窮遠(yuǎn)處取整,取余運(yùn)算 c 向 0 方向取整。
    //結(jié)論:a 為正整數(shù)時(shí),取模運(yùn)算和取余運(yùn)算結(jié)果相同;a 為負(fù)整數(shù)時(shí),兩者結(jié)果不同。
        public static int reverse(int x) {
            long res = 0;
            while (x != 0) {
                res = res * 10 + x % 10;
                x /= 10;
                if (res > Integer.MAX_VALUE || res < Integer.MIN_VALUE) return 0;
            }
            return (int)res;
        }
    }
    
  5. 兩個(gè)鏈表相加

    package leetcode;
    
    public class AddTwoNumbers {
        /**
         Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
         Output: 7 -> 0 -> 8
         time : O(n)
         space : O(n)
         * @param l1
         * @param l2
         * @return
         */
         public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
            ListNode dummyNode = new ListNode(0);
            ListNode cur = dummyNode;
            int sum = 0;
            while (l1 != null || l2 != null) {
                if(l1 != null) {
                    sum += l1.val;
                    l1 = l1.next;
                }
                if(l2 != null) {
                    sum += l2.val;
                    l2 = l2.next;
                }
                cur.next = new ListNode(sum % 10);//真正存入的結(jié)果值
                sum /= 10;//結(jié)果0、1,相當(dāng)于進(jìn)位,結(jié)果用于下次的鏈表值相加
                cur = cur.next;
            }
             //用于判斷最后一次的結(jié)果,是否要進(jìn)位+1
            if(sum == 1) {
                cur.next = new ListNode(1);
            }
            return dummyNode.next;//因?yàn)閐ummyNode是不動(dòng)的,一直==0,cur是往后遍歷的,結(jié)果是從cur.next開始放的
        }
    }
    
  6. 二叉樹前序,中序,后序 ,層序,鋸齒遍歷

    1. 前序迭代

      package leetcode;
      
      import java.util.ArrayList;
      import java.util.List;
      import java.util.Stack;
      
      public class BinaryTreePreorderTraversal {
          //根左右
        //方法一:遞歸
          public static List<Integer> preorderTraversal(TreeNode root) {
              List<Integer> res = new ArrayList<>();
              if (root == null) return res;
              helper(res, root);
              return res;
          }
      
          public static void helper(List<Integer> res, TreeNode root) {
              if (root == null) return;
              res.add(root.val);
              helper(res, root.left);
              helper(res, root.right);
          }
        //方法二:迭代
          public static List<Integer> preorderTraversal2(TreeNode root) {
              List<Integer> res = new ArrayList<>();
              if (root == null) return res;
              Stack<TreeNode> stack = new Stack<>();
              stack.push(root);
              while (!stack.isEmpty()) {
                  TreeNode cur = stack.pop();
                  //記得先讓右子樹入棧,棧是先進(jìn)后出的
                  if (cur.right != null) stack.push(cur.right);
                  if (cur.left != null) stack.push(cur.left);
                  res.add(cur.val);
              }
              return res;
          }
      }
      
    2. 中序

      package leetcode;
      import java.util.ArrayList;
      import java.util.List;
      import java.util.Stack;
      public class BinaryTreeInorderTraversal {
          /**
           * 94. Binary Tree Inorder Traversal
           * Given a binary tree, return the inorder traversal of its nodes' values.
           * time : O(n)
           * space : O(n)
           * @param root
           * @return
           */
          //方法一:遞歸
          //左中右
          public static List<Integer> inorderTraversal(TreeNode root) {
             List<Integer> res = new ArrayList<>();
             if (root == null) return res;
             helper(res, root);
             return res;
          }
      
          public static void helper(List<Integer> res, TreeNode root) {
              if (root == null) return;
              helper(res, root.left);
              res.add(root.val);
              helper(res, root.right);
          }
        //方法二:迭代
          public static List<Integer> inorderTraversal2(TreeNode root) {
              List<Integer> res = new ArrayList<>();
              if (root == null) return res;
              Stack<TreeNode> stack = new Stack<>();
              TreeNode cur = root;//注意中序遍歷進(jìn)入循環(huán)前是沒有入棧操作的,它需要不斷的順著根節(jié)點(diǎn)找下面的節(jié)點(diǎn),所以首先根節(jié)點(diǎn)不能為空
              while (cur != null || !stack.isEmpty()) {
                  while (cur != null) {
                      stack.push(cur);
                      cur = cur.left;//找到最左節(jié)點(diǎn)
                  }
                  cur = stack.pop();//1
                  res.add(cur.val);//左根右:自身->右子節(jié)點(diǎn)
                  cur = cur.right;//當(dāng)某個(gè)節(jié)點(diǎn)的右子節(jié)點(diǎn)為null后,那么需要從棧中彈出新的節(jié)點(diǎn)繼續(xù)遍歷(1處),所以||stack!=null,當(dāng)cur==null且棧空,說明中序遍歷結(jié)束
              }
              return res;
          }
      }
      
    3. 后序

      package leetcode;
      
      import java.util.ArrayList;
      import java.util.LinkedList;
      import java.util.List;
      import java.util.Stack;
      
      public class BinaryTreePostorderTraversal {
          /**
           * 145. Binary Tree Postorder Traversal
           *
           * time : O(n);
           * space : O(n);
           * @param root
           * @return
           */
          //方法一:遞歸
          //左右根
          public static List<Integer> postorderTraversal(TreeNode root) {
              List<Integer> res = new ArrayList<>();
              if (root == null) return res;
              helper(res, root);
              return res;
          }
          public static void helper(List<Integer> res, TreeNode root) {
              if (root == null) return;
              helper(res, root.left);
              helper(res, root.right);
              res.add(root.val);
          }
        //方法二:迭代
          public static List<Integer> postorderTraversal2(TreeNode root) {
              LinkedList<Integer> res = new LinkedList<>();
              if (root == null) return res;
              Stack<TreeNode> stack = new Stack<>();
              stack.push(root);
              while (!stack.isEmpty()) {
                  TreeNode cur = stack.pop();
                  res.addFirst(cur.val);//加在前面addFirst是LinkedList的方法(在列表首部添加元素),ArrayList的方法是add(0,cur.val);
                  if (cur.left != null) stack.push(cur.left);//左
                  if (cur.right != null) stack.push(cur.right);//右
                  //res.addFirst(cur.val);放在這里也一樣,其實(shí)它就是模擬了棧的功能//根
              }
              return res;
          }
          
        //雙棧法,如果要求直接輸出,不是返回List<Integer> ,可以用上
          public List<Integer> postorderTraversal(TreeNode root) {
              List<Integer> res = new ArrayList<>();
              if(root==null) return res;
              Stack<TreeNode> stack = new Stack();
              Stack<TreeNode> stack2 = new Stack();
              stack.push(root);
              while(!stack.isEmpty()){
                  TreeNode cur = stack.pop();
                  if(cur.left!=null) stack.push(cur.left);
                  if(cur.right!=null) stack.push(cur.right);
                  stack2.push(cur);
              }
              while(!stack2.isEmpty()){
                  res.add(stack2.pop().val);
              }
              return res;
          }
      }
      
    4. 層序

      package leetcode;
      
      import org.omg.PortableServer.LIFESPAN_POLICY_ID;
      import java.util.ArrayList;
      import java.util.LinkedList;
      import java.util.List;
      import java.util.Queue;
      
      public class BinaryTreeLevelOrderTraversal {
      
          /**
           * 102. Binary Tree Level Order Traversal
                3
               / \
              9  20
            /  \
           15   7
           [
           [3],
           [9,20],
           [15,7]
           ]
           time : O(n);
           space : O(n);
           * @param root
           * @return
           */
        //https://www.bilibili.com/video/BV1xa411A76q?p=18手畫圖解版+代碼p18
          public static List<List<Integer>> levelOrder(TreeNode root) {
              List<List<Integer>> res = new ArrayList<>();
              if (root == null) return res;
              Queue<TreeNode> queue = new LinkedList<>();
              queue.offer(root);
              while (!queue.isEmpty()) {
                  int size = queue.size();
                  List<Integer> list = new ArrayList<>();
                  for (int i = 0; i < size; i++) {
                      TreeNode cur = queue.poll();
                      if (cur.left != null) queue.offer(cur.left);
                      if (cur.right != null) queue.offer(cur.right);
                      list.add(cur.val);
                  }
                  res.add(list);
              }
              return res;
          }
          //********遞歸版*********
          public static List<List<Integer>> levelOrder2(TreeNode root) {
              List<List<Integer>> res = new ArrayList<>();
              if (root == null) return res;
              helper(res, root, 0);
              return res;
          }
      
          public static void helper(List<List<Integer>> res, TreeNode root, int level)  {
              if (root == null) return;
              if (level >= res.size()) {
                  res.add(new ArrayList<>());
              }
              res.get(level).add(root.val);
              helper(res, root.left, level + 1);
              helper(res, root.right, level + 1);
          }
      
    5. 鋸齒遍歷

      package leetcode;
      
      import java.util.ArrayList;
      import java.util.LinkedList;
      import java.util.List;
      import java.util.Queue;
      
      public class BinaryTreeZigzagLevelOrderTraversal {
          /**
           * 103. Binary Tree Zigzag Level Order Traversal
                3
               / \
              9  20
            /  \
           15   7
           [
           [3],
           [20,9],
           [15,7]
           ]
           time : O(n)
           space : O(n);
           * @param root
           * @return
           */
      
          public static List<List<Integer>> zigzagLevelOrder(TreeNode root) {
              List<List<Integer>> res = new ArrayList<>();
              if (root == null) return res;
              Queue<TreeNode> queue = new LinkedList<>();
              queue.offer(root);
              boolean x = true;//奇數(shù)層還是偶數(shù)層
              while (!queue.isEmpty()) {
                  int size = queue.size();
                  //把他當(dāng)成棧/數(shù)組使用,比我的方法更好,他少使用了一個(gè)棧
                  //每次都new一個(gè),就不需要clear了
                  List<Integer> list = new ArrayList<>();
                  for (int i = 0; i < size; i++) {
                      TreeNode cur = queue.poll();
                      if (x) {
                          list.add(cur.val);
                      } else {
                          list.add(0, cur.val);
                      }
                      if (cur.left != null) {
                          queue.offer(cur.left);
                      }
                      if (cur.right != null) {
                          queue.offer(cur.right);
                      }
                  }
                  res.add(list);
                  x = x ? false : true;//取反
              }
              return res;
          }
        
      
  7. 兩數(shù)之和

    package leetcode;
    import java.util.Arrays;
    import java.util.HashMap;
    
    public class TwoSum {
        public int[] twoSum(int[] nums, int target) {
         HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
          for (int i = 0; i < nums.length; i++) {
             if (map.containsKey(target - nums[i])) {
                 return new int[] { i, map.get(target - nums[i]) };
             }
             map.put(nums[i], i);
          }
          return new int[] { -1, -1 };
       }
    }
    
  8. Pow(x, n)

    package leetcode;
    
    public class Pow {
        public double myPow1(double x, int n) {//遞歸版
            if (n > 0) {
                return pow(x, n);
            } else {
                return  1.0 / pow(x, n);
            }
        }
        public double pow (double x, int n) {
            if (n == 0) {
                return 1;
            }
            double y = pow(x, n / 2);
            if (n % 2 == 0) {
                return y * y;
            } else {
                return y * y * x;
            }
        }
        //time:O(logn),space:O(1)
        public static double myPow2(double x, int n) {
            if (n == 0) return 1;
            double res = 1;
            // int : -6.. ~ +6..  -2^32 ~ 2 ^32-1 Integer.MIN_VALUE
            long abs = Math.abs((long)n);
            while (abs > 0) {
                if (abs % 2 != 0) {
                    res *= x;//有幾次為奇數(shù),就執(zhí)行幾次  7/2一次  3/2 一次  1/2一次
                }
                x *= x;//記錄上一輪的結(jié)果
                abs /= 2;
            }
            if (n < 0) {
                return 1.0 / res;
            }
            return res;
        }
    
  9. 二叉樹的最小深度(LC111)

    package leetcode;
          
          public class MinimumDepthofBinaryTree {
              /**
               * 111. Minimum Depth of Binary Tree
               * Given a binary tree, find its minimum depth.
               The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.
               time : O(n);
               space : O(H);H樹的高度
               * @param root
               * @return
               */
              //還是用深度優(yōu)先搜索 DFS 來完成,萬能的遞歸啊。首先判空,若當(dāng)前結(jié)點(diǎn)不存在,直接返回0。然后看若左子結(jié)點(diǎn)不存在,那么對(duì)右子結(jié)點(diǎn)調(diào)用遞歸函數(shù),并加1返回。反之,若右子結(jié)點(diǎn)不存在,那么對(duì)左子結(jié)點(diǎn)調(diào)用遞歸函數(shù),并加1返回。若左右子結(jié)點(diǎn)都存在,則分別對(duì)左右子結(jié)點(diǎn)調(diào)用遞歸函數(shù),將二者中的較小值加1返回即可
              public static int minDepth(TreeNode root) {
                  //這道題遞歸條件里分為2種情況
                  if (root == null) return 0;
                  //1.如果左孩子和由孩子其中一個(gè)為空,那么需要返回比較大的那個(gè)孩子的深度     
                  if (root.left == null || root.right == null) {
                      //必有一個(gè)為null,相當(dāng)于為0,另一個(gè)返回深度+1
                      return Math.max(minDepth(root.left), minDepth(root.right)) + 1;
                  }
                  //2.一種情況,也就是左右孩子都不為空,返回最小深度+1即可
               return Math.min(minDepth(root.left), minDepth(root.right)) + 1;
              }
       }
    
  10. 平衡二叉樹

    package leetcode;
      public class BalancedBinaryTree {
          /**
           * 110. Balanced Binary Tree
           */
      算法流程:
      helper(root):
          遞歸返回值:
              當(dāng)節(jié)點(diǎn)root 左 / 右子樹的高度差 <2 :則返回以節(jié)點(diǎn)root為根節(jié)點(diǎn)的子樹的最大高度,即節(jié)點(diǎn) root 的左右子樹中最大高度加 1 ( max(left, right) + 1 );
              當(dāng)節(jié)點(diǎn)root 左 / 右子樹的高度差 ≥2:則返回 ?1 ,代表 此子樹不是平衡樹 。
          遞歸終止條件:
              當(dāng)越過葉子節(jié)點(diǎn)時(shí),返回高度 0 ;
              當(dāng)左(右)子樹高度 left== -1 時(shí),代表此子樹的 左(右)子樹 不是平衡樹,因此直接返回 ?1;
      
      isBalanced(root) :
          返回值: 若 recur(root) != 1 ,則說明此樹平衡,返回 true ;否則返回 false。
      
          public boolean isBalanced(TreeNode root) {
              if (root == null) return true;
              return helper(root) != -1;
          }
      
          public int helper(TreeNode root) {
              if (root == null) return 0;
              int l = helper(root.left);
              int r = helper(root.right);
              if (l == -1 || r == -1 || Math.abs(l - r) > 1) {
                  return -1;
              }
           return Math.max(l, r) + 1;
          }
    }
    
  11. 回文數(shù)(LC9)

    package leetcode;
      public class PalindromeNumber {
          /**
           * 9. Palindrome Number
           time : O(n) space : O(1)
           * @param x
           * @return
           */
          public boolean isPalindrome(int x) {
              if (x < 0 || x != 0 && x % 10 == 0) return false;//x%10==0能被10整除的都不是
              int palind = x;
              int rev = 0;
              while (x > 0) {
                  rev = rev * 10 + x % 10;
                  x /= 10;
              }
           return palind == rev;
          }
    } 
    
  12. 二分查找(LC704)

    class Solution {
          public int search(int[] nums, int target) {
              int start = 0;
              int end = nums.length-1;
              //數(shù)組:length  容器:size()  String:length() 
              while(start<=end){//要考慮只有一個(gè)數(shù)的情況
                  int mid = start + (end-start)/2;
                  if(nums[mid]==target) return mid;
                  else if(nums[mid]<target) start = mid+1;
                  else end = mid-1;
              }
           return -1;
          }
    }
    
  13. LRU(LC146)

    //看這個(gè)
      public class LRUCache {
        /**
         * 
         * Node類用于抽象鏈表的節(jié)點(diǎn)
         * key、value存儲(chǔ)鍵、值,
         * before、after分別指向當(dāng)前節(jié)點(diǎn)的前后Node節(jié)點(diǎn);
         *
         */
        class Node {
            int key;
            int value;
            Node before;
            Node after;
        }
        
        /**
         * 使用HashMap緩存Node節(jié)點(diǎn)
         */
        private HashMap<Integer, Node> cache = new HashMap<Integer, Node>();
        /**
         * 最大容量,超過capacity時(shí)繼續(xù)插入會(huì)觸發(fā)刪除最老未被使用的節(jié)點(diǎn)
         */
        private int capacity;
        /**
         * 頭節(jié)點(diǎn)、尾節(jié)點(diǎn)(注意這兩個(gè)節(jié)點(diǎn)不存儲(chǔ)實(shí)際的數(shù)據(jù))
         */
        private Node head, tail;
      
        public LRUCache(int capacity) {
            this.capacity = capacity;
      
            head = new Node();
            head.before = null;
      
            tail = new Node();
            tail.after = null;
      
            head.after = tail;
            tail.before = head;
        }
      
        /**
         * 將節(jié)點(diǎn)插入隊(duì)列頭部
         * 
         * @param node
         */
        private void addToHead(Node node) {
            node.before = head;
            node.after = head.after;
            head.after.before = node;
            head.after = node;
        }
      
        /**
         * 刪除隊(duì)列中的一個(gè)節(jié)點(diǎn)
         * 
         * @param node
         */
        private void removeNode(Node node) {
            //把目標(biāo)節(jié)點(diǎn)的前后節(jié)點(diǎn)保存,然后鏈接
            Node before = node.before;
            Node after = node.after;
            before.after = after;
            after.before = before;
        }
      
        /**
         * 將節(jié)點(diǎn)移動(dòng)到有效數(shù)據(jù)頭部
         * 
         * @param node
         */
        private void moveToHead(Node node) {
              //先刪除掉
            removeNode(node);
              //在插入
            addToHead(node);
        }
      
        /**
         * 刪除有效數(shù)據(jù)尾節(jié)點(diǎn)
         * 
         * @return 尾節(jié)點(diǎn)
         */
        private Node popTail() {
            Node res = tail.before;
            this.removeNode(res);
            return res;
        }
      
        public int get(int key) {
            Node node = cache.get(key);
            if (node == null) {
                return -1; // should raise exception here.
            }
            // 如果獲取到數(shù)據(jù),則將獲取到的節(jié)點(diǎn)移動(dòng)到隊(duì)列頭部;
            moveToHead(node);
            return node.value;
        }
      
        public void put(int key, int value) {
            Node node = cache.get(key);
            if (node == null) {
                 //新寫進(jìn)去的,會(huì)增加節(jié)點(diǎn)
                Node newNode = new Node();
                newNode.key = key;
                newNode.value = value;
                cache.put(key, newNode);
                addToHead(newNode);
                if (cache.size() > capacity) {
                    // 刪除隊(duì)尾有效數(shù)據(jù)節(jié)點(diǎn)
                    Node tail = this.popTail();
                    this.cache.remove(tail.key);
                }
            } else {
                     //已經(jīng)在緩存里
                node.value = value;
                // 在使用get方法獲取值之后,需要將當(dāng)前獲取的節(jié)點(diǎn)移動(dòng)到隊(duì)列頭部
                moveToHead(node);
            }
        }
    }
    
  14. 兩數(shù)相加(LC2)

  15. 計(jì)數(shù)質(zhì)數(shù)(LC204)

    package leetcode;
      public class CountPrimes {
          /**
           * 厄拉多塞篩法,求一組質(zhì)數(shù),時(shí)間復(fù)雜度僅有O(nloglogn)https://www.cnblogs.com/jiaxin359/p/6081629.html
           * 如果從1到n-1分別判斷質(zhì)數(shù),時(shí)間復(fù)雜度為O(nsqrt(n)))
           * @param n
           * @return
           */
          public int countPrimes(int n) {
              boolean[] notPrime = new boolean[n];
              int res = 0;
              for (int i = 2; i < n; i++) {//如果是2,沒有進(jìn)入循環(huán)
                  if (notPrime[i] == false) {//就是質(zhì)數(shù)
                      res++;
                      for (int j = 2; i * j < n; j++) {
                          notPrime[i * j] = true;//i的j倍數(shù)都是非質(zhì)數(shù),將i*j排除掉
                      }
                  }
              }
           return res;
          }
    }
    
  16. 生產(chǎn)者消費(fèi)者模式

    //店員類
      class Clerk {
          private int product = 0;
      
          // 進(jìn)貨
          public synchronized void get() {
              while (product >= 1) {
                  System.out.println("產(chǎn)品已滿!");
                  // 等待
                  try {
                      this.wait();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
              System.out.println(Thread.currentThread().getName() + ":" + ++product);
              this.notifyAll(); // 喚醒
          }
      
          // 售貨
          public synchronized void sale() {
              while (product <= 0) {
                  System.out.println("缺貨!");
                  try {
                      this.wait();// 等待
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
              System.out.println(Thread.currentThread().getName() + ":" + --product);
              this.notifyAll();// 喚醒
          }
      }
      
      // 生產(chǎn)者類
      class Productor implements Runnable {
          private Clerk clerk;
          public Productor(Clerk clerk) {
              this.clerk = clerk;
          }
      
          @Override
          public void run() {
              for (int i = 0; i < 10; i++) {
                  try {
                      Thread.sleep(100);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  clerk.get();
              }
          }
      }
      
      // 消費(fèi)者類
      class Consumer implements Runnable {
          private Clerk clerk;
          public Consumer(Clerk clerk) {
              this.clerk = clerk;
          }
      
          @Override
          public void run() {
              for (int i = 0; i < 10; i++) {
                  clerk.sale();
              }
          }
      }
      
      public class TestProductorAndConsumer {
          public static void main(String[] args) {
              Clerk clerk = new Clerk();
              Productor productor = new Productor(clerk);
              Consumer consumer = new Consumer(clerk);
              new Thread(productor, "Productor A").start();
              new Thread(consumer, "Consumer B").start();
              new Thread(productor, "Productor C").start();
              new Thread(consumer, "Consumer D").start();
          }
    }
    
  17. 刪除鏈表的倒數(shù)第 N 個(gè)結(jié)點(diǎn)(LC19)

    package leetcode;
          public class RemoveNthNodeFromEndofList {
          //https://www.bilibili.com/video/BV1Pq4y1j78o?p=10   b站講解
          class Solution {
              public ListNode getKthFromEnd(ListNode head, int k) {
                  ListNode dummy = new ListNode(0);
                  ListNode slow = dummy;
                  ListNode fast = dummy;
                  dummy.next = head;
                  for (int i = 0; i <= n; i++) { //注意是<=n
                      fast = fast.next;
                  }
                  while (fast != null) {
                      fast = fast.next;
                      slow = slow.next;
                  }
                  slow.next = slow.next.next;//把中間要移除的斷掉
                  return dummy.next;
              }
    }
    
  18. 環(huán)形鏈表(LC141 判斷鏈表是否有環(huán))

      package leetcode;
      import java.util.List;
      
      public class LinkedListCycle {
           //time : O(n);  space : O(1);
          //這道題是快慢指針的經(jīng)典應(yīng)用。只需要設(shè)兩個(gè)指針,一個(gè)每次走一步的慢指針和一個(gè)每次走兩步的快指針,如果鏈表里有環(huán)的話,兩個(gè)指針最終肯定會(huì)相遇
          public static boolean hasCycle(ListNode head) {
              if (head == null || head.next == null) return false;
              ListNode slow = head;
              ListNode fast = head;
              while (fast != null && fast.next != null) {
                  slow = slow.next;
                  fast = fast.next.next;
                  if (slow == fast) {
                      return true;//遇到
                  }
              }
           return false;
          }
      }
    
  19. 環(huán)形鏈表 II(LC142 找到環(huán)形鏈表的入口結(jié)點(diǎn))

    <img src="https://images0.cnblogs.com/blog/354747/201311/05171805-64db9f059a1641e7afaf3dd8223c4fe7.jpg" alt="img" style="zoom: 67%;" />

      package leetcode;
      public class LinkedListCycleII {
      
          /**
           * Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
           * time : O(n)
           * space : O(1)
           */
          //題解:https://www.cnblogs.com/hiddenfox/p/3408931.html
          //因?yàn)閒ast的速度是slow的兩倍,所以fast走的距離是slow的兩倍,有 2(a+b) = a+b+c+b,可以得到a=c(這個(gè)結(jié)論很重要!)。
          //我們已經(jīng)得到了結(jié)論a=c,那么讓兩個(gè)指針分別從X和Z開始走,每次走一步,那么正好會(huì)在Y相遇!也就是環(huán)的第一個(gè)節(jié)點(diǎn)。
          public ListNode detectCycle(ListNode head) {
              if (head == null || head.next == null) return null;
              ListNode slow = head;
              ListNode fast = head;
              while (fast != null && fast.next != null) {
                  slow = slow.next;
                  fast = fast.next.next;
                  if (fast == slow) {//fast比slow多走一個(gè)環(huán)的距離
                      ListNode slow2 = head;
                      while (slow != slow2) {//不管fast繞了多少圈和slow相遇,依然滿足這個(gè)情況
                          slow = slow.next;
                          slow2 = slow2.next;
                      }
                      return slow;
                  }
              }
           return null;
          }
      }
    
  20. 手寫一個(gè)觀察者/建造者...設(shè)計(jì)模式

    觀察者設(shè)計(jì)模式

    被觀察者類

     /**
     * 被觀察者接口
     */
     public Interface Observable {
         // 添加觀察者
         void register(Observer observer);
         
         // 移除觀察者
         void remove(Observer observer);
         
         // 通知觀察者
         void notify(Message msg);
     }
     
     /**
     * 被觀察者類
     */
     public class ObservableClass implement Observable {
         private List<Observer> observers = new ArrayList<>();
         
         @Override
         public void register(Observer observer) {
             observers.add(observer);
         }
         
         @Override
         public void remove(Observer observer) {
             observers.remove(observer);
         }
         
         @Override
         public void notify(Message msg) {
             for(Observer observer: observers) {
              observer.update(msg);
             }
        }
     }
    

    觀察者類

     public interface Observer { 
         // 當(dāng)被觀察者更新時(shí),執(zhí)行處理邏輯
         void update(Message message);
     }
     
     public class ConcreteObserverOne implements Observer { 
         @Override 
         public void update(Message message) { 
             //TODO: 獲取消息通知,執(zhí)行自己的邏輯... 
             System.out.println("ConcreteObserverOne is notified."); 
         }
     }
     
     public class ConcreteObserverTwo implements Observer { 
         @Override
         public void update(Message message) { 
            //TODO: 獲取消息通知,執(zhí)行自己的邏輯... 
             System.out.println("ConcreteObserverTwo is notified."); 
        }
     }
    

    使用示例

     public class Demo { 
         public static void main(String[] args) { 
             ObservableClass observable = new ObservableClass();
             observable.register(new ConcreteObserverOne()); 
             observable.register(new ConcreteObserverTwo()); 
             observable.notify(new Message()); 
        }
     }
    

    使用場(chǎng)景

    • 關(guān)聯(lián)行為場(chǎng)景,需要注意的是,關(guān)聯(lián)行為是可拆分的,而不是“組合”關(guān)系。
    • 事件多級(jí)觸發(fā)場(chǎng)景。
    • 跨系統(tǒng)的消息交換場(chǎng)景,如消息隊(duì)列、事件總線的處理機(jī)制。

    優(yōu)點(diǎn)

    解除耦合,讓耦合的雙方都依賴于抽象,從而使得各自的變換都不會(huì)影響另一邊的變換。

    缺點(diǎn)

    在應(yīng)用觀察者模式時(shí)需要考慮一下開發(fā)效率和運(yùn)行效率的問題,程序中包括一個(gè)被觀察者、多個(gè)觀察者,開發(fā)、調(diào)試等內(nèi)容會(huì)比較復(fù)雜,而且在Java中消息的通知一般是順序執(zhí)行,那么一個(gè)觀察者卡頓,會(huì)影響整體的執(zhí)行效率,在這種情況下,一般會(huì)采用異步實(shí)現(xiàn)。

    建造者設(shè)計(jì)模式

    創(chuàng)建產(chǎn)品類

    //它有三個(gè)部件:CPU 、主板和內(nèi)存。并在里面提供了三個(gè)方法分別用來設(shè)置CPU 、主板和內(nèi)存:
    public class Computer {
        private String mCpu;
        private String mMainboard;
        private String mRam;
    
        public void setmCpu(String mCpu) {
            this.mCpu = mCpu;
        }
    
        public void setmMainboard(String mMainboard) {
            this.mMainboard = mMainboard;
        }
    
        public void setmRam(String mRam) {
            this.mRam = mRam;
        }
    }
    

    創(chuàng)建Builder類規(guī)范產(chǎn)品的組建

    public abstract class Builder {
        public abstract void buildCpu(String cpu);
        public abstract void buildMainboard(String mainboard);
        public abstract void buildRam(String ram);
        public abstract Computer create();
    }
    

    商家實(shí)現(xiàn)了抽象的Builder類,MoonComputerBuilder類用于組裝電腦

    public class MoonComputerBuilder extends Builder {
        private Computer mComputer = new Computer();
        @Override
        public void buildCpu(String cpu) {
            mComputer.setmCpu(cpu);
        }
    
        @Override
        public void buildMainboard(String mainboard) {
            mComputer.setmMainboard(mainboard);
        }
    
        @Override
        public void buildRam(String ram) {
            mComputer.setmRam(ram);
        }
    
        @Override
        public Computer create() {
            return mComputer;
        }
    }
    

    用Dirextor指揮者類來統(tǒng)一組裝過程

    public class Direcror {
        Builder mBuild=null;
        public Direcror(Builder build){
            this.mBuild=build;
        }
        public Computer CreateComputer(String cpu,String mainboard,String ram){
            //規(guī)范建造流程
           this.mBuild.buildMainboard(mainboard);
           this.mBuild.buildCpu(cpu);
           this.mBuild.buildRam(ram);
           return mBuild.create();
        }
    }
    

    客戶端調(diào)用指揮者類

    public class CreatComputer {
        public static void main(String[]args){
            Builder mBuilder=new MoonComputerBuilder();
            Direcror mDirecror=new Direcror(mBuilder);
            //組裝電腦
            mDirecror.CreateComputer("i7-6700","華擎玩家至尊","三星DDR4");
        }
    }
    

    使用場(chǎng)景

    • 當(dāng)創(chuàng)建復(fù)雜對(duì)象的算法應(yīng)該獨(dú)立于該對(duì)象的組成部分以及它們的裝配方式時(shí)。
    • 相同的方法,不同的執(zhí)行順序,產(chǎn)生不同的事件結(jié)果時(shí)。
    • 多個(gè)部件或零件,都可以裝配到一個(gè)對(duì)象中,但是產(chǎn)生的運(yùn)行結(jié)果又不相同時(shí)。
    • 產(chǎn)品類非常復(fù)雜,或者產(chǎn)品類中的調(diào)用順序不同產(chǎn)生了不同的效能。
    • 創(chuàng)建一些復(fù)雜的對(duì)象時(shí),這些對(duì)象的內(nèi)部組成構(gòu)件間的建造順序是穩(wěn)定的,但是對(duì)象的內(nèi)部組成構(gòu)件面臨著復(fù)雜的變化。

    優(yōu)點(diǎn):

    • 使用建造者模式可以使客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)。
    • 具體的建造者類之間是相互獨(dú)立的,容易擴(kuò)展。
    • 由于具體的建造者是獨(dú)立的,因此可以對(duì)建造過程逐步細(xì)化,而不對(duì)其他的模塊產(chǎn)生任何影響。

    缺點(diǎn):

    • 產(chǎn)生多余的Build對(duì)象以及Dirextor類。
  21. 線程交替打印

    //其他方法: https://segmentfault.com/a/1190000021433079?utm_source=sf-similar-article
    //*********3個(gè)線程交替打印0->100***************
    public class Main012 {
        public static void main(String[] args) {
            int threadCount = 3;
            int max = 100;
            for (int i = 0; i < threadCount; i++) {
                new Thread(new PrintSequenceThread(i, threadCount, max)).start();
            }
        }
    }
    
    class PrintSequenceThread implements Runnable {
        private static final Object mLock = new Object();
        
        /**
         * 當(dāng)前即將打印的數(shù)字
         */
        private static int current = 0;
        
        /**
         * 當(dāng)前線程編號(hào),從0開始
         */
        private int threadNo;
        /**
         * 線程數(shù)量
         */
        private int threadCount;
        
        /**
         * 打印的最大數(shù)值
         */
        private int max;
        public PrintSequenceThread(int threadNo, int threadCount, int max) {
            this.threadNo = threadNo;
            this.threadCount = threadCount;
            this.max = max;
        }
        
        @Override
        public void run() {
            while (true) {
                synchronized (mLock) {
                    // 判斷是否輪到當(dāng)前線程執(zhí)行
                    while (current % threadCount != threadNo) {
                        if (current > max) {
                            break;
                        }
                        try {
                            // 如果不是,則當(dāng)前線程進(jìn)入wait
                            mLock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    // 最大值跳出循環(huán)
                    if (current > max) {
                        break;
                    }
                    System.out.println("thread-" + threadNo + " : " + current);
                    current++;
                    // 喚醒其他wait線程
                    mLock.notifyAll();
                }
            }
        }
    }
    //*******************************************************************************************
    //3個(gè)線程并發(fā)打印從0到n,不是交替打印,每個(gè)線程可能連續(xù)執(zhí)行,有的線程可能長(zhǎng)時(shí)間按得不到執(zhí)行
    public class Main12_1 {
        public static void main(String[] args) {
            int threadCount = 3;
            for (int i = 0; i < threadCount; i++) {
                new Thread(new PrintSequenceThread1(i, threadCount)).start();
            }
        }
    }
    class PrintSequenceThread1 implements Runnable {
        
        private static final Object mLock = new Object();
        
        /**
         * 當(dāng)前即將打印的數(shù)字
         */
        private static int current = 0;
        
        /**
         * 當(dāng)前線程編號(hào),從0開始
         */
        private int threadNo;
        
        /**
         * 線程數(shù)量
         */
        private int threadCount;
        
        public PrintSequenceThread1(int threadNo, int threadCount) {
            this.threadNo = threadNo;
            this.threadCount = threadCount;
        }
        
        @Override
        public void run() {//3個(gè)線程并發(fā)打印從0到n,不是交替打印,每個(gè)線程可能連續(xù)執(zhí)行,有的線程可能長(zhǎng)時(shí)間按得不到執(zhí)行
            while (true) {
                synchronized (mLock) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("thread-" + threadNo + " : " + current);
                    current++;
                }
            }
        }
    }
    ****************************************************************************************
    //***************//3個(gè)線程交替打印從0到n*****************
    public class Main12_2 {
        public static void main(String[] args) {
            int threadCount = 3;
            for (int i = 0; i < threadCount; i++) {
                //3個(gè)線程交替打印從0到n
                new Thread(new PrintSequenceThread2(i, threadCount)).start();
            }
        }
    }
    class PrintSequenceThread2 implements Runnable {
        private static final Object mLock = new Object();
        /**
         * 當(dāng)前即將打印的數(shù)字
         */
        private static int current = 0;
        /**
         * 當(dāng)前線程編號(hào),從0開始
         */
        private int threadNo;
        /**
         * 線程數(shù)量
         */
        private int threadCount;
        
        public PrintSequenceThread2(int threadNo, int threadCount) {
            this.threadNo = threadNo;
            this.threadCount = threadCount;
        }
        
        @Override
        public void run() {//3個(gè)線程交替打印從0到n
            while (true) {
                synchronized (mLock) {
                    // 判斷是否輪到當(dāng)前線程執(zhí)行
                    while (current % threadCount != threadNo) {//一定是while,而不是if
                        try {
                        //是while的原因,當(dāng)這個(gè)線程被notifyAll時(shí),它會(huì)從mLock.wait()這句之后繼續(xù)往下執(zhí)行,即使當(dāng)前沒有輪到它
                            //寫成while,如果它依然滿足條件(沒有輪到它),它會(huì)一直阻塞在這里
                            // 如果不是,則當(dāng)前線程進(jìn)入wait
                            mLock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    try {
                        Thread.sleep(100);//停頓100ms,為了讓打印不要太快
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("thread-" + threadNo + " : " + current);
                    current++;
                    // 喚醒其他wait線程
                    mLock.notifyAll();
             }
            }
     }
    }
    
  22. 斐波拉切數(shù)列

    /*大家都知道斐波那契數(shù)列,現(xiàn)在要求輸入一個(gè)整數(shù)n,請(qǐng)你輸出斐波那契數(shù)列的第n項(xiàng)(從0開始,第0項(xiàng)為0)。
              n<=39*/
              //第1題:Fibonacci數(shù)列
              //判題OJ:https://www.nowcoder.com/practice/c6c7742f5ba7442aada113136ddea0c3?tpId=13&tqId=11160&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking
            //正推法
              public int Fibonacci(int n) {
                  //first:前面2個(gè),second:前面1個(gè)
                  int first = 1, second = 1;
                  int third = 0;
                  if(n == 1 || n == 2) return 1;
                  for(int i = 3; i <= n; i++){
                      third = first + second;//將上一次保留下來的相加  例如5=4+3
                      first = second;//保留上一輪的結(jié)果3
                    second = third;//保留這一輪的結(jié)果4
                  }
               return third;
              }
    
  23. 青蛙跳臺(tái)階

       //第1題:跳臺(tái)階
      //判題OJ:https://www.nowcoder.com/practice/22243d016f6b47f2a6928b4313c85387?tpId=13&tqId=11162&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking
                public int climbStairs(int n) {
                    if (n <= 2) return n;
                    int fisrt = 1;
                    int second = 2;
                    int third = 3;
                    for (int i = 2; i < n; i++) {
                        third = fisrt + second;
                        fisrt = second;
                        second = third;
                    }
                    return third;
                }
      
      //第2題:變態(tài)跳臺(tái)階
      //判題OJ:https://www.nowcoder.com/practice/22243d016f6b47f2a6928b4313c85387?tpId=13&tqId=11162&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking
      每個(gè)臺(tái)階可以看作一塊木板,讓青蛙跳上去,n個(gè)臺(tái)階就有n塊木板,最后一塊木板是青蛙到達(dá)的位子,必須存在,其他n-1塊木板可以任意選擇是否存在,則每個(gè)木板有存在和不存在兩種選擇,n-1塊木板就有2^(n-1)種,可以直接得到結(jié)果,不用那么麻煩分析吧
                public int JumpFloorII(int target) {
                     return 1<< target-1;
                }
      //類似題: https://www.zybang.com/question/104cecdfb73ed0ca724706dccad2f9d0.html
      //跳臺(tái)階3:(此題好像不能用非遞歸)
      //n階樓梯,每次能走1階或2階或5階,問:到n階總共有多少種走法
                public int jump(int n) {
                    if (n == 0) return 1;
                    if (n <= 2) return n;
                    if (n < 5) return jump(n - 1) + jump(n - 2);
                    return jump(n - 1) + jump(n - 2) + jump(n - 5);
                }
    
  24. 1個(gè)數(shù)組(有重復(fù)元素)找相加等于0的數(shù)有多少對(duì)

    //有點(diǎn)力扣167的意思
      //給定一個(gè)已按照 非遞減順序排列  的整數(shù)數(shù)組 numbers ,請(qǐng)你從數(shù)組中找出兩個(gè)數(shù)滿足相加之和等于目標(biāo)數(shù) target 。見題解下的雙指針解法
      class Solution {
      public int[] twoSum(int[] numbers, int target) {
          int i = 0;
          int j = numbers.length - 1;
          while (i < j) {
              int sum = numbers[i] + numbers[j];
              if (sum < target) {
                  i++;
              } else if (sum > target) {
                  j--;
              } else {
                  return new int[]{i+1, j+1};
              }
          }
          return new int[]{-1, -1};
         }
      }
      //以上不是本題的題解
      //思路:先排序,然后雙指針解法,跟上面一樣
    
  25. 兩棧實(shí)現(xiàn)隊(duì)列

      class CQueue {
              LinkedList<Integer> A, B;
              public CQueue() {
                  A = new LinkedList<Integer>();
                  B = new LinkedList<Integer>();
              }
              public void appendTail(int value) {
                  A.addLast(value);
              }
              public int deleteHead() {
                  if(!B.isEmpty()) return B.removeLast();
                  if(A.isEmpty()) return -1;
                  while(!A.isEmpty())
                      B.addLast(A.removeLast());
                  return B.removeLast();
              }
          }
    
  26. 計(jì)算View樹的深度

         private int maxDeep(View view) {
              //當(dāng)前的view已經(jīng)是最底層view了,不能往下累加層數(shù)了,返回0,代表view下面只有0層了
              if (!(view instanceof ViewGroup)) {
                  return 0;
              }
              ViewGroup vp = (ViewGroup) view;
              //雖然是viewgroup,但是如果并沒有任何子view,那么也已經(jīng)是最底層view了,不能往下累加層數(shù)了,返回0,代表view下面只有0層了
              if (vp.getChildCount() == 0) {
                  return 0;
              }
              //用來記錄最大層數(shù)
              int max = 0;
              //廣度遍歷view
              for (int i = 0; i < vp.getChildCount(); i++) {
                  //由于vp擁有子view,所以下面還有一層,因?yàn)榭梢?1,來疊加一層,然后再遞歸幾歲算它的子view的層數(shù)
                  int deep = maxDeep(vp.getChildAt(i)) + 1;
                  //比較哪個(gè)大就記錄哪個(gè)
                  if (deep > max) {
                      max = deep;
                  }
              }
           return max;
          }
    
    1. 十大排序

      1. 冒泡

        //冒泡排序:Time:O(n^2),Space:(1),最好Time:O(n),最壞Time:O(n^2) 穩(wěn)定  
        public class maoPao {
            public static void main(String[] args) {
                BubbleSort(new int[]{5, 4, 9, 4, 2, 3, 1});
            }
        
            private static void BubbleSort(int[] arr) {
                for (int i = 0; i < arr.length; i++) {
                    for (int j = arr.length-1; j >i ; j--) {
                        if (arr[j]<arr[i]){
                            swap(arr,i,j);
                        }
                    }
                }
                for (int i : arr) {
                    System.out.print(i);
                }
            }
        
            private static void swap(int[] arr, int i, int j) {
                arr[i] = arr[i] ^ arr[j];
                arr[j] = arr[i] ^ arr[j];
                arr[i] = arr[i] ^ arr[j];
            }
        }
        
      2. 選擇

        /*
         * 默認(rèn)第一個(gè)值最小,比較第一個(gè)值開始和后面2開始到n-1的大小,小則交換
         * 然后 拿第二個(gè)值和后面3開始到n-1的大小,小則交換
         * */
        //選擇排序:Time:O(n^2),Space:(1),最好Time:O(n^2),最壞Time:O(n^2) 穩(wěn)定  
        public class SelectionSort {
            public static void selectionSort(int[] arr) {
                if (arr == null && arr.length < 2) {
                    return;
                }
                for (int i = 0; i < arr.length - 1; i++) {
                    //第一個(gè)判斷結(jié)束后,下一個(gè)作為min
                    int minIndex = i;
                    for (int j = i + 1; j < arr.length; j++) {
                        minIndex = arr[j] < arr[minIndex] ? j : minIndex;
                    }
                    swap(arr, i, minIndex);
                }
                //set可以去重
                Collection<Integer> collection = new LinkedHashSet<>();
                for (int i = 0; i < arr.length; i++) {
                    collection.add(arr[i]);
                    System.out.print(arr[i]);
                }
            }
        
            private static void swap(int[] arr, int i, int minIndex) {
                int tmp = arr[minIndex];
                arr[minIndex] = arr[i];
                arr[i] = tmp;
            }
        
            public static void main(String[] args) {
                int[] arr = {5, 2, 1, 6, 7, 5, 6};
                selectionSort(arr);
            }
        }
        
      3. 插入

        //插入排序:Time:O(n^2),Space:(1) ;最好(全部有序)Time:O(n);最壞(全部逆序)Time:O(n^2),穩(wěn)定
        public class InsertionSort {
            public static void insertionSort(int[] arr) {
                if (arr == null && arr.length < 2) {
                    return;
                }
                //0~0 有序的
                //0~i 想有序
                for (int i = 1; i < arr.length; i++) {    //0~i做到有序
                    for (int j = i - 1; j >= 0; j--) {    //if部分可以 :int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--
                        if (arr[j] > arr[j + 1]) {
                            swap(arr, j, j + 1);
                        }
                    }
                }
                //set可以去重
                Collection<Integer> collection = new LinkedHashSet<>();
                for (int i = 0; i < arr.length; i++) {
                    collection.add(arr[i]);
                    System.out.print(arr[i]);
                }
                System.out.println();
                System.out.println(collection);
            }
        
            private static void swap(int[] arr, int i, int j) {
                arr[i] = arr[i] ^ arr[j];
                arr[j] = arr[i] ^ arr[j];
                arr[i] = arr[i] ^ arr[j];
            }
        
            public static void main(String[] args) {
                int[] arr = {5, 4, 9, 4, 2, 3, 1};
                insertionSort(arr);
            }
        }
        
      4. 快排

        //平均/最好Time:O(nlogn),Space:O(logn).最壞Time:O(n^2),不穩(wěn)定
        public class kuaiPai {
            public static void main(String[] args) {
                int[] ints = {5, 7, 6, 5, 3};
                quickSort(ints,0,4);
                for (int anInt : ints) {
                    System.out.print(anInt);
                }
            }
            public static void quickSort(int nums[], int left, int right) {
                if (left >= right) return;// left>right肯定不行,left=right時(shí),只有一個(gè)數(shù)也沒必要繼續(xù)排序
                int pos = partition(nums, left, right);
                quickSort(nums, left, pos - 1);// 非遞歸要用棧來模擬,那還是算了吧
                quickSort(nums, pos + 1, right);
            }
            public static int partition(int nums[], int left, int right) {
                int pivot = nums[left];
                int l = left + 1;
                int r = right;
                while (l <= r) {// 算上left至少兩個(gè)數(shù)
                    if (nums[l] > pivot && nums[r] < pivot) {
                        swap(nums, l++, r--);
                    }
                    if (nums[l] <= pivot) l++;//沒有滿足上述條件的也要向中間移動(dòng)
                    if (nums[r] >= pivot) r--;
                }//r<l時(shí)退出循環(huán),此時(shí)r處的元素比基準(zhǔn)點(diǎn)pivot(nums[left])小
                swap(nums, left, r);
                return r;
            }
            public static void swap(int arr[], int i, int j) {
                arr[i] = arr[i] ^ arr[j];
                arr[j] = arr[i] ^ arr[j];
                arr[i] = arr[i] ^ arr[j];
            }
        }
        
      5. 堆排

        // 堆排序:Time:O(nlogn),Space:(1) ;最好/最壞Time:O(nlogn);不穩(wěn)定 
             import java.util.*;
             
             public class Main {
                 public void heapSort(int[] arr) {
                     initHeap(arr);// //構(gòu)建大頂堆, 從第一個(gè)非葉子結(jié)點(diǎn)從下至上,從右至左調(diào)整結(jié)構(gòu)
                     for (int i = arr.length - 1; i > 0; i--) {
                         // 調(diào)整堆結(jié)構(gòu)+交換堆頂元素與末尾元素
                         // 逐步將每個(gè)最大值的根節(jié)點(diǎn)與末尾元素交換,并且再調(diào)整二叉樹,使其成為大頂堆
                         //將堆頂記錄和當(dāng)前未經(jīng)排序子序列的最后一個(gè)記錄交換
                         swap(arr, 0, i);// 將堆頂元素與末尾元素進(jìn)行交換,交換后最后一個(gè)元素為最大
                         // 交換之后,需要重新檢查堆是否符合大頂堆,不符合則要調(diào)整
                         adjustHeap(arr, 0, i);// 調(diào)整根節(jié)點(diǎn)滿足最大堆,
                     }
                 }
             
                 // 初始化最大堆
                 public void initHeap(int[] arr) {
                     // 從第一個(gè)非葉子結(jié)點(diǎn)從下至上,從右至左調(diào)整結(jié)構(gòu)
                      //依次以索引arr.length / 2 - 1到0的元素為堆頂(根節(jié)點(diǎn)),調(diào)整大頂堆,
                     for (int i = arr.length / 2 - 1; i >= 0; i--) {
                         adjustHeap(arr, i, arr.length);
                     }
                 }
                 //以i為根節(jié)點(diǎn),調(diào)整最大堆
                 // 調(diào)整大頂堆(僅是調(diào)整過程,建立在大頂堆已構(gòu)建的基礎(chǔ)上)
                 public void adjustHeap(int[] arr, int i, int size) {//i是堆頂坐標(biāo),size是數(shù)組元素個(gè)數(shù),不是坐標(biāo)
                     int temp = arr[i];// 先取出當(dāng)前元素i////根節(jié)點(diǎn)
                     int child = 2 * i + 1;// 左子節(jié)點(diǎn),從i結(jié)點(diǎn)的左子結(jié)點(diǎn)開始,也就是2i+1處開始
                     while (child < size) {// 有左子結(jié)點(diǎn)
                         if (child + 1 < size && arr[child + 1] > arr[child]) {// 且有右子節(jié)點(diǎn),如果左子結(jié)點(diǎn)小于右子結(jié)點(diǎn),child指向右子結(jié)點(diǎn)
                             child++;
                         }
                         // 如果父節(jié)點(diǎn)小于孩子結(jié)點(diǎn),則需要交換
                         // 如果子節(jié)點(diǎn)大于父節(jié)點(diǎn),將子節(jié)點(diǎn)值賦給父節(jié)點(diǎn)(不用進(jìn)行交換)
                         if (arr[child] > temp) {// 這里的arr[child] 左右子節(jié)點(diǎn)中較大的那個(gè)子節(jié)點(diǎn)//子節(jié)點(diǎn)>根節(jié)點(diǎn)
                             arr[i] = arr[child];//子節(jié)點(diǎn)值=父節(jié)點(diǎn)值
                             i = child;//索引變成子節(jié)點(diǎn)的,向下遍歷到子節(jié)點(diǎn)
                             child = 2 * i + 1;//child變成i的左子節(jié)點(diǎn)繼續(xù)遍歷//這里的child就是子節(jié)點(diǎn)的子節(jié)點(diǎn)了
                         } else {
                             break;// 大頂堆結(jié)構(gòu)未被破壞,不需要調(diào)整
                         }
                     }
                     arr[i] = temp;// 將temp值放到最終的位置
                 }
             
                 public void swap(int[] arr, int a, int b) {
                     int temp = arr[a];
                     arr[a] = arr[b];
                     arr[b] = temp;
                 }
                  
                  //測(cè)試用
                  //測(cè)試OJ:https://www.nowcoder.com/questionTerminal/3385982ae71d4a1ca8bf3d03614c0325
                 /**public static void main(String[] args) {
                     Scanner cin = new Scanner(System.in);
                     int number = Integer.parseInt(cin.nextLine());
                     String[] data = cin.nextLine().split(" ");
                     int[] array = new int[number];
                     for (int i = 0; i < number; i++) {
                         array[i] = Integer.parseInt(data[i]);
                     }
                     new Main().heapSort(array);
                     for (int i = 0; i < number; i++) {
                         if (i != number - 1)
                             System.out.print(array[i] + " ");
                         else
                             System.out.print(array[i]);
                  }
                 }*/
          
             }
        
      6. 歸并

        //歸并排序:Time:O(nlogn),Space:(1),最好Time:O(nlogn),最壞Time:O(nlogn) 不穩(wěn)定  
        public class MergeSort {
            public static void mergeSort(int[] arr, int leftPtr, int rightPtr, int rightBound) {
                if (arr == null && arr.length < 2) {
                    return;
                }
                int mid = rightPtr - 1;
                int[] temp = new int[rightBound - leftPtr + 1];
                int i = leftPtr;
                int j = rightPtr;
                int k = 0;
                while (i <= mid && j <= rightBound) {
                    temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
                 /*   if (arr[i] < arr[j]) {
                        temp[k] = arr[i];
                        i++;
                        k++;
                    } else {
                        temp[k] = arr[j];
                        j++;
                        k++;
                    }*/
                }
                //如果有剩余(只會(huì)有一邊) 直接復(fù)制(已經(jīng)排過序的)
                while (i <= mid) {
                    temp[k++] = arr[i++];
                }
                while (j <= rightBound) {
                    temp[k++] = arr[j++];
                }
                for (int m = 0; m < temp.length; m++) {
                    arr[leftPtr + m] = temp[m];
                }
        
            }
        
            private static void sort(int[] arr, int left, int right) {
                if (left == right) return;
                //分成兩半
                int mid = left + (right - left) / 2;
                //左邊排序
                sort(arr, left, mid);
                //右邊排序
                sort(arr, mid + 1, right );
                mergeSort(arr, left, mid + 1, right);
            }
        
            private static void print(int[] arr) {
                for (int i = 0; i < arr.length; i++) {
                    System.out.print(arr[i] + " ");
                }
            }
        
            public static void main(String[] args) {
                int[] arr = {1, 4, 7, 8, 3, 6, 9};
                sort(arr, 0, arr.length - 1);
                print(arr);
            }
        }
        
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,179評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,444評(píng)論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,948評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,185評(píng)論 0 287
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,717評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,602評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,794評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,045評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評(píng)論 1 281
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,414評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,750評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容

  • 一趟結(jié)束后能夠確定一個(gè)元素的最終位置的排序方法有: 簡(jiǎn)單選擇排序、快速排序、冒泡排序、堆排序 穩(wěn)定性定義:排序前后...
    Zak1閱讀 305評(píng)論 0 0
  • 目錄 1 左神部分集錦 2 Leetcode前150題 3 牛客網(wǎng)劍指offer 4 JavaG 5 題...
    小小千千閱讀 575評(píng)論 0 0
  • ● 如何打印二叉樹每層的節(jié)點(diǎn)? 考察點(diǎn):二叉樹 參考回答: 實(shí)現(xiàn)代碼: import java.util.Arra...
    le_u閱讀 495評(píng)論 0 0
  • 上節(jié)課學(xué)習(xí)了二分搜索樹這樣一種有序數(shù)據(jù)結(jié)構(gòu) ,本節(jié)課將借助二分搜索樹來實(shí)現(xiàn)更高級(jí)的數(shù)據(jù)結(jié)構(gòu)--集合與映射。 1. ...
    xkzhai閱讀 399評(píng)論 0 1
  • 各校歷年復(fù)試機(jī)試試題 清華、北大、華科試題詳細(xì)筆記部分,少筆記部分與少數(shù)leetcode【含個(gè)人整理筆記】 一、詳...
    十里江城閱讀 1,194評(píng)論 0 1