├── settings.gradle ├── .gitignore ├── src └── main │ └── java │ ├── algorithm │ ├── swordtooffer │ │ ├── Test17.java │ │ ├── Test16.java │ │ ├── Test7.java │ │ ├── Test20Mirror.java │ │ ├── Test4.java │ │ ├── Test10.java │ │ ├── Test3.java │ │ ├── Test12.java │ │ ├── Test9.java │ │ ├── Test15.java │ │ ├── Test8.java │ │ ├── Test13.java │ │ ├── Test11.java │ │ ├── Test14.java │ │ ├── Test5.java │ │ ├── Test21ClockwisePrintingMatrix.java │ │ ├── Test18_MergeTwoSortedLinkedlists.java │ │ ├── Test19_SubstructureOfTheTree.java │ │ └── Test6.java │ ├── list │ │ ├── ListNode.java │ │ └── LinkedList.java │ ├── tree │ │ ├── TreeNode.java │ │ ├── Algorithm4thBTree.java │ │ └── BinaryTree.java │ ├── leetcode │ │ ├── L_28.java │ │ ├── dp │ │ │ ├── L_121_BestTimetoBuyandSellStock.java │ │ │ ├── L_62_UniquePaths.java │ │ │ ├── L_70_ClimbingStairs.java │ │ │ ├── Main.java │ │ │ ├── L_3_LongestSubstringWithoutRepeatingCharacters.java │ │ │ ├── Backpack.java │ │ │ ├── KingMiningGold.java │ │ │ ├── L_322_CoinChange.java │ │ │ ├── L_64_MinimumPathSum.java │ │ │ ├── L_140_WordBreakII.java │ │ │ ├── L_72_EditDistance.java │ │ │ ├── L_91_DecodeWays.java │ │ │ ├── L_63_UniquePathsII.java │ │ │ ├── L_494_TargetSum.java │ │ │ └── L_139_WordBreak.java │ │ ├── L_53_MaximumSubarray.java │ │ ├── L_100_SameTree.java │ │ ├── L_21_MergeTwoSortedLists.java │ │ ├── L_38_CountAndSay.java │ │ ├── L_23_MergekSortedLists.java │ │ ├── L_35_SearchInsertPosition.java │ │ ├── L_147_InsertionSortList.java │ │ ├── L_111_MinimumDepthOfBinaryTree.java │ │ ├── L_199_BinaryTreeRightSideView.java │ │ ├── L_150_EvaluateReversePolishNotation.java │ │ ├── L_94_BinaryTreeInorderTraversal.java │ │ ├── L_144_BinaryTreePreorderTraversal.java │ │ ├── L_204_Count_Primes.java │ │ ├── L_143_Reorder_List.java │ │ ├── Leetcode_149_MaxPointsOnALine.java │ │ └── L_148_SortList.java │ ├── sort │ │ ├── Bubble.java │ │ ├── Shell.java │ │ ├── Insertion.java │ │ ├── Sort.java │ │ ├── Selection.java │ │ ├── Test1.java │ │ ├── Radix.java │ │ ├── Merge.java │ │ ├── Heap.java │ │ ├── Test2.java │ │ └── Quick.java │ ├── search │ │ └── BinarySearch.java │ ├── tencent │ │ └── Main.java │ ├── security │ │ ├── SHAUtil.java │ │ ├── AESUtil.java │ │ ├── DESUtil.java │ │ ├── MD5Util.java │ │ ├── SM3Digest.java │ │ ├── SM3.java │ │ └── RSAUtil.java │ └── graph │ │ ├── Graph.java │ │ └── DFSTest.java │ └── Demo.java ├── README.md ├── gradlew.bat └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'algorithm' 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | gradle/ 4 | out/ 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test17.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | /** 4 | * 5 | * 输入一个链表,反转链表后,输出链表的所有元素 6 | *即链表面试题 第二题 list/LinkedList.java 7 | * @author 李文浩 8 | * @version 2017/9/10. 9 | */ 10 | public class Test17 { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/algorithm/list/ListNode.java: -------------------------------------------------------------------------------- 1 | package algorithm.list; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/7/6 6 | */ 7 | public class ListNode { 8 | int val; 9 | public ListNode next; 10 | 11 | ListNode(int x) { 12 | val = x; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/algorithm/tree/TreeNode.java: -------------------------------------------------------------------------------- 1 | package algorithm.tree; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/7/6 6 | */ 7 | public class TreeNode { 8 | int val = 0; 9 | public TreeNode left = null; 10 | public TreeNode right = null; 11 | 12 | public TreeNode(int val) { 13 | this.val = val; 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_28.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/12/15 6 | */ 7 | public class L_28 { 8 | public static void main(String[] args) { 9 | new L_28().strStr("1", ""); 10 | } 11 | 12 | public int strStr(String haystack, String needle) { 13 | return haystack.indexOf(needle); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/algorithm/sort/Bubble.java: -------------------------------------------------------------------------------- 1 | package algorithm.sort; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/2/4 6 | */ 7 | public class Bubble { 8 | public static void sort(int[] a) { 9 | //外层循环控制比较的次数 10 | for (int i = 0; i < a.length - 1; i++) { 11 | //内层循环控制到达位置 12 | for (int j = 0; j < a.length - i - 1; j++) { 13 | //前面的元素比后面大就交换 14 | if (a[j] > a[j + 1]) { 15 | int temp = a[j]; 16 | a[j] = a[j + 1]; 17 | a[j + 1] = temp; 18 | } 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/algorithm/sort/Shell.java: -------------------------------------------------------------------------------- 1 | package algorithm.sort; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/1/25 6 | */ 7 | public class Shell { 8 | public static void sort(int[] a) { 9 | int length = a.length; 10 | int h = 1; 11 | while (h < length / 3) h = 3 * h + 1; 12 | for (; h >= 1; h /= 3) { 13 | for (int i = 0; i < a.length - h; i += h) { 14 | for (int j = i + h; j > 0; j -= h) { 15 | if (a[j] < a[j - h]) { 16 | int temp = a[j]; 17 | a[j] = a[j - h]; 18 | a[j - h] = temp; 19 | } 20 | } 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/dp/L_121_BestTimetoBuyandSellStock.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode.dp; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/5/6 6 | */ 7 | public class L_121_BestTimetoBuyandSellStock { 8 | public static void main(String[] args) { 9 | int[] prices = {7, 1, 5, 3, 6, 4}; 10 | System.out.println(new L_121_BestTimetoBuyandSellStock().maxProfit(prices)); 11 | 12 | } 13 | 14 | /** 15 | * max = max{max,prices[i]-min} 16 | * @param prices 17 | * @return 18 | */ 19 | public int maxProfit(int[] prices) { 20 | int min = Integer.MAX_VALUE; 21 | int max = 0; 22 | for (int price : prices) { 23 | min = Math.min(min, price); 24 | max = Math.max(max, price - min); 25 | } 26 | return max; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_53_MaximumSubarray.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/12/27 6 | */ 7 | public class L_53_MaximumSubarray { 8 | public static void main(String[] args) { 9 | int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; 10 | System.out.println(new L_53_MaximumSubarray().maxSubArray(nums)); 11 | } 12 | 13 | public int maxSubArray(int[] nums) { 14 | if (nums.length == 1) 15 | return nums[0]; 16 | 17 | int localMax = nums[0]; 18 | int globalMax = nums[0]; 19 | 20 | for (int i = 1; i < nums.length; i++) { 21 | localMax = Math.max(nums[i] + localMax, nums[i]); 22 | globalMax = Math.max(localMax, globalMax); 23 | } 24 | 25 | return globalMax; 26 | 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_100_SameTree.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/7/3 6 | */ 7 | public class L_100_SameTree { 8 | static class TreeNode { 9 | int val; 10 | TreeNode left; 11 | TreeNode right; 12 | 13 | TreeNode(int x) { 14 | val = x; 15 | } 16 | } 17 | 18 | 19 | public boolean isSameTree(TreeNode p, TreeNode q) { 20 | if (q == null && p == null) { 21 | return true; 22 | } else if (q != null && p != null) { 23 | if (p.val != q.val) { 24 | return false; 25 | } else { 26 | return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); 27 | } 28 | } else { 29 | return false; 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_21_MergeTwoSortedLists.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/7/2 6 | */ 7 | public class L_21_MergeTwoSortedLists { 8 | 9 | public ListNode mergeTwoLists(ListNode l1, ListNode l2) { 10 | ListNode head = new ListNode(-1); 11 | ListNode current = head; 12 | while (l1 != null && l2 != null) { 13 | if (l1.val < l2.val) { 14 | current.next = l1; 15 | l1 = l1.next; 16 | } else { 17 | current.next = l2; 18 | l2 = l2.next; 19 | } 20 | current = current.next; 21 | } 22 | 23 | 24 | if (l1 != null) { 25 | current.next = l1; 26 | } 27 | if (l2 != null) { 28 | current.next = l2; 29 | } 30 | 31 | 32 | return head.next; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test16.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | /** 4 | * 5 | * 输入一个链表,输出该链表中倒数第k个结点。 6 | * 7 | * 即链表面试题 第三题 list/LinkedList.java 8 | * @author 李文浩 9 | * @version 2017/9/10. 10 | */ 11 | public class Test16 { 12 | 13 | public static class ListNode { 14 | int val; 15 | ListNode next = null; 16 | 17 | ListNode(int val) { 18 | this.val = val; 19 | } 20 | } 21 | 22 | 23 | public ListNode FindKthToTail(ListNode head, int k) { 24 | if (k <= 0 || head == null) { 25 | return null; 26 | } 27 | ListNode p2 = head, p1 = head; 28 | while (k-- > 1 && p1 != null) { 29 | p1 = p1.next; 30 | } 31 | // 说明k>size,因此返回null 32 | if (k > 1 || p1 == null) { 33 | return null; 34 | } 35 | while (p1.next != null) { 36 | p1 = p1.next; 37 | p2 = p2.next; 38 | } 39 | return p2; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/algorithm/sort/Insertion.java: -------------------------------------------------------------------------------- 1 | package algorithm.sort; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/1/25 6 | */ 7 | public class Insertion { 8 | 9 | /** 10 | * 通过交换进行插入排序,借鉴冒泡排序 11 | * 12 | * @param a 13 | */ 14 | public static void sort(int[] a) { 15 | for (int i = 0; i < a.length - 1; i++) { 16 | for (int j = i + 1; j > 0; j--) { 17 | if (a[j] < a[j - 1]) { 18 | int temp = a[j]; 19 | a[j] = a[j - 1]; 20 | a[j - 1] = temp; 21 | } 22 | } 23 | } 24 | } 25 | 26 | /** 27 | * 通过将较大的元素都向右移动而不总是交换两个元素 28 | * 29 | * @param a 30 | */ 31 | public static void sort2(int[] a) { 32 | for (int i = 1; i < a.length; i++) { 33 | int num = a[i]; 34 | int j; 35 | for (j = i; j > 0 && num < a[j]; j--) { 36 | a[j] = a[j - 1]; 37 | } 38 | a[j] = num; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/dp/L_62_UniquePaths.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode.dp; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * @author 李文浩 7 | * @date 2018/12/27 8 | */ 9 | public class L_62_UniquePaths { 10 | public static void main(String[] args) { 11 | System.out.println(new L_62_UniquePaths().uniquePaths(3, 3)); 12 | System.out.println(new L_62_UniquePaths().uniquePaths(7, 3)); 13 | } 14 | 15 | /** 16 | * 实际上是跳台阶的二维变种 17 | * 状态转移公式: 18 | * F(n,m) = F(n-1,m)+F(n,m-1) 19 | * 边界条件: 20 | * 当m或n==0时,只有1种方式。 21 | * 22 | * @param m 23 | * @param n 24 | * @return 25 | */ 26 | public int uniquePaths(int m, int n) { 27 | int[][] dp = new int[n][m]; 28 | for (int i = 0; i < n; i++) { 29 | Arrays.fill(dp[i], 1); 30 | } 31 | for (int i = 1; i < n; i++) { 32 | for (int j = 1; j < m; j++) { 33 | dp[i][j] = dp[i][j - 1] + dp[i - 1][j]; 34 | } 35 | } 36 | return dp[n - 1][m - 1]; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/dp/L_70_ClimbingStairs.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode.dp; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/12/27 6 | */ 7 | public class L_70_ClimbingStairs { 8 | public static void main(String[] args) { 9 | System.out.println(new L_70_ClimbingStairs().climbStairs(1)); 10 | System.out.println(new L_70_ClimbingStairs().climbStairs(2)); 11 | System.out.println(new L_70_ClimbingStairs().climbStairs(3)); 12 | System.out.println(new L_70_ClimbingStairs().climbStairs(4)); 13 | System.out.println(new L_70_ClimbingStairs().climbStairs(5)); 14 | } 15 | 16 | /** 17 | * 实际类似斐波那契数列 18 | * 状态转移方程: 19 | * F(n) = F(n-1)+F(n-2) 20 | * 21 | * @param n 22 | * @return 23 | */ 24 | public int climbStairs(int n) { 25 | if (n == 1) { 26 | return 1; 27 | } 28 | int a = 1, b = 2; 29 | for (int i = 3; i <= n; i++) { 30 | int temp = b; 31 | b = b + a; 32 | a = temp; 33 | } 34 | return b; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/dp/Main.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode.dp; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2019-1-26 6 | */ 7 | public class Main { 8 | public static void main(String[] args) { 9 | //Scanner in = new Scanner(System.in); 10 | //int n = 0; 11 | //if (in.hasNextLine()) { 12 | // n = Integer.parseInt(in.nextLine()); 13 | //} 14 | //int[] nums = new int[1000]; 15 | //int i = 0; 16 | //while (i < 1000 && in.hasNextInt()) { 17 | // nums[i++] = in.nextInt(); 18 | //} 19 | // 20 | //System.out.println(new Main().danceRoom(n, nums)); 21 | 22 | int[] nums1 = {1, 2}; 23 | System.out.println(new Main().danceRoom(2, nums1)); 24 | 25 | } 26 | 27 | public int danceRoom(int n, int[] p) { 28 | int[][] dp = new int[n + 1][n + 1]; 29 | dp[1][1] = 1; 30 | for (int i = 1; i < n + 1; i++) { 31 | for (int j = 1; j < i; j++) { 32 | dp[i][j] = 0; 33 | } 34 | } 35 | return 0; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test7.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。 7 | * 8 | * @author 李文浩 9 | * @version 2017/8/5. 10 | */ 11 | public class Test7 { 12 | 13 | Stack stack1 = new Stack(); 14 | Stack stack2 = new Stack(); 15 | 16 | public static void main(String[] args) { 17 | Test7 queue = new Test7(); 18 | queue.push(1); 19 | queue.push(2); 20 | queue.push(3); 21 | // queue.push(4); 22 | System.out.println(queue.pop()); 23 | System.out.println(queue.pop()); 24 | System.out.println(queue.pop()); 25 | // System.out.println(queue.pop()); 26 | } 27 | public void push(int node) { 28 | 29 | while (!stack1.isEmpty()) { 30 | stack2.push(stack1.pop()); 31 | } 32 | stack1.push(node); 33 | while (!stack2.isEmpty()) { 34 | stack1.push(stack2.pop()); 35 | } 36 | } 37 | 38 | public int pop() { 39 | return stack1.pop(); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/dp/L_3_LongestSubstringWithoutRepeatingCharacters.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode.dp; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | /** 7 | * @author 李文浩 8 | * @date 2019-1-26 9 | */ 10 | public class L_3_LongestSubstringWithoutRepeatingCharacters { 11 | public static void main(String[] args) { 12 | System.out.println(new L_3_LongestSubstringWithoutRepeatingCharacters().lengthOfLongestSubstring("abcabcbb")); 13 | System.out.println(new L_3_LongestSubstringWithoutRepeatingCharacters().lengthOfLongestSubstring("aaaaaaabcbb")); 14 | } 15 | 16 | public int lengthOfLongestSubstring(String s) { 17 | int n = s.length(); 18 | Set set = new HashSet<>(); 19 | int ans = 0, i = 0, j = 0; 20 | while (i < n && j < n) { 21 | // try to extend the range [i, j] 22 | if (!set.contains(s.charAt(j))) { 23 | set.add(s.charAt(j++)); 24 | ans = Math.max(ans, j - i); 25 | } else { 26 | set.remove(s.charAt(i++)); 27 | } 28 | } 29 | return ans; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test20Mirror.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | import algorithm.tree.BinaryTree; 4 | import algorithm.tree.TreeNode; 5 | 6 | /** 7 | * @author 李文浩 8 | * @date 2018/7/6 9 | */ 10 | public class Test20Mirror { 11 | 12 | public static void main(String[] args) { 13 | TreeNode root1 = new TreeNode(8); 14 | TreeNode root2 = new TreeNode(8); 15 | TreeNode root3 = new TreeNode(7); 16 | TreeNode root4 = new TreeNode(9); 17 | TreeNode root5 = new TreeNode(2); 18 | 19 | root1.left = root2; 20 | root1.right = root3; 21 | root2.left = root4; 22 | root2.right = root5; 23 | BinaryTree.inOrder(root1); 24 | new Test20Mirror().Mirror(root1); 25 | System.out.println(); 26 | BinaryTree.inOrder(root1); 27 | 28 | } 29 | 30 | 31 | public void Mirror(TreeNode root) { 32 | if (root != null) { 33 | TreeNode temp = root.right; 34 | root.right = root.left; 35 | root.left = temp; 36 | Mirror(root.left); 37 | Mirror(root.right); 38 | 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test4.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * 请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。 7 | * 8 | * @author 李文浩 9 | * @version 2017/7/20. 10 | */ 11 | public class Test4 { 12 | public static void main(String[] args) { 13 | Test4 test4 = new Test4(); 14 | System.out.println(test4.replaceSpace(new StringBuffer("We Are Happy."))); 15 | } 16 | 17 | /** 18 | * 19 | * 因为牛客网给的是StringBuffer类,这个类只有一个replace方法,因此先把空格的位置记录下来, 20 | * 然后当已经替换一个空格的时候,新的空格的位置等于前面空格的数量乘以2 21 | * 22 | * @param str 23 | * @return 24 | */ 25 | public String replaceSpace(StringBuffer str) { 26 | ArrayList list = new ArrayList(10); 27 | for (int i = 0; i < str.length(); i++) { 28 | if (' ' == str.charAt(i)) { 29 | list.add(i); 30 | } 31 | } 32 | for (int i = 0; i < list.size(); i++) { 33 | list.set(i, list.get(i) + i * 2); 34 | str.replace(list.get(i), list.get(i) + 1, "%20"); 35 | } 36 | return str.toString(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test10.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | /** 4 | * @author 李文浩 5 | * @version 2017/8/11. 6 | *

7 | *

8 | * 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。 9 | *

10 | * 思路: 11 | */ 12 | public class Test10 { 13 | public static void main(String[] args) { 14 | Test10 test10 = new Test10(); 15 | System.out.println(test10.JumpFloor(10)); 16 | System.out.println(test10.JumpFloor2(10)); 17 | } 18 | 19 | /** 20 | * @param target 21 | * @return 22 | */ 23 | public int JumpFloor(int target) { 24 | if (target <= 0) { 25 | return 0; 26 | } 27 | int f1 = 1, f2 = 0; 28 | for (int i = 1; i <= target; i++) { 29 | f1 = f1 + f2; 30 | f2 = f1 - f2; 31 | } 32 | return f1; 33 | } 34 | 35 | /** 36 | * @param target 37 | * @return 38 | */ 39 | public int JumpFloor2(int target) { 40 | if (target == 1) { 41 | return 1; 42 | } else if (target == 2) { 43 | return 2; 44 | } else { 45 | return JumpFloor2(target - 1) + JumpFloor2(target - 2); 46 | } 47 | 48 | } 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/algorithm/sort/Sort.java: -------------------------------------------------------------------------------- 1 | package algorithm.sort; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/1/25 6 | */ 7 | public class Sort { 8 | public static void main(String[] args) { 9 | long staruTime = System.currentTimeMillis(); 10 | // int[] a = {10, 2, 6, 3, 50, 26, 34, 6}; 11 | int[] a = new int[20]; 12 | for (int i = 0; i < a.length; i++) 13 | a[i] = (int) (Math.random() * 100); 14 | System.out.print("排序前: "); 15 | for (int n : a) 16 | System.out.print(n + " "); 17 | System.out.println(); 18 | 19 | //插入排序 20 | // Insertion.sort(a); 21 | //希尔排序 22 | // Shell.sort(a); 23 | //选择排序 24 | 25 | //堆排序 26 | // Heap.sort(a); 27 | //冒泡排序 28 | // Bubble.sort(a); 29 | // 快速排序 30 | // Quick.sortByStack(a); 31 | // Quick.sort(a, 0, a.length - 1); 32 | Quick.sortThreeWay(a, 0, a.length - 1); 33 | //归并排序 34 | // Merge.sort(a); 35 | 36 | System.out.print("排序后: "); 37 | for (int i : a) 38 | System.out.print(i + " "); 39 | long endTime = System.currentTimeMillis(); 40 | System.out.println(); 41 | System.out.println((endTime - staruTime) + "ms"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_38_CountAndSay.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/12/16 6 | */ 7 | public class L_38_CountAndSay { 8 | 9 | public static void main(String[] args) { 10 | for (int i = 0; i < 10; i++) { 11 | System.out.println(new L_38_CountAndSay().countAndSay(i)); 12 | } 13 | } 14 | 15 | /** 16 | * 17 | * @param n 18 | * @return 19 | */ 20 | public String countAndSay(int n) { 21 | String s = "1"; 22 | for (int i = 0; i < n - 1; i++) { 23 | StringBuilder sb = new StringBuilder(); 24 | for (int j = 0; j < s.length(); ) { 25 | if (j + 1 == s.length() || s.charAt(j) != s.charAt(j + 1)) { 26 | sb.append(1).append(s.charAt(j)); 27 | j++; 28 | } else { 29 | int k = j + 1; 30 | while (k < s.length() && s.charAt(k) == s.charAt(j)) { 31 | k++; 32 | } 33 | int m = k - j; 34 | sb.append(m).append(s.charAt(j)); 35 | j = k; 36 | } 37 | } 38 | s = sb.toString(); 39 | } 40 | return s; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/dp/Backpack.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode.dp; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/5/4 6 | */ 7 | public class Backpack { 8 | 9 | public static void main(String[] args) { 10 | int w = 11; //物品个数,背包容量 11 | int[] value = {0, 6, 3, 5, 4, 6}; //各个物品的价值 12 | int[] weight = {0, 2, 2, 3, 5, 4}; //各个物品的重量 13 | System.out.println(getMaxValue(weight, value, w)); 14 | 15 | } 16 | 17 | public static int getMaxValue(int[] weight, int[] value, int w) { 18 | int length = value.length; 19 | int[][] table = new int[length][w + 1]; 20 | for (int i = 1; i < length; i++) { //物品 21 | for (int j = 1; j < w + 1; j++) { //背包大小 22 | if (weight[i] > j) { 23 | //当前物品i的重量比背包容量j大,装不下,肯定就是不装 24 | table[i][j] = table[i - 1][j]; 25 | // System.out.print(table[i][j]+ " "); 26 | } else { //装得下,Max{装物品i, 不装物品i} 27 | table[i][j] = Math.max(table[i - 1][j], table[i - 1][j - weight[i]] + value[i]); 28 | //System.out.print(table[i][j]+ " "); 29 | } 30 | } 31 | // System.out.println(); 32 | } 33 | return table[length - 1][w]; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/algorithm/sort/Selection.java: -------------------------------------------------------------------------------- 1 | package algorithm.sort; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/1/25 6 | */ 7 | public class Selection { 8 | public static void main(String[] args) { 9 | long staruTime = System.currentTimeMillis(); 10 | int[] num = new int[10]; 11 | for (int i = 0; i < num.length; i++) 12 | num[i] = (int) (Math.random() * 100); 13 | 14 | for (int n : num) 15 | System.out.print(n + " "); 16 | System.out.println(); 17 | 18 | sort(num); 19 | for (int i : num) 20 | System.out.print(i + " "); 21 | long endTime = System.currentTimeMillis(); 22 | System.out.println(); 23 | System.out.println((endTime - staruTime) + "ms"); 24 | } 25 | 26 | public static void sort(int[] a) { 27 | for (int i = 0; i < a.length; i++) { 28 | int min = i; 29 | //选出之后待排序中值最小的位置 30 | for (int j = i + 1; j < a.length; j++) { 31 | if (a[j] < a[min]) { 32 | min = j; 33 | } 34 | } 35 | //最小值不等于当前值时进行交换 36 | if (min != i) { 37 | int temp = a[i]; 38 | a[i] = a[min]; 39 | a[min] = temp; 40 | } 41 | } 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/dp/KingMiningGold.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode.dp; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/12/27 6 | */ 7 | public class KingMiningGold { 8 | public static void main(String[] args) { 9 | int g[] = {0, 200, 300, 350, 400, 500}; 10 | int p[] = {0, 3, 2, 4, 5, 5}; 11 | System.out.println(new KingMiningGold().kingMiningGold(g, p, 10)); 12 | } 13 | 14 | /** 15 | * 16 | * @param g 17 | * @param p 18 | * @param sum 19 | * @return 20 | */ 21 | public int kingMiningGold(int[] g, int[] p, int sum) { 22 | int m = g.length; 23 | int n = sum; 24 | int[][] dp = new int[m + 1][n + 1]; 25 | for (int i = 1; i < m + 1; i++) { 26 | for (int j = 1; j < n + 1; j++) { 27 | if (j >= p[i - 1]) { 28 | dp[i][j] = Math.max(dp[i - 1][j - p[i - 1]] + g[i - 1], dp[i - 1][j]); 29 | } else { 30 | dp[i][j] = dp[i - 1][j]; 31 | } 32 | 33 | } 34 | } 35 | for (int i = 0; i < m + 1; i++) { 36 | for (int j = 0; j < n + 1; j++) { 37 | System.out.print(dp[i][j] + "\t"); 38 | } 39 | System.out.println(); 40 | } 41 | 42 | return dp[m][n]; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test3.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | /** 4 | * 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数, 5 | * 输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 6 | * 7 | * @author 李文浩 8 | * @version 2017/7/20. 9 | */ 10 | public class Test3 { 11 | public static void main(String[] args) { 12 | int[][] array = {{1, 5, 9}, {2, 6, 10}}; 13 | Test3 solution = new Test3(); 14 | System.out.println(solution.Find(3, array)); 15 | } 16 | 17 | public boolean Find(int target, int[][] array) { 18 | if (array == null || array.length < 1 || array[0].length < 1) { 19 | return false; 20 | } 21 | 22 | int rows = array.length; // 数组的行数 23 | int cols = array[1].length; // 数组行的列数 24 | 25 | int row = 0; // 起始开始的行号 26 | int col = cols - 1; // 起始开始的列号 27 | 28 | // 要查找的位置确保在数组之内 29 | while (row >= 0 && row < rows && col >= 0 && col < cols) { 30 | if (array[row][col] == target) { // 如果找到了就直接退出 31 | return true; 32 | } else if (array[row][col] > target) { // 如果找到的数比要找的数大,说明要找的数在当前数的左边 33 | col--; // 列数减一,代表向左移动 34 | } else { // 如果找到的数比要找的数小,说明要找的数在当前数的下边 35 | row++; // 行数加一,代表向下移动 36 | } 37 | } 38 | 39 | return false; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_23_MergekSortedLists.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/7/2 6 | */ 7 | public class L_23_MergekSortedLists { 8 | public class ListNode { 9 | int val; 10 | ListNode next; 11 | 12 | ListNode(int x) { 13 | val = x; 14 | } 15 | } 16 | 17 | public ListNode mergeKLists(ListNode[] lists) { 18 | if (lists == null) { 19 | return null; 20 | } 21 | ListNode head = null; 22 | for (int i = 0; i < lists.length; i++) { 23 | head = mergeTwoLists(head, lists[i]); 24 | } 25 | return head; 26 | } 27 | 28 | public ListNode mergeTwoLists(ListNode l1, ListNode l2) { 29 | ListNode head = new ListNode(-1); 30 | ListNode current = head; 31 | while (l1 != null && l2 != null) { 32 | if (l1.val < l2.val) { 33 | current.next = l1; 34 | l1 = l1.next; 35 | } else { 36 | current.next = l2; 37 | l2 = l2.next; 38 | } 39 | current = current.next; 40 | } 41 | 42 | 43 | if (l1 != null) { 44 | current.next = l1; 45 | } 46 | if (l2 != null) { 47 | current.next = l2; 48 | } 49 | 50 | 51 | return head.next; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/algorithm/sort/Test1.java: -------------------------------------------------------------------------------- 1 | package algorithm.sort; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * @author 李文浩 7 | * @date 2018/11/16 8 | */ 9 | public class Test1 { 10 | public static void main(String[] args) { 11 | String s1 = "12ddddd3##"; 12 | Stack stack1 = new Stack<>(); 13 | for (int i = 0; i < s1.length(); i++) { 14 | if (s1.charAt(i) != '#') { 15 | stack1.push(s1.charAt(i)); 16 | } else { 17 | if (!stack1.isEmpty()) { 18 | stack1.pop(); 19 | } 20 | } 21 | } 22 | 23 | String s2 = "dddd###DDDD"; 24 | Stack stack2 = new Stack<>(); 25 | for (int i = 0; i < s2.length(); i++) { 26 | if (s2.charAt(i) != '#') { 27 | stack2.push(s2.charAt(i)); 28 | } else { 29 | if (!stack2.isEmpty()) { 30 | stack2.pop(); 31 | } 32 | } 33 | } 34 | boolean result = false; 35 | while ((!stack1.isEmpty() && !stack2.isEmpty()) && (stack1.peek() == stack2.peek())) { 36 | stack1.pop(); 37 | stack2.pop(); 38 | } 39 | if (stack1.isEmpty() && stack2.isEmpty()) { 40 | result = true; 41 | } 42 | System.out.println(result); 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/dp/L_322_CoinChange.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode.dp; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * @author 李文浩 7 | * @date 2018/4/23 8 | */ 9 | public class L_322_CoinChange { 10 | public static void main(String[] args) { 11 | System.out.println(new L_322_CoinChange().coinChange(new int[]{1, 3, 5}, 4)); 12 | System.out.println(new L_322_CoinChange().coinChange(new int[]{1}, 1)); 13 | System.out.println(new L_322_CoinChange().coinChange(new int[]{2}, 1)); 14 | } 15 | 16 | /** 17 | * 状态转移方程: 18 | * dp[i] = min(dp[i-coins[0]]...dp[i-coins[j]]...dp[i-coins[coins.len-1]])+1 19 | * 20 | * @param coins 21 | * @param amount 22 | * @return 23 | */ 24 | public int coinChange(int[] coins, int amount) { 25 | int dp[] = new int[amount + 1]; 26 | Arrays.fill(dp, -1); 27 | dp[0] = 0; 28 | for (int i = 1; i < dp.length; i++) { 29 | boolean flag = false; 30 | int min = Integer.MAX_VALUE; 31 | for (int coin : coins) { 32 | if (i - coin >= 0 && dp[i - coin] != -1) { 33 | flag = true; 34 | min = Math.min(min, dp[i - coin]); 35 | } 36 | } 37 | if (flag) { 38 | dp[i] = min + 1; 39 | } 40 | } 41 | return dp[amount]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/dp/L_64_MinimumPathSum.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode.dp; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/12/27 6 | */ 7 | public class L_64_MinimumPathSum { 8 | public static void main(String[] args) { 9 | int[][] nums = { 10 | {1, 3, 1}, 11 | {1, 5, 1}, 12 | {4, 2, 1} 13 | }; 14 | System.out.println(new L_64_MinimumPathSum().minPathSum(nums)); 15 | int[][] nums2 = { 16 | {1, 0} 17 | }; 18 | System.out.println(new L_64_MinimumPathSum().minPathSum(nums2)); 19 | } 20 | 21 | /** 22 | * 62题变种而已 23 | * 24 | * @param grid 25 | * @return 26 | */ 27 | public int minPathSum(int[][] grid) { 28 | if (grid == null || grid.length == 0) { 29 | return 0; 30 | } 31 | int n = grid.length; 32 | int m = grid[0].length; 33 | for (int i = 1; i < n; i++) { 34 | grid[i][0] = grid[i - 1][0] + grid[i][0]; 35 | } 36 | for (int i = 1; i < m; i++) { 37 | grid[0][i] = grid[0][i - 1] + grid[0][i]; 38 | } 39 | for (int i = 1; i < n; i++) { 40 | for (int j = 1; j < m; j++) { 41 | grid[i][j] = Math.min(grid[i][j - 1], grid[i - 1][j]) + grid[i][j]; 42 | } 43 | } 44 | return grid[n - 1][m - 1]; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_35_SearchInsertPosition.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/12/15 6 | */ 7 | public class L_35_SearchInsertPosition { 8 | public static void main(String[] args) { 9 | int[] nums = {1, 3, 5, 6, 8}; 10 | int n = 7; 11 | System.out.println(new L_35_SearchInsertPosition().searchInsert(nums, 0)); 12 | System.out.println(new L_35_SearchInsertPosition().searchInsert(nums, 2)); 13 | System.out.println(new L_35_SearchInsertPosition().searchInsert(nums, 3)); 14 | System.out.println(new L_35_SearchInsertPosition().searchInsert(nums, 6)); 15 | System.out.println(new L_35_SearchInsertPosition().searchInsert(nums, 7)); 16 | System.out.println(new L_35_SearchInsertPosition().searchInsert(nums, 10)); 17 | 18 | } 19 | 20 | /** 21 | * 二分查找 22 | * 没找到的话low 代表插入的序号 23 | * @param nums 24 | * @param target 25 | * @return 26 | */ 27 | public int searchInsert(int[] nums, int target) { 28 | int low = 0, high = nums.length - 1; 29 | while (low <= high) { 30 | int mid = low + (high - low) / 2; 31 | if (nums[mid] < target) { 32 | low = mid + 1; 33 | } else if (nums[mid] > target) { 34 | high = mid - 1; 35 | } else { 36 | return mid; 37 | } 38 | } 39 | return low; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test12.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | /** 4 | * @author 李文浩 5 | * @version 2017/8/13. 6 | *

7 | *

8 | * 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。 9 | * 请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? 10 | *

11 | * 思路 12 | * 要么横着摆,要么竖着摆,所以只有两种可能 13 | * 1. n-1 横着一根 14 | * 2. n-2 竖着摆两根 15 | */ 16 | public class Test12 { 17 | public static void main(String[] args) { 18 | Test12 test12 = new Test12(); 19 | System.out.println(test12.RectCover(10)); 20 | System.out.println(test12.RectCover2(0)); 21 | } 22 | 23 | /** 24 | * 递归 25 | * 26 | * @param target 27 | * @return 28 | */ 29 | public int RectCover(int target) { 30 | if (target == 0) { 31 | return 0; 32 | } else if (target == 1) { 33 | return 1; 34 | } else if (target == 2) { 35 | return 2; 36 | } else { 37 | return RectCover(target - 1) + RectCover(target - 2); 38 | } 39 | } 40 | 41 | /** 42 | * 迭代 43 | * 44 | * @param target 45 | * @return 46 | */ 47 | public int RectCover2(int target) { 48 | if (target == 0) { 49 | return 0; 50 | } 51 | int f1 = 0, f2 = 1; 52 | for (int i = 0; i <= target; i++) { 53 | f1 = f1 + f2; 54 | f2 = f1 - f2; 55 | } 56 | return f1; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_147_InsertionSortList.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/2/21 6 | */ 7 | 8 | 9 | public class L_147_InsertionSortList { 10 | /** 11 | * 新建一个链表,然后遍历原链表按序插入新链表 12 | * 13 | * @param head 14 | * @return 15 | */ 16 | public ListNode insertionSortList(ListNode head) { 17 | if (head == null || head.next == null) { 18 | return head; 19 | } 20 | 21 | ListNode headNext = head.next; 22 | head.next = null; 23 | //遍历原链表 24 | for (ListNode node = headNext, nodeNext = node.next; node != null; node = nodeNext, nodeNext = nodeNext == null ? null : nodeNext.next) { 25 | ListNode p1 = head, p2 = null; 26 | //插入新链表 27 | for (; p1 != null; p2 = p1, p1 = p1.next) { 28 | if (p1.val > node.val) { 29 | //小于头节点 30 | if (p1 == head) { 31 | head = node; 32 | head.next = p1; 33 | 34 | } else { 35 | //在中间 36 | p2.next = node; 37 | node.next = p1; 38 | } 39 | break; 40 | } 41 | } 42 | //大于尾节点 43 | if (p1 == null) { 44 | p2.next = node; 45 | node.next = null; 46 | } 47 | } 48 | 49 | 50 | return head; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test9.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | /** 4 | * @author 李文浩 5 | * @version 2017/8/5. 6 | */ 7 | public class Test9 { 8 | public static void main(String[] args) { 9 | Test9 test9 = new Test9(); 10 | System.out.println(test9.Fibonacci(10)); 11 | System.out.println(test9.Fibonacci2(10)); 12 | } 13 | 14 | 15 | /** 16 | * 为什么不采用递归?因为递归实际是大量调用自身,当数量足够大的时候,需要同时保存成千上百个调用记录,容易发生内存溢出。 17 | * 怎么优化? 18 | * 1. 采用尾递归,但是Java并没有基于尾递归进行优化,也就是说Java中采用递归还是无法避免很容易发生"栈溢出"错误(stack overflow)。 19 | * 因为尾递归都是位于调用函数的最后一行,此时可以删除以前所保存的函数内变量,想当于每次只调用了一个函数。 20 | * 2. 采用迭代 21 | * 22 | * @param n 23 | * @return 24 | */ 25 | public int Fibonacci(int n) { 26 | if (n <= 0) { 27 | return 0; 28 | } 29 | int f1 = 0, f2 = 1; 30 | for (int i = 1; i <= n; i++) { 31 | f1 = f1 + f2; 32 | f2 = f1 - f2; 33 | } 34 | // 下面这种写法更为巧妙 35 | // while (n-- > 0) { 36 | // f1 = f1 + f2; 37 | // f2 = f1 - f2; 38 | // } 39 | return f1; 40 | } 41 | 42 | 43 | /** 44 | * 采用递归的方式 45 | * 46 | * @param n 47 | * @return 48 | */ 49 | public int Fibonacci2(int n) { 50 | if (n <= 0) { 51 | return 0; 52 | } 53 | if (n == 1 || n == 2) { 54 | return 1; 55 | } else { 56 | return Fibonacci2(n - 1) + Fibonacci2(n - 2); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/dp/L_140_WordBreakII.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode.dp; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * @author 李文浩 7 | * @date 2018/3/18 8 | */ 9 | public class L_140_WordBreakII { 10 | 11 | public static void main(String[] args) { 12 | String s = "pineapplepenapple"; 13 | String[] wordDict = {"apple", "pen", "applepen", "pine", "pineapple"}; 14 | List set = new ArrayList<>(Arrays.asList(wordDict)); 15 | System.out.println(new L_140_WordBreakII().wordBreak(s, set)); 16 | } 17 | 18 | public ArrayList wordBreak(String s, List wordDict) { 19 | return DFS(s, wordDict, new HashMap>()); 20 | } 21 | 22 | // DFS function returns an array including all substrings derived from s. 23 | ArrayList DFS(String s, List wordDict, HashMap> map) { 24 | if (map.containsKey(s)) 25 | return map.get(s); 26 | 27 | ArrayList res = new ArrayList(); 28 | if (s.length() == 0) { 29 | res.add(""); 30 | return res; 31 | } 32 | for (String word : wordDict) { 33 | if (s.startsWith(word)) { 34 | List sublist = DFS(s.substring(word.length()), wordDict, map); 35 | for (String sub : sublist) 36 | res.add(word + (sub.isEmpty() ? "" : " ") + sub); 37 | } 38 | } 39 | map.put(s, res); 40 | return res; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/dp/L_72_EditDistance.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode.dp; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2019-1-16 6 | */ 7 | public class L_72_EditDistance { 8 | public static void main(String[] args) { 9 | System.out.println(new L_72_EditDistance().minDistance("", "")); 10 | System.out.println(new L_72_EditDistance().minDistance("a", "b")); 11 | System.out.println(new L_72_EditDistance().minDistance("aa", "bb")); 12 | System.out.println(new L_72_EditDistance().minDistance("horse", "ros")); 13 | System.out.println(new L_72_EditDistance().minDistance("intention", "execution")); 14 | System.out.println(new L_72_EditDistance().minDistance("plasma", "altruism")); 15 | } 16 | 17 | public int minDistance(String word1, String word2) { 18 | int n = word1.length(); 19 | int m = word2.length(); 20 | int[][] dp = new int[n + 1][m + 1]; 21 | for (int i = 0; i < m + 1; i++) { 22 | dp[0][i] = i; 23 | } 24 | for (int i = 0; i < n + 1; i++) { 25 | dp[i][0] = i; 26 | } 27 | for (int i = 1; i < n + 1; i++) { 28 | for (int j = 1; j < m + 1; j++) { 29 | if (word1.charAt(i - 1) == word2.charAt(j - 1)) { 30 | dp[i][j] = dp[i - 1][j - 1]; 31 | } else { 32 | dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1; 33 | } 34 | } 35 | } 36 | return dp[n][m]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/dp/L_91_DecodeWays.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode.dp; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/5/6 6 | */ 7 | public class L_91_DecodeWays { 8 | public static void main(String[] args) { 9 | 10 | String str = "11020"; 11 | str.replaceAll("'10'", "3"); 12 | "re".replaceAll("re", "d"); 13 | System.out.println("re".replaceAll("re", "d")); 14 | System.out.println(str.replaceAll("10|20", "3")); 15 | System.out.println(new L_91_DecodeWays().numDecodings(str)); 16 | 17 | } 18 | 19 | public int numDecodings(String s) { 20 | if (s == null || s.length() == 0) { 21 | return 0; 22 | } 23 | int n = s.length(); 24 | char[] c = s.toCharArray(); 25 | // 对于台阶,需要前两步的值,所以数组最小是3 26 | int[] step = new int[Math.max(n + 1, 3)]; 27 | step[0] = 1; 28 | step[1] = 0; 29 | // 第一个字符不是0,则第一步初始为1 30 | if (c[0] != '0') { 31 | step[1] = 1; 32 | } 33 | // step[i] = step[i - 1] + step[i - 2]; 34 | // 只不过加step[i - 2]时,需要对c[i - 2]和c[i - 1]判断,组合是否<=26 35 | for (int i = 2; i <= n; i++) { 36 | step[i] = 0; 37 | if (c[i - 1] != '0') { 38 | step[i] += step[i - 1]; 39 | } 40 | if (c[i - 2] != '0') { 41 | if ((c[i - 2] - '0') * 10 + (c[i - 1] - '0') <= 26) { 42 | step[i] += step[i - 2]; 43 | } 44 | } 45 | } 46 | return step[n]; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test15.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | /** 4 | * @author 李文浩 5 | * @version 2017/9/4. 6 | *

7 | * 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分, 8 | * 所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。 9 | */ 10 | public class Test15 { 11 | public static void main(String[] args) { 12 | int[] arr = {1, 80, 10, 89, 84, 48, 45, 53, 28, 75, 25}; 13 | // int[] arr = new int[10]; 14 | // for (int i = 0; i < arr.length; i++) { 15 | // arr[i] = (int) (Math.random() * 100); 16 | // } 17 | Test15 test15 = new Test15(); 18 | test15.reOrderArray(arr); 19 | 20 | } 21 | 22 | /** 23 | * 冒泡排序 24 | * 遍历数组,如果是奇数开始向前交换,然后再从此奇数开始,如果是偶数就交换,直到第一个数 25 | * 26 | * @param array 27 | */ 28 | public void reOrderArray(int[] array) { 29 | for (int i = 0; i < array.length; i++) { 30 | System.out.print(array[i] + " "); 31 | } 32 | System.out.println(); 33 | for (int i = 1; i < array.length; i++) { 34 | if (array[i] % 2 == 1) { 35 | for (int j = i; j > 0; j--) { 36 | if (array[j - 1] % 2 == 0) { 37 | int temp = array[j - 1]; 38 | array[j - 1] = array[j]; 39 | array[j] = temp; 40 | } 41 | } 42 | } 43 | } 44 | for (int i = 0; i < array.length; i++) { 45 | System.out.print(array[i] + " "); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_111_MinimumDepthOfBinaryTree.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | 4 | /** 5 | * Given a binary tree, find its minimum depth.The minimum depth is the number 6 | * of roots along the shortest path from the root root down to the nearest leaf root. 7 | *

8 | * 思路: 9 | * 递归,若为空树返回0; 10 | * 若左子树为空,则返回右子树的最小深度+1;(加1是因为要加上根这一层,下同) 11 | * 若右子树为空,则返回左子树的最小深度+1; 12 | * 若左右子树均不为空,则取左、右子树最小深度的较小值,+1; 13 | * 14 | * @author 李文浩 15 | * @date 2018/2/12 16 | */ 17 | public class L_111_MinimumDepthOfBinaryTree { 18 | 19 | public static class TreeNode { 20 | int val; 21 | TreeNode left; 22 | TreeNode right; 23 | 24 | TreeNode(int x) { 25 | val = x; 26 | } 27 | } 28 | 29 | public static void main(String[] args) { 30 | Object object = new Object(); 31 | object = 10; 32 | System.out.println(object); 33 | } 34 | 35 | public int minDepth(TreeNode root) { 36 | if (root == null) { 37 | return 0; 38 | } 39 | TreeNode left = root.left, right = root.right; 40 | if (left == null && right == null) { 41 | return 1; 42 | } 43 | if (left == null && right != null) { 44 | return minDepth(right) + 1; 45 | } 46 | if (right == null && left != null) { 47 | return minDepth(left) + 1; 48 | } 49 | int leftHeight = minDepth(left); 50 | int rightHeight = minDepth(right); 51 | return leftHeight < rightHeight ? leftHeight + 1 : rightHeight + 1; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_199_BinaryTreeRightSideView.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | 4 | import java.util.ArrayList; 5 | import java.util.LinkedList; 6 | import java.util.List; 7 | import java.util.Queue; 8 | 9 | /** 10 | * @author 李文浩 11 | * @date 2018/3/31 12 | */ 13 | public class L_199_BinaryTreeRightSideView { 14 | static class TreeNode { 15 | int val; 16 | TreeNode left; 17 | TreeNode right; 18 | 19 | TreeNode(int x) { 20 | val = x; 21 | } 22 | } 23 | 24 | /** 25 | * 因为题目的二叉树并不是满二叉树,所以采用层序遍历的方式。 26 | * 将以前层序遍历中一个个出队的方式变为一层层出队, 27 | * 这样就能定位最右边的节点。 28 | * @param root 29 | * @return 30 | */ 31 | public List rightSideView(TreeNode root) { 32 | List list = new ArrayList<>(); 33 | if (root == null) { 34 | return list; 35 | } 36 | Queue queue = new LinkedList<>(); 37 | queue.offer(root); 38 | while (!queue.isEmpty()) { 39 | int size = queue.size(); 40 | //将每一层的节点都出队 41 | for (int i = 0; i < size; i++) { 42 | TreeNode treeNode = queue.poll(); 43 | if (i == 0) { 44 | list.add(treeNode.val); 45 | } 46 | if (treeNode.right != null) { 47 | queue.offer(treeNode.right); 48 | } 49 | if (treeNode.left != null) { 50 | queue.offer(treeNode.left); 51 | } 52 | } 53 | } 54 | return list; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/algorithm/sort/Radix.java: -------------------------------------------------------------------------------- 1 | package algorithm.sort; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * @author 李文浩 7 | * @date 2018/2/5 8 | */ 9 | public class Radix { 10 | public static void sort(int[] arr) { 11 | if (arr.length <= 1) return; 12 | 13 | //取得数组中的最大数,并取得位数 14 | int max = 0; 15 | for (int i = 0; i < arr.length; i++) { 16 | if (max < arr[i]) { 17 | max = arr[i]; 18 | } 19 | } 20 | int maxDigit = 1; 21 | while (max / 10 > 0) { 22 | maxDigit++; 23 | max = max / 10; 24 | } 25 | //申请一个桶空间 26 | int[][] buckets = new int[10][arr.length]; 27 | int base = 10; 28 | 29 | //从低位到高位,对每一位遍历,将所有元素分配到桶中 30 | for (int i = 0; i < maxDigit; i++) { 31 | int[] bktLen = new int[10]; //存储各个桶中存储元素的数量 32 | 33 | //分配:将所有元素分配到桶中 34 | for (int j = 0; j < arr.length; j++) { 35 | int whichBucket = (arr[j] % base) / (base / 10); 36 | buckets[whichBucket][bktLen[whichBucket]] = arr[j]; 37 | bktLen[whichBucket]++; 38 | } 39 | 40 | //收集:将不同桶里数据挨个捞出来,为下一轮高位排序做准备,由于靠近桶底的元素排名靠前,因此从桶底先捞 41 | int k = 0; 42 | for (int b = 0; b < buckets.length; b++) { 43 | for (int p = 0; p < bktLen[b]; p++) { 44 | arr[k++] = buckets[b][p]; 45 | } 46 | } 47 | System.out.println("Sorting: " + Arrays.toString(arr)); 48 | base *= 10; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/algorithm/search/BinarySearch.java: -------------------------------------------------------------------------------- 1 | package algorithm.search; 2 | 3 | /** 4 | * 二分查找的时间复杂度是 O(lg n) 5 | * 二分查找的查找要求是有序数组 6 | * 7 | * @author 李文浩 8 | * @version 2017/8/5. 9 | */ 10 | public class BinarySearch { 11 | 12 | public static void main(String[] args) { 13 | int[] a = {1, 2, 3, 4, 5, 6}; 14 | BinarySearch binarySearch = new BinarySearch(); 15 | System.out.println(binarySearch.bsearchWithoutRecursion(a, 2)); 16 | } 17 | 18 | /** 19 | * 递归 20 | * 21 | * @param array 22 | * @param low 23 | * @param high 24 | * @param target 25 | * @return 26 | */ 27 | int binarysearch(int array[], int low, int high, int target) { 28 | if (low > high) return -1; 29 | int mid = low + (high - low) / 2; 30 | if (array[mid] > target) 31 | return binarysearch(array, low, mid - 1, target); 32 | if (array[mid] < target) 33 | return binarysearch(array, mid + 1, high, target); 34 | return mid; 35 | } 36 | 37 | /** 38 | * 非递归 39 | * 40 | * @param a 41 | * @param key 42 | * @return 43 | */ 44 | int bsearchWithoutRecursion(int a[], int key) { 45 | int low = 0; 46 | int high = a.length - 1; 47 | while (low <= high) { 48 | int mid = low + (high - low) / 2; 49 | if (a[mid] > key) 50 | high = mid - 1; 51 | else if (a[mid] < key) 52 | low = mid + 1; 53 | else 54 | return mid; 55 | } 56 | return -1; 57 | } 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test8.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | /** 4 | * 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 5 | * 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。 6 | * 7 | * @author 李文浩 8 | * @version 2017/8/5. 9 | */ 10 | public class Test8 { 11 | 12 | public static void main(String[] args) { 13 | // int[] a = {6, 3, 4, 5}; 14 | int[] a = {6501, 6828, 6963, 7036, 7422, 7674, 8146, 8468, 8704, 8717, 9170, 9359, 9719, 9895, 9896, 9913, 9962, 154, 293, 334, 492, 1323, 1479, 1539, 1727, 1870, 1943, 2383, 2392, 2996, 3282, 3812, 3903, 4465, 4605, 4665, 4772, 4828, 5142, 5437, 5448, 5668, 5706, 5725, 6300, 6335}; 15 | Test8 test8 = new Test8(); 16 | System.out.println(test8.minNumberInRotateArray(a)); 17 | } 18 | 19 | /** 20 | * 思路: 21 | * 我们的目标是最小的那个数,即接近有最大的数向最小的数过渡的那个点, 22 | * 因此,通过判断array[mid]与array[0]的大小关系,即可判断到底是low增加还是hight减小。 23 | * 24 | * @param array 25 | * @return 26 | */ 27 | public int minNumberInRotateArray(int[] array) { 28 | if (array.length == 0) { 29 | return 0; 30 | } 31 | int low = 0, high = array.length - 1; 32 | while (low <= high) { 33 | int mid = (low + high) / 2; 34 | if (array[mid] < array[mid - 1]) { 35 | return array[mid]; 36 | } else { 37 | if (array[mid] < array[0]) { 38 | high = mid + 1; 39 | } else { 40 | low = mid - 1; 41 | } 42 | } 43 | } 44 | return 888888; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 我觉得学习算法的态度就是思考,思考,再思考。 3 | 4 | 1. 第一个是思考是指 **面对** ,不要看见算法题就觉得很难,不去想,然后通过搜索来得到答案,可是你应该明白,在如今这个互联网时代,资料随处可得,重要的是你面对问题的时候是否可以真正分析,调试,解决它。 5 | 2. 第二个思考是通过你所具备的知识和能够查阅的资料去 **解决** 它,无论怎么样,其实我们都是可以对一个问题有办法解决,最差的也可以通过穷举法来列出来,只不过这样很傻就是了,然后我们通过查看别人的优秀解法来完善自己的解法。 6 | 3. 第三个思考是根据实际情况来 **选择** 实际的解法,因为很多时候最优的解决其实并不适用于现在情况,这个时候就要求我们能够有所取舍。 7 | 8 | 9 | 我希望通过学习算法来让自己考虑问题变得全面和面对一个自己没有遇到过的问题(无论是逻辑上的还是业务上)能够冷静地思考解决问题的方法。 10 | 11 | # 基本数据结构 12 | 13 | **线性**: 14 | 15 | - 链表 16 | - [面试中的Java链表](http://www.cnblogs.com/morethink/p/7401101.html) 17 | - [Java实现单链表的快速排序和归并排序](http://www.cnblogs.com/morethink/p/8452914.html) 18 | - 栈 19 | - 队列(优先队列) 20 | 21 | **非线性**: 22 | - 树 23 | - [Java实现二叉树的先序、中序、后序、层序遍历(递归和非递归)](http://www.cnblogs.com/morethink/p/7265817.html) 24 | - 图 25 | 26 | # 排序 27 | $$ 28 | \begin{cases}内部排序 \begin{cases}插入排序\begin{cases}直接插入排序\\希尔排序\end{cases}\\选择排序\begin{cases}简单选择排序\\堆排序\end{cases}\\交换排序\begin{cases}冒泡排序\\快速排序 \end{cases}\\归并排序\\ 29 | 基数排序\end{cases}\\外部排序 \end{cases} 30 | $$ 31 | 32 | - [Java实现八大排序算法](http://www.cnblogs.com/morethink/p/8419151.html) 33 | 34 | 35 | # 查找 36 | 37 | - 二分查找 38 | - [Java实现二分查找算法](http://www.cnblogs.com/morethink/p/8379475.html) 39 | 40 | # 字符串 41 | 42 | # 递归 43 | 44 | [尾调用优化](http://www.ruanyifeng.com/blog/2015/04/tail-call.html) 45 | 46 | # 动态规划 47 | 48 | 49 | 50 | # LeetCode 51 | 52 | LeetCode Java Solutions:https://leetcode.morethink.cn/#/source/ 53 | 54 | # 剑指offer 55 | 56 | 所有题都在牛客网 ac: 57 | 题目地址:https://www.nowcoder.com/ta/coding-interviews 58 | Java solutions:https://github.com/morethink/algorithm/tree/master/src/algorithm/swordtooffer 59 | 60 | 牛客网的第n题对应于`src/algorithm/swordtooffer`中的`Testn+2`。 61 | -------------------------------------------------------------------------------- /src/main/java/algorithm/sort/Merge.java: -------------------------------------------------------------------------------- 1 | package algorithm.sort; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/2/4 6 | */ 7 | public class Merge { 8 | 9 | //归并所需的辅助数组 10 | private static int[] aux; 11 | 12 | public static void sort(int[] a) { 13 | //一次性分配空间 14 | aux = new int[a.length]; 15 | sort(a, 0, a.length - 1); 16 | } 17 | 18 | public static void sort(int[] a, int low, int high) { 19 | if (low >= high) { 20 | return; 21 | } 22 | int mid = (low + high) / 2; 23 | //将左半边排序 24 | sort(a, low, mid); 25 | //将右半边排序 26 | sort(a, mid + 1, high); 27 | merge(a, low, mid, high); 28 | } 29 | 30 | /** 31 | * 该方法先将所有元素复制到aux[]中,然后在归并会a[]中。方法咋归并时(第二个for循环) 32 | * 进行了4个条件判断: 33 | * - 左半边用尽(取右半边的元素) 34 | * - 右半边用尽(取左半边的元素) 35 | * - 右半边的当前元素小于左半边的当前元素(取右半边的元素) 36 | * - 右半边的当前元素大于等于左半边的当前元素(取左半边的元素) 37 | * @param a 38 | * @param low 39 | * @param mid 40 | * @param high 41 | */ 42 | public static void merge(int[] a, int low, int mid, int high) { 43 | //将a[low..mid]和a[mid+1..high]归并 44 | int i = low, j = mid + 1; 45 | for (int k = low; k <= high; k++) { 46 | aux[k] = a[k]; 47 | } 48 | 49 | for (int k = low; k <= high; k++) { 50 | if (i > mid) { 51 | a[k] = aux[j++]; 52 | } else if (j > high) { 53 | a[k] = aux[i++]; 54 | } else if (aux[j] < aux[i]) { 55 | a[k] = aux[j++]; 56 | } else { 57 | a[k] = aux[i++]; 58 | } 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test13.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | /** 4 | * @author 李文浩 5 | * @version 2017/8/13. 6 | *

7 | * 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。 8 | */ 9 | public class Test13 { 10 | public static void main(String[] args) { 11 | Test13 test13 = new Test13(); 12 | System.out.println(test13.NumberOf1(-2147483648)); 13 | System.out.println(test13.NumberOf1(2147483647)); 14 | System.out.println(Integer.toBinaryString(-2147483648 / 2)); 15 | System.out.println(Integer.toBinaryString(2147483647)); 16 | System.out.println(-2147483648 % 2); 17 | } 18 | 19 | /** 20 | *

21 | * 如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0, 22 | * 原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。 23 | *

24 | *

25 | *

26 | *

27 | * 举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1, 28 | * 而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。 29 | * 这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。 30 | * 如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0. 31 | * 那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。 32 | * 33 | * @param n 34 | * @return 35 | */ 36 | public int NumberOf1(int n) { 37 | int count = 0; 38 | while (n != 0) { 39 | count++; 40 | n = n & (n - 1); 41 | } 42 | return count; 43 | } 44 | 45 | /** 46 | * 前人的智慧 47 | * 48 | * @param n 49 | * @return 50 | */ 51 | public int NumberOf12(int n) { 52 | 53 | // return Integer.toBinaryString(n).replaceAll("0","").length(); 54 | return Integer.bitCount(n); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/dp/L_63_UniquePathsII.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode.dp; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/12/27 6 | */ 7 | public class L_63_UniquePathsII { 8 | public static void main(String[] args) { 9 | int[][] nums = { 10 | {0, 0, 0}, 11 | {0, 1, 0}, 12 | {0, 0, 0}, 13 | {0, 0, 0} 14 | }; 15 | System.out.println(new L_63_UniquePathsII().uniquePathsWithObstacles(nums)); 16 | int[][] nums2 = { 17 | {1, 0} 18 | }; 19 | System.out.println(new L_63_UniquePathsII().uniquePathsWithObstacles(nums2)); 20 | } 21 | 22 | /** 23 | * 其实跟62题很像,只不过需要先处理一下数据 24 | * 25 | * @param obstacleGrid 26 | * @return 27 | */ 28 | public int uniquePathsWithObstacles(int[][] obstacleGrid) { 29 | if (obstacleGrid == null || obstacleGrid.length == 0) { 30 | return 1; 31 | } 32 | int n = obstacleGrid.length; 33 | int m = obstacleGrid[0].length; 34 | int[][] dp = new int[n][m]; 35 | for (int i = 0; i < n; i++) { 36 | if (obstacleGrid[i][0] == 1) { 37 | break; 38 | } 39 | dp[i][0] = 1; 40 | } 41 | for (int i = 0; i < m; i++) { 42 | if (obstacleGrid[0][i] == 1) { 43 | break; 44 | } 45 | dp[0][i] = 1; 46 | } 47 | 48 | for (int i = 1; i < n; i++) { 49 | for (int j = 1; j < m; j++) { 50 | dp[i][j] = dp[i][j - 1] + dp[i - 1][j]; 51 | if (obstacleGrid[i][j] == 1) { 52 | dp[i][j] = 0; 53 | } 54 | } 55 | } 56 | return dp[n - 1][m - 1]; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_150_EvaluateReversePolishNotation.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * Evaluate the value of an arithmetic expression in Reverse Polish Notation. 7 | * Valid operators are+,-,*,/. Each operand may be an integer or another expression. 8 | * 思路: 9 | * 操作数入栈;遇到操作符时,操作数出栈,求值,将结果入栈; 10 | * 当一遍后,栈顶就是表达式的值。因此逆波兰表达式的求值使用堆栈结构很容易实现,并且能很快求值。 11 | * 12 | * @author 李文浩 13 | * @date 2018/2/13 14 | */ 15 | public class L_150_EvaluateReversePolishNotation { 16 | public static void main(String[] args) { 17 | String[] tokens = {"3", "4", "-", "5", "+"}; 18 | System.out.println(evalRPN(tokens)); 19 | } 20 | 21 | 22 | public static int evalRPN(String[] tokens) { 23 | Stack operands = new Stack<>(); 24 | for (String token : tokens) { 25 | if (token.equals("+") || token.equals("-") || token.equals("*") || token.equals("/")) { 26 | int num2 = operands.pop(); 27 | int num1 = operands.pop(); 28 | int num = 0; 29 | switch (token) { 30 | case "+": 31 | num = num1 + num2; 32 | break; 33 | case "-": 34 | num = num1 - num2; 35 | break; 36 | case "*": 37 | num = num1 * num2; 38 | break; 39 | case "/": 40 | num = num1 / num2; 41 | break; 42 | } 43 | operands.push(num); 44 | } else { 45 | operands.push(Integer.valueOf(token)); 46 | } 47 | } 48 | return operands.peek(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/Demo.java: -------------------------------------------------------------------------------- 1 | import com.google.common.cache.Cache; 2 | import com.google.common.cache.CacheBuilder; 3 | import com.google.common.cache.CacheLoader; 4 | import com.google.common.cache.LoadingCache; 5 | 6 | import java.util.concurrent.Callable; 7 | import java.util.concurrent.ExecutionException; 8 | 9 | /** 10 | * @author 李文浩 11 | * @date 2018/12/28 12 | */ 13 | public class Demo { 14 | /** 15 | * CacheLoader 16 | */ 17 | public void loadingCache() { 18 | LoadingCache graphs = CacheBuilder.newBuilder() 19 | .maximumSize(1000).build(new CacheLoader() { 20 | @Override 21 | public String load(String key) throws Exception { 22 | System.out.println("key:" + key); 23 | if ("key".equals(key)) { 24 | return "key return result"; 25 | } else { 26 | return "get-if-absent-compute"; 27 | } 28 | } 29 | }); 30 | String resultVal = null; 31 | try { 32 | resultVal = graphs.get("key"); 33 | } catch (ExecutionException e) { 34 | e.printStackTrace(); 35 | } 36 | 37 | System.out.println(resultVal); 38 | } 39 | 40 | /** 41 | * Callable 42 | */ 43 | public void callablex() throws ExecutionException { 44 | Cache cache = CacheBuilder.newBuilder() 45 | .maximumSize(1000).build(); 46 | String result = cache.get("key", new Callable() { 47 | public String call() { 48 | return "result"; 49 | } 50 | }); 51 | System.out.println(result); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/algorithm/sort/Heap.java: -------------------------------------------------------------------------------- 1 | package algorithm.sort; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/2/3 6 | */ 7 | public class Heap { 8 | 9 | public static void main(String[] args) { 10 | int[] a = {10, 2, 6, 3, 50, 26, 34, 6}; 11 | // int[] a = new int[20]; 12 | // for (int i = 0; i < a.length; i++) 13 | // a[i] = (int) (Math.random() * 10000); 14 | // num[0] = 0; 15 | System.out.print("排序前: "); 16 | for (int n : a) 17 | System.out.print(n + " "); 18 | System.out.println(); 19 | Heap.sort(a); 20 | System.out.print("排序后: "); 21 | for (int i : a) 22 | System.out.print(i + " "); 23 | } 24 | 25 | /** 26 | * @param a 27 | */ 28 | public static void sort(int[] a) { 29 | 30 | for (int i = a.length - 1; i > 0; i--) { 31 | max_heapify(a, i); 32 | 33 | //堆顶元素(第一个元素)与Kn交换 34 | int temp = a[0]; 35 | a[0] = a[i]; 36 | a[i] = temp; 37 | } 38 | } 39 | 40 | /*** 41 | * 42 | * 将数组堆化 43 | * i = 第一个非叶子节点。 44 | * 从第一个非叶子节点开始即可。无需从最后一个叶子节点开始。 45 | * 叶子节点可以看作已符合堆要求的节点,根节点就是它自己且自己以下值为最大。 46 | * 47 | * @param a 48 | * @param n 49 | */ 50 | public static void max_heapify(int[] a, int n) { 51 | int child; 52 | for (int i = (n - 1) / 2; i >= 0; i--) { 53 | //左子节点位置 54 | child = 2 * i + 1; 55 | //右子节点存在且大于左子节点,child变成右子节点 56 | if (child != n && a[child] < a[child + 1]) { 57 | child++; 58 | } 59 | //交换父节点与左右子节点中的最大值 60 | if (a[i] < a[child]) { 61 | int temp = a[i]; 62 | a[i] = a[child]; 63 | a[child] = temp; 64 | } 65 | } 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/main/java/algorithm/tencent/Main.java: -------------------------------------------------------------------------------- 1 | package algorithm.tencent; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * @author 李文浩 8 | * @date 2018/1/28 9 | */ 10 | public class Main { 11 | public static List list; 12 | 13 | static { 14 | list = new ArrayList(40); 15 | list.add(2); 16 | } 17 | 18 | public static void main(String[] args) { 19 | System.out.println(getPrimePair(12)); 20 | } 21 | 22 | public static int getPrimePair(int n) { 23 | if (n <=3 || n >= 1000) { 24 | return 0; 25 | } 26 | //0 1 不算做素数,2一定是素数 27 | for (int i = 3; i < n; i += 2) { 28 | if (isPrime(i)) { 29 | list.add(i); 30 | } 31 | } 32 | // System.out.print("素数为:"); 33 | // for (Integer integer : list) { 34 | // System.out.print(integer + " "); 35 | // } 36 | // System.out.println(); 37 | Integer[] integers = new Integer[list.size()]; 38 | list.toArray(integers); 39 | 40 | int num = 0; 41 | for (int i = 0; i < integers.length; i++) { 42 | for (int j = integers.length - 1; j >= i; j--) { 43 | if ((integers[i] + integers[j]) == n) { 44 | num++; 45 | } 46 | } 47 | } 48 | return num; 49 | } 50 | 51 | public static boolean isPrime(int n) { 52 | for (int i = 0; list.get(i) <= (int) Math.sqrt(n); i++) { 53 | if (n % list.get(i) == 0) 54 | return false; 55 | } 56 | return true; 57 | } 58 | 59 | 60 | public static void prime() { 61 | for (int n = 0; n <= 200; n++) { 62 | if (isPrime(n)) { 63 | list.add(n); 64 | } 65 | } 66 | } 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test11.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | /** 4 | * @author 李文浩 5 | * @version 2017/8/13. 6 | *

7 | *

8 | * 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。 9 | *

10 | * 思路 11 | * 因为n级台阶,第一步有n种跳法:跳1级、跳2级、到跳n级 12 | * 跳1级,剩下n-1级,则剩下跳法是f(n-1) 13 | * 跳2级,剩下n-2级,则剩下跳法是f(n-2) 14 | * 所以f(n)=f(n-1)+f(n-2)+...+f(1) 15 | * 因为f(n-1)=f(n-2)+f(n-3)+...+f(1) 16 | * 所以f(n)=f(n-1)+f(n-1)=2*f(n-1) 17 | */ 18 | public class Test11 { 19 | public static void main(String[] args) { 20 | Test11 test11 = new Test11(); 21 | System.out.println(test11.JumpFloorII(10)); 22 | System.out.println(test11.JumpFloorII2(10)); 23 | System.out.println(test11.JumpFloorII3(10)); 24 | } 25 | 26 | /** 27 | * 递归 28 | * 29 | * @param target 30 | * @return 31 | */ 32 | public int JumpFloorII2(int target) { 33 | if (target <= 0) { 34 | return -1; 35 | } else if (target == 1) { 36 | return 1; 37 | } else { 38 | return 2 * JumpFloorII2(target - 1); 39 | } 40 | } 41 | 42 | /** 43 | * 迭代 44 | * 45 | * @param target 46 | * @return 47 | */ 48 | public int JumpFloorII3(int target) { 49 | int f = 1; 50 | for (int i = 1; i < target; i++) { 51 | f *= 2; 52 | } 53 | return f; 54 | } 55 | 56 | /** 57 | * 此种思路充分说明了数学是算法的皇后 58 | *

59 | * 每个台阶都有跳与不跳两种情况(第n阶台阶必须跳),所以总共有 2 ^ (n - 1)种跳法, 60 | * 61 | * @param target 62 | * @return 63 | */ 64 | public int JumpFloorII(int target) { 65 | //使用Math类的方法 66 | // return (int) Math.pow(2, target - 1); 67 | //2^(n-1)可以用位移操作进行,更快 68 | return 1 << --target; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/dp/L_494_TargetSum.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode.dp; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2019-1-20 6 | */ 7 | public class L_494_TargetSum { 8 | public static void main(String[] args) { 9 | int[] nums = {1, 12, 4, 9, 2, 3, 4, 5}; 10 | System.out.println(new L_494_TargetSum().findTargetSumWays(nums, 10)); 11 | int[] nums2 = {1000}; 12 | System.out.println(new L_494_TargetSum().findTargetSumWays(nums2, -1000)); 13 | int[] nums3 = {1, 2, 7, 9, 981}; 14 | System.out.println(new L_494_TargetSum().findTargetSumWays(nums3, 1000000000)); 15 | int[] nums4 = {0, 0, 0, 0, 0, 0, 0, 0, 1}; 16 | System.out.println(new L_494_TargetSum().findTargetSumWays(nums4, 1)); 17 | int[] nums5 = {1, 1, 1, 1, 1}; 18 | System.out.println(new L_494_TargetSum().findTargetSumWays(nums5, 3)); 19 | } 20 | 21 | public int findTargetSumWays(int[] nums, int S) { 22 | int sum = 0; 23 | for (int i = 0; i < nums.length; i++) { 24 | sum += nums[i]; 25 | } 26 | if (S > sum || S < -sum) { 27 | return 0; 28 | } 29 | int[][] dp = new int[nums.length + 1][sum * 2 + 1]; 30 | dp[0][0 + sum] = 1; 31 | for (int i = 1; i <= nums.length; i++) { 32 | for (int j = 0; j <= sum * 2; j++) { 33 | if (j - nums[i - 1] >= 0) { 34 | dp[i][j] += dp[i - 1][j - nums[i - 1]]; 35 | } 36 | if (j + nums[i - 1] <= 2 * sum) { 37 | dp[i][j] += dp[i - 1][j + nums[i - 1]]; 38 | } 39 | } 40 | } 41 | for (int i = 0; i <= nums.length; i++) { 42 | for (int j = 0; j <= sum * 2; j++) { 43 | System.out.print(dp[i][j] + "\t"); 44 | } 45 | System.out.println(); 46 | } 47 | return dp[nums.length][S + sum]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_94_BinaryTreeInorderTraversal.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Stack; 6 | 7 | /** 8 | * @author 李文浩 9 | * @date 2018/2/24 10 | */ 11 | public class L_94_BinaryTreeInorderTraversal { 12 | static class TreeNode { 13 | int val; 14 | TreeNode left; 15 | TreeNode right; 16 | 17 | TreeNode(int x) { 18 | val = x; 19 | } 20 | } 21 | 22 | public static void main(String[] args) { 23 | TreeNode treeNode1 = new TreeNode(1); 24 | TreeNode treeNode2 = new TreeNode(2); 25 | TreeNode treeNode3 = new TreeNode(3); 26 | TreeNode treeNode4 = new TreeNode(4); 27 | TreeNode treeNode5 = new TreeNode(5); 28 | TreeNode treeNode6 = new TreeNode(6); 29 | treeNode1.left = treeNode2; 30 | treeNode1.right = treeNode3; 31 | treeNode2.left = treeNode4; 32 | treeNode2.right = treeNode5; 33 | treeNode3.left = treeNode6; 34 | List list = new L_94_BinaryTreeInorderTraversal().inorderTraversal(treeNode1); 35 | 36 | for (Integer integer : list) { 37 | System.out.print(integer + " "); 38 | } 39 | 40 | } 41 | 42 | public List inorderTraversal(TreeNode root) { 43 | List list = new ArrayList<>(); 44 | TreeNode node = root; 45 | Stack stack = new Stack<>(); 46 | //左子树全部入栈 47 | while (node != null) { 48 | stack.push(node); 49 | node = node.left; 50 | } 51 | //将弹出节点和弹出节点的左子树入栈 52 | while (!stack.empty()) { 53 | node = stack.pop(); 54 | list.add(node.val); 55 | node = node.right; 56 | while (node != null) { 57 | stack.push(node); 58 | node = node.left; 59 | } 60 | 61 | } 62 | 63 | 64 | return list; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test14.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | /** 4 | * @author 李文浩 5 | * @version 2017/8/13. 6 | *

7 | *

8 | * 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。 9 | */ 10 | public class Test14 { 11 | public static void main(String[] args) { 12 | Test14 test14 = new Test14(); 13 | System.out.println(test14.Power(2, 3)); 14 | System.out.println(test14.Power(0, 5)); 15 | System.out.println(test14.Power(2, -3)); 16 | System.out.println(test14.Power(2, 0)); 17 | System.out.println(test14.Power(-2, 2)); 18 | } 19 | 20 | /** 21 | * 代码的完整性 22 | * 23 | * @param base 24 | * @param exponent 25 | * @return 26 | */ 27 | public double Power(double base, int exponent) { 28 | if (exponent == 0) { 29 | return 1; 30 | } 31 | boolean isFu = false; 32 | if (exponent < 0) { 33 | exponent = -exponent; 34 | isFu = true; 35 | } 36 | 37 | double num = base; 38 | while (exponent-- > 1) { 39 | num *= base; 40 | } 41 | return isFu ? 1 / num : num; 42 | } 43 | 44 | /** 45 | * 采取移位操作 46 | * 47 | * @param base 48 | * @param n 49 | * @return 50 | */ 51 | public double Power2(double base, int n) { 52 | double res = 1, curr = base; 53 | int exponent; 54 | if (n > 0) { 55 | exponent = n; 56 | } else if (n < 0) { 57 | if (base == 0) 58 | throw new RuntimeException("分母不能为0"); 59 | exponent = -n; 60 | } else {// n==0 61 | return 1;// 0的0次方 62 | } 63 | while (exponent != 0) { 64 | if ((exponent & 1) == 1) 65 | res *= curr; 66 | curr *= curr;// 翻倍 67 | exponent >>= 1;// 右移一位 68 | } 69 | return n >= 0 ? res : (1 / res); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_144_BinaryTreePreorderTraversal.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Stack; 6 | 7 | /** 8 | * @author 李文浩 9 | * @date 2018/2/24 10 | */ 11 | public class L_144_BinaryTreePreorderTraversal { 12 | static class TreeNode { 13 | int val; 14 | TreeNode left; 15 | TreeNode right; 16 | 17 | TreeNode(int x) { 18 | val = x; 19 | } 20 | } 21 | 22 | public static void main(String[] args) { 23 | TreeNode treeNode1 = new TreeNode(1); 24 | TreeNode treeNode2 = new TreeNode(2); 25 | TreeNode treeNode3 = new TreeNode(3); 26 | TreeNode treeNode4 = new TreeNode(4); 27 | TreeNode treeNode5 = new TreeNode(5); 28 | TreeNode treeNode6 = new TreeNode(6); 29 | treeNode1.left = treeNode2; 30 | treeNode1.right = treeNode3; 31 | treeNode2.left = treeNode4; 32 | treeNode2.right = treeNode5; 33 | treeNode3.left = treeNode6; 34 | List list = new L_144_BinaryTreePreorderTraversal().preorderTraversal(treeNode1); 35 | 36 | for (Integer integer : list) { 37 | System.out.print(integer + " "); 38 | } 39 | 40 | } 41 | 42 | public List preorderTraversal(TreeNode root) { 43 | List list = new ArrayList<>(); 44 | TreeNode node = root; 45 | Stack stack = new Stack<>(); 46 | //左子树全部入栈 47 | while (node != null) { 48 | list.add(node.val); 49 | stack.push(node); 50 | node = node.left; 51 | } 52 | //将弹出节点和弹出节点的左子树入栈 53 | while (!stack.empty()) { 54 | node = stack.pop().right; 55 | while (node != null) { 56 | list.add(node.val); 57 | stack.push(node); 58 | node = node.left; 59 | } 60 | 61 | } 62 | 63 | 64 | return list; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test5.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | /** 4 | * @author 李文浩 5 | * @version 2017/7/29. 6 | *

7 | * 输入一个链表,从尾到头打印链表每个节点的值。 8 | */ 9 | 10 | 11 | import java.util.ArrayList; 12 | 13 | public class Test5 { 14 | public static class ListNode { 15 | int val; 16 | ListNode next = null; 17 | 18 | ListNode(int val) { 19 | this.val = val; 20 | } 21 | } 22 | 23 | public static void main(String[] args) { 24 | ListNode listNode = new ListNode(1); 25 | ListNode listNode2 = new ListNode(2); 26 | ListNode listNode3 = new ListNode(3); 27 | listNode.next = listNode2; 28 | listNode2.next = listNode3; 29 | System.out.println(new Test5().printListFromTailToHead(listNode)); 30 | } 31 | 32 | /** 33 | * 解决问题有两种方法 34 | * 1. 翻转链表 35 | * 2. 将链表的值存储之后在翻转 36 | * 37 | * 查看别人发现,还可使用递归,其实你应该明白凡是跟栈相关的都可通过递归实现 38 | * @param listNode 39 | * @return 40 | */ 41 | // public ArrayList printListFromTailToHead(ListNode listNode) { 42 | // Stack stack = new Stack<>(); 43 | // while (listNode != null) { 44 | // stack.push(listNode.val); 45 | // listNode = listNode.next; 46 | // 47 | // } 48 | // ArrayList arrayList = new ArrayList(); 49 | // while (!stack.isEmpty()) { 50 | // arrayList.add(stack.pop()); 51 | // } 52 | // System.out.println(arrayList); 53 | // return arrayList; 54 | // } 55 | 56 | /** 57 | * 递归版本 58 | * 59 | * @param listNode 60 | * @return 61 | */ 62 | 63 | private ArrayList arrayList = new ArrayList<>(); 64 | 65 | public ArrayList printListFromTailToHead(ListNode listNode) { 66 | if (listNode == null) { 67 | return arrayList; 68 | } else { 69 | printListFromTailToHead(listNode.next); 70 | arrayList.add(listNode.val); 71 | } 72 | return arrayList; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test21ClockwisePrintingMatrix.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * @author 李文浩 7 | * @date 2018/7/6 8 | */ 9 | public class Test21ClockwisePrintingMatrix { 10 | 11 | public static void main(String[] args) { 12 | int[][] matrix = { 13 | {1, 2, 3, 4}, 14 | {5, 6, 7, 8}, 15 | {9, 10, 11, 12}, 16 | {13, 14, 15, 16} 17 | }; 18 | 19 | matrix = new int[][]{ 20 | {1} 21 | }; 22 | // 23 | //matrix = new int[][]{ 24 | // {1, 2}, 25 | // {3, 4} 26 | //}; 27 | 28 | 29 | System.out.println(new Test21ClockwisePrintingMatrix().printMatrix(matrix)); 30 | } 31 | 32 | public ArrayList printMatrix(int[][] matrix) { 33 | 34 | int i = 0, j = 0, m1 = 0, m2 = 0, n1 = matrix.length, n2 = matrix[0].length, time = n1; 35 | ArrayList list = new ArrayList<>(); 36 | if (matrix.length == 1) { 37 | for (int val : matrix[0]) { 38 | list.add(val); 39 | } 40 | return list; 41 | } 42 | while (time != 0) { 43 | while (j < n2 - 1) { 44 | System.out.println(matrix[i][j]+"j++"); 45 | list.add(matrix[i][j]); 46 | j++; 47 | } 48 | time--; 49 | n2--; 50 | while (i < n1 - 1) { 51 | System.out.println(matrix[i][j]+"i++"); 52 | list.add(matrix[i][j]); 53 | i++; 54 | } 55 | n1--; 56 | 57 | 58 | while (j > m2) { 59 | System.out.println(matrix[i][j]+"j--"); 60 | list.add(matrix[i][j]); 61 | j--; 62 | } 63 | time--; 64 | m2++; 65 | 66 | while (i > m1) { 67 | System.out.println(matrix[i][j]+"i--"); 68 | list.add(matrix[i][j]); 69 | i--; 70 | } 71 | m1++; 72 | 73 | i = m1; 74 | j = m2; 75 | } 76 | 77 | return list; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test18_MergeTwoSortedLinkedlists.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/7/2 6 | */ 7 | public class Test18_MergeTwoSortedLinkedlists { 8 | private static class ListNode { 9 | int val; 10 | ListNode next = null; 11 | 12 | ListNode(int val) { 13 | this.val = val; 14 | } 15 | } 16 | 17 | public static void main(String[] args) { 18 | ListNode list1 = null, list2 = null; 19 | for (int i = 1; i < 10; i += 2) { 20 | list1 = addBack(list1, i); 21 | //list2 = addBack(list2, i + 1); 22 | } 23 | list2 = addBack(list2, 11); 24 | list2 = addBack(list2, 12); 25 | list2 = addBack(list2, 13); 26 | list2 = addBack(list2, 14); 27 | 28 | show(new Test18_MergeTwoSortedLinkedlists().Merge(list1, list2)); 29 | //show(new Test18_MergeTwoSortedLinkedlists().Merge2(list1, list2)); 30 | } 31 | 32 | public static ListNode addBack(ListNode head, int val) { 33 | ListNode newNode = new ListNode(val); 34 | newNode.next = null; 35 | ListNode node = head; 36 | if (head == null) { 37 | head = newNode; 38 | return head; 39 | } 40 | while (node.next != null) { 41 | node = node.next; 42 | } 43 | node.next = newNode; 44 | 45 | return head; 46 | } 47 | 48 | public static void show(ListNode node) { 49 | while (node != null) { 50 | System.out.print(node.val + " "); 51 | node = node.next; 52 | } 53 | 54 | System.out.println(); 55 | } 56 | 57 | public ListNode Merge(ListNode list1, ListNode list2) { 58 | ListNode head = new ListNode(-1); 59 | ListNode current = head; 60 | while (list1 != null && list2 != null) { 61 | if (list1.val < list2.val) { 62 | current.next = list1; 63 | list1 = list1.next; 64 | } else { 65 | current.next = list2; 66 | list2 = list2.next; 67 | } 68 | current = current.next; 69 | } 70 | 71 | 72 | if (list1 != null) { 73 | current.next = list1; 74 | } 75 | if (list2 != null) { 76 | current.next = list2; 77 | } 78 | 79 | 80 | return head.next; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test19_SubstructureOfTheTree.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/7/2 6 | */ 7 | public class Test19_SubstructureOfTheTree { 8 | public static class TreeNode { 9 | int val = 0; 10 | TreeNode left = null; 11 | TreeNode right = null; 12 | 13 | public TreeNode(int val) { 14 | this.val = val; 15 | } 16 | } 17 | 18 | 19 | public static void main(String[] args) { 20 | 21 | TreeNode root1 = new TreeNode(8); 22 | TreeNode root2 = new TreeNode(8); 23 | TreeNode root3 = new TreeNode(7); 24 | TreeNode root4 = new TreeNode(9); 25 | TreeNode root5 = new TreeNode(2); 26 | 27 | root1.left = root2; 28 | root1.right = root3; 29 | root2.left = root4; 30 | root2.right = root5; 31 | 32 | System.out.println(new Test19_SubstructureOfTheTree().HasSubtree(root1, root2)); 33 | ; 34 | } 35 | 36 | /** 37 | * 思路:参考剑指offer 38 | * 1. 首先设置标志位result = false,因为一旦匹配成功result就设为true, 39 | * 剩下的代码不会执行,如果匹配不成功,默认返回false 40 | * 2. 递归思想,如果根节点相同则递归调用DoesTree1HaveTree2(), 41 | * 如果根节点不相同,则判断tree1的左子树和tree2是否相同, 42 | * 再判断右子树和tree2是否相同 43 | * 3. 注意null的条件,HasSubTree中,如果两棵树都不为空才进行判断, 44 | * DoesTree1HasTree2中,如果Tree2为空,则说明第二棵树遍历完了,即匹配成功, 45 | * tree1为空有两种情况(1)如果tree1为空&&tree2不为空说明不匹配, 46 | * (2)如果tree1为空,tree2为空,说明匹配。 47 | *   48 | * 49 | * @param root1 50 | * @param root2 51 | * @return 52 | */ 53 | public boolean HasSubtree(TreeNode root1, TreeNode root2) { 54 | boolean result = false; 55 | if (root1 != null && root2 != null) { 56 | if (root1.val == root2.val) { 57 | result = DoesTree1HaveTree2(root1, root2); 58 | } 59 | if (!result) { 60 | result = HasSubtree(root1.left, root2); 61 | } 62 | if (!result) { 63 | result = HasSubtree(root1.right, root2); 64 | } 65 | } 66 | return result; 67 | } 68 | 69 | public boolean DoesTree1HaveTree2(TreeNode root1, TreeNode root2) { 70 | if (root1 == null && root2 != null) return false; 71 | if (root2 == null) return true; 72 | if (root1.val != root2.val) return false; 73 | return DoesTree1HaveTree2(root1.left, root2.left) && DoesTree1HaveTree2(root1.right, root2.right); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/main/java/algorithm/sort/Test2.java: -------------------------------------------------------------------------------- 1 | package algorithm.sort; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | 6 | /** 7 | * @author 李文浩 8 | * @date 2018/11/16 9 | */ 10 | public class Test2 { 11 | public static void main(String[] args) { 12 | String s1 = "12ddddd3###"; 13 | Queue queue1 = new LinkedList<>(); 14 | int cycle = 0; 15 | for (int i = 0; i < s1.length(); i++) { 16 | if (s1.charAt(i) == '#') { 17 | if (cycle > 0) { 18 | for (int j = 0; j < cycle; j++) { 19 | ((LinkedList) queue1).pop(); 20 | ((LinkedList) queue1).pop(); 21 | } 22 | for (int j = cycle; j >= 0; j--) { 23 | ((LinkedList) queue1).push(i - (2 * j + 1)); 24 | ((LinkedList) queue1).push(i - (2 * j)); 25 | } 26 | } else { 27 | queue1.offer(i - 1); 28 | queue1.offer(i); 29 | } 30 | cycle++; 31 | } else { 32 | cycle = 0; 33 | } 34 | } 35 | 36 | String s2 = "dddd###DDDD"; 37 | cycle = 0; 38 | Queue queue2 = new LinkedList<>(); 39 | for (int i = 0; i < s2.length(); i++) { 40 | if (s1.charAt(i) == '#') { 41 | if (cycle > 0) { 42 | for (int j = 0; j < cycle; j++) { 43 | ((LinkedList) queue2).pop(); 44 | ((LinkedList) queue2).pop(); 45 | } 46 | for (int j = cycle; j >= 0; j--) { 47 | ((LinkedList) queue2).push(i - (2 * j + 1)); 48 | ((LinkedList) queue2).push(i - (2 * j)); 49 | } 50 | } else { 51 | queue2.offer(i - 1); 52 | queue2.offer(i); 53 | } 54 | cycle++; 55 | } else { 56 | cycle = 0; 57 | } 58 | } 59 | boolean result = false; 60 | int i = 0, j = 0; 61 | while (i <= s1.length() && j < s2.length()) { 62 | if (i == queue1.peek()) { 63 | ((LinkedList) queue1).pop(); 64 | } 65 | if (j == queue2.peek()) { 66 | ((LinkedList) queue1).pop(); 67 | } 68 | i++; 69 | j++; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/algorithm/security/SHAUtil.java: -------------------------------------------------------------------------------- 1 | package algorithm.security; 2 | 3 | /** 4 | * @author 李文浩 5 | * @version 2017/3/26. 6 | */ 7 | 8 | import com.google.common.base.Strings; 9 | 10 | import java.security.MessageDigest; 11 | 12 | /** 13 | * SHA的全称是Secure Hash Algorithm,即安全散列算法 14 | * Created by fangzhipeng on 2017/3/21. 15 | */ 16 | public class SHAUtil { 17 | 18 | /** 19 | * 定义加密方式 20 | */ 21 | private final static String KEY_SHA = "SHA"; 22 | private final static String KEY_SHA1 = "SHA-1"; 23 | /** 24 | * 全局数组 25 | */ 26 | private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", 27 | "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; 28 | 29 | /** 30 | * 构造函数 31 | */ 32 | public SHAUtil() { 33 | 34 | } 35 | 36 | /** 37 | * SHA 加密 38 | * 39 | * @param data 需要加密的字节数组 40 | * @return 加密之后的字节数组 41 | * @throws Exception 42 | */ 43 | public static byte[] encryptSHA(byte[] data) throws Exception { 44 | // 创建具有指定算法名称的信息摘要 45 | // MessageDigest sha = MessageDigest.getInstance(KEY_SHA); 46 | MessageDigest sha = MessageDigest.getInstance(KEY_SHA1); 47 | // 使用指定的字节数组对摘要进行最后更新 48 | sha.update(data); 49 | // 完成摘要计算并返回 50 | return sha.digest(); 51 | } 52 | 53 | /** 54 | * SHA 加密 55 | * 56 | * @param data 需要加密的字符串 57 | * @return 加密之后的字符串 58 | * @throws Exception 59 | */ 60 | public static String encryptSHA(String data) throws Exception { 61 | // 验证传入的字符串 62 | if (Strings.isNullOrEmpty(data)) { 63 | return ""; 64 | } 65 | // 创建具有指定算法名称的信息摘要 66 | MessageDigest sha = MessageDigest.getInstance(KEY_SHA); 67 | // 使用指定的字节数组对摘要进行最后更新 68 | sha.update(data.getBytes()); 69 | // 完成摘要计算 70 | byte[] bytes = sha.digest(); 71 | // 将得到的字节数组变成字符串返回 72 | return byteArrayToHexString(bytes); 73 | } 74 | 75 | /** 76 | * 将一个字节转化成十六进制形式的字符串 77 | * 78 | * @param b 字节数组 79 | * @return 字符串 80 | */ 81 | private static String byteToHexString(byte b) { 82 | int ret = b; 83 | //System.out.println("ret = " + ret); 84 | if (ret < 0) { 85 | ret += 256; 86 | } 87 | int m = ret / 16; 88 | int n = ret % 16; 89 | return hexDigits[m] + hexDigits[n]; 90 | } 91 | 92 | /** 93 | * 转换字节数组为十六进制字符串 94 | * 95 | * @param bytes 字节数组 96 | * @return 十六进制字符串 97 | */ 98 | private static String byteArrayToHexString(byte[] bytes) { 99 | StringBuffer sb = new StringBuffer(); 100 | for (int i = 0; i < bytes.length; i++) { 101 | sb.append(byteToHexString(bytes[i])); 102 | } 103 | return sb.toString(); 104 | } 105 | 106 | /** 107 | * 测试方法 108 | * 109 | * @param args 110 | */ 111 | public static void main(String[] args) throws Exception { 112 | String key = "123"; 113 | System.out.println(encryptSHA(key)); 114 | } 115 | } -------------------------------------------------------------------------------- /src/main/java/algorithm/swordtooffer/Test6.java: -------------------------------------------------------------------------------- 1 | package algorithm.swordtooffer; 2 | 3 | /** 4 | * @author 李文浩 5 | * @version 2017/7/30. 6 | * 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 7 | * 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。 8 | * 思路 9 | */ 10 | 11 | 12 | public class Test6 { 13 | 14 | public static class TreeNode { 15 | int val; 16 | TreeNode left; 17 | TreeNode right; 18 | 19 | TreeNode(int x) { 20 | val = x; 21 | } 22 | } 23 | 24 | public TreeNode reConstructBinaryTree(int[] pre, int[] in) { 25 | 26 | if (pre == null || in == null || pre.length != in.length || in.length < 1) { 27 | return null; 28 | } 29 | 30 | return construct(pre, 0, pre.length - 1, in, 0, in.length - 1); 31 | // 下面是另一个更好的解法 32 | // return reConstructBinaryTree(pre, 0, pre.length - 1, in, 0, in.length - 1); 33 | } 34 | 35 | public static TreeNode construct(int[] preorder, int ps, int pe, int[] inorder, int is, int ie) { 36 | 37 | // 开始位置大于结束位置说明已经没有需要处理的元素了 38 | if (ps > pe) { 39 | return null; 40 | } 41 | // 取前序遍历的第一个数字,就是当前的根结点 42 | int value = preorder[ps]; 43 | int index = is; 44 | // 在中序遍历的数组中找根结点的位置 45 | while (index <= ie && inorder[index] != value) { 46 | index++; 47 | } 48 | 49 | // 如果在整个中序遍历的数组中没有找到,说明输入的参数是不合法的,抛出异常 50 | if (index > ie) { 51 | throw new RuntimeException("Invalid input"); 52 | } 53 | 54 | // 创建当前的根结点,并且为结点赋值 55 | TreeNode node = new TreeNode(value); 56 | 57 | 58 | // 递归构建当前根结点的左子树,左子树的元素个数:index-is+1个 59 | // 左子树对应的前序遍历的位置在[ps+1, ps+index-is] 60 | // 左子树对应的中序遍历的位置在[is, index-1] 61 | node.left = construct(preorder, ps + 1, ps + index - is, inorder, is, index - 1); 62 | // 递归构建当前根结点的右子树,右子树的元素个数:ie-index个 63 | // 右子树对应的前序遍历的位置在[ps+index-is+1, pe] 64 | // 右子树对应的中序遍历的位置在[index+1, ie] 65 | node.right = construct(preorder, ps + index - is + 1, pe, inorder, index + 1, ie); 66 | 67 | // 返回创建的根结点 68 | return node; 69 | } 70 | 71 | 72 | //前序遍历{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6} 73 | 74 | /** 75 | * 大神级解法 76 | * 77 | * @param pre 78 | * @param startPre 79 | * @param endPre 80 | * @param in 81 | * @param startIn 82 | * @param endIn 83 | * @return 84 | */ 85 | private TreeNode reConstructBinaryTree(int[] pre, int startPre, int endPre, int[] in, int startIn, int endIn) { 86 | 87 | if (startPre > endPre || startIn > endIn) 88 | return null; 89 | TreeNode root = new TreeNode(pre[startPre]); 90 | 91 | for (int i = startIn; i <= endIn; i++) 92 | if (in[i] == pre[startPre]) { 93 | root.left = reConstructBinaryTree(pre, startPre + 1, startPre + i - startIn, in, startIn, i - 1); 94 | root.right = reConstructBinaryTree(pre, i - startIn + startPre + 1, endPre, in, i + 1, endIn); 95 | } 96 | return root; 97 | } 98 | 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/algorithm/sort/Quick.java: -------------------------------------------------------------------------------- 1 | package algorithm.sort; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * Created by think on 2016/9/16. 7 | */ 8 | public class Quick { 9 | public static void sort(int[] a, int low, int high) { 10 | //已经排完 11 | if (low >= high) { 12 | return; 13 | } 14 | int left = low; 15 | int right = high; 16 | 17 | //保存基准值 18 | int pivot = a[left]; 19 | while (left < right) { 20 | //从后向前找到比基准小的元素 21 | while (left < right && a[right] >= pivot) { 22 | right--; 23 | } 24 | a[left] = a[right]; 25 | //从前往后找到比基准大的元素 26 | while (left < right && a[left] <= pivot) 27 | left++; 28 | a[right] = a[left]; 29 | } 30 | // 放置基准值,准备分治递归快排 31 | a[left] = pivot; 32 | sort(a, low, left - 1); 33 | sort(a, left + 1, high); 34 | } 35 | 36 | 37 | public static void sortThreeWay(int[] a, int lo, int hi) { 38 | if (lo >= hi) { 39 | return; 40 | } 41 | int v = a[lo], lt = lo, i = lo + 1, gt = hi; 42 | while (i <= gt) { 43 | if (a[i] < v) { 44 | swap(a, i++, lt++); 45 | } else if (a[i] > v) { 46 | swap(a, i, gt--); 47 | } else { 48 | i++; 49 | } 50 | } 51 | sortThreeWay(a, lo, lt - 1); 52 | sortThreeWay(a, gt + 1, hi); 53 | } 54 | 55 | private static void swap(int[] a, int i, int j) { 56 | int t = a[i]; 57 | a[i] = a[j]; 58 | a[j] = t; 59 | } 60 | 61 | public static void sortByStack(int[] a) { 62 | Stack stack = new Stack(); 63 | 64 | //初始状态的左右指针入栈 65 | stack.push(0); 66 | stack.push(a.length - 1); 67 | while (!stack.isEmpty()) { 68 | //出栈进行划分 69 | int high = stack.pop(); 70 | int low = stack.pop(); 71 | 72 | int pivotIndex = partition(a, low, high); 73 | 74 | //保存中间变量 75 | if (pivotIndex > low) { 76 | stack.push(low); 77 | stack.push(pivotIndex - 1); 78 | } 79 | if (pivotIndex < high && pivotIndex >= 0) { 80 | stack.push(pivotIndex + 1); 81 | stack.push(high); 82 | } 83 | } 84 | } 85 | 86 | private static int partition(int[] a, int low, int high) { 87 | if (low >= high) return -1; 88 | int left = low; 89 | int right = high; 90 | //保存基准的值 91 | int pivot = a[left]; 92 | while (left < right) { 93 | //从后向前找到比基准小的元素,插入到基准位置中 94 | while (left < right && a[right] >= pivot) { 95 | right--; 96 | } 97 | a[left] = a[right]; 98 | //从前往后找到比基准大的元素 99 | while (left < right && a[left] <= pivot) { 100 | left++; 101 | } 102 | a[right] = a[left]; 103 | } 104 | //放置基准值,准备分治递归快排 105 | a[left] = pivot; 106 | return left; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_204_Count_Primes.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by think on 2016/9/20. 8 | */ 9 | public class L_204_Count_Primes { 10 | public static void main(String[] args) { 11 | //获取开始时 12 | long startTime = System.currentTimeMillis(); 13 | System.out.println("The num is " + new L_204_Count_Primes().countPrimes4(2000000)); 14 | long endTime = System.currentTimeMillis(); 15 | //获取结束时间 16 | System.out.println("程序运行时间: " + (endTime - startTime) + "ms"); 17 | } 18 | 19 | /** 20 | * 穷举 21 | * 22 | * @param n 23 | * @return 24 | */ 25 | public int countPrimes(int n) { 26 | int num = 0; 27 | for (int i = 2; i < n; i++) { 28 | boolean flag = true; 29 | for (int j = 2; j < i - 1; j++) 30 | if (i % j == 0) { 31 | flag = false; 32 | break; 33 | } 34 | if (flag) { 35 | num++; 36 | } 37 | } 38 | return num; 39 | } 40 | 41 | /** 42 | * 只能是奇数且小于$\sqrt{n}$ 43 | * 44 | * @param n 45 | * @return 46 | */ 47 | public int countPrimes2(int n) { 48 | int num = 1; 49 | for (int i = 3; i < n; i += 2) { 50 | boolean flag = true; 51 | for (int j = 2; j <= (int) Math.sqrt(i); j++) 52 | if (i % j == 0) { 53 | flag = false; 54 | break; 55 | } 56 | if (flag) { 57 | num++; 58 | } 59 | } 60 | return num; 61 | } 62 | 63 | /** 64 | * 试除法 65 | * 66 | * @param n 67 | * @return 68 | */ 69 | public int countPrimes3(int n) { 70 | if (n < 3) { 71 | return 0; 72 | } 73 | //0 1 不算做素数,2一定是素数 74 | List list = new ArrayList<>(); 75 | list.add(2); 76 | boolean flag; 77 | for (int i = 3; i < n; i += 2) { 78 | flag = true; 79 | for (int j = 0; j < list.size() && list.get(j) <= (int) Math.sqrt(n); j++) { 80 | if (i % list.get(j) == 0) { 81 | flag = false; 82 | break; 83 | } 84 | } 85 | if (flag) { 86 | list.add(i); 87 | } 88 | } 89 | return list.size(); 90 | } 91 | 92 | /** 93 | * 筛法 94 | * 95 | * @param n 96 | * @return 97 | */ 98 | public int countPrimes4(int n) { 99 | if (n < 3) { 100 | return 0; 101 | } 102 | //false代表素数,true代表非素数 103 | boolean[] flags = new boolean[n]; 104 | //0不是素数 105 | flags[0] = true; 106 | //1不是素数 107 | flags[1] = true; 108 | int num = n - 2; 109 | for (int i = 2; i <= (int) Math.sqrt(n); i++) { 110 | //当i为素数时,i的所有倍数都不是素数 111 | if (!flags[i]) { 112 | for (int j = 2 * i; j < n; j += i) { 113 | if (!flags[j]) { 114 | flags[j] = true; 115 | num--; 116 | } 117 | } 118 | 119 | } 120 | } 121 | return num; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_143_Reorder_List.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/3/15 6 | */ 7 | public class L_143_Reorder_List { 8 | 9 | public static void main(String[] args) { 10 | 11 | ListNode head = new ListNode(1); 12 | for (int i = 2; i < 9; i++) 13 | head = addBack(head, i); 14 | System.out.print("reorder前:"); 15 | print(head); 16 | System.out.print("reorder后:"); 17 | new L_143_Reorder_List().reorderList(head); 18 | print(head); 19 | } 20 | 21 | public static ListNode addBack(ListNode head, int val) { 22 | ListNode newNode = new ListNode(val); 23 | newNode.next = null; 24 | ListNode node = head; 25 | if (head == null) { 26 | head = newNode; 27 | return head; 28 | } 29 | while (node.next != null) { 30 | node = node.next; 31 | } 32 | node.next = newNode; 33 | 34 | return head; 35 | } 36 | 37 | public static void print(ListNode node) { 38 | while (node != null) { 39 | System.out.print(node.val + " "); 40 | node = node.next; 41 | } 42 | System.out.println(); 43 | } 44 | 45 | 46 | /** 47 | * 通过快慢指针找到中间节点,拆分链表为两部分,将后半部分链表翻转,然后合并两部分 48 | * 49 | * @param head 50 | */ 51 | public void reorderList(ListNode head) { 52 | if (head == null || head.next == null) { 53 | return; 54 | } 55 | //快慢指针找到中间节点 56 | ListNode slow = head, fast = head, temp = null; 57 | while (fast.next != null && fast.next.next != null) { 58 | slow = slow.next; 59 | fast = fast.next.next; 60 | } 61 | //拆分链表为两部分 62 | temp = slow.next; 63 | slow.next = null; 64 | slow = temp; 65 | 66 | //后半边进行头插法,翻转链表 67 | ListNode node = slow.next; 68 | slow.next = null; 69 | while (node != null) { 70 | temp = node.next; 71 | node.next = slow; 72 | slow = node; 73 | node = temp; 74 | 75 | } 76 | //合并链表 77 | merge(head, slow); 78 | } 79 | 80 | public void merge(ListNode head1, ListNode head2) { 81 | ListNode p = head1, q = head2, temp; 82 | while (p != null && q != null) { 83 | temp = p.next; 84 | p.next = q; 85 | p = temp; 86 | temp = q.next; 87 | q.next = p; 88 | q = temp; 89 | 90 | } 91 | } 92 | 93 | /** 94 | * 暴力法,找到Ln节点,然后移动到L1和L2之间,以此类推 95 | * 96 | * @param head 97 | */ 98 | public void reorderList2(ListNode head) { 99 | if (head == null) { 100 | return; 101 | } 102 | ListNode p1 = head, p2 = head.next, node1 = p1, node2 = p2; 103 | 104 | while (p2 != null && p2.next != null) { 105 | 106 | //找到Ln 107 | while (node2.next != null) { 108 | node1 = node2; 109 | node2 = node2.next; 110 | } 111 | //移动Ln到L1和L2中间 112 | node1.next = null; 113 | p1.next = node2; 114 | node2.next = p2; 115 | //重新更改位置 116 | p1 = p2; 117 | p2 = p2.next; 118 | node2 = p2; 119 | node1 = p1; 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/algorithm/security/AESUtil.java: -------------------------------------------------------------------------------- 1 | package algorithm.security; 2 | 3 | /** 4 | * 5 | * https://www.jianshu.com/p/2196a601d444 6 | * 7 | * 8 | * @author 李文浩 9 | * @version 2017/3/26. 10 | */ 11 | 12 | import javax.crypto.BadPaddingException; 13 | import javax.crypto.Cipher; 14 | import javax.crypto.IllegalBlockSizeException; 15 | import javax.crypto.NoSuchPaddingException; 16 | import javax.crypto.spec.SecretKeySpec; 17 | import java.security.InvalidKeyException; 18 | import java.security.NoSuchAlgorithmException; 19 | import java.util.Base64; 20 | 21 | /** 22 | * Created by fangzhipeng on 2017/3/21. 23 | */ 24 | public class AESUtil { 25 | 26 | static byte[] key = "w@#$4@#$s^&3*&^4".getBytes(); 27 | 28 | final static String algorithm = "AES"; 29 | 30 | public static String encrypt(String data) { 31 | 32 | byte[] dataToSend = data.getBytes(); 33 | Cipher c = null; 34 | try { 35 | c = Cipher.getInstance(algorithm); 36 | } catch (NoSuchAlgorithmException e) { 37 | // TODO Auto-generated catch block 38 | e.printStackTrace(); 39 | } catch (NoSuchPaddingException e) { 40 | // TODO Auto-generated catch block 41 | e.printStackTrace(); 42 | } 43 | SecretKeySpec k = new SecretKeySpec(key, algorithm); 44 | try { 45 | c.init(Cipher.ENCRYPT_MODE, k); 46 | } catch (InvalidKeyException e) { 47 | // TODO Auto-generated catch block 48 | e.printStackTrace(); 49 | } 50 | byte[] encryptedData = "".getBytes(); 51 | try { 52 | encryptedData = c.doFinal(dataToSend); 53 | } catch (IllegalBlockSizeException e) { 54 | // TODO Auto-generated catch block 55 | e.printStackTrace(); 56 | } catch (BadPaddingException e) { 57 | // TODO Auto-generated catch block 58 | e.printStackTrace(); 59 | } 60 | byte[] encryptedByteValue = Base64.getEncoder().encode(encryptedData); 61 | return new String(encryptedByteValue);//.toString(); 62 | } 63 | 64 | public static String decrypt(String data) { 65 | 66 | byte[] encryptedData = Base64.getDecoder().decode(data); 67 | Cipher c = null; 68 | try { 69 | c = Cipher.getInstance(algorithm); 70 | } catch (NoSuchAlgorithmException e) { 71 | // TODO Auto-generated catch block 72 | e.printStackTrace(); 73 | } catch (NoSuchPaddingException e) { 74 | // TODO Auto-generated catch block 75 | e.printStackTrace(); 76 | } 77 | SecretKeySpec k = 78 | new SecretKeySpec(key, algorithm); 79 | try { 80 | c.init(Cipher.DECRYPT_MODE, k); 81 | } catch (InvalidKeyException e1) { 82 | // TODO Auto-generated catch block 83 | e1.printStackTrace(); 84 | } 85 | byte[] decrypted = null; 86 | try { 87 | decrypted = c.doFinal(encryptedData); 88 | } catch (IllegalBlockSizeException e) { 89 | // TODO Auto-generated catch block 90 | e.printStackTrace(); 91 | } catch (BadPaddingException e) { 92 | // TODO Auto-generated catch block 93 | e.printStackTrace(); 94 | } 95 | return new String(decrypted); 96 | } 97 | 98 | public static void main(String[] args) { 99 | String password = encrypt("12233440988:1239874389888:dd333"); 100 | System.out.println(password); 101 | System.out.println(decrypt(password)); 102 | } 103 | } -------------------------------------------------------------------------------- /src/main/java/algorithm/security/DESUtil.java: -------------------------------------------------------------------------------- 1 | package algorithm.security; 2 | 3 | /** 4 | * @author 李文浩 5 | * @version 2017/3/26. 6 | */ 7 | 8 | import sun.misc.BASE64Decoder; 9 | import sun.misc.BASE64Encoder; 10 | 11 | import javax.crypto.Cipher; 12 | import javax.crypto.SecretKey; 13 | import javax.crypto.SecretKeyFactory; 14 | import javax.crypto.spec.DESKeySpec; 15 | import java.io.IOException; 16 | import java.security.SecureRandom; 17 | 18 | 19 | /** 20 | * Data Encryptin Standard 21 | * 数据加密标准 22 | */ 23 | public class DESUtil { 24 | 25 | 26 | private final static String DES = "DES"; 27 | 28 | /** 29 | * Description 根据键值进行加密 30 | * 31 | * @param data 32 | * @param key 加密键byte数组 33 | * @return 34 | * @throws Exception 35 | */ 36 | public static String encrypt(String data, String key) throws Exception { 37 | byte[] bt = encrypt(data.getBytes(), key.getBytes()); 38 | String strs = new BASE64Encoder().encode(bt); 39 | return strs; 40 | } 41 | 42 | /** 43 | * Description 根据键值进行解密 44 | * 45 | * @param data 46 | * @param key 加密键byte数组 47 | * @return 48 | * @throws IOException 49 | * @throws Exception 50 | */ 51 | public static String decrypt(String data, String key) throws Exception, 52 | Exception { 53 | if (data == null) 54 | return null; 55 | BASE64Decoder decoder = new BASE64Decoder(); 56 | byte[] buf = decoder.decodeBuffer(data); 57 | byte[] bt = decrypt(buf, key.getBytes()); 58 | return new String(bt); 59 | } 60 | 61 | /** 62 | * Description 根据键值进行加密 63 | * 64 | * @param data 65 | * @param key 加密键byte数组 66 | * @return 67 | * @throws Exception 68 | */ 69 | private static byte[] encrypt(byte[] data, byte[] key) throws Exception { 70 | // 生成一个可信任的随机数源 71 | SecureRandom sr = new SecureRandom(); 72 | 73 | // 从原始密钥数据创建DESKeySpec对象 74 | DESKeySpec dks = new DESKeySpec(key); 75 | 76 | // 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象 77 | SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES); 78 | SecretKey securekey = keyFactory.generateSecret(dks); 79 | 80 | // Cipher对象实际完成加密操作 81 | Cipher cipher = Cipher.getInstance(DES); 82 | 83 | // 用密钥初始化Cipher对象 84 | cipher.init(Cipher.ENCRYPT_MODE, securekey, sr); 85 | 86 | return cipher.doFinal(data); 87 | } 88 | 89 | 90 | /** 91 | * Description 根据键值进行解密 92 | * 93 | * @param data 94 | * @param key 加密键byte数组 95 | * @return 96 | * @throws Exception 97 | */ 98 | private static byte[] decrypt(byte[] data, byte[] key) throws Exception { 99 | // 生成一个可信任的随机数源 100 | SecureRandom sr = new SecureRandom(); 101 | 102 | // 从原始密钥数据创建DESKeySpec对象 103 | DESKeySpec dks = new DESKeySpec(key); 104 | 105 | // 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象 106 | SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES); 107 | SecretKey securekey = keyFactory.generateSecret(dks); 108 | 109 | // Cipher对象实际完成解密操作 110 | Cipher cipher = Cipher.getInstance(DES); 111 | 112 | // 用密钥初始化Cipher对象 113 | cipher.init(Cipher.DECRYPT_MODE, securekey, sr); 114 | 115 | return cipher.doFinal(data); 116 | } 117 | 118 | public static void main(String[] args) throws Exception { 119 | String sStr = encrypt("122222112222:12343232323:jajwwwwslwskwkkwksk", "wew2323w233321ws233w"); 120 | System.out.println(sStr); 121 | String mStr = decrypt(sStr, "wew2323w233321ws233w"); 122 | System.out.println(mStr); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/algorithm/security/MD5Util.java: -------------------------------------------------------------------------------- 1 | package algorithm.security; 2 | 3 | /** 4 | * @author 李文浩 5 | * @version 2017/3/26. 6 | */ 7 | 8 | import java.math.BigInteger; 9 | import java.security.MessageDigest; 10 | import java.security.NoSuchAlgorithmException; 11 | 12 | /** 13 | * Message Digest Algorithm 5(信息摘要算法5) 14 | */ 15 | public class MD5Util { 16 | /** 17 | * Constructs the MD5Util object and sets the string whose MD5Util is to be 18 | * computed. 19 | * 20 | * @param inStr 21 | * the String whose MD5Util is to be computed 22 | */ 23 | 24 | 25 | public final static String COMMON_KEY = "zhongzhuoxin#@!321"; 26 | 27 | public MD5Util() { 28 | 29 | } 30 | 31 | public final static String str2MD5(String inStr) { 32 | char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 33 | 'a', 'b', 'c', 'd', 'e', 'f'}; 34 | try { 35 | byte[] strTemp = inStr.getBytes("UTF-8"); 36 | MessageDigest mdTemp = MessageDigest.getInstance("MD5"); 37 | mdTemp.update(strTemp); 38 | byte[] md = mdTemp.digest(); 39 | int j = md.length; 40 | char str[] = new char[j * 2]; 41 | int k = 0; 42 | int sum =0; 43 | for (int i = 0; i < md.length; i++) 44 | sum+=md[i]; 45 | System.out.println(Integer.toString(16)); 46 | 47 | for (int i = 0; i < j; i++) { 48 | byte byte0 = md[i]; 49 | str[k++] = hexDigits[byte0 >>> 4 & 0xf]; 50 | System.out.print(str[k - 1] + " "); 51 | str[k++] = hexDigits[byte0 & 0xf]; 52 | System.out.print(str[k - 1] + " "); 53 | 54 | } 55 | return new String(str); 56 | } catch (Exception e) { 57 | return null; 58 | } 59 | } 60 | 61 | 62 | //--MD5Util 63 | private static final char HEX_DIGITS[] = {'0', '1', '2', '3', '4', '5', 64 | '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 65 | 66 | public static String toHexString(byte[] b) { // String to byte 67 | StringBuilder sb = new StringBuilder(b.length * 2); 68 | for (int i = 0; i < b.length; i++) { 69 | sb.append(HEX_DIGITS[(b[i] & 0xf0) >>> 4]); 70 | sb.append(HEX_DIGITS[b[i] & 0x0f]); 71 | } 72 | return sb.toString(); 73 | } 74 | 75 | public static String AndroidMd5(String s) { 76 | try { 77 | // Create MD5Util Hash 78 | MessageDigest digest = MessageDigest 79 | .getInstance("MD5"); 80 | digest.update(s.getBytes()); 81 | byte messageDigest[] = digest.digest(); 82 | 83 | return toHexString(messageDigest); 84 | } catch (NoSuchAlgorithmException e) { 85 | e.printStackTrace(); 86 | } 87 | 88 | return ""; 89 | } 90 | 91 | public static void main(String[] args) { 92 | 93 | String m = MD5Util.str2MD5("a"); 94 | // String m = MD5Util.str2MD5("swwwwwwwwwwdkinner"); 95 | 96 | System.out.print(m.length() + " "); 97 | System.out.println(m); 98 | 99 | System.out.println(m.length() + " " + MD5Util.str2MD5("a")); 100 | 101 | // 生成一个MD5加密计算摘要 102 | MessageDigest md = null; 103 | try { 104 | md = MessageDigest.getInstance("MD5"); 105 | } catch (NoSuchAlgorithmException e) { 106 | e.printStackTrace(); 107 | } 108 | // 计算md5函数 109 | md.update("liwenhao".getBytes()); 110 | // digest()最后确定返回md5 hash值,返回值为8为字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符 111 | // BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值 112 | System.out.println(new BigInteger(1, md.digest()).toString(16)); 113 | 114 | } 115 | } -------------------------------------------------------------------------------- /src/main/java/algorithm/security/SM3Digest.java: -------------------------------------------------------------------------------- 1 | package algorithm.security; 2 | 3 | /** 4 | * @author 李文浩 5 | * @version 2017/6/27. 6 | */ 7 | 8 | public class SM3Digest { 9 | /** 10 | * SM3值的长度 11 | */ 12 | private static final int BYTE_LENGTH = 32; 13 | 14 | /** 15 | * SM3分组长度 16 | */ 17 | private static final int BLOCK_LENGTH = 64; 18 | 19 | /** 20 | * 缓冲区长度 21 | */ 22 | private static final int BUFFER_LENGTH = BLOCK_LENGTH * 1; 23 | 24 | /** 25 | * 缓冲区 26 | */ 27 | private byte[] xBuf = new byte[BUFFER_LENGTH]; 28 | 29 | /** 30 | * 缓冲区偏移量 31 | */ 32 | private int xBufOff; 33 | 34 | /** 35 | * 初始向量 36 | */ 37 | private byte[] V = SM3.iv.clone(); 38 | 39 | private int cntBlock = 0; 40 | 41 | public SM3Digest() { 42 | } 43 | 44 | public SM3Digest(SM3Digest t) { 45 | System.arraycopy(t.xBuf, 0, this.xBuf, 0, t.xBuf.length); 46 | this.xBufOff = t.xBufOff; 47 | System.arraycopy(t.V, 0, this.V, 0, t.V.length); 48 | } 49 | 50 | /** 51 | * SM3结果输出 52 | * 53 | * @return 54 | * @param out 保存SM3结构的缓冲区 55 | * @param outOff 缓冲区偏移量 56 | */ 57 | public int doFinal(byte[] out, int outOff) { 58 | byte[] tmp = doFinal(); 59 | System.arraycopy(tmp, 0, out, 0, tmp.length); 60 | return BYTE_LENGTH; 61 | } 62 | 63 | public void reset() { 64 | xBufOff = 0; 65 | cntBlock = 0; 66 | V = SM3.iv.clone(); 67 | } 68 | 69 | /** 70 | * 明文输入 71 | * 72 | * @param in 明文输入缓冲区 73 | * @param inOff 缓冲区偏移量 74 | * @param len 明文长度 75 | */ 76 | public void update(byte[] in, int inOff, int len) { 77 | int partLen = BUFFER_LENGTH - xBufOff; 78 | int inputLen = len; 79 | int dPos = inOff; 80 | if (partLen < inputLen) { 81 | System.arraycopy(in, dPos, xBuf, xBufOff, partLen); 82 | inputLen -= partLen; 83 | dPos += partLen; 84 | doUpdate(); 85 | while (inputLen > BUFFER_LENGTH) { 86 | System.arraycopy(in, dPos, xBuf, 0, BUFFER_LENGTH); 87 | inputLen -= BUFFER_LENGTH; 88 | dPos += BUFFER_LENGTH; 89 | doUpdate(); 90 | } 91 | } 92 | 93 | System.arraycopy(in, dPos, xBuf, xBufOff, inputLen); 94 | xBufOff += inputLen; 95 | } 96 | 97 | private void doUpdate() { 98 | byte[] B = new byte[BLOCK_LENGTH]; 99 | for (int i = 0; i < BUFFER_LENGTH; i += BLOCK_LENGTH) { 100 | System.arraycopy(xBuf, i, B, 0, B.length); 101 | doHash(B); 102 | } 103 | xBufOff = 0; 104 | } 105 | 106 | private void doHash(byte[] B) { 107 | byte[] tmp = SM3.CF(V, B); 108 | System.arraycopy(tmp, 0, V, 0, V.length); 109 | cntBlock++; 110 | } 111 | 112 | private byte[] doFinal() { 113 | byte[] B = new byte[BLOCK_LENGTH]; 114 | byte[] buffer = new byte[xBufOff]; 115 | System.arraycopy(xBuf, 0, buffer, 0, buffer.length); 116 | byte[] tmp = SM3.padding(buffer, cntBlock); 117 | for (int i = 0; i < tmp.length; i += BLOCK_LENGTH) { 118 | System.arraycopy(tmp, i, B, 0, B.length); 119 | doHash(B); 120 | } 121 | return V; 122 | } 123 | 124 | public void update(byte in) { 125 | byte[] buffer = new byte[]{in}; 126 | update(buffer, 0, 1); 127 | } 128 | 129 | public int getDigestSize() { 130 | return BYTE_LENGTH; 131 | } 132 | 133 | public static boolean f(int i){ 134 | if (i==1){ 135 | System.out.println("true"); 136 | return false; 137 | } 138 | System.out.println("false"); 139 | return false; 140 | } 141 | public static void main(String[] args) { 142 | if (f(1)&&f(0)){ 143 | 144 | } 145 | } 146 | } 147 | 148 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/dp/L_139_WordBreak.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode.dp; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * @author 李文浩 7 | * @date 2018/3/18 8 | */ 9 | public class L_139_WordBreak { 10 | 11 | public static void main(String[] args) { 12 | Set set = new HashSet<>(); 13 | 14 | String[] strs = {"cbc", "bcda", "adb", "ddca", "bad", "bbb", "dad", "dac", "ba", "aa", "bd", "abab", "bb", 15 | "dbda", "cb", "caccc", "d", "dd", "aadb", "cc", "b", "bcc", "bcd", "cd", "cbca", "bbd", "ddd", 16 | "dabb", "ab", "acd", "a", "bbcc", "cdcbd", "cada", "dbca", "ac", "abacd", "cba", "cdb", 17 | "dbac", "aada", "cdcda", "cdc", "dbc", "dbcb", "bdb", "ddbdd", "cadaa", "ddbc", "babb"}; 18 | 19 | for (String str : strs) { 20 | set.add(str); 21 | } 22 | // 23 | // 重复的值,连续的重复值, 24 | System.out.println(new L_139_WordBreak().wordBreak("bccdbacdbdacddabbaaaadababadad", set)); 25 | 26 | set.clear(); 27 | set.add("a"); 28 | set.add("b"); 29 | set.add("bbb"); 30 | set.add("bbbb"); 31 | 32 | System.out.println(new L_139_WordBreak().wordBreak("bb", set)); 33 | // 34 | set.clear(); 35 | set.add("leet"); 36 | set.add("code"); 37 | // System.out.println("leetcode".lastIndexOf("leet")); 38 | // System.out.println("leetcode".lastIndexOf("code")); 39 | // System.out.println("leetcode".substring(0, 4)); 40 | System.out.println(new L_139_WordBreak().wordBreak("leetcode", set)); 41 | 42 | set.clear(); 43 | set.add("dog"); 44 | set.add("s"); 45 | set.add("gs"); 46 | System.out.println(new L_139_WordBreak().wordBreak("dogs", set)); 47 | 48 | set.clear(); 49 | set.add("car"); 50 | set.add("ca"); 51 | set.add("rs"); 52 | System.out.println(new L_139_WordBreak().wordBreak("cars", set)); 53 | } 54 | 55 | 56 | /** 57 | * 暴力法,排除每一个可能,从一个单词单词,直到s可以分词或者dict为空 58 | * 59 | * @param s 60 | * @param dict 61 | * @return 62 | */ 63 | public boolean wordBreak(String s, Set dict) { 64 | 65 | Set cl = new HashSet<>(); 66 | //删完单词 67 | while (!dict.isEmpty()) { 68 | String words = s; 69 | String first = ""; 70 | boolean flag = false; 71 | 72 | cl.add(""); 73 | while (words.length() != 0) { 74 | if (cl.contains("") && cl.size() == 1) { 75 | cl.clear(); 76 | } 77 | Iterator iterator = dict.iterator(); 78 | while (iterator.hasNext()) { 79 | String word = (String) iterator.next(); 80 | if (words.contains(word) && words.startsWith(word)) { 81 | cl.add(word); 82 | } 83 | } 84 | if (cl.isEmpty() && words.length() != 0) { 85 | break; 86 | } else { 87 | String s1 = cl.iterator().next(); 88 | if (!flag) { 89 | first = s1; 90 | flag = true; 91 | } 92 | words = words.replaceFirst(s1, ""); 93 | System.out.println(words); 94 | cl.clear(); 95 | } 96 | } 97 | 98 | System.out.println(first + words.length()); 99 | if (words.length() != 0) { 100 | dict.remove(first); 101 | } else { 102 | break; 103 | } 104 | 105 | } 106 | return !dict.isEmpty(); 107 | } 108 | 109 | /** 110 | * 动态规划 111 | * 112 | * 状态转移方程: 113 | * f(i) 表示s[0,i]是否可以分词 114 | * f(i) = f(j) && f(j+1,i); 0 <= j < i; 115 | * 116 | * @param s 117 | * @param dict 118 | * @return 119 | */ 120 | public boolean wordBreak2(String s, Set dict) { 121 | int len = s.length(); 122 | boolean[] arrays = new boolean[len + 1]; 123 | arrays[0] = true; 124 | for (int i = 1; i <= len; ++i) { 125 | for (int j = 0; j < i; ++j) { 126 | if (arrays[j] && dict.contains(s.substring(j, i))) { 127 | arrays[i] = true; 128 | break; 129 | } 130 | } 131 | } 132 | return arrays[len]; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/Leetcode_149_MaxPointsOnALine.java: -------------------------------------------------------------------------------- 1 | //package algorithm.leetcode; 2 | // 3 | //import java.util.HashMap; 4 | // 5 | ///** 6 | // * Given n points on a 2D plane, find the maximum number of points that lie on the same straight line. 7 | // * 8 | // * @author 李文浩 9 | // * @date 2018/2/13 10 | // */ 11 | //public class Leetcode_149_MaxPointsOnALine { 12 | // public static class Point { 13 | // int x; 14 | // int y; 15 | // 16 | // Point() { 17 | // x = 0; 18 | // y = 0; 19 | // } 20 | // 21 | // Point(int a, int b) { 22 | // x = a; 23 | // y = b; 24 | // Map = new HashMap(); 25 | // } 26 | // } 27 | // 28 | // public static void main(String[] args) { 29 | // Point[] points = { 30 | //// new Point(10, 10), 31 | //// new Point(20, 22), 32 | //// new Point(30, 31), 33 | //// new Point(40, 40), 34 | //// new Point(23, 26), 35 | //// new Point(65, 36), 36 | // new Point(-4, -4), 37 | // new Point(-8, -582), 38 | // new Point(-3, 3), 39 | // new Point(-9, -651), 40 | // new Point(9, 591), 41 | // }; 42 | // for (Point point : points) { 43 | // System.out.print(point.x + "," + point.y + " "); 44 | // } 45 | // System.out.println(); 46 | // Leetcode_149_MaxPointsOnALine maxPointsOnALine = new Leetcode_149_MaxPointsOnALine(); 47 | // System.out.println(maxPointsOnALine.maxPoints(points)); 48 | // } 49 | // 50 | // public int maxPoints(Point[] points) { 51 | // if (points.length == 0) { 52 | // return 0; 53 | // } else if (points.length == 1) { 54 | // return 1; 55 | // } else if (points.length == 2) { 56 | // return 2; 57 | // } else { 58 | // int[] nums = new int[points.length]; 59 | // for (int i = 0; i < points.length - 2; i++) { 60 | // int[] nums1 = new int[points.length]; 61 | // for (int k = i + 1; k < points.length - 1; k++) { 62 | // int num = 2 + repeatPoints(points, i); 63 | // 64 | // 65 | //// if (points[i].x == points[k].x && points[i].y != points[k].y) { 66 | //// for (int j = i + 2; j < points.length; j++) { 67 | //// if (points[i].x == points[j].x) { 68 | //// num++; 69 | //// } 70 | //// } 71 | //// } else { 72 | // double slope = (points[i].y - points[k].y) * 1.0 / (points[i].x - points[k].x); 73 | // System.out.print("第" + i + "个点的斜率:" + slope + " "); 74 | // for (int j = i + 2; j < points.length; j++) { 75 | // System.out.print((points[i].y - points[j].y) * 1.0 / (points[i].x - points[j].x) + " "); 76 | // 77 | // if (slope == (points[i].y - points[j].y) * 1.0 / (points[i].x - points[j].x)) { 78 | // num++; 79 | // } 80 | // } 81 | // System.out.println(); 82 | //// } 83 | // nums[1] = num; 84 | // 85 | // } 86 | // int max = 0; 87 | // for (int m = max + 1; m < nums1.length; m++) { 88 | // if (nums1[i] > nums1[max]) { 89 | // max = i; 90 | // } 91 | // } 92 | // nums[i] = nums1[max]; 93 | // } 94 | // for (int i : nums) { 95 | // System.out.print(i + " "); 96 | // } 97 | // System.out.println(); 98 | // int max = 0; 99 | // for (int i = max + 1; i < nums.length; i++) { 100 | // if (nums[i] > nums[max]) { 101 | // max = i; 102 | // } 103 | // } 104 | // return nums[max] > points.length ? points.length : nums[max]; 105 | // } 106 | // } 107 | // 108 | // 109 | // public int repeatPoints(Point[] points, int k) { 110 | // int num = -1; 111 | // for (int i = 0; i < points.length; i++) { 112 | // if (points[i].x == points[k].x && points[i].y == points[k].y) { 113 | // num++; 114 | // } 115 | // } 116 | // return num; 117 | // } 118 | //} 119 | -------------------------------------------------------------------------------- /src/main/java/algorithm/graph/Graph.java: -------------------------------------------------------------------------------- 1 | package algorithm.graph; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | import java.util.Scanner; 6 | 7 | /** 8 | * @author 李文浩 9 | * @date 2018/6/23 10 | */ 11 | 12 | public class Graph { 13 | 14 | final int MIN = -256; 15 | final int MAX = 256; 16 | int numVertexes; 17 | int numEdges; 18 | String[] vex; 19 | int acr[][]; 20 | 21 | public Graph() { 22 | vex = new String[MAX]; 23 | acr = new int[MAX][MAX]; 24 | } 25 | 26 | // 建立无向网图, 给图中的 顶点内容 和 边权重 赋值 27 | public void buildGraph(Graph g) { 28 | //读入顶点数和边数 29 | System.out.println("请输入顶点数:\n"); 30 | Scanner sc = new Scanner(System.in); 31 | numVertexes = sc.nextInt(); 32 | 33 | System.out.println("请输入边数:\n"); 34 | numEdges = sc.nextInt(); 35 | 36 | //读入顶点信息,建立顶点表 37 | for (int i = 0; i < g.numVertexes; i++) { 38 | System.out.print("第" + i + "个顶点信息为:"); 39 | vex[i] = sc.next(); 40 | } 41 | 42 | 43 | //邻接矩阵初始化 44 | for (int i = 0; i < g.numVertexes; i++) 45 | for (int j = 0; j < g.numVertexes; j++) { 46 | g.acr[i][j] = MIN; 47 | } 48 | 49 | //读入边信息,建立邻接矩阵 50 | for (int k = 0; k < g.numEdges; k++) { 51 | System.out.println("请输入边(i,j)的下标i:\n"); 52 | int i = sc.nextInt(); 53 | System.out.println("请输入边(i,j)的下标j:\n"); 54 | int j = sc.nextInt(); 55 | System.out.println("请输入边(i,j)的权重w:\n"); 56 | int w = sc.nextInt(); 57 | 58 | g.acr[i][j] = w; 59 | g.acr[j][i] = g.acr[i][j]; 60 | } 61 | sc.close(); 62 | } 63 | 64 | 65 | public void printGraph(Graph g) { 66 | System.out.print(" "); 67 | for (int i = 0; i < g.numVertexes; i++) { 68 | System.out.print(g.vex[i] + " "); 69 | } 70 | 71 | for (int i = 0; i < g.numVertexes; i++) { 72 | System.out.println(); 73 | System.out.print(g.vex[i] + " "); 74 | for (int j = 0; j < g.numVertexes; j++) { 75 | System.out.print(g.acr[i][j] + " "); 76 | } 77 | 78 | } 79 | } 80 | 81 | //深度优先遍历 82 | public void DFSTraverse(Graph g) { 83 | //建立访问数组 84 | boolean[] visited = new boolean[g.numVertexes]; 85 | //初始化 访问顶点 86 | for (int i = 0; i < visited.length; i++) 87 | visited[i] = false; 88 | //对未访问顶点调用DFS 89 | for (int i = 0; i < visited.length; i++) 90 | if (!visited[i]) 91 | DFS(g, i, visited); 92 | } 93 | 94 | 95 | //从顶点i开始进行的深度优先搜索DFS 96 | public void DFS(Graph g, int i, boolean[] visited) { 97 | visited[i] = true; 98 | System.out.print("已访问" + g.vex[i] + " "); 99 | for (int j = 0; j < g.numVertexes; j++) { 100 | if (g.acr[i][j] > 0 && !visited[j]) 101 | DFS(g, j, visited); 102 | } 103 | 104 | } 105 | 106 | 107 | //广度优先搜索 108 | public void BFS(Graph g) { 109 | int i = 0; 110 | Queue q = new LinkedList(); 111 | //建立访问数组 112 | boolean visited[] = new boolean[MAX]; 113 | //初始化 访问顶点 114 | for (i = 0; i < g.numVertexes; i++) 115 | visited[i] = false; 116 | 117 | //对每个顶点做循环 118 | for (i = 0; i < g.numVertexes; i++) { 119 | if (!visited[i]) { 120 | visited[i] = true; 121 | System.out.print("已访问" + g.vex[i] + " "); 122 | q.offer(i); 123 | while (!q.isEmpty()) { 124 | i = q.poll(); 125 | for (int j = 0; j < g.numVertexes; j++) { 126 | if (g.acr[i][j] > 0 && !visited[j]) { 127 | visited[j] = true; 128 | System.out.print("已访问" + g.vex[j] + " "); 129 | q.offer(j); 130 | } 131 | } 132 | 133 | 134 | } 135 | 136 | } 137 | } 138 | } 139 | 140 | public static void main(String[] args) { 141 | Graph g = new Graph(); 142 | g.buildGraph(g); 143 | g.printGraph(g); 144 | 145 | System.out.println(); 146 | System.out.println("深度优先搜索结果:"); 147 | g.DFSTraverse(g); 148 | 149 | System.out.println(); 150 | System.out.println("广度优先搜索结果:"); 151 | g.BFS(g); 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/algorithm/graph/DFSTest.java: -------------------------------------------------------------------------------- 1 | package algorithm.graph; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * @author 李文浩 7 | * @date 2018/6/24 8 | */ 9 | public class DFSTest { 10 | 11 | // 存储节点信息 12 | private char[] vertices; 13 | 14 | // 存储边信息(邻接矩阵) 15 | private int[][] arcs; 16 | 17 | // 图的节点数 18 | private int vexnum; 19 | 20 | // 记录节点是否已被遍历 21 | private boolean[] visited; 22 | 23 | // 初始化 24 | public DFSTest(int n) { 25 | vexnum = n; 26 | vertices = new char[n]; 27 | arcs = new int[n][n]; 28 | visited = new boolean[n]; 29 | for (int i = 0; i < vexnum; i++) { 30 | for (int j = 0; j < vexnum; j++) { 31 | arcs[i][j] = 0; 32 | } 33 | } 34 | } 35 | 36 | // 添加边(无向图) 37 | public void addEdge(int i, int j) { 38 | // 边的头尾不能为同一节点 39 | if (i == j) return; 40 | 41 | arcs[i][j] = 1; 42 | arcs[j][i] = 1; 43 | } 44 | 45 | // 设置节点集 46 | public void setVertices(char[] vertices) { 47 | this.vertices = vertices; 48 | } 49 | 50 | // 设置节点访问标记 51 | public void setVisited(boolean[] visited) { 52 | this.visited = visited; 53 | } 54 | 55 | // 打印遍历节点 56 | public void visit(int i) { 57 | System.out.print(vertices[i] + " "); 58 | } 59 | 60 | // 从第i个节点开始深度优先遍历 61 | private void traverse(int i) { 62 | // 标记第i个节点已遍历 63 | visited[i] = true; 64 | // 打印当前遍历的节点 65 | visit(i); 66 | 67 | // 遍历邻接矩阵中第i个节点的直接联通关系 68 | for (int j = 0; j < vexnum; j++) { 69 | // 目标节点与当前节点直接联通,并且该节点还没有被访问,递归 70 | if (arcs[i][j] == 1 && visited[j] == false) { 71 | traverse(j); 72 | } 73 | } 74 | } 75 | 76 | // 图的深度优先遍历(递归) 77 | public void DFSTraverse() { 78 | // 初始化节点遍历标记 79 | for (int i = 0; i < vexnum; i++) { 80 | visited[i] = false; 81 | } 82 | 83 | // 从没有被遍历的节点开始深度遍历 84 | for (int i = 0; i < vexnum; i++) { 85 | if (visited[i] == false) { 86 | // 若是连通图,只会执行一次 87 | traverse(i); 88 | } 89 | } 90 | } 91 | 92 | // 图的深度优先遍历(非递归) 93 | public void DFSTraverse2() { 94 | // 初始化节点遍历标记 95 | for (int i = 0; i < vexnum; i++) { 96 | visited[i] = false; 97 | } 98 | 99 | Stack s = new Stack(); 100 | for (int i = 0; i < vexnum; i++) { 101 | if (!visited[i]) { 102 | //连通子图起始节点 103 | s.add(i); 104 | do { 105 | // 出栈 106 | int curr = s.pop(); 107 | 108 | // 如果该节点还没有被遍历,则遍历该节点并将子节点入栈 109 | if (visited[curr] == false) { 110 | // 遍历并打印 111 | visit(curr); 112 | visited[curr] = true; 113 | 114 | // 没遍历的子节点入栈 115 | for (int j = vexnum - 1; j >= 0; j--) { 116 | if (arcs[curr][j] == 1 && visited[j] == false) { 117 | s.add(j); 118 | } 119 | } 120 | } 121 | } while (!s.isEmpty()); 122 | } 123 | } 124 | } 125 | 126 | public static void main(String[] args) { 127 | DFSTest g = new DFSTest(9); 128 | char[] vertices = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'}; 129 | g.setVertices(vertices); 130 | 131 | g.addEdge(0, 1); 132 | g.addEdge(0, 5); 133 | g.addEdge(1, 0); 134 | g.addEdge(1, 2); 135 | g.addEdge(1, 6); 136 | g.addEdge(1, 8); 137 | g.addEdge(2, 1); 138 | g.addEdge(2, 3); 139 | g.addEdge(2, 8); 140 | g.addEdge(3, 2); 141 | g.addEdge(3, 4); 142 | g.addEdge(3, 6); 143 | g.addEdge(3, 7); 144 | g.addEdge(3, 8); 145 | g.addEdge(4, 3); 146 | g.addEdge(4, 5); 147 | g.addEdge(4, 7); 148 | g.addEdge(5, 0); 149 | g.addEdge(5, 4); 150 | g.addEdge(5, 6); 151 | g.addEdge(6, 1); 152 | g.addEdge(6, 3); 153 | g.addEdge(6, 5); 154 | g.addEdge(6, 7); 155 | g.addEdge(7, 3); 156 | g.addEdge(7, 4); 157 | g.addEdge(7, 6); 158 | g.addEdge(8, 1); 159 | g.addEdge(8, 2); 160 | g.addEdge(8, 3); 161 | 162 | System.out.print("深度优先遍历(递归):"); 163 | g.DFSTraverse(); 164 | 165 | System.out.println(); 166 | 167 | System.out.print("深度优先遍历(非递归):"); 168 | g.DFSTraverse2(); 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /src/main/java/algorithm/leetcode/L_148_SortList.java: -------------------------------------------------------------------------------- 1 | package algorithm.leetcode; 2 | 3 | /** 4 | * @author 李文浩 5 | * @date 2018/2/16 6 | */ 7 | 8 | class ListNode { 9 | int val; 10 | ListNode next; 11 | 12 | ListNode(int x) { 13 | val = x; 14 | next = null; 15 | } 16 | } 17 | 18 | public class L_148_SortList { 19 | 20 | public static void main(String[] args) { 21 | 22 | ListNode head = new ListNode(12); 23 | // head = addBack(head, 3); 24 | // head = addBack(head, 10); 25 | // head = addBack(head, 25); 26 | // head = addBack(head, 12); 27 | // head = addBack(head, 5); 28 | // head = addBack(head, 55); 29 | // head = addBack(head, 55); 30 | 31 | // for (int i = 0; i < 10; i++) 32 | // head = addBack(head, (int) (Math.random() * 100)); 33 | System.out.print("排序前:"); 34 | print(head); 35 | 36 | // head = new L_148_SortList().sortList(head); 37 | 38 | head = new L_147_InsertionSortList().insertionSortList(head); 39 | System.out.print("排序后:"); 40 | print(head); 41 | } 42 | public static ListNode addBack(ListNode head, int val) { 43 | ListNode newNode = new ListNode(val); 44 | newNode.next = null; 45 | ListNode node = head; 46 | if (head == null) { 47 | head = newNode; 48 | return head; 49 | } 50 | while (node.next != null) { 51 | node = node.next; 52 | } 53 | node.next = newNode; 54 | 55 | return head; 56 | } 57 | 58 | public static void print(ListNode node) { 59 | while (node != null) { 60 | System.out.print(node.val + " "); 61 | node = node.next; 62 | } 63 | System.out.println(); 64 | } 65 | 66 | 67 | public static void quickSort(ListNode head, ListNode end) { 68 | if (head != end) { 69 | ListNode node = partion(head, end); 70 | quickSort(head, node); 71 | quickSort(node.next, end); 72 | } 73 | } 74 | 75 | public static ListNode partion(ListNode head, ListNode end) { 76 | ListNode p1 = head, p2 = head.next; 77 | 78 | //走到末尾才停 79 | while (p2 != end) { 80 | 81 | //大于key值时,p1向前走一步,交换p1与p2的值 82 | if (p2.val < head.val) { 83 | p1 = p1.next; 84 | 85 | int temp = p1.val; 86 | p1.val = p2.val; 87 | p2.val = temp; 88 | } 89 | p2 = p2.next; 90 | } 91 | 92 | //当有序时,不交换p1和key值 93 | if (p1 != head) { 94 | int temp = p1.val; 95 | p1.val = head.val; 96 | head.val = temp; 97 | } 98 | return p1; 99 | } 100 | 101 | 102 | public ListNode sortList(ListNode head) { 103 | //采用快速排序 104 | // quickSort(head, null); 105 | // return head; 106 | 107 | //采用归并排序 108 | if (head == null || head.next == null) { 109 | return head; 110 | } 111 | //获取中间结点 112 | ListNode mid = getMid(head); 113 | ListNode right = mid.next; 114 | mid.next = null; 115 | //合并 116 | return mergeSort(sortList(head), sortList(right)); 117 | } 118 | 119 | /** 120 | * 获取链表的中间结点,偶数时取中间第一个 121 | * 122 | * @param head 123 | * @return 124 | */ 125 | private ListNode getMid(ListNode head) { 126 | if (head == null || head.next == null) { 127 | return head; 128 | } 129 | //快慢指针 130 | ListNode slow = head, quick = head; 131 | //快2步,慢一步 132 | while (quick.next != null && quick.next.next != null) { 133 | slow = slow.next; 134 | quick = quick.next.next; 135 | } 136 | return slow; 137 | } 138 | 139 | /** 140 | * 141 | * 归并两个有序的链表 142 | * 143 | * @param head1 144 | * @param head2 145 | * @return 146 | */ 147 | private ListNode mergeSort(ListNode head1, ListNode head2) { 148 | ListNode p1 = head1, p2 = head2, head; 149 | //得到头节点的指向 150 | if (head1.val < head2.val) { 151 | head = head1; 152 | p1 = p1.next; 153 | } else { 154 | head = head2; 155 | p2 = p2.next; 156 | } 157 | 158 | ListNode p = head; 159 | //比较链表中的值 160 | while (p1 != null && p2 != null) { 161 | 162 | if (p1.val <= p2.val) { 163 | p.next = p1; 164 | p1 = p1.next; 165 | p = p.next; 166 | } else { 167 | p.next = p2; 168 | p2 = p2.next; 169 | p = p.next; 170 | } 171 | } 172 | //第二条链表空了 173 | if (p1 != null) { 174 | p.next = p1; 175 | } 176 | //第一条链表空了 177 | if (p2 != null) { 178 | p.next = p2; 179 | } 180 | return head; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /src/main/java/algorithm/security/SM3.java: -------------------------------------------------------------------------------- 1 | package algorithm.security; 2 | 3 | public class SM3 4 | { 5 | public static final byte[] iv = { 0x73, (byte) 0x80, 0x16, 0x6f, 0x49, 6 | 0x14, (byte) 0xb2, (byte) 0xb9, 0x17, 0x24, 0x42, (byte) 0xd7, 7 | (byte) 0xda, (byte) 0x8a, 0x06, 0x00, (byte) 0xa9, 0x6f, 0x30, 8 | (byte) 0xbc, (byte) 0x16, 0x31, 0x38, (byte) 0xaa, (byte) 0xe3, 9 | (byte) 0x8d, (byte) 0xee, 0x4d, (byte) 0xb0, (byte) 0xfb, 0x0e, 10 | 0x4e }; 11 | 12 | public static int[] Tj = new int[64]; 13 | 14 | static 15 | { 16 | for (int i = 0; i < 16; i++) 17 | { 18 | Tj[i] = 0x79cc4519; 19 | } 20 | 21 | for (int i = 16; i < 64; i++) 22 | { 23 | Tj[i] = 0x7a879d8a; 24 | } 25 | } 26 | 27 | public static byte[] CF(byte[] V, byte[] B) 28 | { 29 | int[] v, b; 30 | v = convert(V); 31 | b = convert(B); 32 | return convert(CF(v, b)); 33 | } 34 | 35 | private static int[] convert(byte[] arr) 36 | { 37 | int[] out = new int[arr.length / 4]; 38 | byte[] tmp = new byte[4]; 39 | for (int i = 0; i < arr.length; i += 4) 40 | { 41 | System.arraycopy(arr, i, tmp, 0, 4); 42 | out[i / 4] = bigEndianByteToInt(tmp); 43 | } 44 | return out; 45 | } 46 | 47 | private static byte[] convert(int[] arr) 48 | { 49 | byte[] out = new byte[arr.length * 4]; 50 | byte[] tmp = null; 51 | for (int i = 0; i < arr.length; i++) 52 | { 53 | tmp = bigEndianIntToByte(arr[i]); 54 | System.arraycopy(tmp, 0, out, i * 4, 4); 55 | } 56 | return out; 57 | } 58 | 59 | public static int[] CF(int[] V, int[] B) 60 | { 61 | int a, b, c, d, e, f, g, h; 62 | int ss1, ss2, tt1, tt2; 63 | a = V[0]; 64 | b = V[1]; 65 | c = V[2]; 66 | d = V[3]; 67 | e = V[4]; 68 | f = V[5]; 69 | g = V[6]; 70 | h = V[7]; 71 | 72 | int[][] arr = expand(B); 73 | int[] w = arr[0]; 74 | int[] w1 = arr[1]; 75 | 76 | for (int j = 0; j < 64; j++) 77 | { 78 | ss1 = (bitCycleLeft(a, 12) + e + bitCycleLeft(Tj[j], j)); 79 | ss1 = bitCycleLeft(ss1, 7); 80 | ss2 = ss1 ^ bitCycleLeft(a, 12); 81 | tt1 = FFj(a, b, c, j) + d + ss2 + w1[j]; 82 | tt2 = GGj(e, f, g, j) + h + ss1 + w[j]; 83 | d = c; 84 | c = bitCycleLeft(b, 9); 85 | b = a; 86 | a = tt1; 87 | h = g; 88 | g = bitCycleLeft(f, 19); 89 | f = e; 90 | e = P0(tt2); 91 | 92 | /*System.out.print(j+" "); 93 | System.out.print(Integer.toHexString(a)+" "); 94 | System.out.print(Integer.toHexString(b)+" "); 95 | System.out.print(Integer.toHexString(c)+" "); 96 | System.out.print(Integer.toHexString(d)+" "); 97 | System.out.print(Integer.toHexString(e)+" "); 98 | System.out.print(Integer.toHexString(f)+" "); 99 | System.out.print(Integer.toHexString(g)+" "); 100 | System.out.print(Integer.toHexString(h)+" "); 101 | System.out.println("");*/ 102 | } 103 | // System.out.println(""); 104 | 105 | int[] out = new int[8]; 106 | out[0] = a ^ V[0]; 107 | out[1] = b ^ V[1]; 108 | out[2] = c ^ V[2]; 109 | out[3] = d ^ V[3]; 110 | out[4] = e ^ V[4]; 111 | out[5] = f ^ V[5]; 112 | out[6] = g ^ V[6]; 113 | out[7] = h ^ V[7]; 114 | 115 | return out; 116 | } 117 | 118 | private static int[][] expand(int[] B) 119 | { 120 | int W[] = new int[68]; 121 | int W1[] = new int[64]; 122 | for (int i = 0; i < B.length; i++) 123 | { 124 | W[i] = B[i]; 125 | } 126 | 127 | for (int i = 16; i < 68; i++) 128 | { 129 | W[i] = P1(W[i - 16] ^ W[i - 9] ^ bitCycleLeft(W[i - 3], 15)) 130 | ^ bitCycleLeft(W[i - 13], 7) ^ W[i - 6]; 131 | } 132 | 133 | for (int i = 0; i < 64; i++) 134 | { 135 | W1[i] = W[i] ^ W[i + 4]; 136 | } 137 | 138 | int arr[][] = new int[][] { W, W1 }; 139 | return arr; 140 | } 141 | 142 | private static byte[] bigEndianIntToByte(int num) 143 | { 144 | return back(Util.intToBytes(num)); 145 | } 146 | 147 | private static int bigEndianByteToInt(byte[] bytes) 148 | { 149 | return Util.byteToInt(back(bytes)); 150 | } 151 | 152 | private static int FFj(int X, int Y, int Z, int j) 153 | { 154 | if (j >= 0 && j <= 15) 155 | { 156 | return FF1j(X, Y, Z); 157 | } 158 | else 159 | { 160 | return FF2j(X, Y, Z); 161 | } 162 | } 163 | 164 | private static int GGj(int X, int Y, int Z, int j) 165 | { 166 | if (j >= 0 && j <= 15) 167 | { 168 | return GG1j(X, Y, Z); 169 | } 170 | else 171 | { 172 | return GG2j(X, Y, Z); 173 | } 174 | } 175 | 176 | // 逻辑位运算函数 177 | private static int FF1j(int X, int Y, int Z) 178 | { 179 | int tmp = X ^ Y ^ Z; 180 | return tmp; 181 | } 182 | 183 | private static int FF2j(int X, int Y, int Z) 184 | { 185 | int tmp = ((X & Y) | (X & Z) | (Y & Z)); 186 | return tmp; 187 | } 188 | 189 | private static int GG1j(int X, int Y, int Z) 190 | { 191 | int tmp = X ^ Y ^ Z; 192 | return tmp; 193 | } 194 | 195 | private static int GG2j(int X, int Y, int Z) 196 | { 197 | int tmp = (X & Y) | (~X & Z); 198 | return tmp; 199 | } 200 | 201 | private static int P0(int X) 202 | { 203 | int y = rotateLeft(X, 9); 204 | y = bitCycleLeft(X, 9); 205 | int z = rotateLeft(X, 17); 206 | z = bitCycleLeft(X, 17); 207 | int t = X ^ y ^ z; 208 | return t; 209 | } 210 | 211 | private static int P1(int X) 212 | { 213 | int t = X ^ bitCycleLeft(X, 15) ^ bitCycleLeft(X, 23); 214 | return t; 215 | } 216 | 217 | /** 218 | * 对最后一个分组字节数据padding 219 | * 220 | * @param in 221 | * @param bLen 222 | * 分组个数 223 | * @return 224 | */ 225 | public static byte[] padding(byte[] in, int bLen) 226 | { 227 | int k = 448 - (8 * in.length + 1) % 512; 228 | if (k < 0) 229 | { 230 | k = 960 - (8 * in.length + 1) % 512; 231 | } 232 | k += 1; 233 | byte[] padd = new byte[k / 8]; 234 | padd[0] = (byte) 0x80; 235 | long n = in.length * 8 + bLen * 512; 236 | byte[] out = new byte[in.length + k / 8 + 64 / 8]; 237 | int pos = 0; 238 | System.arraycopy(in, 0, out, 0, in.length); 239 | pos += in.length; 240 | System.arraycopy(padd, 0, out, pos, padd.length); 241 | pos += padd.length; 242 | byte[] tmp = back(Util.longToBytes(n)); 243 | System.arraycopy(tmp, 0, out, pos, tmp.length); 244 | return out; 245 | } 246 | 247 | /** 248 | * 字节数组逆序 249 | * 250 | * @param in 251 | * @return 252 | */ 253 | private static byte[] back(byte[] in) 254 | { 255 | byte[] out = new byte[in.length]; 256 | for (int i = 0; i < out.length; i++) 257 | { 258 | out[i] = in[out.length - i - 1]; 259 | } 260 | 261 | return out; 262 | } 263 | 264 | public static int rotateLeft(int x, int n) 265 | { 266 | return (x << n) | (x >> (32 - n)); 267 | } 268 | 269 | private static int bitCycleLeft(int n, int bitLen) 270 | { 271 | bitLen %= 32; 272 | byte[] tmp = bigEndianIntToByte(n); 273 | int byteLen = bitLen / 8; 274 | int len = bitLen % 8; 275 | if (byteLen > 0) 276 | { 277 | tmp = byteCycleLeft(tmp, byteLen); 278 | } 279 | 280 | if (len > 0) 281 | { 282 | tmp = bitSmall8CycleLeft(tmp, len); 283 | } 284 | 285 | return bigEndianByteToInt(tmp); 286 | } 287 | 288 | private static byte[] bitSmall8CycleLeft(byte[] in, int len) 289 | { 290 | byte[] tmp = new byte[in.length]; 291 | int t1, t2, t3; 292 | for (int i = 0; i < tmp.length; i++) 293 | { 294 | t1 = (byte) ((in[i] & 0x000000ff) << len); 295 | t2 = (byte) ((in[(i + 1) % tmp.length] & 0x000000ff) >> (8 - len)); 296 | t3 = (byte) (t1 | t2); 297 | tmp[i] = (byte) t3; 298 | } 299 | 300 | return tmp; 301 | } 302 | 303 | private static byte[] byteCycleLeft(byte[] in, int byteLen) 304 | { 305 | byte[] tmp = new byte[in.length]; 306 | System.arraycopy(in, byteLen, tmp, 0, in.length - byteLen); 307 | System.arraycopy(in, 0, tmp, in.length - byteLen, byteLen); 308 | return tmp; 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /src/main/java/algorithm/tree/Algorithm4thBTree.java: -------------------------------------------------------------------------------- 1 | package algorithm.tree; 2 | 3 | 4 | /** 5 | * This is Algorithm 4th BTree. 6 | *

7 | *

8 | * B-tree. 9 | * from https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/BTree.java 10 | *

11 | *

12 | * Limitations 13 | * ----------- 14 | * - Assumes M is even and M >= 4 15 | * - should b be an array of children or list (it would help with 16 | * casting to make it a list) 17 | **/ 18 | 19 | public class Algorithm4thBTree, Value> { 20 | // max children per B-tree node = M-1 21 | // (must be even and greater than 2) 22 | private static final int M = 4; 23 | 24 | private Node root; // root of the B-tree 25 | private int height; // height of the B-tree 26 | private int n; // number of key-value pairs in the B-tree 27 | 28 | // helper B-tree node data type 29 | private static final class Node { 30 | private int m; // number of children 31 | private Entry[] children = new Entry[M]; // the array of children 32 | 33 | // create a node with k children 34 | private Node(int k) { 35 | m = k; 36 | } 37 | } 38 | 39 | // internal nodes: only use key and next 40 | // external nodes: only use key and value 41 | private static class Entry { 42 | private Comparable key; 43 | private final Object val; 44 | private Node next; // helper field to iterate over array entries 45 | 46 | public Entry(Comparable key, Object val, Node next) { 47 | this.key = key; 48 | this.val = val; 49 | this.next = next; 50 | } 51 | } 52 | 53 | /** 54 | * Initializes an empty B-tree. 55 | */ 56 | public Algorithm4thBTree() { 57 | root = new Node(0); 58 | } 59 | 60 | /** 61 | * Returns true if this symbol table is empty. 62 | * 63 | * @return {@code true} if this symbol table is empty; {@code false} otherwise 64 | */ 65 | public boolean isEmpty() { 66 | return size() == 0; 67 | } 68 | 69 | /** 70 | * Returns the number of key-value pairs in this symbol table. 71 | * 72 | * @return the number of key-value pairs in this symbol table 73 | */ 74 | public int size() { 75 | return n; 76 | } 77 | 78 | /** 79 | * Returns the height of this B-tree (for debugging). 80 | * 81 | * @return the height of this B-tree 82 | */ 83 | public int height() { 84 | return height; 85 | } 86 | 87 | 88 | /** 89 | * Returns the value associated with the given key. 90 | * 91 | * @param key the key 92 | * @return the value associated with the given key if the key is in the symbol table 93 | * and {@code null} if the key is not in the symbol table 94 | * @throws IllegalArgumentException if {@code key} is {@code null} 95 | */ 96 | public Value get(Key key) { 97 | if (key == null) throw new IllegalArgumentException("argument to get() is null"); 98 | return search(root, key, height); 99 | } 100 | 101 | private Value search(Node x, Key key, int ht) { 102 | Entry[] children = x.children; 103 | 104 | // external node 105 | if (ht == 0) { 106 | for (int j = 0; j < x.m; j++) { 107 | if (eq(key, children[j].key)) return (Value) children[j].val; 108 | } 109 | } else { 110 | // internal node 111 | for (int j = 0; j < x.m; j++) { 112 | if (j + 1 == x.m || less(key, children[j + 1].key)) 113 | return search(children[j].next, key, ht - 1); 114 | } 115 | } 116 | return null; 117 | } 118 | 119 | 120 | /** 121 | * Inserts the key-value pair into the symbol table, overwriting the old value 122 | * with the new value if the key is already in the symbol table. 123 | * If the value is {@code null}, this effectively deletes the key from the symbol table. 124 | * 125 | * @param key the key 126 | * @param val the value 127 | * @throws IllegalArgumentException if {@code key} is {@code null} 128 | */ 129 | public void put(Key key, Value val) { 130 | if (key == null) throw new IllegalArgumentException("argument key to put() is null"); 131 | Node u = insert(root, key, val, height); 132 | n++; 133 | if (u == null) return; 134 | 135 | // need to split root 136 | Node t = new Node(2); 137 | t.children[0] = new Entry(root.children[0].key, null, root); 138 | t.children[1] = new Entry(u.children[0].key, null, u); 139 | root = t; 140 | height++; 141 | } 142 | 143 | private Node insert(Node h, Key key, Value val, int ht) { 144 | int j; 145 | Entry t = new Entry(key, val, null); 146 | 147 | // external node 148 | if (ht == 0) { 149 | for (j = 0; j < h.m; j++) { 150 | if (less(key, h.children[j].key)) break; 151 | } 152 | } 153 | 154 | // internal node 155 | else { 156 | for (j = 0; j < h.m; j++) { 157 | if ((j + 1 == h.m) || less(key, h.children[j + 1].key)) { 158 | Node u = insert(h.children[j++].next, key, val, ht - 1); 159 | if (u == null) return null; 160 | t.key = u.children[0].key; 161 | t.next = u; 162 | break; 163 | } 164 | } 165 | } 166 | 167 | for (int i = h.m; i > j; i--) 168 | h.children[i] = h.children[i - 1]; 169 | h.children[j] = t; 170 | h.m++; 171 | if (h.m < M) return null; 172 | else return split(h); 173 | } 174 | 175 | // split node in half 176 | private Node split(Node h) { 177 | Node t = new Node(M / 2); 178 | h.m = M / 2; 179 | for (int j = 0; j < M / 2; j++) 180 | t.children[j] = h.children[M / 2 + j]; 181 | return t; 182 | } 183 | 184 | /** 185 | * Returns a string representation of this B-tree (for debugging). 186 | * 187 | * @return a string representation of this B-tree. 188 | */ 189 | public String toString() { 190 | return toString(root, height, "") + "\n"; 191 | } 192 | 193 | private String toString(Node h, int ht, String indent) { 194 | StringBuilder s = new StringBuilder(); 195 | Entry[] children = h.children; 196 | 197 | if (ht == 0) { 198 | for (int j = 0; j < h.m; j++) { 199 | s.append(indent + children[j].key + " " + children[j].val + "\n"); 200 | } 201 | } else { 202 | for (int j = 0; j < h.m; j++) { 203 | if (j > 0) s.append(indent + "(" + children[j].key + ")\n"); 204 | s.append(toString(children[j].next, ht - 1, indent + " ")); 205 | } 206 | } 207 | return s.toString(); 208 | } 209 | 210 | 211 | // comparison functions - make Comparable instead of Key to avoid casts 212 | private boolean less(Comparable k1, Comparable k2) { 213 | return k1.compareTo(k2) < 0; 214 | } 215 | 216 | private boolean eq(Comparable k1, Comparable k2) { 217 | return k1.compareTo(k2) == 0; 218 | } 219 | 220 | 221 | /** 222 | * Unit tests the {@code Algorithm4thBTree} data type. 223 | * 224 | * @param args the command-line arguments 225 | */ 226 | public static void main(String[] args) { 227 | //Algorithm4thBTree st = new Algorithm4thBTree<>(); 228 | //String[] letters = {"Q", "W", "E", "R", "T", "Y", "U", "I", "O", "A", "S", 229 | // "D", "F", "G", "H", "J", "K", "L", "Z", "X", "C", "V", "B", 230 | // "N", "M", "P"}; 231 | // 232 | // 233 | //for (int i = 0; i < letters.length; i++) { 234 | // st.put(letters[i], letters[i]); 235 | //} 236 | //System.out.println(); 237 | //System.out.println("M:" + st.get("M")); 238 | //System.out.println("N:" + st.get("N")); 239 | //System.out.println("ll:" + st.get("lll")); 240 | //System.out.println("size: " + st.size()); 241 | //System.out.println("height: " + st.height()); 242 | //System.out.println(st); 243 | //System.out.println(); 244 | // 245 | Algorithm4thBTree st = new Algorithm4thBTree<>(); 246 | 247 | for (int i = 1; i < 7; i++) { 248 | st.put(i, i); 249 | } 250 | System.out.println(); 251 | System.out.println("0:" + st.get(0)); 252 | System.out.println("1:" + st.get(1)); 253 | System.out.println("100:" + st.get(100)); 254 | System.out.println("size: " + st.size()); 255 | System.out.println("height: " + st.height()); 256 | System.out.println(st); 257 | System.out.println(); 258 | 259 | 260 | } 261 | 262 | } 263 | 264 | -------------------------------------------------------------------------------- /src/main/java/algorithm/tree/BinaryTree.java: -------------------------------------------------------------------------------- 1 | package algorithm.tree; 2 | 3 | 4 | import java.util.LinkedList; 5 | import java.util.Queue; 6 | import java.util.Stack; 7 | 8 | /** 9 | * @author 李文浩 10 | * @version 2017/7/30. 11 | */ 12 | public class BinaryTree { 13 | 14 | /** 15 | * 节点定义 16 | */ 17 | //static class TreeNode { 18 | // int val; 19 | // TreeNode left; 20 | // TreeNode right; 21 | // 22 | // TreeNode(int x) { 23 | // val = x; 24 | // } 25 | //} 26 | 27 | public static void main(String[] args) { 28 | TreeNode treeNode1 = new TreeNode(1); 29 | TreeNode treeNode2 = new TreeNode(2); 30 | TreeNode treeNode3 = new TreeNode(3); 31 | TreeNode treeNode4 = new TreeNode(4); 32 | TreeNode treeNode5 = new TreeNode(5); 33 | TreeNode treeNode6 = new TreeNode(6); 34 | treeNode1.left = treeNode2; 35 | treeNode1.right = treeNode3; 36 | treeNode2.left = treeNode4; 37 | treeNode2.right = treeNode5; 38 | treeNode3.left = treeNode6; 39 | System.out.println("高度:" + height(treeNode1)); 40 | System.out.print("层序遍历:"); 41 | levelTraversal(treeNode1); 42 | System.out.print("\n先序递归:"); 43 | preOrder(treeNode1); 44 | System.out.print("\n中序递归:"); 45 | inOrder(treeNode1); 46 | System.out.print("\n后序递归:"); 47 | postOrder(treeNode1); 48 | System.out.print("\n先序非递归:"); 49 | preOrderStack(treeNode1); 50 | System.out.print("\n先序非递归2:"); 51 | preOrderStack2(treeNode1); 52 | System.out.print("\n中序非递归:"); 53 | inOrderStack(treeNode1); 54 | System.out.print("\n后序非递归:"); 55 | postOrderStack(treeNode1); 56 | System.out.print("\n后序非递归2:"); 57 | postOrderStack2(treeNode1); 58 | } 59 | 60 | /** 61 | * 高度,左右子树中的较大值 62 | * 63 | * @param node 64 | * @return 65 | */ 66 | public static int height(TreeNode node) { 67 | if (node == null) { 68 | return 0; 69 | } 70 | int leftHeight = height(node.left); 71 | int rightHeight = height(node.right); 72 | return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1; 73 | } 74 | 75 | /** 76 | * 层序遍历一颗二叉树,用广度优先搜索的思想,使用一个队列来按照层的顺序存放节点 77 | * 先将根节点入队列,只要队列不为空,然后出队列,并访问,接着讲访问节点的左右子树依次入队列 78 | * 79 | * @param node 80 | */ 81 | public static void levelTraversal(TreeNode node) { 82 | if (node == null) 83 | return; 84 | Queue queue = new LinkedList(); 85 | queue.offer(node); 86 | TreeNode treeNode; 87 | while (!queue.isEmpty()) { 88 | treeNode = queue.poll(); 89 | System.out.print(treeNode.val + " "); 90 | if (treeNode.left != null) { 91 | queue.offer(treeNode.left); 92 | } 93 | if (treeNode.right != null) { 94 | queue.offer(treeNode.right); 95 | } 96 | } 97 | } 98 | 99 | /** 100 | * 先序递归 101 | * 102 | * @param treeNode 103 | */ 104 | public static void preOrder(TreeNode treeNode) { 105 | if (treeNode != null) { 106 | System.out.print(treeNode.val + " "); 107 | preOrder(treeNode.left); 108 | preOrder(treeNode.right); 109 | } 110 | } 111 | 112 | /** 113 | * 中序递归 114 | * 115 | * @param treeNode 116 | */ 117 | public static void inOrder(TreeNode treeNode) { 118 | if (treeNode != null) { 119 | inOrder(treeNode.left); 120 | System.out.print(treeNode.val + " "); 121 | inOrder(treeNode.right); 122 | } 123 | } 124 | 125 | /** 126 | * 后序递归 127 | * 128 | * @param treeNode 129 | */ 130 | public static void postOrder(TreeNode treeNode) { 131 | if (treeNode != null) { 132 | postOrder(treeNode.left); 133 | postOrder(treeNode.right); 134 | System.out.print(treeNode.val + " "); 135 | } 136 | } 137 | 138 | /** 139 | * 先序非递归: 140 | * 这种实现类似于图的深度优先遍历(DFS)。 141 | * 维护一个栈,将根节点入栈,然后只要栈不为空,出栈并访问, 142 | * 接着依次将访问节点的右节点、左节点入栈。 143 | * 这种方式应该是对先序遍历的一种特殊实现(看上去简单明了), 144 | * 但是不具备很好的扩展性,在中序和后序方式中不适用 145 | * 146 | * @param root 147 | */ 148 | public static void preOrderStack(TreeNode root) { 149 | Stack stack = new Stack<>(); 150 | stack.push(root); 151 | while (!stack.isEmpty()) { 152 | TreeNode treeNode = stack.pop(); 153 | System.out.print(treeNode.val + " "); 154 | if (treeNode.right != null) { 155 | stack.push(treeNode.right); 156 | } 157 | if (treeNode.left != null) { 158 | stack.push(treeNode.left); 159 | } 160 | } 161 | } 162 | 163 | /** 164 | * 先序非递归2: 165 | * 利用栈模拟递归过程实现循环先序遍历二叉树。 166 | * 这种方式具备扩展性,它模拟递归的过程,将左子树点不断的压入栈,直到null, 167 | * 然后处理栈顶节点的右子树。 168 | * 169 | * @param root 170 | */ 171 | public static void preOrderStack2(TreeNode root) { 172 | Stack stack = new Stack<>(); 173 | TreeNode treeNode = root; 174 | while (treeNode != null || !stack.isEmpty()) { 175 | //将左子树点不断的压入栈 176 | while (treeNode != null) { 177 | //先访问再入栈 178 | System.out.print(treeNode.val + " "); 179 | stack.push(treeNode); 180 | treeNode = treeNode.left; 181 | } 182 | //出栈并处理右子树 183 | if (!stack.isEmpty()) { 184 | treeNode = stack.pop(); 185 | treeNode = treeNode.right; 186 | } 187 | 188 | } 189 | 190 | } 191 | 192 | /** 193 | * 中序非递归: 194 | * 利用栈模拟递归过程实现循环中序遍历二叉树。 195 | * 思想和上面的先序非递归2相同, 196 | * 只是访问的时间是在左子树都处理完直到null的时候出栈并访问。 197 | * 198 | * @param treeNode 199 | */ 200 | public static void inOrderStack(TreeNode treeNode) { 201 | Stack stack = new Stack<>(); 202 | while (treeNode != null || !stack.isEmpty()) { 203 | while (treeNode != null) { 204 | stack.push(treeNode); 205 | treeNode = treeNode.left; 206 | } 207 | //左子树进栈完毕 208 | if (!stack.isEmpty()) { 209 | treeNode = stack.pop(); 210 | System.out.print(treeNode.val + " "); 211 | treeNode = treeNode.right; 212 | } 213 | } 214 | } 215 | 216 | public static class TagNode { 217 | TreeNode treeNode; 218 | boolean isFirst; 219 | } 220 | 221 | /** 222 | * 后序非递归: 223 | * 后序遍历不同于先序和中序,它是要先处理完左右子树, 224 | * 然后再处理根(回溯)。 225 | *

226 | *

227 | * 对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点, 228 | * 此时该结点出现在栈顶,但是此时不能将其出栈并访问,因此其右孩子还为被访问。 229 | * 所以接下来按照相同的规则对其右子树进行相同的处理,当访问完其右孩子时,该结点又出现在栈顶, 230 | * 此时可以将其出栈并访问。这样就保证了正确的访问顺序。 231 | * 可以看出,在这个过程中,每个结点都两次出现在栈顶,只有在第二次出现在栈顶时,才能访问它。 232 | * 因此需要多设置一个变量标识该结点是否是第一次出现在栈顶,这里是在树结构里面加一个标记,然后合成一个新的TagNode。 233 | * 234 | * @param treeNode 235 | */ 236 | public static void postOrderStack(TreeNode treeNode) { 237 | Stack stack = new Stack<>(); 238 | TagNode tagNode; 239 | while (treeNode != null || !stack.isEmpty()) { 240 | //沿左子树一直往下搜索,直至出现没有左子树的结点 241 | while (treeNode != null) { 242 | tagNode = new TagNode(); 243 | tagNode.treeNode = treeNode; 244 | tagNode.isFirst = true; 245 | stack.push(tagNode); 246 | treeNode = treeNode.left; 247 | } 248 | 249 | if (!stack.isEmpty()) { 250 | tagNode = stack.pop(); 251 | //表示是第一次出现在栈顶 252 | if (tagNode.isFirst == true) { 253 | tagNode.isFirst = false; 254 | stack.push(tagNode); 255 | treeNode = tagNode.treeNode.right; 256 | } else { 257 | //第二次出现在栈顶 258 | System.out.print(tagNode.treeNode.val + " "); 259 | treeNode = null; 260 | } 261 | } 262 | } 263 | } 264 | 265 | /** 266 | * 后序非递归2: 267 | * 要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它; 268 | * 或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。 269 | * 若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被访问, 270 | * 左孩子和右孩子都在根结点前面被访问。 271 | * 272 | * @param treeNode 273 | */ 274 | public static void postOrderStack2(TreeNode treeNode) { 275 | Stack stack = new Stack<>(); 276 | TreeNode currentTreeNode; 277 | TreeNode preTreeNode = null; 278 | stack.push(treeNode); 279 | 280 | while (!stack.isEmpty()) { 281 | currentTreeNode = stack.peek(); 282 | //如果当前结点没有孩子结点或者孩子节点都已被访问过 283 | if ((currentTreeNode.left == null && currentTreeNode.right == null) || 284 | (preTreeNode != null && (preTreeNode == currentTreeNode.left || preTreeNode == currentTreeNode.right))) { 285 | System.out.print(currentTreeNode.val + " "); 286 | stack.pop(); 287 | preTreeNode = currentTreeNode; 288 | } else { 289 | if (currentTreeNode.right != null) { 290 | stack.push(currentTreeNode.right); 291 | } 292 | if (currentTreeNode.left != null) { 293 | stack.push(currentTreeNode.left); 294 | } 295 | } 296 | } 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /src/main/java/algorithm/security/RSAUtil.java: -------------------------------------------------------------------------------- 1 | package algorithm.security; 2 | 3 | /** 4 | * @author 李文浩 5 | * @version 2017/3/26. 6 | *

7 | * Created by fangzhipeng on 2017/3/21. 8 | * RSA :RSA非对称加密算法是1977年由Ron Rivest、 Adi Shamirh和LenAdleman开发 * 的, RSA取名来 9 | * 自开发他们三者的名字。 10 | * 参考:http://blog.csdn.net/wangqiuyun/article/details/42143957 11 | */ 12 | import javax.crypto.BadPaddingException; 13 | import javax.crypto.Cipher; 14 | import javax.crypto.IllegalBlockSizeException; 15 | import javax.crypto.NoSuchPaddingException; 16 | import java.io.*; 17 | import java.security.*; 18 | import java.security.interfaces.RSAPrivateKey; 19 | import java.security.interfaces.RSAPublicKey; 20 | import java.security.spec.InvalidKeySpecException; 21 | import java.security.spec.PKCS8EncodedKeySpec; 22 | import java.security.spec.X509EncodedKeySpec; 23 | import java.util.Base64; 24 | 25 | public class RSAUtil { 26 | 27 | 28 | /** 29 | * 字节数据转字符串专用集合 30 | */ 31 | private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6', 32 | '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 33 | 34 | /** 35 | * 随机生成密钥对 36 | */ 37 | public static void genKeyPair(String filePath) { 38 | // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象 39 | KeyPairGenerator keyPairGen = null; 40 | try { 41 | keyPairGen = KeyPairGenerator.getInstance("RSA"); 42 | } catch (NoSuchAlgorithmException e) { 43 | // TODO Auto-generated catch block 44 | e.printStackTrace(); 45 | } 46 | // 初始化密钥对生成器,密钥大小为96-1024位 47 | keyPairGen.initialize(1024, new SecureRandom()); 48 | // 生成一个密钥对,保存在keyPair中 49 | KeyPair keyPair = keyPairGen.generateKeyPair(); 50 | // 得到私钥 51 | RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); 52 | // 得到公钥 53 | RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); 54 | try { 55 | // 得到公钥字符串 56 | // 得到私钥字符串 57 | String privateKeyString = new String(Base64.getEncoder().encode(privateKey.getEncoded())); 58 | String publicKeyString = new String(Base64.getEncoder().encode(publicKey.getEncoded())); 59 | // 将密钥对写入到文件 60 | 61 | File file1 = new File(filePath + "publicKey.keystore"); 62 | File file2 = new File(filePath + "privateKey.keystore"); 63 | if (!file1.exists()) { 64 | file1.createNewFile(); 65 | } 66 | if (!file2.exists()) { 67 | file2.createNewFile(); 68 | } 69 | FileWriter pubfw = new FileWriter(filePath + "/publicKey.keystore"); 70 | FileWriter prifw = new FileWriter(filePath + "/privateKey.keystore"); 71 | BufferedWriter pubbw = new BufferedWriter(pubfw); 72 | BufferedWriter pribw = new BufferedWriter(prifw); 73 | pubbw.write(publicKeyString); 74 | pribw.write(privateKeyString); 75 | pubbw.flush(); 76 | pubbw.close(); 77 | pubfw.close(); 78 | pribw.flush(); 79 | pribw.close(); 80 | prifw.close(); 81 | } catch (Exception e) { 82 | e.printStackTrace(); 83 | } 84 | } 85 | 86 | /** 87 | * 从文件中输入流中加载公钥 88 | * 89 | * @param 90 | * 91 | * @throws Exception 92 | * 加载公钥时产生的异常 93 | */ 94 | public static String loadPublicKeyByFile(String path) throws Exception { 95 | try { 96 | BufferedReader br = new BufferedReader(new FileReader(path 97 | + "/publicKey.keystore")); 98 | String readLine = null; 99 | StringBuilder sb = new StringBuilder(); 100 | while ((readLine = br.readLine()) != null) { 101 | sb.append(readLine); 102 | } 103 | br.close(); 104 | return sb.toString(); 105 | } catch (IOException e) { 106 | throw new Exception("公钥数据流读取错误"); 107 | } catch (NullPointerException e) { 108 | throw new Exception("公钥输入流为空"); 109 | } 110 | } 111 | 112 | /** 113 | * 从字符串中加载公钥 114 | * 115 | * @param publicKeyStr 116 | * 公钥数据字符串 117 | * @throws Exception 118 | * 加载公钥时产生的异常 119 | */ 120 | public static RSAPublicKey loadPublicKeyByStr(String publicKeyStr) 121 | throws Exception { 122 | try { 123 | byte[] buffer = Base64.getDecoder().decode(publicKeyStr); 124 | KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 125 | X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer); 126 | return (RSAPublicKey) keyFactory.generatePublic(keySpec); 127 | } catch (NoSuchAlgorithmException e) { 128 | throw new Exception("无此算法"); 129 | } catch (InvalidKeySpecException e) { 130 | throw new Exception("公钥非法"); 131 | } catch (NullPointerException e) { 132 | throw new Exception("公钥数据为空"); 133 | } 134 | } 135 | 136 | /** 137 | * 从文件中加载私钥 138 | * 139 | * @param 140 | * 141 | * @return 是否成功 142 | * @throws Exception 143 | */ 144 | public static String loadPrivateKeyByFile(String path) throws Exception { 145 | try { 146 | BufferedReader br = new BufferedReader(new FileReader(path 147 | + "/privateKey.keystore")); 148 | String readLine = null; 149 | StringBuilder sb = new StringBuilder(); 150 | while ((readLine = br.readLine()) != null) { 151 | sb.append(readLine); 152 | } 153 | br.close(); 154 | return sb.toString(); 155 | } catch (IOException e) { 156 | throw new Exception("私钥数据读取错误"); 157 | } catch (NullPointerException e) { 158 | throw new Exception("私钥输入流为空"); 159 | } 160 | } 161 | 162 | public static RSAPrivateKey loadPrivateKeyByStr(String privateKeyStr) 163 | throws Exception { 164 | try { 165 | byte[] buffer = Base64.getDecoder().decode(privateKeyStr); 166 | PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer); 167 | KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 168 | return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); 169 | } catch (NoSuchAlgorithmException e) { 170 | throw new Exception("无此算法"); 171 | } catch (InvalidKeySpecException e) { 172 | throw new Exception("私钥非法"); 173 | } catch (NullPointerException e) { 174 | throw new Exception("私钥数据为空"); 175 | } 176 | } 177 | 178 | /** 179 | * 公钥加密过程 180 | * 181 | * @param publicKey 182 | * 公钥 183 | * @param plainTextData 184 | * 明文数据 185 | * @return 186 | * @throws Exception 187 | * 加密过程中的异常信息 188 | */ 189 | public static byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData) 190 | throws Exception { 191 | if (publicKey == null) { 192 | throw new Exception("加密公钥为空, 请设置"); 193 | } 194 | Cipher cipher = null; 195 | try { 196 | // 使用默认RSA 197 | cipher = Cipher.getInstance("RSA"); 198 | // cipher= Cipher.getInstance("RSA", new BouncyCastleProvider()); 199 | cipher.init(Cipher.ENCRYPT_MODE, publicKey); 200 | byte[] output = cipher.doFinal(plainTextData); 201 | return output; 202 | } catch (NoSuchAlgorithmException e) { 203 | throw new Exception("无此加密算法"); 204 | } catch (NoSuchPaddingException e) { 205 | e.printStackTrace(); 206 | return null; 207 | } catch (InvalidKeyException e) { 208 | throw new Exception("加密公钥非法,请检查"); 209 | } catch (IllegalBlockSizeException e) { 210 | throw new Exception("明文长度非法"); 211 | } catch (BadPaddingException e) { 212 | throw new Exception("明文数据已损坏"); 213 | } 214 | } 215 | 216 | /** 217 | * 私钥加密过程 218 | * 219 | * @param privateKey 220 | * 私钥 221 | * @param plainTextData 222 | * 明文数据 223 | * @return 224 | * @throws Exception 225 | * 加密过程中的异常信息 226 | */ 227 | public static byte[] encrypt(RSAPrivateKey privateKey, byte[] plainTextData) 228 | throws Exception { 229 | if (privateKey == null) { 230 | throw new Exception("加密私钥为空, 请设置"); 231 | } 232 | Cipher cipher = null; 233 | try { 234 | // 使用默认RSA 235 | cipher = Cipher.getInstance("RSA"); 236 | cipher.init(Cipher.ENCRYPT_MODE, privateKey); 237 | byte[] output = cipher.doFinal(plainTextData); 238 | return output; 239 | } catch (NoSuchAlgorithmException e) { 240 | throw new Exception("无此加密算法"); 241 | } catch (NoSuchPaddingException e) { 242 | e.printStackTrace(); 243 | return null; 244 | } catch (InvalidKeyException e) { 245 | throw new Exception("加密私钥非法,请检查"); 246 | } catch (IllegalBlockSizeException e) { 247 | throw new Exception("明文长度非法"); 248 | } catch (BadPaddingException e) { 249 | throw new Exception("明文数据已损坏"); 250 | } 251 | } 252 | 253 | /** 254 | * 私钥解密过程 255 | * 256 | * @param privateKey 257 | * 私钥 258 | * @param cipherData 259 | * 密文数据 260 | * @return 明文 261 | * @throws Exception 262 | * 解密过程中的异常信息 263 | */ 264 | public static byte[] decrypt(RSAPrivateKey privateKey, byte[] cipherData) 265 | throws Exception { 266 | if (privateKey == null) { 267 | throw new Exception("解密私钥为空, 请设置"); 268 | } 269 | Cipher cipher = null; 270 | try { 271 | // 使用默认RSA 272 | cipher = Cipher.getInstance("RSA"); 273 | // cipher= Cipher.getInstance("RSA", new BouncyCastleProvider()); 274 | cipher.init(Cipher.DECRYPT_MODE, privateKey); 275 | byte[] output = cipher.doFinal(cipherData); 276 | return output; 277 | } catch (NoSuchAlgorithmException e) { 278 | throw new Exception("无此解密算法"); 279 | } catch (NoSuchPaddingException e) { 280 | e.printStackTrace(); 281 | return null; 282 | } catch (InvalidKeyException e) { 283 | throw new Exception("解密私钥非法,请检查"); 284 | } catch (IllegalBlockSizeException e) { 285 | throw new Exception("密文长度非法"); 286 | } catch (BadPaddingException e) { 287 | throw new Exception("密文数据已损坏"); 288 | } 289 | } 290 | 291 | /** 292 | * 公钥解密过程 293 | * 294 | * @param publicKey 295 | * 公钥 296 | * @param cipherData 297 | * 密文数据 298 | * @return 明文 299 | * @throws Exception 300 | * 解密过程中的异常信息 301 | */ 302 | public static byte[] decrypt(RSAPublicKey publicKey, byte[] cipherData) 303 | throws Exception { 304 | if (publicKey == null) { 305 | throw new Exception("解密公钥为空, 请设置"); 306 | } 307 | Cipher cipher = null; 308 | try { 309 | // 使用默认RSA 310 | cipher = Cipher.getInstance("RSA"); 311 | // cipher= Cipher.getInstance("RSA", new BouncyCastleProvider()); 312 | cipher.init(Cipher.DECRYPT_MODE, publicKey); 313 | byte[] output = cipher.doFinal(cipherData); 314 | return output; 315 | } catch (NoSuchAlgorithmException e) { 316 | throw new Exception("无此解密算法"); 317 | } catch (NoSuchPaddingException e) { 318 | e.printStackTrace(); 319 | return null; 320 | } catch (InvalidKeyException e) { 321 | throw new Exception("解密公钥非法,请检查"); 322 | } catch (IllegalBlockSizeException e) { 323 | throw new Exception("密文长度非法"); 324 | } catch (BadPaddingException e) { 325 | throw new Exception("密文数据已损坏"); 326 | } 327 | } 328 | 329 | /** 330 | * 字节数据转十六进制字符串 331 | * 332 | * @param data 333 | * 输入数据 334 | * @return 十六进制内容 335 | */ 336 | public static String byteArrayToString(byte[] data) { 337 | StringBuilder stringBuilder = new StringBuilder(); 338 | for (int i = 0; i < data.length; i++) { 339 | // 取出字节的高四位 作为索引得到相应的十六进制标识符 注意无符号右移 340 | stringBuilder.append(HEX_CHAR[(data[i] & 0xf0) >>> 4]); 341 | // 取出字节的低四位 作为索引得到相应的十六进制标识符 342 | stringBuilder.append(HEX_CHAR[(data[i] & 0x0f)]); 343 | if (i < data.length - 1) { 344 | stringBuilder.append(' '); 345 | } 346 | } 347 | return stringBuilder.toString(); 348 | } 349 | 350 | 351 | public static void main(String[] args) throws Exception { 352 | String filepath = "F:/temp/"; 353 | File file = new File(filepath); 354 | if (!file.exists()) { 355 | file.mkdir(); 356 | } 357 | genKeyPair(filepath); 358 | System.out.println("--------------公钥加密私钥解密过程-------------------"); 359 | String plainText = "1223333323:8783737321232:dewejj28i33e92hhsxxxx"; 360 | //公钥加密过程 361 | byte[] cipherData = encrypt(loadPublicKeyByStr(loadPublicKeyByFile(filepath)), plainText.getBytes()); 362 | String cipher = new String(Base64.getEncoder().encode(cipherData)); 363 | //私钥解密过程 364 | byte[] res = decrypt(loadPrivateKeyByStr(loadPrivateKeyByFile(filepath)), Base64.getDecoder().decode(cipher)); 365 | String restr = new String(res); 366 | System.out.println("原文:" + plainText); 367 | System.out.println("加密密文:" + cipher); 368 | System.out.println("解密:" + restr); 369 | System.out.println(); 370 | } 371 | } -------------------------------------------------------------------------------- /src/main/java/algorithm/list/LinkedList.java: -------------------------------------------------------------------------------- 1 | package algorithm.list; 2 | 3 | 4 | import java.util.Stack; 5 | 6 | /** 7 | * 8 | *

9 | *

10 | * 1. 求单链表中结点的个数 11 | * 2. 将单链表反转 12 | * 3. 查找单链表中的倒数第K个结点(k > 0) 13 | * 4. 查找单链表的中间结点 14 | * 5. 从尾到头打印单链表 15 | * 6. 已知两个单链表pHead1和pHead2各自有序,把它们合并成一个链表依然有序 16 | * 7. 判断一个单链表中是否有环 17 | * 8. 已知一个单链表中存在环,求进入环中的第一个节点 18 | * 9. 判断两个单链表是否相交 19 | * 10. 求两个单链表相交的第一个节点 20 | * 11. 给出一单链表头指针 head 和一节点指针 deletedNode,O(1)时间复杂度删除节点deletedNode 21 | * 12. 链表的冒泡排序 22 | * 13. 单链表的双冒泡排序 23 | * 快慢指针,新的链表 24 | */ 25 | 26 | 27 | public class LinkedList { 28 | private static class ListNode { 29 | int val; 30 | ListNode next; 31 | public ListNode() { 32 | } 33 | public ListNode(int val) { 34 | this.val = val; 35 | } 36 | } 37 | 38 | public static void main(String[] args) { 39 | ListNode head = null, head2 = null; 40 | for (int i = 1; i <= 5; i++) { 41 | head = addBack(head, i); 42 | } 43 | for (int i = 2; i <= 6; i++) { 44 | head2 = addBack(head2, i); 45 | } 46 | show(head); 47 | System.out.println("1. 求单链表中结点的个数"); 48 | System.out.println("size=" + size(head)); 49 | 50 | System.out.println("2. 将单链表反转"); 51 | head = reverse(head); 52 | show(head); 53 | head = reverse(head); 54 | 55 | System.out.println("3. 查找单链表中的倒数第K个结点(k > 0)"); 56 | ListNode kListNode = getKNode(head, 0); 57 | System.out.println(kListNode != null ? kListNode.val : null); 58 | 59 | System.out.println("4. 查找单链表的中间结点"); 60 | ListNode midListNode = getMidNode(head); 61 | System.out.println(midListNode != null ? midListNode.val : null); 62 | 63 | System.out.println("5. 从尾到头打印单链表"); 64 | reversePrint2(head); 65 | System.out.println(); 66 | 67 | System.out.println("6. 已知两个单链表pHead1 和pHead2 各自有序,把它们合并成一个链表依然有序"); 68 | ListNode head6 = null, head7 = null; 69 | for (int i = 1; i <= 10; i++) { 70 | head6 = addBack(head6, (int) (Math.random() * 100)); 71 | } 72 | bubbleSort(head6); 73 | 74 | for (int i = 1; i <= 10; i++) { 75 | head7 = addBack(head7, (int) (Math.random() * 100)); 76 | } 77 | bubbleSort(head7); 78 | System.out.println(); 79 | show(merge(head6, head7)); 80 | 81 | System.out.println("7. 判断一个单链表中是否有环"); 82 | ListNode head3 = null; 83 | for (int i = 1; i <= 5; i++) { 84 | head3 = addBack(head3, i); 85 | } 86 | ListNode p = head3; 87 | while (p.next != null) { 88 | p = p.next; 89 | } 90 | 91 | p.next = head3.next; 92 | 93 | System.out.println(hasRing(head3)); 94 | 95 | System.out.println("8. 已知一个单链表中存在环,求进入环中的第一个节点"); 96 | System.out.println(getFirstRingNode(head3).val); 97 | System.out.println("9. 判断两个单链表是否相交"); 98 | ListNode head4 = null, head5 = null, p4; 99 | for (int i = 2; i <= 6; i++) { 100 | head4 = addBack(head4, i); 101 | } 102 | p4 = head4; 103 | while (p4.next != null) { 104 | p4 = p4.next; 105 | } 106 | p4.next = head.next; 107 | for (int i = 2; i <= 6; i++) { 108 | head5 = addBack(head5, i); 109 | } 110 | p4 = head5; 111 | while (p4.next != null) { 112 | p4 = p4.next; 113 | } 114 | p4.next = head.next; 115 | System.out.println(isIntersect(head4, head5)); 116 | 117 | System.out.println("10. 求两个单链表相交的第一个节点"); 118 | System.out.println(firstIntersectNode(head4, head5).val); 119 | System.out.println("11. 给出一单链表头指针 head 和一节点指针 deletedNode,O(1)时间复杂度删除节点deletedNode"); 120 | 121 | head = delete(head, head); 122 | show(head); 123 | System.out.println("12. 单链表的冒泡排序"); 124 | ListNode head12 = null; 125 | for (int i = 1; i <= 10; i++) { 126 | head12 = addBack(head12, (int) (Math.random() * 100)); 127 | } 128 | bubbleSort(head12); 129 | System.out.println("13. 单链表的双冒泡排序"); 130 | ListNode head13 = null; 131 | for (int i = 1; i <= 10; i++) { 132 | head13 = addBack(head13, (int) (Math.random() * 100)); 133 | } 134 | doubleBubblesort(head13, null); 135 | show(head13); 136 | } 137 | 138 | public static ListNode addBack(ListNode head, int num) { 139 | ListNode newListNode = new ListNode(); 140 | newListNode.val = num; 141 | newListNode.next = null; 142 | ListNode listNode = head; 143 | if (head == null) { 144 | head = newListNode; 145 | return head; 146 | } 147 | while (listNode.next != null) { 148 | listNode = listNode.next; 149 | } 150 | listNode.next = newListNode; 151 | 152 | return head; 153 | } 154 | 155 | public static void show(ListNode listNode) { 156 | while (listNode != null) { 157 | // System.out.println(listNode.val + " " + listNode+" "+listNode.next); 158 | System.out.print(listNode.val + " "); 159 | listNode = listNode.next; 160 | } 161 | System.out.println(); 162 | } 163 | 164 | /** 165 | * 1. 求单链表中结点的个数 166 | *

167 | * 思路 168 | * 169 | * @param head 170 | * @return 171 | */ 172 | public static int size(ListNode head) { 173 | int size = 0; 174 | while (head != null) { 175 | size++; 176 | head = head.next; 177 | } 178 | return size; 179 | } 180 | 181 | /** 182 | * 2. 将单链表反转 183 | *

184 | * 构建一个新的链表,依次将本链表的节点插入到新链表的最前端,即可完成链表的反转。 185 | * 186 | * @param head 187 | * @return 188 | */ 189 | public static ListNode reverse(ListNode head) { 190 | ListNode p1 = head, p2; 191 | head = null; 192 | while (p1 != null) { 193 | p2 = p1; 194 | p1 = p1.next; 195 | 196 | //头插法 197 | p2.next = head; 198 | head = p2; 199 | } 200 | return head; 201 | } 202 | 203 | /** 204 | * 3. 查找单链表中的倒数第K个结点(k > 0) 205 | * 第一种解法是得到顺数的第 size+k-1 个节点,即为倒数的第K歌节点 206 | * 第二种解法是快慢指针,主要思路就是使用两个指针,先让前面的指针走到正向第k个结点, 207 | * 这样前后两个指针的距离差是k-1,之后前后两个指针一起向前走,前面的指针走到最后一个结点时, 208 | * 后面指针所指结点就是倒数第k个结点,下面采用这种解法。 209 | * 210 | * @param head 211 | * @return 212 | */ 213 | public static ListNode getKNode(ListNode head, int k) { 214 | if (k <= 0 || head == null) { 215 | return null; 216 | } 217 | ListNode p2 = head, p1 = head; 218 | while (k-- > 1 && p1 != null) { 219 | p1 = p1.next; 220 | } 221 | // 说明k>size,因此返回null 222 | if (k > 1 || p1 == null) { 223 | return null; 224 | } 225 | while (p1.next != null) { 226 | p1 = p1.next; 227 | p2 = p2.next; 228 | } 229 | return p2; 230 | } 231 | 232 | /** 233 | * 4. 查找单链表的中间结点 234 | * 采用快慢指针,p1每次走两步,p2每次走一步,奇数返回size/2+1,偶数返回size/2, 235 | * 注意链表为空,链表结点个数为1和2的情况。 236 | * 237 | * @param head 238 | * @return 239 | */ 240 | public static ListNode getMidNode(ListNode head) { 241 | if (head == null) { 242 | return null; 243 | } 244 | ListNode p1 = head, p2 = head; 245 | while (p1.next != null) { 246 | if (p1 == null) { 247 | break; 248 | } 249 | p1 = p1.next.next; 250 | p2 = p2.next; 251 | 252 | } 253 | return p2; 254 | } 255 | 256 | /** 257 | * 5. 从尾到头打印单链表 258 | * 用栈 259 | * 260 | * @param listNode 261 | */ 262 | public static void reversePrint(ListNode listNode) { 263 | Stack stack = new Stack<>(); 264 | while (listNode != null) { 265 | stack.push(listNode); 266 | listNode = listNode.next; 267 | } 268 | while (!stack.isEmpty()) { 269 | System.out.print(stack.pop().val + " "); 270 | } 271 | 272 | } 273 | 274 | /** 275 | * 5. 从尾到头打印单链表 276 | * 递归 277 | * 278 | * @param listNode 279 | */ 280 | public static void reversePrint2(ListNode listNode) { 281 | if (listNode != null) { 282 | reversePrint2(listNode.next); 283 | System.out.print(listNode.val + " "); 284 | } 285 | } 286 | 287 | /** 288 | * 6. 已知两个单链表pHead1 和pHead2 各自有序,把它们合并成一个链表依然有序 289 | *

290 | * 类似于归并排序 291 | * 292 | * @param list1 293 | * @param list2 294 | * @return 295 | */ 296 | public static ListNode merge(ListNode list1, ListNode list2) { 297 | ListNode head = new ListNode(-1); 298 | ListNode current = head; 299 | while (list1 != null && list2 != null) { 300 | if (list1.val <= list2.val) { 301 | current.next = list1; 302 | list1 = list1.next; 303 | } else { 304 | current.next = list2; 305 | list2 = list2.next; 306 | } 307 | current = current.next; 308 | } 309 | if (list1 != null) { 310 | current.next = list1; 311 | } 312 | if (list2 != null) { 313 | current.next = list2; 314 | } 315 | return head.next; 316 | } 317 | 318 | /** 319 | * 7. 判断一个单链表中是否有环 320 | *

321 | * 这里也是用到两个指针。如果一个链表中有环,也就是说用一个指针去遍历, 322 | * 是永远走不到头的。因此,我们可以用两个指针去遍历,一个指针一次走两步, 323 | * 一个指针一次走一步,如果有环,两个指针肯定会在环中相遇。时间复杂度为O(n) 324 | * 325 | * @param head 326 | * @return 327 | */ 328 | public static boolean hasRing(ListNode head) { 329 | ListNode p1 = head, p2 = head; 330 | while (p1 != null && p1.next != null) { 331 | p1 = p1.next.next; 332 | p2 = p2.next; 333 | if (p1 == p2) { 334 | return true; 335 | } 336 | } 337 | return false; 338 | } 339 | 340 | /** 341 | * 8. 已知一个单链表中存在环,求进入环中的第一个节点 342 | *

343 | * **解题思路**: 由上题可知,按照 p2 每次两步,p1 每次一步的方式走,发现 p2 和 p1 重合, 344 | * 确定了单向链表有环路了。接下来,让p2回到链表的头部,重新走,每次步长不是走2了, 345 | * 而是走1,那么当 p1 和 p2 再次相遇的时候,就是环路的入口了。 346 | *

347 | * *为什么?**:假定起点到环入口点的距离为 a,p1 和 p2 的相交点M与环入口点的距离为b, 348 | * 环路的周长为L,当 p1 和 p2 第一次相遇的时候,假定 p1 走了 n 步。那么有: 349 | *

350 | * p1走的路径: a+b = n; 351 | * p2走的路径: a+b+k*L = 2*n; p2 比 p1 多走了k圈环路,总路程是p1的2倍 352 | *

353 | * 根据上述公式可以得到 k*L=a+b=n显然,如果从相遇点M开始,p1 再走 n 步的话, 354 | * 还可以再回到相遇点,同时p2从头开始走的话,经过n步,也会达到相遇点M。 355 | *

356 | * 显然在这个步骤当中 p1 和 p2 只有前 a 步走的路径不同,所以当 p1 和 p2 再次重合的时候,必然是在链表的环路入口点上。 357 | * 358 | * @param head 359 | * @return 360 | */ 361 | public static ListNode getFirstRingNode(ListNode head) { 362 | ListNode p1 = head, p2 = head; 363 | while (p1 != null && p1.next != null) { 364 | p1 = p1.next.next; 365 | p2 = p2.next; 366 | if (p1 == p2) { 367 | p1 = head; 368 | while (p1 != p2) { 369 | p1 = p1.next; 370 | p2 = p2.next; 371 | } 372 | break; 373 | } 374 | } 375 | return p1; 376 | } 377 | 378 | /** 379 | * 9. 判断两个单链表是否相交 380 | * 如果两个链表相交,那么相交之后的节点应该相同,那么最后那个节点应该也相同 381 | * 382 | * @param head1 383 | * @param head2 384 | * @return 385 | */ 386 | public static boolean isIntersect(ListNode head1, ListNode head2) { 387 | ListNode p1 = head1, p2 = head2; 388 | while (p1.next != null) { 389 | p1 = p1.next; 390 | } 391 | while (p2.next != null) { 392 | p2 = p2.next; 393 | } 394 | return p1 == p2; 395 | } 396 | 397 | /** 398 | * 10. 求两个单链表相交的第一个节点 399 | * 采用对齐的思想。计算两个链表的长度 L1 , L2,分别用两个指针 p1 , p2 指向两个链表的头, 400 | * 然后将较长链表的 p1(假设为 p1)向后移动L2 - L1个节点,然后再同时向后移动p1 , p2, 401 | * 直到 p1 = p2。相遇的点就是相交的第一个节点。 402 | * 403 | * @param head1 404 | * @param head2 405 | * @return 406 | */ 407 | public static ListNode firstIntersectNode(ListNode head1, ListNode head2) { 408 | int len1 = size(head1); 409 | int len2 = size(head2); 410 | ListNode p1 = head1, p2 = head2; 411 | if (len1 > len2) { 412 | for (int i = 1; i < len1 - len2; i++) { 413 | p1 = p1.next; 414 | } 415 | } else { 416 | for (int i = 1; i < len2 - len1; i++) { 417 | p2 = p2.next; 418 | } 419 | } 420 | while (p1 != p2) { 421 | p1 = p1.next; 422 | p2 = p2.next; 423 | } 424 | 425 | return p1; 426 | } 427 | 428 | /** 429 | * 11. 给出一单链表头指针 head 和一节点指针 deletedListNode,O(1)时间复杂度删除节点deletedNode 430 | * 431 | * 将deletedNode下一个节点的值复制给deletedNode节点,然后删除deletedNode节点,但是对于要删除的节点是最后一个节点的时候要做处理。 432 | * 433 | * @param head 434 | */ 435 | public static ListNode delete(ListNode head, ListNode deletedListNode) { 436 | if (deletedListNode.next != null) { 437 | deletedListNode.val = deletedListNode.next.val; 438 | deletedListNode.next = deletedListNode.next.next; 439 | } else { 440 | if (head == deletedListNode) { 441 | return null; 442 | } 443 | ListNode p = head; 444 | while (p.next != deletedListNode) { 445 | p = p.next; 446 | } 447 | p.next = null; 448 | } 449 | return head; 450 | } 451 | 452 | /** 453 | * 12 单链表的冒泡排序 454 | * 455 | * 对于数组的冒泡排序是上层for循环控制次数,下次for循环控制距离,对于链表的冒泡排序而言, 456 | * 首先让tail指针为null,一次循环比较完之后,在等于最后一个节点,倒数第二个节点。。。就是通过tail指针控制循环比较的次数和距离。 457 | * @param head 458 | */ 459 | public static void bubbleSort(ListNode head) { 460 | ListNode tail = null; 461 | ListNode p1; 462 | while (head != tail) { 463 | for (p1 = head; p1.next != tail; p1 = p1.next) { 464 | if (p1.val > p1.next.val) { 465 | int temp = p1.val; 466 | p1.val = p1.next.val; 467 | p1.next.val = temp; 468 | } 469 | } 470 | tail = p1; 471 | } 472 | show(head); 473 | } 474 | 475 | /** 476 | * 13 单链表的双冒泡排序 477 | * 478 | * @param start 479 | * @param end 480 | */ 481 | public static void doubleBubblesort(ListNode start, ListNode end) { 482 | if (start != end) { 483 | ListNode p1 = start; 484 | ListNode p2 = p1.next; 485 | while (p2 != end) { 486 | if (p2.val < start.val) { 487 | p1 = p1.next; 488 | int temp = p1.val; 489 | p1.val = p2.val; 490 | p2.val = temp; 491 | } 492 | p2 = p2.next; 493 | } 494 | int temp = p1.val; 495 | p1.val = start.val; 496 | start.val = temp; 497 | doubleBubblesort(start, p1); 498 | doubleBubblesort(p1.next, null); 499 | } 500 | 501 | } 502 | 503 | } 504 | --------------------------------------------------------------------------------