單例模式,是一種經常使用的軟件設計模式。在它的核心結構中只包含一個被稱爲單例的特殊類。經過單例模式能夠保證系統中,應用該模式的類一個類只有一個實例。即一個類只有一個對象實例。java
從具體實現角度來講,主要有三點:一是單例模式的類只提供私有的構造函數,二是類定義中含有一個該類的靜態私有對象,三是該類提供了一個靜態的公有的函數用於建立或獲取它自己的靜態私有對象。node
// 懶漢式-線程不安全 public class Singleton { private static Singleton uniqueInstance; private Singleton() { } public static Singleton getUniqueInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } } // 雙重校驗鎖-線程安全 public class Singleton { private volatile static Singleton uniqueInstance; private Singleton() { } public static Singleton getUniqueInstance() { if (uniqueInstance == null) { synchronized (Singleton.class) { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } } return uniqueInstance; } } // 靜態內部類實現 public class Singleton { private Singleton() { } private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getUniqueInstance() { return SingletonHolder.INSTANCE; } }
/** * 03 數組中重複的數字 * 在一個長度爲 n 的數組裏的全部數字都在 0 到 n-1 的範圍內。數組中某些數字是重複的,但不知道 有幾個數字是重複的,也不知道每一個數字重複幾回。請找出數組中任意一個重複的數字。 */ public class _03 { public static boolean duplicate(int[] numbers,int length,int[] duplication) { int [] array = new int[length]; for (int i = 0; i < length; ++i) { if (array[numbers[i]] == 0) { array[numbers[i]] = 1; } else { duplication[0] =numbers[i]; return true; } } return false; } public static void main(String[] args) { int[] numbers = {2, 3, 1, 0, 2, 5, 3}; int[] duplication = new int[1]; System.out.println(duplicate(numbers, numbers.length, duplication) + " " + duplication[0]); } }
/** * 04 二維數組中的查找 * 在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。 請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。 */ public class _04 { public static boolean find(int target, int [][] array) { if (array == null || array.length == 0) { return false; } if (array[0].length == 0) { return false; } int col = array[0].length; int row = array.length; int i = row - 1; int j = 0; while (j < col && i >= 0) { if (array[i][j] == target) { return true; } else if (array[i][j] > target) { i--; } else { j++; } } return false; } public static void main(String[] args) { int[][] array = {{1, 4, 7, 11, 15}, {2, 5, 8, 12, 19}}; System.out.println("Find(5, array) = " + find(5, array)); } }
/** * 05 替換空格 * 請實現一個函數,將一個字符串中的每一個空格替換成「%20」。例如,當字符串爲We Are Happy.則通過 替換以後的字符串爲We%20Are%20Happy。 * * 在字符串尾部填充任意字符,使得字符串的長度等於替換以後的長度。由於一個空格要替換成三個字符 (%20),所以當遍歷到一個空格時,須要在尾部填充兩個任意字符。 * 令 P1 指向字符串原來的末尾位置,P2 指向字符串如今的末尾位置。P1 和 P2 從後向前遍歷,當 P1 遍歷到一個空格時,就須要令 P2 指向的位置依次填充 02%(注意是逆序的),不然就填充上 P1 指向字符的值。 * 從後向前遍是爲了在改變 P2 所指向的內容時,不會影響到 P1 遍歷原來字符串的內容 */ public class _05 { public static String replaceSpace(StringBuffer str) { int originalLength = str.length(); for (int i = 0; i < originalLength; ++i) { if (str.charAt(i) == ' ') { // 每遇到一個空格擴充2個長度 str.append(" "); } } int newLength = str.length(); int j = newLength - 1; for (int i = originalLength - 1; i >= 0; --i) { if (str.charAt(i) == ' ') { str.setCharAt(j--, '0'); str.setCharAt(j--, '2'); str.setCharAt(j--, '%'); } else { str.setCharAt(j--, str.charAt(i)); } } return str.toString(); } public static void main(String[] args) { replaceSpace(new StringBuffer("We Are Happy")); } }
import java.util.ArrayList; import java.util.List; import java.util.Stack; /** * 06 從頭至尾打印鏈表 * 輸入一個鏈表,按鏈表值從尾到頭的順序返回一個ArrayList。 */ /** * public class ListNode { * int val; * ListNode next = null; * * ListNode(int val) { * this.val = val; * } * } * */ public class _06 { /** * 使用堆棧 * @param listNode 鏈表頭結點 * @return 反向打印全部值 */ public static ArrayList<Integer> printListFromTailToHead(ListNode listNode) { if (listNode == null) { return new ArrayList<>(); } Stack<Integer> stack = new Stack<>(); ArrayList<Integer> arrayList = new ArrayList<>(); while (listNode != null) { stack.push(listNode.val); listNode = listNode.next; } while (!stack.empty()) { arrayList.add(stack.pop()); } return arrayList; } /** * 使用遞歸 * @param listNode 鏈表頭結點 * @return 反向打印全部值 */ public static ArrayList<Integer> printListFromTailToHead1(ListNode listNode) { if (listNode == null) { return new ArrayList<>(); } ArrayList<Integer> arrayList = printListFromTailToHead1(listNode.next); arrayList.add(listNode.val); return arrayList; } /** * 使用頭插法 * 構建一個頭指針head,初始head->next = null,而後不斷將listNode中的節點加到head後 面,至關於反向鏈表 * @param listNode 鏈表頭結點 * @return 反向打印全部值 */ public static ArrayList<Integer> printListFromTailToHead2(ListNode listNode) { if (listNode == null) { return new ArrayList<>(); } // 頭指針 ListNode head = new ListNode(0); head.next = null; // 將listNode中的結點逐步加到head後面 while (listNode != null) { // 先存儲下一個結點 ListNode nextNode = listNode.next; // 頭插 listNode.next = head.next; head.next = listNode; // 繼續下一次循環 listNode = nextNode; } ArrayList<Integer> arrayList = new ArrayList<>(); // 頭結點 listNode = head.next; while (listNode != null) { arrayList.add(listNode.val); listNode = listNode.next; } return arrayList; } public static void main(String[] args) { // 構造一個鏈表 ListNode listNode1 = new ListNode(1); ListNode listNode2 = new ListNode(2); listNode2.next = null; listNode1.next = listNode2; List<Integer> list = printListFromTailToHead2(listNode1); for (Integer integer : list) { System.out.println(integer + " "); } } /** * 內部類:至關於一個鏈表節點 */ public static class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } }
/** * 07 重建二叉樹 * 輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結 果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列 {4,7,2,1,5,3,8,6},則重建二叉樹並返回。 * 前序遍歷的第一個值爲根節點的值,使用這個值將中序遍歷結果分紅兩部分,左部分爲樹的左子樹中序 遍歷結果,右部分爲樹的右子樹中序遍歷的結果,而後就能夠接着分別對左右子樹遞歸下去。 */ /** * Definition for binary tree * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ public class _07 { /** * 重建二叉樹 * @param pre 前序遍歷 * @param in 中序遍歷 * @return 返回重建的二叉樹 */ public static TreeNode reConstructBinaryTree(int [] pre,int [] in) { if (pre == null || in == null || pre.length == 0 || in.length == 0) { return null; } return reConstructBinaryTree(pre, in, 0, pre.length - 1, 0, in.length - 1); } /** * 擴充前序與中序的索引參數,便於遞歸重建二叉樹 * @param pre 前序遍歷 * @param in 中序遍歷 * @param preStart 前序遍歷數組的開始索引 * @param preEnd 前序遍歷數組的結束索引 * @param inStart 中序遍歷數組的開始索引 * @param inEnd 中序遍歷數組的結束索引 * @return 返回重建的二叉樹 */ private static TreeNode reConstructBinaryTree(int [] pre,int [] in, int preStart, int preEnd, int inStart, int inEnd) { if (preStart > preEnd) { return null; } // 根節點的值是前序遍歷的第一個值 int rootValue = pre[preStart]; TreeNode treeNode = new TreeNode(rootValue); // 找到中序遍歷序列中的根節點的位置,遞歸獲得左右節點 for (int i = inStart; i <= inEnd; ++i) { if (in[i] == pre[preStart]) { treeNode.left = reConstructBinaryTree(pre, in, preStart + 1, preStart + i - inStart, inStart, i - 1); treeNode.right = reConstructBinaryTree(pre, in, preStart + i - inStart + 1, preEnd, i + 1, inEnd); break; } } return treeNode; } public static void main(String[] args) { int[] pre = {1, 2, 4, 7, 3, 5, 6, 8}; int[] in = {4, 7, 2, 1, 5, 3, 8, 6}; reConstructBinaryTree(pre, in); } /** * 內部類:二叉樹結構 */ public static class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } }
/** * 08 二叉樹的下一個結點 * 給定一個二叉樹和其中的一個結點,請找出中序遍歷順序的下一個結點而且返回。注意,樹中的結點不 僅包含左右子結點,同時包含指向父結點的指針 * 分三種狀況: * (1)當前節點有右子樹,則下一個中序遍歷節點是右子樹中的最左節點 * (2)當前節點沒有右子樹,且該節點是其父節點的左節點,則下一個節點是其父節點 * (3)當前節點沒有右子樹,且該節點是其父節點的右節點,則沿着其父節點向上遍歷,直到找到一個 是其父節點的左節點的節點,這個節點的父節點即爲所求 */ /** public class TreeLinkNode { int val; TreeLinkNode left = null; TreeLinkNode right = null; TreeLinkNode next = null; TreeLinkNode(int val) { this.val = val; } } */ public class _08 { public static TreeLinkNode GetNext(TreeLinkNode pNode) { if (pNode == null) { return null; } // 當前節點有右子樹 if (pNode.right != null) { pNode = pNode.right; while (pNode.left != null) { pNode = pNode.left; } return pNode; } // 當前節點沒有右子樹,且該節點沒有父節點,是根節點 if (pNode.next == null) { return null; } // 當前節點沒有右子樹,且該節點是其父節點的左節點 if (pNode.next.left == pNode) { return pNode.next; } // 當前節點沒有右子樹,且該節點是其父節點的右節點 if (pNode.next.right == pNode) { pNode = pNode.next; while (pNode.next != null) { if (pNode.next.left != pNode) { pNode = pNode.next; } else { return pNode.next; } } } return null; } /** * 二叉樹結構,包含了指向其父節點的指針 */ public static class TreeLinkNode { int val; TreeLinkNode left = null; TreeLinkNode right = null; TreeLinkNode next = null; TreeLinkNode(int val) { this.val = val; } } }
import java.util.Stack; /** * 09 用兩個棧實現隊列 * 用兩個棧來實現一個隊列,完成隊列的Push和Pop操做。 隊列中的元素爲int類型 * 棧1的push對應隊列的deleteHead操做,棧2的pop操做對應隊列的insertTail操做,當棧2爲空時, 將棧1的元素依次出棧,進到棧2中,若是棧1也爲空,則提示隊列爲空 */ public class _09 { Stack<Integer> stack1 = new Stack<Integer>(); Stack<Integer> stack2 = new Stack<Integer>(); /** * 隊列的insertTail操做 * @param node 插入隊尾的元素 */ public void push(int node) { stack1.push(node); } /** * 隊列的deleteHead操做 * @return 刪除隊列頭部 */ public int pop() { if (stack2.empty()) { if (stack1.empty()) { throw new RuntimeException("隊列爲空"); } else { while (!stack1.isEmpty()) { stack2.push(stack1.pop()); } } } return stack2.pop(); } }
/** * 10 斐波那契數列 * 你們都知道斐波那契數列,後一項等於前兩項和,如今要求輸入一個整數n,請你輸出斐波那契數列的 第n項(從0開始,第0項爲0,n<=39)。 * 使用兩個數將結果前兩項緩存便可 */ public class _10 { public static int Fibonacci(int n) { int result = 0; int temp1 = 0; int temp2 = 1; if (n == 1) { return temp2; } for (int i = 1; i < n; i++) { result = temp1 + temp2; temp1 = temp2; temp2 = result; } return result; } public static void main(String[] args) { System.out.println("Fibonacci(3) = " + Fibonacci(3)); } }