-
單例
定義:保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。
-
餓漢模式
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á)到懶加載的效果。
-
懶漢模式(線程不安全)
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
-
懶漢模式(線程安全)
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í)候我們是用不到同步的,所以不建議用這種模式。
-
雙重檢查模式 (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。
-
靜態(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)部類單例模式。
-
枚舉單例
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ā)很少用枚舉,可讀性并不是很高,不建議用。
-
使用容器實(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),降低了耦合度。
-
-
反轉(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; } }
-
字符串轉(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要放在前面 } }
-
整數(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; } }
-
兩個(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開始放的 } }
-
二叉樹前序,中序,后序 ,層序,鋸齒遍歷
-
前序迭代
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; } }
-
中序
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; } }
-
后序
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; } }
-
層序
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); }
-
鋸齒遍歷
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; }
-
-
兩數(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 }; } }
-
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; }
-
二叉樹的最小深度(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; } }
-
平衡二叉樹
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; } }
-
回文數(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; } }
-
二分查找(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; } }
-
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); } } }
兩數(shù)相加(LC2)
-
計(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; } }
-
生產(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(); } }
-
刪除鏈表的倒數(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; } }
-
環(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; } }
-
環(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; } }
-
手寫一個(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類。
-
線程交替打印
//其他方法: 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(); } } } }
-
斐波拉切數(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; }
-
青蛙跳臺(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); }
-
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}; } } //以上不是本題的題解 //思路:先排序,然后雙指針解法,跟上面一樣
-
兩棧實(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(); } }
-
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; }
-
十大排序
-
冒泡
//冒泡排序: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]; } }
-
選擇
/* * 默認(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); } }
-
插入
//插入排序: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); } }
-
快排
//平均/最好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]; } }
-
堆排
// 堆排序: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]); } }*/ }
-
歸并
//歸并排序: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); } }
-
-
算法(1)
?著作權(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ù)。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
- 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
- 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
- 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
- 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
- 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
- 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
- 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
- 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
- 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
- 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
- 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
推薦閱讀更多精彩內(nèi)容
- 一趟結(jié)束后能夠確定一個(gè)元素的最終位置的排序方法有: 簡(jiǎn)單選擇排序、快速排序、冒泡排序、堆排序 穩(wěn)定性定義:排序前后...
- ● 如何打印二叉樹每層的節(jié)點(diǎn)? 考察點(diǎn):二叉樹 參考回答: 實(shí)現(xiàn)代碼: import java.util.Arra...
- 上節(jié)課學(xué)習(xí)了二分搜索樹這樣一種有序數(shù)據(jù)結(jié)構(gòu) ,本節(jié)課將借助二分搜索樹來實(shí)現(xiàn)更高級(jí)的數(shù)據(jù)結(jié)構(gòu)--集合與映射。 1. ...
- 各校歷年復(fù)試機(jī)試試題 清華、北大、華科試題詳細(xì)筆記部分,少筆記部分與少數(shù)leetcode【含個(gè)人整理筆記】 一、詳...