├── .gitignore ├── README.md ├── 五角星.png ├── 公众号 └── 扫码关注微信公众号.png ├── 剑指offer ├── Test02.java ├── Test03.java ├── Test04.java ├── Test05.java ├── Test06.java ├── Test07.java ├── Test08.java ├── Test09.java ├── Test10.java ├── Test11.java ├── Test12.java ├── Test13.java ├── Test14.java ├── Test15.java ├── Test16.java ├── Test17.java ├── Test18.java ├── Test19.java ├── Test20.java ├── Test21.java ├── Test22.java ├── Test23.java ├── Test24.java ├── Test25.java ├── Test26.java ├── Test27.java ├── Test28.java ├── Test29.java ├── Test30.java ├── Test31.java ├── Test32.java ├── Test33.java ├── Test34.java ├── Test35.java ├── Test36.java ├── Test37.java ├── Test38.java ├── Test39.java ├── Test40.java ├── Test41.java ├── Test42.java ├── Test43.java ├── Test44.java ├── Test45.java ├── Test47.java ├── Test49.java ├── Test50.java ├── Test51.java ├── Test52.java ├── Test53.java ├── Test54.java ├── Test55.java ├── Test56.java ├── Test57.java ├── Test58.java ├── Test59.java ├── Test60.java ├── Test61.java ├── Test62.java ├── Test63.java ├── Test64.java ├── Test65.java ├── Test66.java └── Test67.java ├── 操作系统 ├── 操作系统.md └── 操作系统.png ├── 数据库系统 ├── 数据库.md └── 数据库.png ├── 计算机网络 ├── 计算机网络.md └── 计算机网络.png └── 设计模式 ├── 设计模式.md └── 设计模式.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Example user template template 3 | ### Example user template 4 | 5 | # IntelliJ project files 6 | .idea 7 | *.iml 8 | out 9 | gen 10 | src 11 | pom.xml 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![正在加载图片](./五角星.png) 2 | 3 | 看了许多书籍博客,遇到面试还是一团乱糟糟的样子,也许,你只缺这一个思维导图来帮你! 4 | 5 | 本项目不会局限于某一编程语言,凡是技术人相关知识,我都会做成思维导图的形式,坚持不懈。 6 | 7 | ## 操作系统 8 | 9 | ![正在加载图片](./操作系统/操作系统.png) 10 | 11 | [操作系统](./操作系统/操作系统.md) 12 | 13 | ## 计算机网络 14 | 15 | ![正在加载图片](./计算机网络/计算机网络.png) 16 | 17 | [计算机网络](./计算机网络/计算机网络.md) 18 | 19 | ## 数据库系统 20 | 21 | ![正在加载图片](./数据库系统/数据库.png) 22 | 23 | [数据库系统](./数据库系统/数据库.md) 24 | 25 | ## 设计模式 26 | 27 | ![正在加载图片](./设计模式/设计模式.png) 28 | 29 | [设计模式](./设计模式/设计模式.md) 30 | 31 | # 关于 32 | 33 | 更多思维导图制作中... 34 | 35 | # 鸣谢 36 | 37 | CYC2018 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /五角星.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossDing/Pentagram/be12b225a29d18d2e024211c588100cb3bf54504/五角星.png -------------------------------------------------------------------------------- /公众号/扫码关注微信公众号.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossDing/Pentagram/be12b225a29d18d2e024211c588100cb3bf54504/公众号/扫码关注微信公众号.png -------------------------------------------------------------------------------- /剑指offer/Test02.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-21 6 | * Time: 13:58 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test02 { 10 | 11 | /** 12 | * 单例模式,懒汉式,线程安全 13 | */ 14 | public static class Singleton { 15 | private final static Singleton INSTANCE = new Singleton(); 16 | 17 | private Singleton() { 18 | 19 | } 20 | 21 | public static Singleton getInstance() { 22 | return INSTANCE; 23 | } 24 | } 25 | 26 | /** 27 | * 单例模式,饿汉式,线程不安全 28 | */ 29 | public static class Singleton2 { 30 | private static Singleton2 instance = null; 31 | 32 | private Singleton2() { 33 | 34 | } 35 | 36 | public static Singleton2 getInstance() { 37 | if (instance == null) { 38 | instance = new Singleton2(); 39 | } 40 | 41 | return instance; 42 | } 43 | } 44 | 45 | 46 | /** 47 | * 单例模式,饿汉式,线程安全,多线程环境下效率不高 48 | */ 49 | public static class Singleton3 { 50 | private static Singleton3 instance = null; 51 | 52 | private Singleton3() { 53 | 54 | } 55 | 56 | public static synchronized Singleton3 getInstance() { 57 | if (instance == null) { 58 | instance = new Singleton3(); 59 | } 60 | 61 | return instance; 62 | } 63 | } 64 | 65 | /** 66 | * 单例模式,饿汉式,变种,线程安全 67 | */ 68 | public static class Singleton4 { 69 | private static Singleton4 instance = null; 70 | 71 | static { 72 | instance = new Singleton4(); 73 | } 74 | 75 | private Singleton4() { 76 | 77 | } 78 | 79 | public static Singleton4 getInstance() { 80 | return instance; 81 | } 82 | } 83 | 84 | /** 85 | * 单例模式,懒汉式,使用静态内部类,线程安全【推荐】 86 | */ 87 | public static class Singleton5 { 88 | private final static class SingletonHolder { 89 | private static final Singleton5 INSTANCE = new Singleton5(); 90 | } 91 | 92 | private Singleton5() { 93 | 94 | } 95 | 96 | public static Singleton5 getInstance() { 97 | return SingletonHolder.INSTANCE; 98 | } 99 | } 100 | 101 | /** 102 | * 静态内部类,使用枚举方式,线程安全【推荐】 103 | */ 104 | public enum Singleton6 { 105 | INSTANCE; 106 | 107 | public void whateverMethod() { 108 | 109 | } 110 | } 111 | 112 | /** 113 | * 静态内部类,使用双重校验锁,线程安全【推荐】 114 | */ 115 | public static class Singleton7 { 116 | private volatile static Singleton7 instance = null; 117 | 118 | private Singleton7() { 119 | 120 | } 121 | 122 | public static Singleton7 getInstance() { 123 | if (instance == null) { 124 | synchronized (Singleton7.class) { 125 | if (instance == null) { 126 | instance = new Singleton7(); 127 | } 128 | } 129 | } 130 | 131 | return instance; 132 | } 133 | } 134 | 135 | public static void main(String[] args) { 136 | System.out.println(Singleton.getInstance() == Singleton.getInstance()); 137 | System.out.println(Singleton2.getInstance() == Singleton2.getInstance()); 138 | System.out.println(Singleton3.getInstance() == Singleton3.getInstance()); 139 | System.out.println(Singleton4.getInstance() == Singleton4.getInstance()); 140 | System.out.println(Singleton5.getInstance() == Singleton5.getInstance()); 141 | System.out.println(Singleton6.INSTANCE == Singleton6.INSTANCE); 142 | System.out.println(Singleton7.getInstance() == Singleton7.getInstance()); 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /剑指offer/Test03.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-21 6 | * Time: 18:43 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test03 { 10 | /** 11 | * 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。 12 | * 请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 13 | *

14 | * 规律:首先选取数组中右上角的数字。如果该数字等于要查找的数字,查找过程结束: 15 | * 如果该数字大于要查找的数字,剔除这个数字所在的列:如果该数字小于要查找的数字,剔除这个数字所在的行。 16 | * 也就是说如果要查找的数字不在数组的右上角,则每-次都在数组的查找范围中剔除)行或者一列,这样每一步都可以缩小 17 | * 查找的范围,直到找到要查找的数字,或者查找范围为空。 18 | * 19 | * @param matrix 待查找的数组 20 | * @param number 要查找的数 21 | * @return 查找结果,true找到,false没有找到 22 | */ 23 | public static boolean find(int[][] matrix, int number) { 24 | 25 | // 输入条件判断 26 | if (matrix == null || matrix.length < 1 || matrix[0].length < 1) { 27 | return false; 28 | } 29 | 30 | int rows = matrix.length; // 数组的行数 31 | int cols = matrix[1].length; // 数组行的列数 32 | 33 | int row = 0; // 起始开始的行号 34 | int col = cols - 1; // 起始开始的列号 35 | 36 | // 要查找的位置确保在数组之内 37 | while (row >= 0 && row < rows && col >= 0 && col < cols) { 38 | if (matrix[row][col] == number) { // 如果找到了就直接退出 39 | return true; 40 | } else if (matrix[row][col] > number) { // 如果找到的数比要找的数大,说明要找的数在当前数的左边 41 | col--; // 列数减一,代表向左移动 42 | } else { // 如果找到的数比要找的数小,说明要找的数在当前数的下边 43 | row++; // 行数加一,代表向下移动 44 | } 45 | } 46 | 47 | return false; 48 | } 49 | 50 | public static void main(String[] args) { 51 | int[][] matrix = { 52 | {1, 2, 8, 9}, 53 | {2, 4, 9, 12}, 54 | {4, 7, 10, 13}, 55 | {6, 8, 11, 15} 56 | }; 57 | System.out.println(find(matrix, 7)); // 要查找的数在数组中 58 | System.out.println(find(matrix, 5)); // 要查找的数不在数组中 59 | System.out.println(find(matrix, 1)); // 要查找的数是数组中最小的数字 60 | System.out.println(find(matrix, 15)); // 要查找的数是数组中最大的数字 61 | System.out.println(find(matrix, 0)); // 要查找的数比数组中最小的数字还小 62 | System.out.println(find(matrix, 16)); // 要查找的数比数组中最大的数字还大 63 | System.out.println(find(null, 16)); // 健壮性测试,输入空指针 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /剑指offer/Test04.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-21 6 | * Time: 19:09 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test04 { 10 | /** 11 | * 请实现一个函数,把字符串中的每个空格替换成"%20",例如“We are happy.“,则输出”We%20are%20happy.“。 12 | * 13 | * @param string 要转换的字符数组 14 | * @param usedLength 已经字符数组中已经使用的长度 15 | * @return 转换后使用的字符长度,-1表示处理失败 16 | */ 17 | public static int replaceBlank(char[] string, int usedLength) { 18 | // 判断输入是否合法 19 | if (string == null || string.length < usedLength) { 20 | return -1; 21 | } 22 | 23 | // 统计字符数组中的空白字符数 24 | int whiteCount = 0; 25 | for (int i = 0; i < usedLength; i++) { 26 | if (string[i] == ' ') { 27 | whiteCount++; 28 | } 29 | } 30 | 31 | // 计算转换后的字符长度是多少 32 | int targetLength = whiteCount * 2 + usedLength; 33 | int tmp = targetLength; // 保存长度结果用于返回 34 | if (targetLength > string.length) { // 如果转换后的长度大于数组的最大长度,直接返回失败 35 | return -1; 36 | } 37 | 38 | // 如果没有空白字符就不用处理 39 | if (whiteCount == 0) { 40 | return usedLength; 41 | } 42 | 43 | usedLength--; // 从后向前,第一个开始处理的字符 44 | targetLength--; // 处理后的字符放置的位置 45 | 46 | // 字符中有空白字符,一直处理到所有的空白字符处理完 47 | while (usedLength >= 0 && usedLength < targetLength) { 48 | // 如是当前字符是空白字符,进行"%20"替换 49 | if (string[usedLength] == ' ') { 50 | string[targetLength--] = '0'; 51 | string[targetLength--] = '2'; 52 | string[targetLength--] = '%'; 53 | } else { // 否则移动字符 54 | string[targetLength--] = string[usedLength]; 55 | } 56 | usedLength--; 57 | } 58 | 59 | return tmp; 60 | } 61 | 62 | public static void main(String[] args) { 63 | char[] string = new char[50]; 64 | string[0] = ' '; 65 | string[1] = 'e'; 66 | string[2] = ' '; 67 | string[3] = ' '; 68 | string[4] = 'r'; 69 | string[5] = 'e'; 70 | string[6] = ' '; 71 | string[7] = ' '; 72 | string[8] = 'a'; 73 | string[9] = ' '; 74 | string[10] = 'p'; 75 | string[11] = ' '; 76 | 77 | int length = replaceBlank(string, 12); 78 | System.out.println(new String(string, 0, length)); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /剑指offer/Test05.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * Author: 王俊超 7 | * Date: 2015-04-21 8 | * Time: 21:36 9 | * Declaration: All Rights Reserved !!! 10 | */ 11 | public class Test05 { 12 | /** 13 | * 结点对象 14 | */ 15 | public static class ListNode { 16 | int val; // 结点的值 17 | ListNode nxt; // 下一个结点 18 | } 19 | 20 | /** 21 | * 输入个链表的头结点,从尾到头反过来打印出每个结点的值 22 | * 使用栈的方式进行 23 | * 24 | * @param root 链表头结点 25 | */ 26 | public static void printListInverselyUsingIteration(ListNode root) { 27 | Stack stack = new Stack<>(); 28 | while (root != null) { 29 | stack.push(root); 30 | root = root.nxt; 31 | } 32 | ListNode tmp; 33 | while (!stack.isEmpty()) { 34 | tmp = stack.pop(); 35 | System.out.print(tmp.val + " "); 36 | } 37 | } 38 | 39 | /** 40 | * 输入个链表的头结点,从尾到头反过来打印出每个结点的值 41 | * 使用栈的方式进行 42 | * 43 | * @param root 链表头结点 44 | */ 45 | public static void printListInverselyUsingRecursion(ListNode root) { 46 | if (root != null) { 47 | printListInverselyUsingRecursion(root.nxt); 48 | System.out.print(root.val + " "); 49 | } 50 | } 51 | 52 | public static void main(String[] args) { 53 | ListNode root = new ListNode(); 54 | root.val = 1; 55 | root.nxt = new ListNode(); 56 | root.nxt.val = 2; 57 | root.nxt.nxt = new ListNode(); 58 | root.nxt.nxt.val = 3; 59 | root.nxt.nxt.nxt = new ListNode(); 60 | root.nxt.nxt.nxt.val = 4; 61 | root.nxt.nxt.nxt.nxt = new ListNode(); 62 | root.nxt.nxt.nxt.nxt.val = 5; 63 | 64 | printListInverselyUsingIteration(root); 65 | System.out.println(); 66 | printListInverselyUsingRecursion(root); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /剑指offer/Test06.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-22 6 | * Time: 08:17 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test06 { 10 | /** 11 | * 二叉树节点类 12 | */ 13 | public static class BinaryTreeNode { 14 | int value; 15 | BinaryTreeNode left; 16 | BinaryTreeNode right; 17 | } 18 | 19 | /** 20 | * 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二节树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 21 | * 22 | * @param preorder 前序遍历 23 | * @param inorder 中序遍历 24 | * @return 树的根结点 25 | */ 26 | public static BinaryTreeNode construct(int[] preorder, int[] inorder) { 27 | // 输入的合法性判断,两个数组都不能为空,并且都有数据,而且数据的数目相同 28 | if (preorder == null || inorder == null || preorder.length != inorder.length || inorder.length < 1) { 29 | return null; 30 | } 31 | 32 | return construct(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1); 33 | } 34 | 35 | /** 36 | * 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二节树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 37 | * 38 | * @param preorder 前序遍历 39 | * @param ps 前序遍历的开始位置 40 | * @param pe 前序遍历的结束位置 41 | * @param inorder 中序遍历 42 | * @param is 中序遍历的开始位置 43 | * @param ie 中序遍历的结束位置 44 | * @return 树的根结点 45 | */ 46 | public static BinaryTreeNode construct(int[] preorder, int ps, int pe, int[] inorder, int is, int ie) { 47 | 48 | // 开始位置大于结束位置说明已经没有需要处理的元素了 49 | if (ps > pe) { 50 | return null; 51 | } 52 | // 取前序遍历的第一个数字,就是当前的根结点 53 | int value = preorder[ps]; 54 | int index = is; 55 | // 在中序遍历的数组中找根结点的位置 56 | while (index <= ie && inorder[index] != value) { 57 | index++; 58 | } 59 | 60 | // 如果在整个中序遍历的数组中没有找到,说明输入的参数是不合法的,抛出异常 61 | if (index > ie) { 62 | throw new RuntimeException("Invalid input"); 63 | } 64 | 65 | // 创建当前的根结点,并且为结点赋值 66 | BinaryTreeNode node = new BinaryTreeNode(); 67 | node.value = value; 68 | 69 | // 递归构建当前根结点的左子树,左子树的元素个数:index-is+1个 70 | // 左子树对应的前序遍历的位置在[ps+1, ps+index-is] 71 | // 左子树对应的中序遍历的位置在[is, index-1] 72 | node.left = construct(preorder, ps + 1, ps + index - is, inorder, is, index - 1); 73 | // 递归构建当前根结点的右子树,右子树的元素个数:ie-index个 74 | // 右子树对应的前序遍历的位置在[ps+index-is+1, pe] 75 | // 右子树对应的中序遍历的位置在[index+1, ie] 76 | node.right = construct(preorder, ps + index - is + 1, pe, inorder, index + 1, ie); 77 | 78 | // 返回创建的根结点 79 | return node; 80 | } 81 | 82 | // 中序遍历二叉树 83 | public static void printTree(BinaryTreeNode root) { 84 | if (root != null) { 85 | printTree(root.left); 86 | System.out.print(root.value + " "); 87 | printTree(root.right); 88 | } 89 | 90 | } 91 | 92 | // 普通二叉树 93 | // 1 94 | // / \ 95 | // 2 3 96 | // / / \ 97 | // 4 5 6 98 | // \ / 99 | // 7 8 100 | private static void test1() { 101 | int[] preorder = {1, 2, 4, 7, 3, 5, 6, 8}; 102 | int[] inorder = {4, 7, 2, 1, 5, 3, 8, 6}; 103 | BinaryTreeNode root = construct(preorder, inorder); 104 | printTree(root); 105 | } 106 | 107 | // 所有结点都没有右子结点 108 | // 1 109 | // / 110 | // 2 111 | // / 112 | // 3 113 | // / 114 | // 4 115 | // / 116 | // 5 117 | private static void test2() { 118 | int[] preorder = {1, 2, 3, 4, 5}; 119 | int[] inorder = {5, 4, 3, 2, 1}; 120 | BinaryTreeNode root = construct(preorder, inorder); 121 | printTree(root); 122 | } 123 | 124 | // 所有结点都没有左子结点 125 | // 1 126 | // \ 127 | // 2 128 | // \ 129 | // 3 130 | // \ 131 | // 4 132 | // \ 133 | // 5 134 | private static void test3() { 135 | int[] preorder = {1, 2, 3, 4, 5}; 136 | int[] inorder = {1, 2, 3, 4, 5}; 137 | BinaryTreeNode root = construct(preorder, inorder); 138 | printTree(root); 139 | } 140 | 141 | // 树中只有一个结点 142 | private static void test4() { 143 | int[] preorder = {1}; 144 | int[] inorder = {1}; 145 | BinaryTreeNode root = construct(preorder, inorder); 146 | printTree(root); 147 | } 148 | 149 | // 完全二叉树 150 | // 1 151 | // / \ 152 | // 2 3 153 | // / \ / \ 154 | // 4 5 6 7 155 | private static void test5() { 156 | int[] preorder = {1, 2, 4, 5, 3, 6, 7}; 157 | int[] inorder = {4, 2, 5, 1, 6, 3, 7}; 158 | BinaryTreeNode root = construct(preorder, inorder); 159 | printTree(root); 160 | } 161 | 162 | // 输入空指针 163 | private static void test6() { 164 | construct(null, null); 165 | } 166 | 167 | // 输入的两个序列不匹配 168 | private static void test7() { 169 | int[] preorder = {1, 2, 4, 5, 3, 6, 7}; 170 | int[] inorder = {4, 2, 8, 1, 6, 3, 7}; 171 | BinaryTreeNode root = construct(preorder, inorder); 172 | printTree(root); 173 | } 174 | 175 | 176 | public static void main(String[] args) { 177 | 178 | test1(); 179 | System.out.println(); 180 | test2(); 181 | System.out.println(); 182 | test3(); 183 | System.out.println(); 184 | test4(); 185 | System.out.println(); 186 | test5(); 187 | System.out.println(); 188 | test6(); 189 | System.out.println(); 190 | test7(); 191 | 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /剑指offer/Test07.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * Author: 王俊超 7 | * Date: 2015-04-22 8 | * Time: 09:11 9 | * Declaration: All Rights Reserved !!! 10 | */ 11 | public class Test07 { 12 | /** 13 | * 用两个栈模拟的队列 14 | * 用两个核实现一个队列。队列的声明如下,诸实现它的两个函数appendTail和deleteHead, 15 | * 分别完成在队列尾部插入结点和在队列头部删除结点的功能。 16 | */ 17 | public static class MList { 18 | // 插入栈,只用于插入的数据 19 | private Stack stack1 = new Stack<>(); 20 | // 弹出栈,只用于弹出数据 21 | private Stack stack2 = new Stack<>(); 22 | 23 | public MList() { 24 | } 25 | 26 | // 添加操作,成在队列尾部插入结点 27 | public void appendTail(T t) { 28 | stack1.add(t); 29 | } 30 | 31 | // 删除操作,在队列头部删除结点 32 | public T deleteHead() { 33 | 34 | // 先判断弹出栈是否为空,如果为空就将插入栈的所有数据弹出栈, 35 | // 并且将弹出的数据压入弹出栈中 36 | if (stack2.isEmpty()) { 37 | while (!stack1.isEmpty()) { 38 | stack2.add(stack1.pop()); 39 | } 40 | } 41 | 42 | // 如果弹出栈中还没有数据就抛出异常 43 | if (stack2.isEmpty()) { 44 | throw new RuntimeException("No more element."); 45 | } 46 | 47 | // 返回弹出栈的栈顶元素,对应的就是队首元素。 48 | return stack2.pop(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /剑指offer/Test08.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-22 6 | * Time: 10:57 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test08 { 10 | 11 | /** 12 | * 把一个数组最开始的若干个元素搬到数组的末尾, 我们称之数组的旋转。 13 | * 输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。 14 | * 例如数组{3, 4, 5, 1, 2}为{l ,2, 3, 4, 5}的一个旋转,该数组的最小值为 15 | * 16 | * @param numbers 旋转数组 17 | * @return 数组的最小值 18 | */ 19 | public static int min(int[] numbers) { 20 | // 判断输入是否合法 21 | if (numbers == null || numbers.length == 0) { 22 | throw new RuntimeException("Invalid input."); 23 | } 24 | 25 | // 开始处理的第一个位置 26 | int lo = 0; 27 | // 开始处理的最后一个位置 28 | int hi = numbers.length - 1; 29 | // 设置初始值 30 | int mi = lo; 31 | 32 | // 确保lo在前一个排好序的部分,hi在排好序的后一个部分 33 | while (numbers[lo] >= numbers[hi]) { 34 | // 当处理范围只有两个数据时,返回后一个结果 35 | // 因为numbers[lo] >= numbers[hi]总是成立,后一个结果对应的是最小的值 36 | if (hi - lo == 1) { 37 | return numbers[hi]; 38 | } 39 | 40 | // 取中间的位置 41 | mi = lo + (hi - lo) / 2; 42 | 43 | // 如果三个数都相等,则需要进行顺序处理,从头到尾找最小的值 44 | if (numbers[mi] == numbers[lo] && numbers[hi] == numbers[mi]) { 45 | return minInorder(numbers, lo, hi); 46 | } 47 | 48 | // 如果中间位置对应的值在前一个排好序的部分,将lo设置为新的处理位置 49 | if (numbers[mi] >= numbers[lo]) { 50 | lo = mi; 51 | } 52 | // 如果中间位置对应的值在后一个排好序的部分,将hi设置为新的处理位置 53 | else if (numbers[mi] <= numbers[hi]) { 54 | hi = mi; 55 | } 56 | } 57 | 58 | // 返回最终的处理结果 59 | return numbers[mi]; 60 | } 61 | 62 | /** 63 | * 找数组中的最小值 64 | * 65 | * @param numbers 数组 66 | * @param start 数组的起始位置 67 | * @param end 数组的结束位置 68 | * @return 找到的最小的数 69 | */ 70 | public static int minInorder(int[] numbers, int start, int end) { 71 | int result = numbers[start]; 72 | for (int i = start + 1; i <= end; i++) { 73 | if (result > numbers[i]) { 74 | result = numbers[i]; 75 | } 76 | } 77 | return result; 78 | } 79 | 80 | 81 | public static void main(String[] args) { 82 | // 典型输入,单调升序的数组的一个旋转 83 | int[] array1 = {3, 4, 5, 1, 2}; 84 | System.out.println(min(array1)); 85 | 86 | // 有重复数字,并且重复的数字刚好的最小的数字 87 | int[] array2 = {3, 4, 5, 1, 1, 2}; 88 | System.out.println(min(array2)); 89 | 90 | // 有重复数字,但重复的数字不是第一个数字和最后一个数字 91 | int[] array3 = {3, 4, 5, 1, 2, 2}; 92 | System.out.println(min(array3)); 93 | 94 | // 有重复的数字,并且重复的数字刚好是第一个数字和最后一个数字 95 | int[] array4 = {1, 0, 1, 1, 1}; 96 | System.out.println(min(array4)); 97 | 98 | // 单调升序数组,旋转0个元素,也就是单调升序数组本身 99 | int[] array5 = {1, 2, 3, 4, 5}; 100 | System.out.println(min(array5)); 101 | 102 | // 数组中只有一个数字 103 | int[] array6 = {2}; 104 | System.out.println(min(array6)); 105 | 106 | // 数组中数字都相同 107 | int[] array7 = {1, 1, 1, 1, 1, 1, 1}; 108 | System.out.println(min(array7)); 109 | System.out.println(min(array6)); 110 | 111 | // 输入NULL 112 | System.out.println(min(null)); 113 | 114 | 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /剑指offer/Test09.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-23 6 | * Time: 09:09 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test09 { 10 | 11 | /** 12 | * 写一个函数,输入n,求斐波那契(Fibonacci) 数列的第n项 13 | * @param n Fibonacci数的项数 14 | * @return 第n项的结果 15 | */ 16 | public static long fibonacci(int n) { 17 | 18 | // 当输入非正整数的时候返回0 19 | if (n <= 0) { 20 | return 0; 21 | } 22 | 23 | // 输入1或者2的时候返回1 24 | if (n == 1 || n == 2) { 25 | return 1; 26 | } 27 | 28 | // 记录前两个(第n-2个)的Fibonacci数的值 29 | long prePre = 1; 30 | // 记录前两个(第n-1个)的Fibonacci数的值 31 | long pre = 1; 32 | // 记录前两个(第n个)的Fibonacci数的值 33 | long current = 2; 34 | 35 | // 求解第n个的Fibonacci数的值 36 | for (int i = 3; i <= n ; i++) { 37 | // 求第i个的Fibonacci数的值 38 | current = prePre + pre; 39 | // 更新记录的结果,prePre原先记录第i-2个Fibonacci数的值 40 | // 现在记录第i-1个Fibonacci数的值 41 | prePre = pre; 42 | // 更新记录的结果,pre原先记录第i-1个Fibonacci数的值 43 | // 现在记录第i个Fibonacci数的值 44 | pre = current; 45 | } 46 | 47 | // 返回所求的结果 48 | return current; 49 | } 50 | 51 | public static void main(String[] args) { 52 | System.out.println(fibonacci(0)); 53 | System.out.println(fibonacci(1)); 54 | System.out.println(fibonacci(2)); 55 | System.out.println(fibonacci(3)); 56 | System.out.println(fibonacci(4)); 57 | System.out.println(fibonacci(5)); 58 | System.out.println(fibonacci(6)); 59 | System.out.println(fibonacci(7)); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /剑指offer/Test10.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-23 6 | * Time: 09:21 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test10 { 10 | 11 | /** 12 | * 请实现一个函数, 输入一个整数,输出该数二进制表示中1的个数。 13 | * 例如把9表示成二进制是1001 ,有2位是1. 因此如果输入9,该出2。 14 | * 15 | * @param n 待的数字 16 | * @return 数字中二进制表表的1的数目 17 | */ 18 | public static int numberOfOne(int n) { 19 | // 记录数字中1的位数 20 | int result = 0; 21 | 22 | // JAVA语言规范中,int整形占四个字节,总计32位 23 | // 对每一个位置与1进行求与操作,再累加就可以求出当前数字的表示是多少位1 24 | for (int i = 0; i < 32; i++) { 25 | result += (n & 1); 26 | n >>>= 1; 27 | } 28 | 29 | // 返回求得的结果 30 | return result; 31 | } 32 | 33 | /** 34 | * 请实现一个函数, 输入一个整数,输出该数二进制表示中1的个数。 35 | * 例如把9表示成二进制是1001 ,有2位是1. 因此如果输入9,该出2。 36 | * 【这种方法的效率更高】 37 | * 38 | * @param n 待的数字 39 | * @return 数字中二进制表表的1的数目 40 | */ 41 | public static int numberOfOne2(int n) { 42 | // 记录数字中1的位数 43 | int result = 0; 44 | 45 | // 数字的二进制表示中有多少个1就进行多少次操作 46 | while (n != 0) { 47 | result++; 48 | // 从最右边的1开始,每一次操作都使n的最右的一个1变成了0, 49 | // 即使是符号位也会进行操作。 50 | n = (n - 1) & n; 51 | } 52 | 53 | // 返回求得的结果 54 | return result; 55 | } 56 | 57 | public static void main(String[] args) { 58 | System.out.println(numberOfOne(0B00000000_00000000_00000000_00000000)); // 0 59 | System.out.println(numberOfOne(0B00000000_00000000_00000000_00000001)); // 1 60 | System.out.println(numberOfOne(0B11111111_11111111_11111111_11111111)); // -1 61 | System.out.println(0B01111111_11111111_11111111_11111111 == Integer.MAX_VALUE); 62 | System.out.println(numberOfOne(0B01111111_11111111_11111111_11111111)); // Integer.MAX_VALUE 63 | System.out.println(0B10000000_00000000_00000000_00000000 == Integer.MIN_VALUE); 64 | System.out.println(numberOfOne(0B10000000_00000000_00000000_00000000)); // Integer.MIN_VALUE 65 | 66 | System.out.println(""); 67 | System.out.println(numberOfOne2(0B00000000_00000000_00000000_00000000)); // 0 68 | System.out.println(numberOfOne2(0B00000000_00000000_00000000_00000001)); // 1 69 | System.out.println(numberOfOne2(0B11111111_11111111_11111111_11111111)); // -1 70 | System.out.println(numberOfOne2(0B01111111_11111111_11111111_11111111)); // Integer.MAX_VALUE 71 | System.out.println(numberOfOne2(0B10000000_00000000_00000000_00000000)); // Integer.MIN_VALUE 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /剑指offer/Test11.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-23 6 | * Time: 13:03 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test11 { 10 | 11 | /** 12 | * 实现函数double Power(double base, int exponent),求base的exponent次方。 13 | * 不得使用库函数,同时不需要考虑大数问题。 14 | * 15 | * @param base 指次 16 | * @param exponent 幂 17 | * @return 结果 18 | */ 19 | public static double power(double base, int exponent) { 20 | // 指数和底数不能同时为0 21 | if (base == 0 && exponent == 0) { 22 | throw new RuntimeException("invalid input. base and exponent both are zero"); 23 | } 24 | 25 | // 指数为0就返回1 26 | if (exponent == 0) { 27 | return 1; 28 | } 29 | 30 | 31 | // 求指数的绝对值 32 | long exp = exponent; 33 | if (exponent < 0) { 34 | exp = -exp; 35 | } 36 | 37 | // 求幂次方 38 | double result = powerWithUnsignedExponent(base, exp); 39 | 40 | // 指数是负数,要进行求倒数 41 | if (exponent < 0) { 42 | result = 1 / result; 43 | } 44 | 45 | // 返回结果 46 | return result; 47 | } 48 | 49 | /** 50 | * 求一个数的正整数次幂,不考虑溢出 51 | * 52 | * @param base 指次 53 | * @param exponent 幂 54 | * @return 结果 55 | */ 56 | public static double powerWithUnsignedExponent(double base, long exponent) { 57 | // 如果指数为0,返回1 58 | if (exponent == 0) { 59 | return 1; 60 | } 61 | 62 | // 指数为1,返回底数 63 | if (exponent == 1) { 64 | return base; 65 | } 66 | 67 | // 递归求一半的值 68 | double result = powerWithUnsignedExponent(base, exponent >> 2); 69 | 70 | // 求最终的值,如果是奇数就还要剩以一次底数 71 | result *= result; 72 | if (exponent % 2 != 0) { 73 | result *= base; 74 | } 75 | 76 | // 返回结果 77 | return result; 78 | } 79 | 80 | public static void main(String[] args) { 81 | 82 | System.out.println(0.0000000000000000000000001111 == 0); 83 | System.out.println(0.0000000000000000000000000000 == 0); 84 | 85 | System.out.println(power(2, -4)); 86 | System.out.println(power(2, 4)); 87 | System.out.println(power(2, 0)); 88 | System.out.println(power(0.00000000000000000000000000001, -1)); 89 | System.out.println(power(0.00000000000000000000000000001, 1)); 90 | System.out.println(power(0.00000000000000000000000000001, 0)); 91 | System.out.println(power(0.00000000000000000000000000000, 0)); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /剑指offer/Test12.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-23 6 | * Time: 13:39 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test12 { 10 | 11 | /** 12 | * 输入数字n,按顺序打印出从1最大的n位十进制数。比如输入3,则打印出1、2、3 一直到最大的3位数即999。 13 | * 14 | * @param n 数字的最大位数 15 | */ 16 | public static void printOneToNthDigits(int n) { 17 | // 输入的数字不能为小于1 18 | if (n < 1) { 19 | throw new RuntimeException("The input number must larger than 0"); 20 | } 21 | // 创建一个数组用于打存放值 22 | int[] arr = new int[n]; 23 | printOneToNthDigits(0, arr); 24 | } 25 | 26 | /** 27 | * 输入数字n,按顺序打印出从1最大的n位十进制数。 28 | * 29 | * @param n 当前处理的是第个元素,从0开始计数 30 | * @param arr 存放结果的数组 31 | */ 32 | public static void printOneToNthDigits(int n, int[] arr) { 33 | 34 | // 说明所有的数据排列选择已经处理完了 35 | if (n >= arr.length) { 36 | // 可以输入数组的值 37 | printArray(arr); 38 | } else { 39 | // 对 40 | for (int i = 0; i <= 9; i++) { 41 | arr[n] = i; 42 | printOneToNthDigits(n + 1, arr); 43 | } 44 | } 45 | } 46 | 47 | /** 48 | * 输入数组的元素,从左到右,从第一个非0值到开始输出到最后的元素。 49 | * 50 | * @param arr 要输出的数组 51 | */ 52 | public static void printArray(int[] arr) { 53 | // 找第一个非0的元素 54 | int index = 0; 55 | while (index < arr.length && arr[index] == 0) { 56 | index++; 57 | } 58 | 59 | // 从第一个非0值到开始输出到最后的元素。 60 | for (int i = index; i < arr.length; i++) { 61 | System.out.print(arr[i]); 62 | } 63 | // 条件成立说明数组中有非零元素,所以需要换行 64 | if (index < arr.length) { 65 | System.out.println(); 66 | } 67 | } 68 | 69 | /** 70 | * 输入数字n,按顺序打印出从1最大的n位十进制数。比如输入3,则打印出1、2、3 一直到最大的3位数即999。 71 | * 【第二种方法,比上一种少用内存空间】 72 | * 73 | * @param n 数字的最大位数 74 | */ 75 | public static void printOneToNthDigits2(int n) { 76 | // 输入值必须大于0 77 | if (n < 1) { 78 | throw new RuntimeException("The input number must larger than 0"); 79 | } 80 | 81 | // 创建一个长度为n的数组 82 | int[] arr = new int[n]; 83 | // 为数组元素赋初始值 84 | for (int i = 0; i < arr.length; i++) { 85 | arr[i] = 0; 86 | } 87 | 88 | // 求结果,如果最高位没有进位就一直进行处理 89 | while (addOne(arr) == 0) { 90 | printArray(arr); 91 | } 92 | } 93 | 94 | /** 95 | * 对arr表示的数组的最低位加1 arr中的每个数都不能超过9不能小于0,每个位置模拟一个数位 96 | * 97 | * @param arr 待加数组 98 | * @return 判断最高位是否有进位,如果有进位就返回1,否则返回0 99 | */ 100 | public static int addOne(int[] arr) { 101 | // 保存进位值,因为每次最低位加1 102 | int carry = 1; 103 | // 最低位的位置的后一位 104 | int index = arr.length; 105 | 106 | do { 107 | // 指向上一个处理位置 108 | index--; 109 | // 处理位置的值加上进位的值 110 | arr[index] += carry; 111 | // 求处理位置的进位 112 | carry = arr[index] / 10; 113 | // 求处理位置的值 114 | arr[index] %= 10; 115 | } while (carry != 0 && index > 0); 116 | 117 | // 如果index=0说明已经处理了最高位,carry>0说明最高位有进位,返回1 118 | if (carry > 0 && index == 0) { 119 | return 1; 120 | } 121 | 122 | // 无进位返回0 123 | return 0; 124 | } 125 | 126 | 127 | public static void main(String[] args) { 128 | printOneToNthDigits2(2); 129 | System.out.println(); 130 | printOneToNthDigits(2); 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /剑指offer/Test13.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-23 6 | * Time: 15:17 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test13 { 10 | /** 11 | * 链表结点 12 | */ 13 | public static class ListNode { 14 | int value; // 保存链表的值 15 | ListNode next; // 下一个结点 16 | } 17 | 18 | /** 19 | * 给定单向链表的头指针和一个结点指针,定义一个函数在0(1)时间删除该结点, 20 | * 【注意1:这个方法和文本上的不一样,书上的没有返回值,这个因为JAVA引用传递的原因, 21 | * 如果删除的结点是头结点,如果不采用返回值的方式,那么头结点永远删除不了】 22 | * 【注意2:输入的待删除结点必须是待链表中的结点,否则会引起错误,这个条件由用户进行保证】 23 | * 24 | * @param head 链表表的头 25 | * @param toBeDeleted 待删除的结点 26 | * @return 删除后的头结点 27 | */ 28 | public static ListNode deleteNode(ListNode head, ListNode toBeDeleted) { 29 | 30 | // 如果输入参数有空值就返回表头结点 31 | if (head == null || toBeDeleted == null) { 32 | return head; 33 | } 34 | 35 | // 如果删除的是头结点,直接返回头结点的下一个结点 36 | if (head == toBeDeleted) { 37 | return head.next; 38 | } 39 | 40 | // 下面的情况链表至少有两个结点 41 | 42 | // 在多个节点的情况下,如果删除的是最后一个元素 43 | if (toBeDeleted.next == null) { 44 | // 找待删除元素的前驱 45 | ListNode tmp = head; 46 | while (tmp.next != toBeDeleted) { 47 | tmp = tmp.next; 48 | } 49 | // 删除待结点 50 | tmp.next = null; 51 | 52 | } 53 | // 在多个节点的情况下,如果删除的是某个中间结点 54 | else { 55 | // 将下一个结点的值输入当前待删除的结点 56 | toBeDeleted.value = toBeDeleted.next.value; 57 | // 待删除的结点的下一个指向原先待删除引号的下下个结点,即将待删除的下一个结点删除 58 | toBeDeleted.next = toBeDeleted.next.next; 59 | } 60 | 61 | // 返回删除节点后的链表头结点 62 | return head; 63 | } 64 | 65 | /** 66 | * 输出链表的元素值 67 | * 68 | * @param head 链表的头结点 69 | */ 70 | public static void printList(ListNode head) { 71 | while (head != null) { 72 | System.out.print(head.value + "->"); 73 | head = head.next; 74 | } 75 | System.out.println("null"); 76 | } 77 | 78 | public static void main(String[] args) { 79 | 80 | 81 | ListNode head = new ListNode(); 82 | head.value = 1; 83 | 84 | head.next = new ListNode(); 85 | head.next.value = 2; 86 | 87 | head.next.next = new ListNode(); 88 | head.next.next.value = 3; 89 | 90 | head.next.next.next = new ListNode(); 91 | head.next.next.next.value = 4; 92 | 93 | ListNode middle = head.next.next.next.next = new ListNode(); 94 | head.next.next.next.next.value = 5; 95 | 96 | head.next.next.next.next.next = new ListNode(); 97 | head.next.next.next.next.next.value = 6; 98 | 99 | head.next.next.next.next.next.next = new ListNode(); 100 | head.next.next.next.next.next.next.value = 7; 101 | 102 | head.next.next.next.next.next.next.next = new ListNode(); 103 | head.next.next.next.next.next.next.next.value = 8; 104 | 105 | ListNode last = head.next.next.next.next.next.next.next.next = new ListNode(); 106 | head.next.next.next.next.next.next.next.next.value = 9; 107 | 108 | head = deleteNode(head, null); // 删除的结点为空 109 | printList(head); 110 | ListNode node = new ListNode(); 111 | node.value = 12; 112 | 113 | head = deleteNode(head, head); // 删除头结点 114 | printList(head); 115 | head = deleteNode(head, last); // 删除尾结点 116 | printList(head); 117 | head = deleteNode(head, middle); // 删除中间结点 118 | printList(head); 119 | 120 | head = deleteNode(head, node); // 删除的结点不在链表中 121 | printList(head); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /剑指offer/Test14.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-23 6 | * Time: 15:53 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test14 { 10 | 11 | /** 12 | * 输入一个整数数组,实现一个函数来调整该数组中数字的顺序, 13 | * 使得所有奇数位于数组的前半部分,所有偶数位予数组的后半部分。 14 | * 15 | * @param arr 输入的数组 16 | */ 17 | public static void reorderOddEven(int[] arr) { 18 | // 对于输入的数组为空,或者长度小于2的只接返回 19 | if (arr == null || arr.length < 2) { 20 | return; 21 | } 22 | 23 | // 从左向右记录偶数的位置 24 | int start = 0; 25 | // 从右向左记录奇数的位置 26 | int end = arr.length - 1; 27 | // 开始调整奇数和偶数的位置 28 | while (start < end) { 29 | // 找偶数 30 | while (start < end && arr[start] % 2 != 0) { 31 | start++; 32 | } 33 | // 找奇数 34 | while (start < end && arr[end] % 2 == 0) { 35 | end--; 36 | } 37 | 38 | // 找到后就将奇数和偶数交换位置 39 | // 对于start=end的情况,交换不会产生什么影响 40 | // 所以将if判断省去了 41 | int tmp = arr[start]; 42 | arr[start] = arr[end]; 43 | arr[end] = tmp; 44 | } 45 | } 46 | 47 | /** 48 | * 输出数组的信息 49 | * 50 | * @param arr 待输出数组 51 | */ 52 | public static void printArray(int[] arr) { 53 | if (arr != null && arr.length > 0) { 54 | for (int i : arr) { 55 | System.out.print(i + " "); 56 | } 57 | System.out.println(); 58 | } 59 | } 60 | 61 | public static void main(String[] args) { 62 | int[] array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 63 | reorderOddEven(array); 64 | printArray(array); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /剑指offer/Test15.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-23 6 | * Time: 16:20 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test15 { 10 | public static class ListNode { 11 | int value; 12 | ListNode next; 13 | } 14 | 15 | /** 16 | * 输入一个键表,输出该链表中倒数第k 个结点.为了符合大多数人的习惯, 17 | * 本题从1开始计数,即链表的尾结点是倒数第1个结点.例如一个链表有6个结点, 18 | * 从头结点开始它们的值依次是1、2、3、4、5 6。这个链表的倒数第3个结点是值为4的结点. 19 | * 20 | * @param head 链表的头结点 21 | * @param k 倒数第k个结点 22 | * @return 倒数第k个结点 23 | */ 24 | public static ListNode findKthToTail(ListNode head, int k) { 25 | 26 | // 输入的链表不能为空,并且k大于0 27 | if (k < 1 || head == null) { 28 | return null; 29 | } 30 | 31 | // 指向头结点 32 | ListNode pointer = head; 33 | 34 | // 倒数第k个结点与倒数第一个结点相隔k-1个位置 35 | // pointer先走k-1个位置 36 | for (int i = 1; i < k; i++) { 37 | // 说明还有结点 38 | if (pointer.next != null) { 39 | pointer = pointer.next; 40 | } 41 | // 已经没有节点了,但是i还没有到达k-1说明k太大,链表中没有那么多的元素 42 | else { 43 | // 返回结果 44 | return null; 45 | } 46 | 47 | } 48 | 49 | // pointer还没有走到链表的末尾,那么pointer和head一起走, 50 | // 当pointer走到最后一个结点即,pointer.next=null时,head就是倒数第k个结点 51 | while (pointer.next != null) { 52 | head = head.next; 53 | pointer = pointer.next; 54 | } 55 | 56 | // 返回结果 57 | return head; 58 | } 59 | 60 | public static void main(String[] args) { 61 | ListNode head = new ListNode(); 62 | head.value = 1; 63 | 64 | head.next = new ListNode(); 65 | head.next.value = 2; 66 | 67 | head.next.next = new ListNode(); 68 | head.next.next.value = 3; 69 | 70 | head.next.next.next = new ListNode(); 71 | head.next.next.next.value = 4; 72 | 73 | head.next.next.next.next = new ListNode(); 74 | head.next.next.next.next.value = 5; 75 | 76 | head.next.next.next.next.next = new ListNode(); 77 | head.next.next.next.next.next.value = 6; 78 | 79 | head.next.next.next.next.next.next = new ListNode(); 80 | head.next.next.next.next.next.next.value = 7; 81 | 82 | head.next.next.next.next.next.next.next = new ListNode(); 83 | head.next.next.next.next.next.next.next.value = 8; 84 | 85 | head.next.next.next.next.next.next.next.next = new ListNode(); 86 | head.next.next.next.next.next.next.next.next.value = 9; 87 | 88 | System.out.println(findKthToTail(head, 1).value); // 倒数第一个 89 | System.out.println(findKthToTail(head, 5).value); // 中间的一个 90 | System.out.println(findKthToTail(head, 9).value); // 倒数最后一个就是顺数第一个 91 | 92 | System.out.println(findKthToTail(head, 10)); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /剑指offer/Test16.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-23 6 | * Time: 16:50 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test16 { 10 | public static class ListNode { 11 | int value; 12 | ListNode next; 13 | } 14 | 15 | /** 16 | * 定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。 17 | * 18 | * @param head 链表的头结点 19 | * @return 反转后的链表的头结点 20 | */ 21 | public static ListNode reverseList(ListNode head) { 22 | // 创建一个临时结点,当作尾插法的逻辑头结点 23 | ListNode root = new ListNode(); 24 | // 逻辑头结点点的下一个结点为空 25 | root.next = null; 26 | 27 | // 用于记录要处理的下一个结点 28 | ListNode next; 29 | // 当前处理的结点不为空 30 | while (head != null) { 31 | // 记录要处理的下一个结点 32 | next = head.next; 33 | // 当前结点的下一个结点指向逻辑头结点的下一个结点 34 | head.next = root.next; 35 | // 逻辑头结点的下一个结点指向当前处理的结点 36 | root.next = head; 37 | // 上面操作完成了一个结点的头插 38 | 39 | // 当前结点指向下一个要处理的结点 40 | head = next; 41 | } 42 | 43 | // 逻辑头结点的下一个结点就是返回后的头结点 44 | return root.next; 45 | } 46 | 47 | /** 48 | * 定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。 49 | * 【书本上的方法,不使用逻辑头结点】 50 | * 51 | * @param head 链表的头结点 52 | * @return 反转后的链表的头结点 53 | */ 54 | public static ListNode reverseList2(ListNode head) { 55 | // 用于记录反转后的链表的头结点 56 | ListNode reverseHead = null; 57 | // 用于记录当前处理的结点的 58 | ListNode curr = head; 59 | // 用于记录当前结点的前驱结点 60 | // 前驱结点开始为null,因为了是反转后的最后一个结点的下一个结点,即null 61 | ListNode prev = null; 62 | // 当前结点的下一个结点 63 | ListNode next; 64 | 65 | // 对链表进行尾插法操作 66 | while (curr != null) { 67 | // 记录当前处理的结点,最后一个记录的结点就是反转后的头结点 68 | // 【注意:与书上的不同,因为curr.next=null时,curr此时就最后一个处理的结点, 69 | // 对应到反转后的链表就是第一个结点,书上那样做更精确,只是多了一些判断,可以不要if】 70 | reverseHead = curr; 71 | // 记录当然前下一个结点 72 | next = curr.next; 73 | // 当前结点的下一个结点指向前驱结点,这样当前结点就插入到了反转链表的头部 74 | curr.next = prev; 75 | // 记录当前结点为前驱结点 76 | prev = curr; 77 | // 当前结点点移动到下一个结点 78 | curr = next; 79 | } 80 | 81 | // 返回转后的头结点 82 | return reverseHead; 83 | } 84 | 85 | 86 | /** 87 | * 输出链表的元素值 88 | * 89 | * @param head 链表的头结点 90 | */ 91 | public static void printList(ListNode head) { 92 | while (head != null) { 93 | System.out.print(head.value + "->"); 94 | head = head.next; 95 | } 96 | System.out.println("null"); 97 | } 98 | 99 | public static void main(String[] args) { 100 | ListNode head = new ListNode(); 101 | head.value = 1; 102 | 103 | head.next = new ListNode(); 104 | head.next.value = 2; 105 | 106 | head.next.next = new ListNode(); 107 | head.next.next.value = 3; 108 | 109 | head.next.next.next = new ListNode(); 110 | head.next.next.next.value = 4; 111 | 112 | head.next.next.next.next = new ListNode(); 113 | head.next.next.next.next.value = 5; 114 | 115 | head.next.next.next.next.next = new ListNode(); 116 | head.next.next.next.next.next.value = 6; 117 | 118 | head.next.next.next.next.next.next = new ListNode(); 119 | head.next.next.next.next.next.next.value = 7; 120 | 121 | head.next.next.next.next.next.next.next = new ListNode(); 122 | head.next.next.next.next.next.next.next.value = 8; 123 | 124 | head.next.next.next.next.next.next.next.next = new ListNode(); 125 | head.next.next.next.next.next.next.next.next.value = 9; 126 | 127 | printList(head); 128 | head = reverseList(head); 129 | printList(head); 130 | head = reverseList2(head); 131 | printList(head); 132 | 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /剑指offer/Test17.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-23 6 | * Time: 17:24 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test17 { 10 | public static class ListNode { 11 | int value; 12 | ListNode next; 13 | } 14 | 15 | /** 16 | * 输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的 17 | * 18 | * @param head1 第一个有序链表 19 | * @param head2 第二个有序链表 20 | * @return 合并后的有序链表头 21 | */ 22 | public static ListNode merge(ListNode head1, ListNode head2) { 23 | // 如果第一个链表为空,返回第二个链表头结点 24 | if (head1 == null) { 25 | return head2; 26 | } 27 | 28 | // 如果第二个结点为空,返回第一个链表头结点 29 | if (head2 == null) { 30 | return head1; 31 | } 32 | 33 | // 创建一个临时结点,用于添加元素时方便 34 | ListNode root = new ListNode(); 35 | // 用于指向合并后的新链的尾结点 36 | ListNode pointer = root; 37 | 38 | // 当两个链表都不为空就进行合并操作 39 | while (head1 != null && head2 != null) { 40 | // 下面的操作合并较小的元素 41 | if (head1.value < head2.value) { 42 | pointer.next = head1; 43 | head1 = head1.next; 44 | } else { 45 | pointer.next = head2; 46 | head2 = head2.next; 47 | } 48 | 49 | // 将指针移动到合并后的链表的末尾 50 | pointer = pointer.next; 51 | } 52 | 53 | // 下面的两个if有且只一个if会内的内容会执行 54 | 55 | // 如果第一个链表的元素未处理完将其,接到合并链表的最后一个结点之后 56 | if (head1 != null) { 57 | pointer.next = head1; 58 | } 59 | 60 | // 如果第二个链表的元素未处理完将其,接到合并链表的最后一个结点之后 61 | if (head2 != null) { 62 | pointer.next = head2; 63 | } 64 | 65 | // 返回处理结果 66 | return root.next; 67 | } 68 | 69 | 70 | /** 71 | * 输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的 72 | * 【使用的是递归的解法,不推荐,递归调用的时候会有方法入栈,需要更多的内存】 73 | * 74 | * @param head1 第一个有序链表 75 | * @param head2 第二个有序链表 76 | * @return 合并后的有序链表头 77 | */ 78 | public static ListNode merge2(ListNode head1, ListNode head2) { 79 | // 如果第一个链表为空,返回第二个链表头结点 80 | if (head1 == null) { 81 | return head2; 82 | } 83 | 84 | // 如果第二个链表为空,返回第一个链表头结点 85 | if (head2 == null) { 86 | return head1; 87 | } 88 | 89 | // 记录两个链表中头部较小的结点 90 | ListNode tmp = head1; 91 | if (tmp.value < head2.value) { 92 | // 如果第一个链表的头结点小,就递归处理第一个链表的下一个结点和第二个链表的头结点 93 | tmp.next = merge2(head1.next, head2); 94 | } else { 95 | // 如果第二个链表的头结点小,就递归处理第一个链表的头结点和第二个链表的头结点的下一个结点 96 | tmp = head2; 97 | tmp.next = merge2(head1, head2.next); 98 | } 99 | 100 | // 返回处理结果 101 | return tmp; 102 | } 103 | 104 | /** 105 | * 输出链表的元素值 106 | * 107 | * @param head 链表的头结点 108 | */ 109 | public static void printList(ListNode head) { 110 | while (head != null) { 111 | System.out.print(head.value + "->"); 112 | head = head.next; 113 | } 114 | System.out.println("null"); 115 | } 116 | 117 | public static void main(String[] args) { 118 | ListNode head = new ListNode(); 119 | head.value = 1; 120 | 121 | head.next = new ListNode(); 122 | head.next.value = 2; 123 | 124 | head.next.next = new ListNode(); 125 | head.next.next.value = 3; 126 | 127 | head.next.next.next = new ListNode(); 128 | head.next.next.next.value = 4; 129 | 130 | head.next.next.next.next = new ListNode(); 131 | head.next.next.next.next.value = 5; 132 | 133 | 134 | ListNode head2 = new ListNode(); 135 | head2.value = 1; 136 | 137 | head2.next = new ListNode(); 138 | head2.next.value = 3; 139 | 140 | head2.next.next = new ListNode(); 141 | head2.next.next.value = 5; 142 | 143 | head2.next.next.next = new ListNode(); 144 | head2.next.next.next.value = 6; 145 | 146 | head2.next.next.next.next = new ListNode(); 147 | head2.next.next.next.next.value = 7; 148 | 149 | // head = merge(head, head2); 150 | head = merge2(head, head2); 151 | printList(head); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /剑指offer/Test18.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-23 6 | * Time: 19:06 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test18 { 10 | /** 11 | * 二叉树的树结点 12 | */ 13 | public static class BinaryTreeNode { 14 | int value; 15 | BinaryTreeNode left; 16 | BinaryTreeNode right; 17 | } 18 | 19 | /** 20 | * 输入两棵二叉树A和B,判断B是不是A的子结构。 21 | * 该方法是在A树中找到一个与B树的根节点相等的元素的结点, 22 | * 从这个相等的结点开始判断树B是不是树A的子结构,如果找到其的一个就返回, 23 | * 否则直到所有的结点都找完为止。 24 | * 25 | * @param root1 树A的根结点 26 | * @param root2 树B的根结点 27 | * @return true:树B是树A的子结构,false:树B是不树A的子结构 28 | */ 29 | public static boolean hasSubtree(BinaryTreeNode root1, BinaryTreeNode root2) { 30 | // 只要两个对象是同一个就返回true 31 | // 【注意此处与书本上的不同,书本上的没有这一步】 32 | if (root1 == root2) { 33 | return true; 34 | } 35 | 36 | // 只要树B的根结点点为空就返回true 37 | if (root2 == null) { 38 | return true; 39 | } 40 | 41 | // 树B的根结点不为空,如果树A的根结点为空就返回false 42 | if (root1 == null) { 43 | return false; 44 | } 45 | 46 | // 记录匹配结果 47 | boolean result = false; 48 | 49 | // 如果结点的值相等就,调用匹配方法 50 | if (root1.value == root2.value) { 51 | result = match(root1, root2); 52 | } 53 | 54 | // 如果匹配就直接返回结果 55 | if (result) { 56 | return true; 57 | } 58 | 59 | // 如果不匹配就找树A的左子结点和右子结点进行判断 60 | return hasSubtree(root1.left, root2) || hasSubtree(root1.right, root2); 61 | } 62 | 63 | /** 64 | * 从树A根结点root1和树B根结点root2开始,一个一个元素进行判断,判断B是不是A的子结构 65 | * 66 | * @param root1 树A开始匹配的根结点 67 | * @param root2 树B开始匹配的根结点 68 | * @return 树B是树A的子结构,false:树B是不树A的子结构 69 | */ 70 | public static boolean match(BinaryTreeNode root1, BinaryTreeNode root2) { 71 | // 只要两个对象是同一个就返回true 72 | if (root1 == root2) { 73 | return true; 74 | } 75 | 76 | // 只要树B的根结点点为空就返回true 77 | if (root2 == null) { 78 | return true; 79 | } 80 | // 树B的根结点不为空,如果树A的根结点为空就返回false 81 | if (root1 == null) { 82 | return false; 83 | } 84 | 85 | // 如果两个结点的值相等,则分别判断其左子结点和右子结点 86 | if (root1.value == root2.value) { 87 | return match(root1.left, root2.left) && match(root1.right, root2.right); 88 | } 89 | 90 | // 结点值不相等返回false 91 | return false; 92 | } 93 | 94 | public static void main(String[] args) { 95 | BinaryTreeNode root1 = new BinaryTreeNode(); 96 | root1.value = 8; 97 | root1.right = new BinaryTreeNode(); 98 | root1.right.value = 7; 99 | root1.left = new BinaryTreeNode(); 100 | root1.left.value = 8; 101 | root1.left.left = new BinaryTreeNode(); 102 | root1.left.left.value = 9; 103 | root1.left.right = new BinaryTreeNode(); 104 | root1.left.right.value = 2; 105 | root1.left.right.left = new BinaryTreeNode(); 106 | root1.left.right.left.left = new BinaryTreeNode(); 107 | root1.left.right.left.left.value = 4; 108 | root1.left.right.left.right = new BinaryTreeNode(); 109 | root1.left.right.left.right.value = 7; 110 | 111 | BinaryTreeNode root2 = new BinaryTreeNode(); 112 | root2.value = 8; 113 | root2.left = new BinaryTreeNode(); 114 | root2.left.value = 9; 115 | root2.right = new BinaryTreeNode(); 116 | root2.right.value = 2; 117 | 118 | System.out.println(hasSubtree(root1, root2)); 119 | System.out.println(hasSubtree(root2, root1)); 120 | System.out.println(hasSubtree(root1, root1.left)); 121 | System.out.println(hasSubtree(root1, null)); 122 | System.out.println(hasSubtree(null, root2)); 123 | System.out.println(hasSubtree(null, null)); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /剑指offer/Test19.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-23 6 | * Time: 20:49 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test19 { 10 | /** 11 | * 二叉树的树结点 12 | */ 13 | public static class BinaryTreeNode { 14 | int value; 15 | BinaryTreeNode left; 16 | BinaryTreeNode right; 17 | } 18 | 19 | /** 20 | * 请完成一个函数,输入…个二叉树,该函数输出它的镜像 21 | * 22 | * @param node 二叉树的根结点 23 | */ 24 | public static void mirror(BinaryTreeNode node) { 25 | // 如果当前结点不为空则进行操作 26 | if (node != null) { 27 | // 下面是交换结点左右两个子树 28 | BinaryTreeNode tmp = node.left; 29 | node.left = node.right; 30 | node.right = tmp; 31 | 32 | // 对结点的左右两个子树进行处理 33 | mirror(node.left); 34 | mirror(node.right); 35 | } 36 | } 37 | 38 | public static void printTree(BinaryTreeNode node) { 39 | if (node != null) { 40 | printTree(node.left); 41 | System.out.print(node.value + " "); 42 | printTree(node.right); 43 | } 44 | } 45 | 46 | public static void main(String[] args) { 47 | // 8 48 | // / \ 49 | // 6 10 50 | // / \ / \ 51 | // 5 7 9 11 52 | BinaryTreeNode root = new BinaryTreeNode(); 53 | root.value = 8; 54 | root.left = new BinaryTreeNode(); 55 | root.left.value = 6; 56 | root.left.left = new BinaryTreeNode(); 57 | root.left.left.value = 5; 58 | root.left.right = new BinaryTreeNode(); 59 | root.left.right.value = 7; 60 | root.right = new BinaryTreeNode(); 61 | root.right.value = 10; 62 | root.right.left = new BinaryTreeNode(); 63 | root.right.left.value = 9; 64 | root.right.right = new BinaryTreeNode(); 65 | root.right.right.value = 11; 66 | printTree(root); 67 | System.out.println(); 68 | mirror(root); 69 | printTree(root); 70 | // 1 71 | // / 72 | // 3 73 | // / 74 | // 5 75 | // / 76 | // 7 77 | // / 78 | // 9 79 | BinaryTreeNode root2 = new BinaryTreeNode(); 80 | root2.value = 1; 81 | root2.left = new BinaryTreeNode(); 82 | root2.left.value = 3; 83 | root2.left.left = new BinaryTreeNode(); 84 | root2.left.left.value = 5; 85 | root2.left.left.left = new BinaryTreeNode(); 86 | root2.left.left.left.value = 7; 87 | root2.left.left.left.left = new BinaryTreeNode(); 88 | root2.left.left.left.left.value = 9; 89 | System.out.println("\n"); 90 | printTree(root2); 91 | System.out.println(); 92 | mirror(root2); 93 | printTree(root2); 94 | 95 | // 0 96 | // \ 97 | // 2 98 | // \ 99 | // 4 100 | // \ 101 | // 6 102 | // \ 103 | // 8 104 | BinaryTreeNode root3 = new BinaryTreeNode(); 105 | root3.value = 0; 106 | root3.right = new BinaryTreeNode(); 107 | root3.right.value = 2; 108 | root3.right.right = new BinaryTreeNode(); 109 | root3.right.right.value = 4; 110 | root3.right.right.right = new BinaryTreeNode(); 111 | root3.right.right.right.value = 6; 112 | root3.right.right.right.right = new BinaryTreeNode(); 113 | root3.right.right.right.right.value = 8; 114 | System.out.println("\n"); 115 | printTree(root3); 116 | System.out.println(); 117 | mirror(root3); 118 | printTree(root3); 119 | 120 | 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /剑指offer/Test20.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-23 6 | * Time: 21:22 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test20 { 10 | /** 11 | * 输入一个矩阵,按照从外向里以顺时针的顺序依次打印每一个数字 12 | * 13 | * @param numbers 输入的二维数组,二维数组必须是N*M的,否则分出错 14 | */ 15 | public static void printMatrixClockWisely(int[][] numbers) { 16 | // 输入的参数不能为空 17 | if (numbers == null) { 18 | return; 19 | } 20 | 21 | // 记录一圈(环)的开始位置的行 22 | int x = 0; 23 | // 记录一圈(环)的开始位置的列 24 | int y = 0; 25 | // 对每一圈(环)进行处理, 26 | // 行号最大是(numbers.length-1)/2 27 | // 列号最大是(numbers[0].length-1)/2 28 | while (x * 2 < numbers.length && y * 2 < numbers[0].length) { 29 | printMatrixInCircle(numbers, x, y); 30 | // 指向下一个要处理的的环的第一个位置 31 | x++; 32 | y++; 33 | } 34 | } 35 | 36 | public static void printMatrixInCircle(int[][] numbers, int x, int y) { 37 | // 数组的行数 38 | int rows = numbers.length; 39 | // 数组的列数 40 | int cols = numbers[0].length; 41 | 42 | // 输出环的上面一行,包括最中的那个数字 43 | for (int i = y; i <= cols - y - 1; i++) { 44 | System.out.print(numbers[x][i] + " "); 45 | } 46 | 47 | // 环的高度至少为2才会输出右边的一列 48 | // rows-x-1:表示的是环最下的那一行的行号 49 | if (rows - x - 1 > x) { 50 | // 因为右边那一列的最上面那一个已经被输出了,所以行呈从x+1开始, 51 | // 输出包括右边那列的最下面那个 52 | for (int i = x + 1; i <= rows - x - 1; i++) { 53 | System.out.print(numbers[i][cols - y - 1] + " "); 54 | } 55 | } 56 | 57 | // 环的高度至少是2并且环的宽度至少是2才会输出下面那一行 58 | // cols-1-y:表示的是环最右那一列的列号 59 | if (rows - x - 1 > x && cols - 1 - y > y) { 60 | // 因为环的左下角的位置已经输出了,所以列号从cols-y-2开始 61 | for (int i = cols - y - 2; i >= y; i--) { 62 | System.out.print(numbers[rows - 1 - x][i] + " "); 63 | } 64 | } 65 | 66 | // 环的宽度至少是2并且环的高度至少是3才会输出最左边那一列 67 | // rows-x-1:表示的是环最下的那一行的行号 68 | if (cols - 1 - y > y && rows - 1 - x > x + 1) { 69 | // 因为最左边那一列的第一个和最后一个已经被输出了 70 | for (int i = rows - 1 - x - 1; i >= x + 1; i--) { 71 | System.out.print(numbers[i][y] + " "); 72 | } 73 | } 74 | } 75 | 76 | public static void main(String[] args) { 77 | int[][] numbers = { 78 | {1, 2, 3, 4, 5}, 79 | {16, 17, 18, 19, 6}, 80 | {15, 24, 25, 20, 7}, 81 | {14, 23, 22, 21, 8}, 82 | {13, 12, 11, 10, 9}, 83 | }; 84 | printMatrixClockWisely(numbers); 85 | System.out.println(); 86 | 87 | int[][] numbers2 = { 88 | {1, 2, 3, 4, 5, 6, 7, 8}, 89 | {22, 23, 24, 25, 26, 27, 28, 9}, 90 | {21, 36, 37, 38, 39, 40, 29, 10}, 91 | {20, 35, 34, 33, 32, 31, 30, 11}, 92 | {19, 18, 17, 16, 15, 14, 13, 12}, 93 | 94 | }; 95 | printMatrixClockWisely(numbers2); 96 | System.out.println(); 97 | 98 | 99 | int[][] numbers3 = { 100 | {1, 2, 3, 4, 5, 6, 7, 8} 101 | }; 102 | printMatrixClockWisely(numbers3); 103 | System.out.println(); 104 | 105 | int[][] numbers4 = { 106 | {1, 2, 3, 4, 5, 6, 7, 8}, 107 | {16, 15, 14, 13, 12, 11, 10, 9} 108 | }; 109 | printMatrixClockWisely(numbers4); 110 | System.out.println(); 111 | 112 | 113 | int[][] numbers5 = { 114 | {1}, 115 | {2}, 116 | {3}, 117 | {4}, 118 | {5}, 119 | {6}, 120 | {7}, 121 | {8} 122 | }; 123 | printMatrixClockWisely(numbers5); 124 | System.out.println(); 125 | 126 | int[][] numbers6 = { 127 | {0, 1}, 128 | {15, 2}, 129 | {14, 3}, 130 | {13, 4}, 131 | {12, 5}, 132 | {11, 6}, 133 | {10, 7}, 134 | {9, 8} 135 | }; 136 | printMatrixClockWisely(numbers6); 137 | System.out.println(); 138 | 139 | 140 | int[][] numbers7 = { 141 | {1, 2}, 142 | {4, 3} 143 | }; 144 | printMatrixClockWisely(numbers7); 145 | System.out.println(); 146 | 147 | int[][] numbers8 = { 148 | {1} 149 | }; 150 | printMatrixClockWisely(numbers8); 151 | System.out.println(); 152 | 153 | // 0个元素的数组 154 | printMatrixClockWisely(new int[][]{{}}); 155 | // 空数组 156 | printMatrixClockWisely(null); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /剑指offer/Test21.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * Author: 王俊超 7 | * Date: 2015-04-24 8 | * Time: 08:41 9 | * Declaration: All Rights Reserved !!! 10 | */ 11 | public class Test21 { 12 | /** 13 | * 定义栈的数据结构,请在该类型中实现一个能够得到校的最小元素的min函数。 14 | * 在该栈中,调用pop、push 及min的时间复杂度都是0(1) 15 | * 16 | * @param 泛型参数 17 | */ 18 | public static class StackWithMin> { 19 | // 数据栈,用于存放插入的数据 20 | private Stack dataStack; 21 | // 最小数位置栈,存放数据栈中最小的数的位置 22 | private Stack minStack; 23 | 24 | // 构造函数 25 | public StackWithMin() { 26 | this.dataStack = new Stack<>(); 27 | this.minStack = new Stack<>(); 28 | } 29 | 30 | /** 31 | * 出栈方法 32 | * @return 栈顶元素 33 | */ 34 | public T pop() { 35 | // 如果栈已经为空,再出栈抛出异常 36 | if (dataStack.isEmpty()) { 37 | throw new RuntimeException("The stack is already empty"); 38 | } 39 | 40 | // 如果有数据,最小数位置栈和数据栈必定是有相同的元素个数, 41 | // 两个栈同时出栈 42 | minStack.pop(); 43 | return dataStack.pop(); 44 | } 45 | 46 | /** 47 | * 元素入栈 48 | * @param t 入栈的元素 49 | */ 50 | public void push(T t) { 51 | // 如果入栈的元素为空就抛出异常 52 | if (t == null) { 53 | throw new RuntimeException("Element can be null"); 54 | } 55 | 56 | // 如果数据栈是空的,只接将元素入栈,同时更新最小数栈中的数据 57 | if (dataStack.isEmpty()) { 58 | dataStack.push(t); 59 | minStack.push(0); 60 | } 61 | // 如果数据栈中有数据 62 | else { 63 | // 获取数据栈中的最小元素(未插入t之前的) 64 | T e = dataStack.get(minStack.peek()); 65 | // 将t入栈 66 | dataStack.push(t); 67 | // 如果插入的数据比栈中的最小元素小 68 | if (t.compareTo(e) < 0) { 69 | // 将新的最小元素的位置入最小栈 70 | minStack.push(dataStack.size() - 1); 71 | } else { 72 | // 插入的元素不比原来的最小元素小,复制最小栈栈顶元素,将其入栈 73 | minStack.push(minStack.peek()); 74 | } 75 | } 76 | } 77 | 78 | /** 79 | * 获取栈中的最小元素 80 | * @return 栈中的最小元素 81 | */ 82 | public T min() { 83 | // 如果最小数公位置栈已经为空(数据栈中已经没有数据了),则抛出异常 84 | if (minStack.isEmpty()) { 85 | throw new RuntimeException("No element in stack."); 86 | } 87 | 88 | // 获取数据栈中的最小元素,并且返回结果 89 | return dataStack.get(minStack.peek()); 90 | } 91 | } 92 | 93 | public static void main(String[] args) { 94 | StackWithMin stack = new StackWithMin<>(); 95 | stack.push(3); 96 | System.out.println(stack.min() == 3); 97 | stack.push(4); 98 | System.out.println(stack.min() == 3); 99 | stack.push(2); 100 | System.out.println(stack.min() == 2); 101 | stack.push(3); 102 | System.out.println(stack.min() == 2); 103 | stack.pop(); 104 | System.out.println(stack.min() == 2); 105 | stack.pop(); 106 | System.out.println(stack.min() == 3); 107 | stack.push(0); 108 | System.out.println(stack.min() == 0); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /剑指offer/Test22.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * Author: 王俊超 7 | * Date: 2015-04-24 8 | * Time: 09:15 9 | * Declaration: All Rights Reserved !!! 10 | */ 11 | public class Test22 { 12 | /** 13 | * 输入两个整数序列,第一个序列表示栈的压入顺序,请判断二个序列是否为该栈的弹出顺序。 14 | * 假设压入栈的所有数字均不相等。例如序列1 、2、3 、4、5 是某栈压栈序列, 15 | * 序列4、5、3、2、1是该压栈序列对应的一个弹出序列, 16 | * 但4、3、5、1、2就不可能是该压棋序列的弹出序列。 17 | * 【与书本的的方法不同】 18 | * 19 | * @param push 入栈序列 20 | * @param pop 出栈序列 21 | * @return true:出栈序列是入栈序列的一个弹出顺序 22 | */ 23 | public static boolean isPopOrder(int[] push, int[] pop) { 24 | // 输入校验,参数不能为空,并且两个数组中必须有数字,并且两个数组中的数字个数相同 25 | // 否则返回false 26 | if (push == null || pop == null || pop.length == 0 || push.length == 0 || push.length != pop.length) { 27 | return false; 28 | } 29 | 30 | // 经过上面的参数校验,两个数组中一定有数据,且数据数目相等 31 | // 用于存放入栈时的数据 32 | Stack stack = new Stack<>(); 33 | // 用于记录入栈数组元素的处理位置 34 | int pushIndex = 0; 35 | // 用于记录出栈数组元素的处理位置 36 | int popIndex = 0; 37 | // 如果还有出栈元素要处理 38 | while (popIndex < pop.length) { 39 | // 入栈元素还未全部入栈的条件下,如果栈为空,或者栈顶的元素不与当前处理的相等,则一直进行栈操作, 40 | // 直到入栈元素全部入栈或者找到了一个与当出栈元素相等的元素 41 | while (pushIndex < push.length && (stack.isEmpty() || stack.peek() != pop[popIndex])) { 42 | // 入栈数组中的元素入栈 43 | stack.push(push[pushIndex]); 44 | // 指向下一个要处理的入栈元素 45 | pushIndex++; 46 | } 47 | 48 | // 如果在上一步的入栈过程中找到了与出栈的元素相等的元素 49 | if (stack.peek() == pop[popIndex]) { 50 | // 将元素出栈 51 | stack.pop(); 52 | // 处理下一个出栈元素 53 | popIndex++; 54 | } 55 | // 如果没有找到与出栈元素相等的元素,说明这个出栈顺序是不合法的 56 | // 就返回false 57 | else { 58 | return false; 59 | } 60 | } 61 | 62 | // 下面的语句总是成立的 63 | // return stack.isEmpty(); 64 | 65 | // 为什么可以直接返回true:对上面的外层while进行分析可知道,对每一个入栈的元素, 66 | // 在stack栈中,通过一些入栈操作,总可以在栈顶上找到与入栈元素值相同的元素, 67 | // 这就说明了这个出栈的顺序是入栈顺序的一个弹出队列,这也可以解释为什么stack.isEmpty() 68 | // 总是返回true,所有的入栈元素都可以进栈,并且可以被匹配到,之后就弹出,最后栈中就无元素。 69 | return true; 70 | } 71 | 72 | /** 73 | * 输入两个整数序列,第一个序列表示栈的压入顺序,请判断二个序列是否为该栈的弹出顺序。 74 | * 【按书本上的思路进行求解,两者相差不大】 75 | * 76 | * @param push 入栈序列 77 | * @param pop 出栈序列 78 | * @return true:出栈序列是入栈序列的一个弹出顺序 79 | */ 80 | public static boolean isPopOrder2(int[] push, int[] pop) { 81 | 82 | // 用于记录判断出栈顺序是不是入栈顺的一个出栈序列,默认false 83 | boolean isPossible = false; 84 | 85 | // 当入栈和出栈数组者都不为空,并且都有数据,并且数据个数都相等 86 | if (push != null && pop != null && push.length > 0 && push.length == pop.length) { 87 | // 用于存放入栈时的数据 88 | Stack stack = new Stack<>(); 89 | // 记录下一个要处理的入栈元素的位置 90 | int nextPush = 0; 91 | // 记录下一个要处理的出栈元素的位置 92 | int nextPop = 0; 93 | // 如果出栈元素没有处理完就继续进行处理 94 | while (nextPop < pop.length) { 95 | // 如果栈为空或者栈顶的元素与当前处理的出栈元素不相同,一直进行操作 96 | while (stack.isEmpty() || stack.peek() != pop[nextPop]) { 97 | // 如果入栈的元素已经全部入栈了,就退出内层循环 98 | if (nextPush >= push.length) { 99 | break; 100 | } 101 | 102 | // 执行到此处说明还有入栈元素可以入栈 103 | // 即将元素入栈 104 | stack.push(push[nextPush]); 105 | // 指向下一个要处理的入栈元素的位置 106 | nextPush++; 107 | } 108 | 109 | // 执行到此处有两种情况: 110 | // 第一种:在栈顶上找到了一个与入栈元素相等的元素 111 | // 第二种:在栈顶上没有找到一个与入栈元素相等的元素,而且输入栈的元素已经全部入栈了 112 | 113 | // 对于第二种情况就说弹出栈的顺序是不符合要求的,退出外层循环 114 | if (stack.peek() != pop[nextPop]) { 115 | break; 116 | } 117 | 118 | // 对应到第一种情况:需要要栈的栈顶元素弹出 119 | stack.pop(); 120 | // 指向下一个要处理的出栈元素的位置 121 | nextPop++; 122 | } 123 | 124 | // 执行到此处有两种情况 125 | // 第一种:外层while循环的在第一种情况下退出, 126 | // 第二种:所有的出栈元素都被正确匹配 127 | 128 | // 对于出现的第一种情况其stack.isEmpty()必不为空,原因为分析如下: 129 | // 所有的入栈元素一定会入栈,但是只有匹配的情况下才会出栈, 130 | // 匹配的次数最多与入栈元素个数元素相同(两个数组的长度相等),如果有不匹配的元素, 131 | // 必然会使出栈的次数比入栈的次数少,这样栈中至少会有一个元素 132 | // 对于第二种情况其stack.isEmpty()一定为空 133 | // 所以书本上的nextPop == pop.length(pNextPop-pPop==nLength)是多余的 134 | if (stack.isEmpty()) { 135 | isPossible = true; 136 | } 137 | } 138 | 139 | return isPossible; 140 | } 141 | 142 | public static void main(String[] args) { 143 | int[] push = {1, 2, 3, 4, 5}; 144 | int[] pop1 = {4, 5, 3, 2, 1}; 145 | int[] pop2 = {3, 5, 4, 2, 1}; 146 | int[] pop3 = {4, 3, 5, 1, 2}; 147 | int[] pop4 = {3, 5, 4, 1, 2}; 148 | 149 | System.out.println("true: " + isPopOrder(push, pop1)); 150 | System.out.println("true: " + isPopOrder(push, pop2)); 151 | System.out.println("false: " + isPopOrder(push, pop3)); 152 | System.out.println("false: " + isPopOrder(push, pop4)); 153 | 154 | int[] push5 = {1}; 155 | int[] pop5 = {2}; 156 | System.out.println("false: " + isPopOrder(push5, pop5)); 157 | 158 | int[] push6 = {1}; 159 | int[] pop6 = {1}; 160 | System.out.println("true: " + isPopOrder(push6, pop6)); 161 | 162 | System.out.println("false: " + isPopOrder(null, null)); 163 | 164 | // 测试方法2 165 | System.out.println(); 166 | System.out.println("true: " + isPopOrder2(push, pop1)); 167 | System.out.println("true: " + isPopOrder2(push, pop2)); 168 | System.out.println("false: " + isPopOrder2(push, pop3)); 169 | System.out.println("false: " + isPopOrder2(push, pop4)); 170 | System.out.println("false: " + isPopOrder2(push5, pop5)); 171 | System.out.println("true: " + isPopOrder2(push6, pop6)); 172 | System.out.println("false: " + isPopOrder2(null, null)); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /剑指offer/Test23.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | 6 | /** 7 | * Author: 王俊超 8 | * Date: 2015-04-24 9 | * Time: 10:39 10 | * Declaration: All Rights Reserved !!! 11 | */ 12 | public class Test23 { 13 | /** 14 | * 二叉树的树结点 15 | */ 16 | public static class BinaryTreeNode { 17 | int value; 18 | BinaryTreeNode left; 19 | BinaryTreeNode right; 20 | } 21 | 22 | /** 23 | * 从上往下打印出二叉树的每个结点,向一层的结点按照从左往右的顺序打印。 24 | * 例如下的二叉树, 25 | * 8 26 | * / \ 27 | * 6 10 28 | * / \ / \ 29 | * 5 7 9 11 30 | * 则依次打印出8、6、10、5、3 、9、11. 31 | * 32 | * @param root 树的结点 33 | */ 34 | public static void printFromToBottom(BinaryTreeNode root) { 35 | 36 | // 当结点非空时才进行操作 37 | if (root != null) { 38 | // 用于存放还未遍历的元素 39 | Queue list = new LinkedList<>(); 40 | // 将根结点入队 41 | list.add(root); 42 | // 用于记录当前处理的结点 43 | BinaryTreeNode curNode; 44 | 45 | // 队列非空则进行处理 46 | while (!list.isEmpty()) { 47 | // 删除队首元素 48 | curNode = list.remove(); 49 | // 输出队首元素的值 50 | System.out.print(curNode.value + " "); 51 | // 如果左子结点不为空,则左子结点入队 52 | if (curNode.left != null) { 53 | list.add(curNode.left); 54 | } 55 | // 如果右子结点不为空,则左子结点入队 56 | if (curNode.right != null) { 57 | list.add(curNode.right); 58 | } 59 | } 60 | } 61 | } 62 | 63 | public static void main(String[] args) { 64 | 65 | // 8 66 | // / \ 67 | // 6 10 68 | // / \ / \ 69 | // 5 7 9 11 70 | BinaryTreeNode root = new BinaryTreeNode(); 71 | root.value = 8; 72 | root.left = new BinaryTreeNode(); 73 | root.left.value = 6; 74 | root.left.left = new BinaryTreeNode(); 75 | root.left.left.value = 5; 76 | root.left.right = new BinaryTreeNode(); 77 | root.left.right.value = 7; 78 | root.right = new BinaryTreeNode(); 79 | root.right.value = 10; 80 | root.right.left = new BinaryTreeNode(); 81 | root.right.left.value = 9; 82 | root.right.right = new BinaryTreeNode(); 83 | root.right.right.value = 11; 84 | printFromToBottom(root); 85 | 86 | // 1 87 | // / 88 | // 3 89 | // / 90 | // 5 91 | // / 92 | // 7 93 | // / 94 | // 9 95 | BinaryTreeNode root2 = new BinaryTreeNode(); 96 | root2.value = 1; 97 | root2.left = new BinaryTreeNode(); 98 | root2.left.value = 3; 99 | root2.left.left = new BinaryTreeNode(); 100 | root2.left.left.value = 5; 101 | root2.left.left.left = new BinaryTreeNode(); 102 | root2.left.left.left.value = 7; 103 | root2.left.left.left.left = new BinaryTreeNode(); 104 | root2.left.left.left.left.value = 9; 105 | System.out.println("\n"); 106 | printFromToBottom(root2); 107 | 108 | // 0 109 | // \ 110 | // 2 111 | // \ 112 | // 4 113 | // \ 114 | // 6 115 | // \ 116 | // 8 117 | BinaryTreeNode root3 = new BinaryTreeNode(); 118 | root3.value = 0; 119 | root3.right = new BinaryTreeNode(); 120 | root3.right.value = 2; 121 | root3.right.right = new BinaryTreeNode(); 122 | root3.right.right.value = 4; 123 | root3.right.right.right = new BinaryTreeNode(); 124 | root3.right.right.right.value = 6; 125 | root3.right.right.right.right = new BinaryTreeNode(); 126 | root3.right.right.right.right.value = 8; 127 | System.out.println("\n"); 128 | printFromToBottom(root3); 129 | 130 | // 1 131 | BinaryTreeNode root4 = new BinaryTreeNode(); 132 | root4.value = 1; 133 | System.out.println("\n"); 134 | printFromToBottom(root4); 135 | 136 | // null 137 | System.out.println("\n"); 138 | printFromToBottom(null); 139 | 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /剑指offer/Test24.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-04-24 6 | * Time: 10:59 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test24 { 10 | /** 11 | * 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。 12 | * 如果是则返回true。否则返回false。假设输入的数组的任意两个数字都互不相同。 13 | * 14 | * @param sequence 某二叉搜索树的后序遍历的结果 15 | * @return true:该数组是某二叉搜索树的后序遍历的结果。false:不是 16 | */ 17 | public static boolean verifySequenceOfBST(int[] sequence) { 18 | 19 | // 输入的数组不能为空,并且有数据 20 | if (sequence == null || sequence.length <= 0) { 21 | return false; 22 | } 23 | 24 | // 有数据,就调用辅助方法 25 | return verifySequenceOfBST(sequence, 0, sequence.length - 1); 26 | } 27 | 28 | /** 29 | * 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。 30 | * 【此方法与上一个方法不同,未进行空值判断,对于数组度为0的情况返回的true也于上题不同, 31 | * 此方法只是上面一个方法的辅助实现,对于数数组为null和数组长度为0的情况,执行结果并非相同】 32 | * 【也就是说此方法只有数组中有数据的情况下才与上面的方法返回同样的结点, 33 | * verifySequenceOfBST(sequence) === 34 | * verifySequenceOfBST(sequence, 0, sequence.length - 1) 35 | * 当sequence中有数据才成立 36 | * 】 37 | * 38 | * @param sequence 某二叉搜索树的后序遍历的结果 39 | * @param start 处理的开始位置 40 | * @param end 处理的结束位置 41 | * @return true:该数组是某二叉搜索树的后序遍历的结果。false:不是 42 | */ 43 | public static boolean verifySequenceOfBST(int[] sequence, int start, int end) { 44 | 45 | // 如果对应要处理的数据只有一个或者已经没有数据要处理(start>end)就返回true 46 | if (start >= end) { 47 | return true; 48 | } 49 | 50 | // 从左向右找第一个不大于根结点(sequence[end])的元素的位置 51 | int index = start; 52 | while (index < end - 1 && sequence[index] < sequence[end]) { 53 | index++; 54 | } 55 | 56 | // 执行到此处[end, index-1]的元素都是小于根结点的(sequence[end]) 57 | // [end, index-1]可以看作是根结点的左子树 58 | 59 | // right用于记录第一个不小于根结点的元素的位置 60 | 61 | int right = index; 62 | 63 | // 接下来要保证[index, end-1]的所有元素都是大于根根点的【A】 64 | // 因为[index, end-1]只有成为根结点的右子树 65 | // 从第一个不小于根结点的元素开始,找第一个不大于根结点的元素 66 | while (index < end - 1 && sequence[index] > sequence[end]) { 67 | index++; 68 | } 69 | 70 | // 如果【A】条件满足,那么一定有index=end-1, 71 | // 如果不满足那说明根结点的右子树[index, end-1]中有小于等于根结点的元素, 72 | // 不符合二叉搜索树的定义,返回false 73 | if (index != end - 1) { 74 | return false; 75 | } 76 | 77 | // 执行到此处说明直到目前为止,还是合法的 78 | // [start, index-1]为根结点左子树的位置 79 | // [index, end-1]为根结点右子树的位置 80 | index = right; 81 | return verifySequenceOfBST(sequence, start, index - 1) && verifySequenceOfBST(sequence, index, end - 1); 82 | } 83 | 84 | public static void main(String[] args) { 85 | // 10 86 | // / \ 87 | // 6 14 88 | // /\ /\ 89 | // 4 8 12 16 90 | int[] data = {4, 8, 6, 12, 16, 14, 10}; 91 | System.out.println("true: " + verifySequenceOfBST(data)); 92 | 93 | // 5 94 | // / \ 95 | // 4 7 96 | // / 97 | // 6 98 | int[] data2 = {4, 6, 7, 5}; 99 | System.out.println("true: " + verifySequenceOfBST(data2)); 100 | 101 | // 5 102 | // / 103 | // 4 104 | // / 105 | // 3 106 | // / 107 | // 2 108 | // / 109 | // 1 110 | int[] data3 = {1, 2, 3, 4, 5}; 111 | System.out.println("true: " + verifySequenceOfBST(data3)); 112 | 113 | // 1 114 | // \ 115 | // 2 116 | // \ 117 | // 3 118 | // \ 119 | // 4 120 | // \ 121 | // 5 122 | int[] data4 = {5, 4, 3, 2, 1}; 123 | System.out.println("true: " + verifySequenceOfBST(data4)); 124 | 125 | // 树中只有1个结点 126 | int[] data5 = {5}; 127 | System.out.println("true: " + verifySequenceOfBST(data5)); 128 | 129 | int[] data6 = {7, 4, 6, 5}; 130 | System.out.println("false: " + verifySequenceOfBST(data6)); 131 | 132 | int[] data7 = {4, 6, 12, 8, 16, 14, 10}; 133 | System.out.println("false: " + verifySequenceOfBST(data7)); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /剑指offer/Test25.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Author: 王俊超 8 | * Date: 2015-04-24 9 | * Time: 13:45 10 | * Declaration: All Rights Reserved !!! 11 | */ 12 | public class Test25 { 13 | /** 14 | * 二叉树的树结点 15 | */ 16 | public static class BinaryTreeNode { 17 | int value; 18 | BinaryTreeNode left; 19 | BinaryTreeNode right; 20 | } 21 | 22 | /** 23 | * 输入一棵二叉树和一个整数, 打印出二叉树中结点值的和为输入整数的所有路径。 24 | * 从树的根结点开始往下一直到叶销点所经过的结点形成一条路径。 25 | * 26 | * @param root 树的根结点 27 | * @param expectedSum 要求的路径和 28 | */ 29 | public static void findPath(BinaryTreeNode root, int expectedSum) { 30 | // 创建一个链表,用于存放根结点到当前处理结点的所经过的结点 31 | List list = new ArrayList<>(); 32 | 33 | // 如果根结点不为空,就调用辅助处理方法 34 | if (root != null) { 35 | findPath(root, 0, expectedSum, list); 36 | } 37 | } 38 | 39 | /** 40 | * @param root 当前要处理的结点 41 | * @param curSum 当前记录的和(还未加上当前结点的值) 42 | * @param expectedSum 要求的路径和 43 | * @param result 根结点到当前处理结点的所经过的结点,(还未包括当前结点) 44 | */ 45 | public static void findPath(BinaryTreeNode root, int curSum, int expectedSum, List result) { 46 | 47 | // 如果结点不为空就进行处理 48 | if (root != null) { 49 | // 加上当前结点的值 50 | curSum += root.value; 51 | // 将当前结点入队 52 | result.add(root.value); 53 | // 如果当前结点的值小于期望的和 54 | if (curSum < expectedSum) { 55 | // 递归处理左子树 56 | findPath(root.left, curSum, expectedSum, result); 57 | // 递归处理右子树 58 | findPath(root.right, curSum, expectedSum, result); 59 | } 60 | // 如果当前和与期望的和相等 61 | else if (curSum == expectedSum) { 62 | // 当前结点是叶结点,则输出结果 63 | if (root.left == null && root.right == null) { 64 | System.out.println(result); 65 | } 66 | } 67 | // 移除当前结点 68 | result.remove(result.size() - 1); 69 | } 70 | } 71 | 72 | public static void main(String[] args) { 73 | // 10 74 | // / \ 75 | // 5 12 76 | // /\ 77 | // 4 7 78 | BinaryTreeNode root = new BinaryTreeNode(); 79 | root.value = 10; 80 | root.left = new BinaryTreeNode(); 81 | root.left.value = 5; 82 | root.left.left = new BinaryTreeNode(); 83 | root.left.left.value = 4; 84 | root.left.right = new BinaryTreeNode(); 85 | root.left.right.value = 7; 86 | root.right = new BinaryTreeNode(); 87 | root.right.value = 12; 88 | 89 | // 有两条路径上的结点和为22 90 | System.out.println("findPath(root, 22);"); 91 | findPath(root, 22); 92 | 93 | // 没有路径上的结点和为15 94 | System.out.println("findPath(root, 15);"); 95 | findPath(root, 15); 96 | 97 | // 有一条路径上的结点和为19 98 | System.out.println("findPath(root, 19);"); 99 | findPath(root, 19); 100 | 101 | 102 | // 5 103 | // / 104 | // 4 105 | // / 106 | // 3 107 | // / 108 | // 2 109 | // / 110 | // 1 111 | BinaryTreeNode root2 = new BinaryTreeNode(); 112 | root2.value = 5; 113 | root2.left = new BinaryTreeNode(); 114 | root2.left.value = 4; 115 | root2.left.left = new BinaryTreeNode(); 116 | root2.left.left.value = 3; 117 | root2.left.left.left = new BinaryTreeNode(); 118 | root2.left.left.left.value = 2; 119 | root2.left.left.left.left = new BinaryTreeNode(); 120 | root2.left.left.left.left.value = 1; 121 | 122 | // 有一条路径上面的结点和为15 123 | System.out.println("findPath(root2, 15);"); 124 | findPath(root2, 15); 125 | 126 | // 没有路径上面的结点和为16 127 | System.out.println("findPath(root2, 16);"); 128 | findPath(root2, 16); 129 | 130 | // 1 131 | // \ 132 | // 2 133 | // \ 134 | // 3 135 | // \ 136 | // 4 137 | // \ 138 | // 5 139 | BinaryTreeNode root3 = new BinaryTreeNode(); 140 | root3.value = 1; 141 | root3.right = new BinaryTreeNode(); 142 | root3.right.value = 2; 143 | root3.right.right = new BinaryTreeNode(); 144 | root3.right.right.value = 3; 145 | root3.right.right.right = new BinaryTreeNode(); 146 | root3.right.right.right.value = 4; 147 | root3.right.right.right.right = new BinaryTreeNode(); 148 | root3.right.right.right.right.value = 5; 149 | 150 | // 有一条路径上面的结点和为15 151 | System.out.println("findPath(root3, 15);"); 152 | findPath(root3, 15); 153 | 154 | // 没有路径上面的结点和为16 155 | System.out.println("findPath(root3, 16);"); 156 | findPath(root3, 16); 157 | 158 | // 树中只有1个结点 159 | BinaryTreeNode root4 = new BinaryTreeNode(); 160 | 161 | root4.value = 1; 162 | // 有一条路径上面的结点和为1 163 | System.out.println("findPath(root4, 1);"); 164 | findPath(root4, 1); 165 | 166 | // 没有路径上面的结点和为2 167 | System.out.println("findPath(root4, 2);"); 168 | findPath(root4, 2); 169 | 170 | // 树中没有结点 171 | System.out.println("findPath(null, 0);"); 172 | findPath(null, 0); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /剑指offer/Test27.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: 王俊超 3 | * Date: 2015-04-25 4 | * Time: 13:50 5 | * Declaration: All Rights Reserved !!! 6 | */ 7 | public class Test27 { 8 | /** 9 | * 二叉树的树结点 10 | */ 11 | public static class BinaryTreeNode { 12 | int value; 13 | BinaryTreeNode left; 14 | BinaryTreeNode right; 15 | } 16 | 17 | /** 18 | * 题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。 19 | * 要求不能创建任何新的结点,只能调整树中结点指针的指向。 20 | * 21 | * @param root 二叉树的根结点 22 | * @return 双向链表的头结点 23 | */ 24 | public static BinaryTreeNode convert(BinaryTreeNode root) { 25 | 26 | // 用于保存处理过程中的双向链表的尾结点 27 | BinaryTreeNode[] lastNode = new BinaryTreeNode[1]; 28 | convertNode(root, lastNode); 29 | 30 | // 找到双向链表的头结点 31 | BinaryTreeNode head = lastNode[0]; 32 | while (head != null && head.left != null) { 33 | head = head.left; 34 | } 35 | return head; 36 | } 37 | 38 | 39 | /** 40 | * 链表表转换操作 41 | * 42 | * @param node 当前的根结点 43 | * @param lastNode 已经处理好的双向链表的尾结点,使用一个长度为1的数组,类似C++中的二级指针 44 | */ 45 | public static void convertNode(BinaryTreeNode node, BinaryTreeNode[] lastNode) { 46 | // 结点不为空 47 | if (node != null) { 48 | 49 | // 如果有左子树就先处理左子树 50 | if (node.left != null) { 51 | convertNode(node.left, lastNode); 52 | } 53 | 54 | // 将当前结点的前驱指向已经处理好的双向链表(由当前结点的左子树构成)的尾结点 55 | node.left = lastNode[0]; 56 | 57 | // 如果左子树转换成的双向链表不为空,设置尾结点的后继 58 | if (lastNode[0] != null) { 59 | lastNode[0].right = node; 60 | } 61 | 62 | // 记录当前结点为尾结点 63 | lastNode[0] = node; 64 | 65 | // 处理右子树 66 | if (node.right != null) { 67 | convertNode(node.right, lastNode); 68 | } 69 | } 70 | } 71 | 72 | 73 | public static void main(String[] args) { 74 | test01(); 75 | test02(); 76 | test03(); 77 | test04(); 78 | test05(); 79 | } 80 | 81 | private static void printList(BinaryTreeNode head) { 82 | while (head != null) { 83 | System.out.print(head.value + "->"); 84 | head = head.right; 85 | } 86 | 87 | System.out.println("null"); 88 | } 89 | 90 | private static void printTree(BinaryTreeNode root) { 91 | if (root != null) { 92 | printTree(root.left); 93 | System.out.print(root.value + "->"); 94 | printTree(root.right); 95 | } 96 | } 97 | 98 | 99 | // 10 100 | // / \ 101 | // 6 14 102 | // /\ /\ 103 | // 4 8 12 16 104 | private static void test01() { 105 | BinaryTreeNode node10 = new BinaryTreeNode(); 106 | node10.value = 10; 107 | 108 | BinaryTreeNode node6 = new BinaryTreeNode(); 109 | node6.value = 6; 110 | 111 | BinaryTreeNode node14 = new BinaryTreeNode(); 112 | node14.value = 14; 113 | 114 | BinaryTreeNode node4 = new BinaryTreeNode(); 115 | node4.value = 4; 116 | 117 | BinaryTreeNode node8 = new BinaryTreeNode(); 118 | node8.value = 8; 119 | 120 | BinaryTreeNode node12 = new BinaryTreeNode(); 121 | node12.value = 12; 122 | 123 | BinaryTreeNode node16 = new BinaryTreeNode(); 124 | node16.value = 16; 125 | 126 | node10.left = node6; 127 | node10.right = node14; 128 | 129 | node6.left = node4; 130 | node6.right = node8; 131 | 132 | node14.left = node12; 133 | node14.right = node16; 134 | 135 | System.out.print("Before convert: "); 136 | printTree(node10); 137 | System.out.println("null"); 138 | BinaryTreeNode head = convert(node10); 139 | System.out.print("After convert : "); 140 | printList(head); 141 | System.out.println(); 142 | 143 | } 144 | 145 | // 5 146 | // / 147 | // 4 148 | // / 149 | // 3 150 | // / 151 | // 2 152 | // / 153 | // 1 154 | private static void test02() { 155 | BinaryTreeNode node1 = new BinaryTreeNode(); 156 | node1.value = 1; 157 | 158 | BinaryTreeNode node2 = new BinaryTreeNode(); 159 | node2.value = 2; 160 | 161 | BinaryTreeNode node3 = new BinaryTreeNode(); 162 | node3.value = 3; 163 | 164 | BinaryTreeNode node4 = new BinaryTreeNode(); 165 | node4.value = 4; 166 | 167 | BinaryTreeNode node5 = new BinaryTreeNode(); 168 | node5.value = 5; 169 | 170 | node5.left = node4; 171 | node4.left = node3; 172 | node3.left = node2; 173 | node2.left = node1; 174 | 175 | System.out.print("Before convert: "); 176 | printTree(node5); 177 | System.out.println("null"); 178 | BinaryTreeNode head = convert(node5); 179 | System.out.print("After convert : "); 180 | printList(head); 181 | System.out.println(); 182 | } 183 | 184 | // 1 185 | // \ 186 | // 2 187 | // \ 188 | // 3 189 | // \ 190 | // 4 191 | // \ 192 | // 5 193 | private static void test03() { 194 | BinaryTreeNode node1 = new BinaryTreeNode(); 195 | node1.value = 1; 196 | 197 | BinaryTreeNode node2 = new BinaryTreeNode(); 198 | node2.value = 2; 199 | 200 | BinaryTreeNode node3 = new BinaryTreeNode(); 201 | node3.value = 3; 202 | 203 | BinaryTreeNode node4 = new BinaryTreeNode(); 204 | node4.value = 4; 205 | 206 | BinaryTreeNode node5 = new BinaryTreeNode(); 207 | node5.value = 5; 208 | 209 | node1.right = node2; 210 | node2.right = node3; 211 | node3.right = node4; 212 | node4.right = node5; 213 | 214 | System.out.print("Before convert: "); 215 | printTree(node1); 216 | System.out.println("null"); 217 | BinaryTreeNode head = convert(node1); 218 | System.out.print("After convert : "); 219 | printList(head); 220 | System.out.println(); 221 | } 222 | 223 | // 只有一个结点 224 | private static void test04() { 225 | BinaryTreeNode node1 = new BinaryTreeNode(); 226 | node1.value = 1; 227 | 228 | System.out.print("Before convert: "); 229 | printTree(node1); 230 | System.out.println("null"); 231 | BinaryTreeNode head = convert(node1); 232 | System.out.print("After convert : "); 233 | printList(head); 234 | System.out.println(); 235 | } 236 | 237 | // 没有结点 238 | private static void test05() { 239 | System.out.print("Before convert: "); 240 | printTree(null); 241 | System.out.println("null"); 242 | BinaryTreeNode head = convert(null); 243 | System.out.print("After convert : "); 244 | printList(head); 245 | System.out.println(); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /剑指offer/Test28.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-05-06 6 | * Time: 08:20 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test28 { 10 | /** 11 | * 题目:输入一个字符串,打印出该字符事中字符的所有排列。例如输入字符串abc。 12 | * 则打印出由字符a、b、c 所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。 13 | * 14 | * @param chars 待排序的字符数组 15 | */ 16 | public static void permutation(char[] chars) { 17 | // 输入较验 18 | if (chars == null || chars.length < 1) { 19 | return; 20 | } 21 | // 进行排列操作 22 | permutation(chars, 0); 23 | } 24 | 25 | /** 26 | * 求字符数组的排列 27 | * 28 | * @param chars 待排列的字符串 29 | * @param begin 当前处理的位置 30 | */ 31 | public static void permutation(char[] chars, int begin) { 32 | // 如果是最后一个元素了,就输出排列结果 33 | if (chars.length - 1 == begin) { 34 | System.out.print(new String(chars) + " "); 35 | } else { 36 | char tmp; 37 | // 对当前还未处理的字符串进行处理,每个字符都可以作为当前处理位置的元素 38 | for (int i = begin; i < chars.length; i++) { 39 | // 下面是交换元素的位置 40 | tmp = chars[begin]; 41 | chars[begin] = chars[i]; 42 | chars[i] = tmp; 43 | 44 | // 处理下一个位置 45 | permutation(chars, begin + 1); 46 | } 47 | } 48 | } 49 | 50 | public static void main(String[] args) { 51 | char[] c1 = {'a', 'b', 'c'}; 52 | permutation(c1); 53 | System.out.println(); 54 | 55 | char[] c2 = {'a', 'b', 'c', 'd'}; 56 | permutation(c2); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /剑指offer/Test29.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-05-06 6 | * Time: 08:44 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test29 { 10 | 11 | /** 12 | * 题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字 13 | * 14 | * @param numbers 输入数组 15 | * @return 找到的数字 16 | */ 17 | public static int moreThanHalfNum(int[] numbers) { 18 | 19 | // 输入校验 20 | if (numbers == null || numbers.length < 1) { 21 | throw new IllegalArgumentException("array length must large than 0"); 22 | } 23 | 24 | // 用于记录出现次数大于数组一半的数 25 | int result = numbers[0]; 26 | // 于当前记录的数不同的数的个数 27 | int count = 1; 28 | // 从第二个数开始向后找 29 | for (int i = 1; i < numbers.length; i++) { 30 | // 如果记数为0 31 | if (count == 0) { 32 | // 重新记录一个数,假设它是出现次数大于数组一半的 33 | result = numbers[i]; 34 | // 记录统计值 35 | count = 1; 36 | } 37 | // 如果记录的值与统计值相等,记数值增加 38 | else if (result == numbers[i]) { 39 | count++; 40 | } 41 | // 如果不相同就减少,相互抵消 42 | else { 43 | count--; 44 | } 45 | } 46 | 47 | // 最后的result可能是出现次数大于数组一半长度的值 48 | // 统计result的出现次数 49 | count = 0; 50 | for (int number : numbers) { 51 | if (result == number) { 52 | count++; 53 | } 54 | } 55 | 56 | // 如果出现次数大于数组的一半就返回对应的值 57 | if (count > numbers.length / 2) { 58 | return result; 59 | } 60 | // 否则输入异常 61 | else { 62 | throw new IllegalArgumentException("invalid input"); 63 | } 64 | } 65 | 66 | public static void main(String[] args) { 67 | // 存在出现次数超过数组长度一半的数字 68 | int numbers[] = {1, 2, 3, 2, 2, 2, 5, 4, 2}; 69 | System.out.println(moreThanHalfNum(numbers)); 70 | 71 | // 出现次数超过数组长度一半的数字都出现在数组的前半部分 72 | int numbers2[] = {2, 2, 2, 2, 2, 1, 3, 4, 5}; 73 | System.out.println(moreThanHalfNum(numbers2)); 74 | 75 | // 出现次数超过数组长度一半的数字都出现在数组的后半部分 76 | int numbers3[] = {1, 3, 4, 5, 2, 2, 2, 2, 2}; 77 | System.out.println(moreThanHalfNum(numbers3)); 78 | 79 | // 只有一个数 80 | int numbers4[] = {1}; 81 | System.out.println(moreThanHalfNum(numbers4)); 82 | 83 | // 输入空指针 84 | moreThanHalfNum(null); 85 | // 不存在出现次数超过数组长度一半的数字 86 | int numbers5[] = {1, 2, 3, 2, 4, 2, 5, 2, 3}; 87 | moreThanHalfNum(numbers5); 88 | } 89 | } 90 | 91 | 92 | -------------------------------------------------------------------------------- /剑指offer/Test31.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-10 6 | * Time: 19:54 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test31 { 10 | /** 11 | * 题目2 输入一个整型数组,数组里有正数也有负数。数组中一个或连 12 | * 续的多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。 13 | * 14 | * @param arr 输入数组 15 | * @return 最大的连续子数组和 16 | */ 17 | public static int findGreatestSumOfSubArray(int[] arr) { 18 | // 参数校验 19 | if (arr == null || arr.length < 1) { 20 | throw new IllegalArgumentException("Array must contain an element"); 21 | } 22 | 23 | // 记录最大的子数组和,开始时是最小的整数 24 | int max = Integer.MIN_VALUE; 25 | // 当前的和 26 | int curMax = 0; 27 | // 数组遍历 28 | for (int i : arr) { 29 | // 如果当前和小于等于0,就重新设置当前和 30 | if (curMax <= 0) { 31 | curMax = i; 32 | } 33 | // 如果当前和大于0,累加当前和 34 | else { 35 | curMax += i; 36 | } 37 | 38 | // 更新记录到的最在的子数组和 39 | if (max < curMax) { 40 | max = curMax; 41 | } 42 | } 43 | 44 | 45 | return max; 46 | } 47 | 48 | public static void main(String[] args) { 49 | int[] data = {1, -2, 3, 10, -4, 7, 2, -5}; 50 | int[] data2 = {-2, -8, -1, -5, -9}; 51 | int[] data3 = {2, 8, 1, 5, 9}; 52 | 53 | System.out.println(findGreatestSumOfSubArray(data)); 54 | System.out.println(findGreatestSumOfSubArray(data2)); 55 | System.out.println(findGreatestSumOfSubArray(data3)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /剑指offer/Test32.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-11 6 | * Time: 09:22 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test32 { 10 | 11 | /** 12 | * 题目:输入一个整数n求从1 到n这n个整数的十进制表示中1 出现的次数。 13 | * @param n 最大的数字 14 | * @return 1-n中,各个数位1出现的次数 15 | */ 16 | public static int numberOf1Between1AndN(int n) { 17 | if (n <= 0) { 18 | return 0; 19 | } 20 | 21 | String value = n + ""; 22 | int[] numbers = new int[value.length()]; 23 | 24 | for (int i = 0; i < numbers.length; i++) { 25 | numbers[i] = value.charAt(i) - '0'; 26 | } 27 | 28 | return numberOf1(numbers, 0); 29 | } 30 | 31 | /** 32 | * 求0-numbers表的数字中的1的个数 33 | * 34 | * @param numbers 数字,如{1, 2, 3, 4, 5}表示数字12345 35 | * @param curIdx 当前处理的位置 36 | * @return 1的个数 37 | */ 38 | private static int numberOf1(int[] numbers, int curIdx) { 39 | 40 | if (numbers == null || curIdx >= numbers.length || curIdx < 0) { 41 | return 0; 42 | } 43 | // 待处理的第一个数字 44 | int first = numbers[curIdx]; 45 | 46 | // 要处理的数字的位数 47 | int length = numbers.length - curIdx; 48 | 49 | // 如果只有一位且这一位是0返回0 50 | if (length == 1 && first == 0) { 51 | return 0; 52 | } 53 | 54 | // 如果只有一位且这一位不是0返回1 55 | if (length == 1 && first > 0) { 56 | return 1; 57 | } 58 | 59 | // 假设numbers是21345 60 | // numFirstDigit是数字10000-19999的第一个位中的数目 61 | int numFirstDigit = 0; 62 | // 如果最高位不是1,如21345,在[1236, 21345]中,最高位1出现的只在[10000, 19999]中,出现1的次数是10^4方个 63 | if (first > 1) { 64 | numFirstDigit = powerBase10(length - 1); 65 | } 66 | // 如果最高位是1,如12345,在[2346, 12345]中,最高位1出现的只在[10000, 12345]中,总计2345+1个 67 | else if (first == 1) { 68 | numFirstDigit = atoi(numbers, curIdx + 1) + 1; 69 | } 70 | 71 | // numOtherDigits,是[1346, 21345]中,除了第一位之外(不看21345中的第一位2)的数位中的1的数目 72 | int numOtherDigits = first * (length - 1) * powerBase10(length - 2); 73 | // numRecursive是1-1234中1的的数目 74 | int numRecursive = numberOf1(numbers, curIdx + 1); 75 | 76 | return numFirstDigit + numOtherDigits + numRecursive; 77 | } 78 | 79 | /** 80 | * 将数字数组转换成数值,如{1, 2, 3, 4, 5},i = 2,结果是345 81 | * @param numbers 数组 82 | * @param i 开始黑气的位置 83 | * @return 转换结果 84 | */ 85 | private static int atoi(int[] numbers, int i) { 86 | int result = 0; 87 | for (int j = i; j < numbers.length; j++) { 88 | result = (result * 10 + numbers[j]); 89 | } 90 | return result; 91 | } 92 | 93 | /** 94 | * 求10的n次方,假定n不为负数 95 | * @param n 幂,非负数 96 | * @return 10的n次方 97 | */ 98 | private static int powerBase10(int n) { 99 | int result = 1; 100 | for (int i = 0; i < n; i++) { 101 | result *= 10; 102 | } 103 | return result; 104 | } 105 | 106 | public static void main(String[] args) { 107 | System.out.println(numberOf1Between1AndN(1)); // 1 108 | System.out.println(numberOf1Between1AndN(5)); // 1 109 | System.out.println(numberOf1Between1AndN(10)); // 2 110 | System.out.println(numberOf1Between1AndN(55)); // 16 111 | System.out.println(numberOf1Between1AndN(99)); // 20 112 | System.out.println(numberOf1Between1AndN(10000)); // 4001 113 | System.out.println(numberOf1Between1AndN(21345)); // 18821 114 | System.out.println(numberOf1Between1AndN(0)); // 0 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /剑指offer/Test33.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.Comparator; 4 | 5 | /** 6 | * Author: 王俊超 7 | * Date: 2015-06-11 8 | * Time: 10:38 9 | * Declaration: All Rights Reserved !!! 10 | */ 11 | public class Test33 { 12 | 13 | /** 14 | * 自定义的排序比较器,实现算法说明的排序原理 15 | */ 16 | private static class MComparator implements Comparator { 17 | 18 | @Override 19 | public int compare(String o1, String o2) { 20 | 21 | if (o1 == null || o2 == null) { 22 | throw new IllegalArgumentException("Arg should not be null"); 23 | } 24 | 25 | String s1 = o1 + o2; 26 | String s2 = o2 + o1; 27 | return s1.compareTo(s2); 28 | } 29 | } 30 | 31 | /** 32 | * 快速排序算法 33 | * 34 | * @param array 待排序数组 35 | * @param start 要排序的起始位置 36 | * @param end 要排序的结束位置 37 | * @param comparator 自定义的比较器 38 | */ 39 | private static void quickSort(String[] array, int start, int end, Comparator comparator) { 40 | 41 | if (start < end) { 42 | String pivot = array[start]; 43 | int left = start; 44 | int right = end; 45 | while (start < end) { 46 | while (start < end && comparator.compare(array[end], pivot) >= 0) { 47 | end--; 48 | } 49 | 50 | array[start] = array[end]; 51 | 52 | while (start < end && comparator.compare(array[start], pivot) <= 0) { 53 | start++; 54 | } 55 | array[end] = array[start]; 56 | 57 | } 58 | 59 | array[start] = pivot; 60 | 61 | quickSort(array, left, start - 1, comparator); 62 | quickSort(array, start + 1, end, comparator); 63 | } 64 | } 65 | 66 | /** 67 | * 题目:输入一个正整数数组,把数组里所有数字拼接起来排成一个数, 68 | * 打印能拼接出的所有数字中最小的一个。 69 | * @param array 输入的数组 70 | * @return 输出结果 71 | */ 72 | public static String printMinNumber(String[] array) { 73 | 74 | if (array == null || array.length < 1) { 75 | throw new IllegalArgumentException("Array must contain value"); 76 | } 77 | 78 | MComparator comparator = new MComparator(); 79 | quickSort(array, 0, array.length - 1, comparator); 80 | 81 | StringBuilder builder = new StringBuilder(256); 82 | for (String s : array) { 83 | builder.append(s); 84 | } 85 | 86 | return builder.toString(); 87 | } 88 | 89 | public static void main(String[] args) { 90 | 91 | String[] data = {"3", "5", "1", "4", "2"}; 92 | System.out.println(printMinNumber(data)); 93 | 94 | String[] data2 = {"3", "32", "321"}; 95 | System.out.println(printMinNumber(data2)); 96 | 97 | String[] data3 = {"3", "323", "32123"}; 98 | System.out.println(printMinNumber(data3)); 99 | 100 | String[] data4 = {"1", "11", "111"}; 101 | System.out.println(printMinNumber(data4)); 102 | 103 | String[] data5 = {"321"}; 104 | System.out.println(printMinNumber(data5)); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /剑指offer/Test34.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-11 6 | * Time: 14:35 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test34 { 10 | /** 11 | * 判断一个数是否只有2,3,5因子(丑数) 12 | * 13 | * @param num 待判断的数,非负 14 | * @return true是丑数,false丑数 15 | */ 16 | private static boolean isUgly(int num) { 17 | while (num % 2 == 0) { 18 | num /= 2; 19 | } 20 | 21 | while (num % 3 == 0) { 22 | num /= 3; 23 | } 24 | 25 | while (num % 5 == 0) { 26 | num /= 5; 27 | } 28 | 29 | return num == 1; 30 | } 31 | 32 | /** 33 | * 找第index个丑数,速度太慢 34 | * 35 | * @param index 第index个丑数 36 | * @return 对应的丑数值 37 | */ 38 | public static int getUglyNumber(int index) { 39 | if (index <= 0) { 40 | return 0; 41 | } 42 | 43 | int num = 0; 44 | int uglyFound = 0; 45 | while (uglyFound < index) { 46 | num++; 47 | if (isUgly(num)) { 48 | ++uglyFound; 49 | } 50 | } 51 | 52 | return num; 53 | } 54 | 55 | /** 56 | * 找第index个丑数,【第二种方法】 57 | * 58 | * @param index 第index个丑数 59 | * @return 对应的丑数值 60 | */ 61 | public static int getUglyNumber2(int index) { 62 | if (index <= 0) { 63 | return 0; 64 | } 65 | 66 | int[] pUglyNumbers = new int[index]; 67 | pUglyNumbers[0] = 1; 68 | int nextUglyIndex = 1; 69 | 70 | int p2 = 0; 71 | int p3 = 0; 72 | int p5 = 0; 73 | 74 | while (nextUglyIndex < index) { 75 | int min = min(pUglyNumbers[p2] * 2, pUglyNumbers[p3] * 3, pUglyNumbers[p5] * 5); 76 | pUglyNumbers[nextUglyIndex] = min; 77 | 78 | while (pUglyNumbers[p2] * 2 <= pUglyNumbers[nextUglyIndex]) { 79 | p2++; 80 | } 81 | 82 | while (pUglyNumbers[p3] * 3 <= pUglyNumbers[nextUglyIndex]) { 83 | p3++; 84 | } 85 | 86 | while (pUglyNumbers[p5] * 5 <= pUglyNumbers[nextUglyIndex]) { 87 | p5++; 88 | } 89 | 90 | nextUglyIndex++; 91 | } 92 | 93 | return pUglyNumbers[nextUglyIndex - 1]; 94 | } 95 | 96 | private static int min(int n1, int n2, int n3) { 97 | int min = n1 < n2 ? n1 : n2; 98 | return min < n3 ? min : n3; 99 | } 100 | 101 | 102 | public static void main(String[] args) { 103 | System.out.println("Solution 1:"); 104 | test1(); 105 | System.out.println(); 106 | 107 | System.out.println("Solution 2:"); 108 | test2(); 109 | } 110 | 111 | private static void test1() { 112 | System.out.println(getUglyNumber(1)); // 1 113 | System.out.println(getUglyNumber(2)); // 2 114 | System.out.println(getUglyNumber(3)); // 3 115 | System.out.println(getUglyNumber(4)); // 4 116 | System.out.println(getUglyNumber(5)); // 5 117 | System.out.println(getUglyNumber(6)); // 6 118 | System.out.println(getUglyNumber(7)); // 8 119 | System.out.println(getUglyNumber(8)); // 9 120 | System.out.println(getUglyNumber(9)); // 10 121 | System.out.println(getUglyNumber(10)); // 12 122 | System.out.println(getUglyNumber(11)); // 15 123 | System.out.println(getUglyNumber(1500)); // 859963392 124 | System.out.println(getUglyNumber(0)); // 0 125 | } 126 | 127 | private static void test2() { 128 | System.out.println(getUglyNumber2(1)); // 1 129 | System.out.println(getUglyNumber2(2)); // 2 130 | System.out.println(getUglyNumber2(3)); // 3 131 | System.out.println(getUglyNumber2(4)); // 4 132 | System.out.println(getUglyNumber2(5)); // 5 133 | System.out.println(getUglyNumber2(6)); // 6 134 | System.out.println(getUglyNumber2(7)); // 8 135 | System.out.println(getUglyNumber2(8)); // 9 136 | System.out.println(getUglyNumber2(9)); // 10 137 | System.out.println(getUglyNumber2(10)); // 12 138 | System.out.println(getUglyNumber2(11)); // 15 139 | System.out.println(getUglyNumber2(1500)); // 859963392 140 | System.out.println(getUglyNumber2(0)); // 0 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /剑指offer/Test35.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.Map; 5 | import java.util.Set; 6 | 7 | /** 8 | * Author: 王俊超 9 | * Date: 2015-06-11 10 | * Time: 16:46 11 | * Declaration: All Rights Reserved !!! 12 | */ 13 | public class Test35 { 14 | public static char firstNotRepeatingChar(String s) { 15 | if (s == null || s.length() < 1) { 16 | throw new IllegalArgumentException("Arg should not be null or empty"); 17 | } 18 | 19 | Map map = new LinkedHashMap<>(); 20 | for (int i = 0; i < s.length(); i++) { 21 | char c = s.charAt(i); 22 | if (map.containsKey(c)) { 23 | map.put(c, -2); 24 | } else { 25 | map.put(c, i); 26 | } 27 | } 28 | 29 | Set> entrySet = map.entrySet(); 30 | // 记录只出现一次的字符的索引 31 | int idx = Integer.MAX_VALUE; 32 | // 记录只出现一次的字符 33 | char result = '\0'; 34 | 35 | // 找最小索引对应的字符 36 | for (Map.Entry entry : entrySet) { 37 | if (entry.getValue() >= 0 && entry.getValue() < idx) { 38 | idx = entry.getValue(); 39 | result = entry.getKey(); 40 | } 41 | } 42 | 43 | return result; 44 | } 45 | 46 | public static void main(String[] args) { 47 | System.out.println(firstNotRepeatingChar("google")); // l 48 | System.out.println(firstNotRepeatingChar("aabccdbd")); // '\0' 49 | System.out.println(firstNotRepeatingChar("abcdefg")); // a 50 | System.out.println(firstNotRepeatingChar("gfedcba")); // g 51 | System.out.println(firstNotRepeatingChar("zgfedcba")); // g 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /剑指offer/Test36.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-11 6 | * Time: 17:20 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test36 { 10 | 11 | public static int inversePairs(int[] data) { 12 | if (data == null || data.length < 1) { 13 | throw new IllegalArgumentException("Array arg should contain at least a value"); 14 | } 15 | 16 | int[] copy = new int[data.length]; 17 | System.arraycopy(data, 0, copy, 0, data.length); 18 | 19 | return inversePairsCore(data, copy, 0, data.length - 1); 20 | } 21 | 22 | private static int inversePairsCore(int[] data, int[] copy, int start, int end) { 23 | 24 | if (start == end) { 25 | copy[start] = data[start]; 26 | return 0; 27 | } 28 | 29 | int length = (end - start) / 2; 30 | int left = inversePairsCore(copy, data, start, start + length); 31 | int right = inversePairsCore(copy, data, start + length + 1, end); 32 | 33 | // 前半段的最后一个数字的下标 34 | int i = start + length; 35 | // 后半段最后一个数字的下标 36 | int j = end; 37 | // 开始拷贝的位置 38 | int indexCopy = end; 39 | // 逆序数 40 | int count = 0; 41 | 42 | while (i >= start && j >= start + length + 1) { 43 | if (data[i] > data[j]) { 44 | copy[indexCopy] = data[i]; 45 | indexCopy--; 46 | i--; 47 | count += j - (start + length); // 对应的逆序数 48 | } else { 49 | copy[indexCopy] = data[j]; 50 | indexCopy--; 51 | j--; 52 | } 53 | } 54 | 55 | for (; i >= start; i--) { 56 | copy[indexCopy] = data[i]; 57 | indexCopy--; 58 | i--; 59 | } 60 | 61 | for (; j >= start + length + 1; j--) { 62 | copy[indexCopy] = data[j]; 63 | indexCopy--; 64 | j--; 65 | } 66 | return count + left + right; 67 | } 68 | 69 | public static void main(String[] args) { 70 | int[] data = {1, 2, 3, 4, 7, 6, 5}; 71 | System.out.println(inversePairs(data)); // 3 72 | int[] data2 = {6, 5, 4, 3, 2, 1}; 73 | System.out.println(inversePairs(data2)); // 15 74 | int[] data3 = {1, 2, 3, 4, 5, 6}; 75 | System.out.println(inversePairs(data3)); // 0 76 | int[] data4 = {1}; 77 | System.out.println(inversePairs(data4)); // 0 78 | int[] data5 = {1, 2}; 79 | System.out.println(inversePairs(data5)); // 0 80 | int[] data6 = {2, 1}; 81 | System.out.println(inversePairs(data6)); // 1 82 | int[] data7 = {1, 2, 1, 2, 1}; 83 | System.out.println(inversePairs(data7)); // 3 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /剑指offer/Test37.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-13 6 | * Time: 09:02 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test37 { 10 | /** 11 | * 链表结点类 12 | */ 13 | private static class ListNode { 14 | int val; 15 | ListNode next; 16 | 17 | public ListNode() { 18 | 19 | } 20 | 21 | public ListNode(int val) { 22 | this.val = val; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return val + ""; 28 | } 29 | } 30 | 31 | /** 32 | * 找两个结点的第一个公共结点,如果没有找到返回null,方法比较好,考虑了两个链表中有null的情况 33 | * 34 | * @param head1 第一个链表 35 | * @param head2 第二个链表 36 | * @return 找到的公共结点,没有返回null 37 | */ 38 | public static ListNode findFirstCommonNode(ListNode head1, ListNode head2) { 39 | int length1 = getListLength(head1); 40 | int length2 = getListLength(head2); 41 | 42 | int diff = length1 - length2; 43 | ListNode longListHead = head1; 44 | ListNode shortListHead = head2; 45 | 46 | if (diff < 0) { 47 | longListHead = head2; 48 | shortListHead = head1; 49 | diff = length2 - length1; 50 | } 51 | 52 | for (int i = 0; i < diff; i++) { 53 | longListHead = longListHead.next; 54 | } 55 | 56 | while (longListHead != null && shortListHead != null && longListHead != shortListHead) { 57 | longListHead = longListHead.next; 58 | shortListHead = shortListHead.next; 59 | } 60 | 61 | // 返回第一个相同的公共结点,如果没有返回null 62 | return longListHead; 63 | } 64 | 65 | private static int getListLength(ListNode head) { 66 | int result = 0; 67 | while (head != null) { 68 | result++; 69 | head = head.next; 70 | } 71 | 72 | return result; 73 | } 74 | 75 | public static void main(String[] args) { 76 | test1(); 77 | test2(); 78 | test3(); 79 | test4(); 80 | } 81 | 82 | private static void test1() { 83 | // 第一个公共结点在链表中间 84 | // 1 - 2 - 3 \ 85 | // 6 - 7 86 | // 4 - 5 / 87 | ListNode n1 = new ListNode(1); 88 | ListNode n2 = new ListNode(2); 89 | ListNode n3 = new ListNode(3); 90 | ListNode n4 = new ListNode(4); 91 | ListNode n5 = new ListNode(5); 92 | ListNode n6 = new ListNode(6); 93 | ListNode n7 = new ListNode(7); 94 | 95 | n1.next = n2; 96 | n2.next = n3; 97 | n3.next = n6; 98 | n6.next = n7; 99 | 100 | n4.next = n5; 101 | n5.next = n6; 102 | 103 | System.out.println(findFirstCommonNode(n1, n4)); // 6 104 | } 105 | 106 | 107 | private static void test2() { 108 | // 没有公共结点 109 | // 1 - 2 - 3 - 4 110 | // 111 | // 5 - 6 - 7 112 | ListNode n1 = new ListNode(1); 113 | ListNode n2 = new ListNode(2); 114 | ListNode n3 = new ListNode(3); 115 | ListNode n4 = new ListNode(4); 116 | ListNode n5 = new ListNode(5); 117 | ListNode n6 = new ListNode(6); 118 | ListNode n7 = new ListNode(7); 119 | 120 | n1.next = n2; 121 | n2.next = n3; 122 | n3.next = n4; 123 | 124 | n5.next = n6; 125 | n6.next = n7; 126 | System.out.println(findFirstCommonNode(n1, n5)); // null 127 | } 128 | 129 | private static void test3() { 130 | // 公共结点是最后一个结点 131 | // 1 - 2 - 3 - 4 \ 132 | // 7 133 | // 5 - 6 / 134 | ListNode n1 = new ListNode(1); 135 | ListNode n2 = new ListNode(2); 136 | ListNode n3 = new ListNode(3); 137 | ListNode n4 = new ListNode(4); 138 | ListNode n5 = new ListNode(5); 139 | ListNode n6 = new ListNode(6); 140 | ListNode n7 = new ListNode(7); 141 | 142 | n1.next = n2; 143 | n2.next = n3; 144 | n3.next = n4; 145 | n4.next = n7; 146 | 147 | n5.next = n6; 148 | n6.next = n7; 149 | System.out.println(findFirstCommonNode(n1, n5)); // 7 150 | } 151 | 152 | private static void test4() { 153 | // 公共结点是第一个结点 154 | // 1 - 2 - 3 - 4 - 5 155 | // 两个链表完全重合 156 | ListNode n1 = new ListNode(1); 157 | ListNode n2 = new ListNode(2); 158 | ListNode n3 = new ListNode(3); 159 | ListNode n4 = new ListNode(4); 160 | ListNode n5 = new ListNode(5); 161 | ListNode n6 = new ListNode(6); 162 | ListNode n7 = new ListNode(7); 163 | 164 | n1.next = n2; 165 | n2.next = n3; 166 | n3.next = n4; 167 | n4.next = n5; 168 | 169 | System.out.println(findFirstCommonNode(n1, n1)); // 1 170 | } 171 | 172 | 173 | } 174 | -------------------------------------------------------------------------------- /剑指offer/Test38.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-13 6 | * Time: 14:24 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test38 { 10 | /** 11 | * 找排序数组中k第一次出现的位置 12 | * 13 | * @param data 14 | * @param k 15 | * @param start 16 | * @param end 17 | * @return 18 | */ 19 | private static int getFirstK(int[] data, int k, int start, int end) { 20 | if (data == null || data.length < 1 || start > end) { 21 | return -1; 22 | } 23 | 24 | int midIdx = start + (end - start) / 2; 25 | int midData = data[midIdx]; 26 | 27 | if (midData == k) { 28 | if (midIdx > 0 && data[midIdx - 1] != k || midIdx == 0) { 29 | return midIdx; 30 | } else { 31 | end = midIdx - 1; 32 | } 33 | } else if (midData > k) { 34 | end = midIdx - 1; 35 | } else { 36 | start = midIdx + 1; 37 | } 38 | 39 | return getFirstK(data, k, start, end); 40 | } 41 | 42 | /** 43 | * 找排序数组中k最后一次出现的位置 44 | * 45 | * @param data 46 | * @param k 47 | * @param start 48 | * @param end 49 | * @return 50 | */ 51 | private static int getLastK(int[] data, int k, int start, int end) { 52 | if (data == null || data.length < 1 || start > end) { 53 | return -1; 54 | } 55 | 56 | int midIdx = start + (end - start) / 2; 57 | int midData = data[midIdx]; 58 | 59 | if (midData == k) { 60 | if (midIdx + 1 < data.length && data[midIdx + 1] != k || midIdx == data.length - 1) { 61 | return midIdx; 62 | } else { 63 | start = midIdx + 1; 64 | } 65 | } else if (midData < k) { 66 | start = midIdx + 1; 67 | } else { 68 | end = midIdx - 1; 69 | } 70 | 71 | return getLastK(data, k, start, end); 72 | } 73 | 74 | /** 75 | * 题目:统计一个数字:在排序数组中出现的次数 76 | * @param data 77 | * @param k 78 | * @return 79 | */ 80 | public static int getNumberOfK(int[] data, int k) { 81 | int number = 0; 82 | if (data != null && data.length > 0) { 83 | int first = getFirstK(data, k, 0, data.length - 1); 84 | int last = getLastK(data, k, 0, data.length - 1); 85 | 86 | if (first > -1 && last > -1) { 87 | number = last - first + 1; 88 | } 89 | } 90 | 91 | return number; 92 | } 93 | 94 | public static void main(String[] args) { 95 | // 查找的数字出现在数组的中间 96 | int[] data1 = {1, 2, 3, 3, 3, 3, 4, 5}; 97 | System.out.println(getNumberOfK(data1, 3)); // 4 98 | 99 | // 查找的数组出现在数组的开头 100 | int[] data2 = {3, 3, 3, 3, 4, 5}; 101 | System.out.println(getNumberOfK(data2, 3)); // 4 102 | 103 | // 查找的数组出现在数组的结尾 104 | int[] data3 = {1, 2, 3, 3, 3, 3}; 105 | System.out.println(getNumberOfK(data3, 3)); // 4 106 | 107 | // 查找的数字不存在 108 | int[] data4 = {1, 3, 3, 3, 3, 4, 5}; 109 | System.out.println(getNumberOfK(data4, 2)); // 0 110 | 111 | // 查找的数字比第一个数字还小,不存在 112 | int[] data5 = {1, 3, 3, 3, 3, 4, 5}; 113 | System.out.println(getNumberOfK(data5, 0)); // 0 114 | 115 | // 查找的数字比最后一个数字还大,不存在 116 | int[] data6 = {1, 3, 3, 3, 3, 4, 5}; 117 | System.out.println(getNumberOfK(data6, 0)); // 0 118 | 119 | // 数组中的数字从头到尾都是查找的数字 120 | int[] data7 = {3, 3, 3, 3}; 121 | System.out.println(getNumberOfK(data7, 3)); // 4 122 | 123 | // 数组中的数字从头到尾只有一个重复的数字,不是查找的数字 124 | int[] data8 = {3, 3, 3, 3}; 125 | System.out.println(getNumberOfK(data8, 4)); // 0 126 | 127 | // 数组中只有一个数字,是查找的数字 128 | int[] data9 = {3}; 129 | System.out.println(getNumberOfK(data9, 3)); // 1 130 | 131 | // 数组中只有一个数字,不是查找的数字 132 | int[] data10 = {3}; 133 | System.out.println(getNumberOfK(data10, 4)); // 0 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /剑指offer/Test40.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-14 6 | * Time: 09:52 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test40 { 10 | public static int[] findNumbersAppearanceOnce(int[] data) { 11 | int[] result = {0, 0}; 12 | 13 | if (data == null || data.length < 2) { 14 | return result; 15 | } 16 | 17 | int xor = 0; 18 | for (int i : data) { 19 | xor ^= i; 20 | } 21 | 22 | int indexOf1 = findFirstBit1(xor); 23 | 24 | for (int i : data) { 25 | if (isBit1(i, indexOf1)) { 26 | result[0] ^= i; 27 | } else { 28 | result[1] ^= i; 29 | } 30 | } 31 | 32 | return result; 33 | } 34 | 35 | private static int findFirstBit1(int num) { 36 | int index = 0; 37 | while ((num & 1) == 0 && index < 32) { 38 | num >>>= 1; 39 | index++; 40 | } 41 | 42 | return index; 43 | } 44 | 45 | private static boolean isBit1(int num, int indexBit) { 46 | num >>>= indexBit; 47 | return (num & 1) == 1; 48 | } 49 | 50 | public static void main(String[] args) { 51 | int[] data1 = {2, 4, 3, 6, 3, 2, 5, 5}; 52 | int[] result1 = findNumbersAppearanceOnce(data1); 53 | System.out.println(result1[0] + " " + result1[1]); 54 | 55 | int[] data2 = {4, 6}; 56 | int[] result2 = findNumbersAppearanceOnce(data2); 57 | System.out.println(result2[0] + " " + result2[1]); 58 | 59 | int[] data3 = {4, 6, 1, 1, 1, 1}; 60 | int[] result3 = findNumbersAppearanceOnce(data3); 61 | System.out.println(result3[0] + " " + result3[1]); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /剑指offer/Test41.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Author: 王俊超 8 | * Date: 2015-06-14 9 | * Time: 10:06 10 | * Declaration: All Rights Reserved !!! 11 | */ 12 | public class Test41 { 13 | /** 14 | * 输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得得它们的和正好是s。 15 | * 如果有多对数字的和等于s,输出任意一对即可。 16 | * 17 | * @param data 18 | * @param sum 19 | * @return 20 | */ 21 | public static List findNumbersWithSum(int[] data, int sum) { 22 | List result = new ArrayList<>(2); 23 | 24 | if (data == null || data.length < 2) { 25 | return result; 26 | } 27 | 28 | int ahead = data.length - 1; 29 | int behind = 0; 30 | long curSum; // 统计和,取long是防止结果溢出 31 | 32 | while (behind < ahead) { 33 | curSum = data[behind] + data[ahead]; 34 | 35 | if (curSum == sum) { 36 | result.add(data[behind]); 37 | result.add(data[ahead]); 38 | break; 39 | } else if (curSum < sum) { 40 | behind++; 41 | } else { 42 | ahead--; 43 | } 44 | } 45 | 46 | return result; 47 | } 48 | 49 | /** 50 | * 题目二:输入一个正数s,打印出所有和为s 的连续正数序列(至少两个数)。 51 | * @param sum 52 | * @return 53 | */ 54 | public static List> findContinuousSequence(int sum) { 55 | List> result = new ArrayList<>(); 56 | if (sum < 3) { 57 | return result; 58 | } 59 | 60 | int small = 1; 61 | int big = 2; 62 | int middle = (1 + sum) / 2; 63 | int curSum = small + big; 64 | 65 | while (small < middle) { 66 | if (curSum == sum) { 67 | List list = new ArrayList<>(2); 68 | for (int i = small; i <= big; i++) { 69 | list.add(i); 70 | } 71 | result.add(list); 72 | } 73 | 74 | while (curSum > sum && small < middle) { 75 | curSum -= small; 76 | small++; 77 | 78 | if (curSum == sum) { 79 | List list = new ArrayList<>(2); 80 | for (int i = small; i <= big; i++) { 81 | list.add(i); 82 | } 83 | result.add(list); 84 | } 85 | } 86 | 87 | big++; 88 | curSum += big; 89 | } 90 | 91 | return result; 92 | } 93 | public static void main(String[] args) { 94 | test01(); 95 | System.out.println("---------------"); 96 | test02(); 97 | } 98 | 99 | private static void test01() { 100 | int[] data1 = {1, 2, 4, 7, 11, 15}; 101 | System.out.println(findNumbersWithSum(data1, 15)); 102 | 103 | int[] data2 = {1, 2, 4, 7, 11, 16}; 104 | System.out.println(findNumbersWithSum(data2, 17)); 105 | 106 | int[] data3 = {1, 2, 4, 7, 11, 16}; 107 | System.out.println(findNumbersWithSum(data3, 10)); 108 | } 109 | 110 | public static void test02(){ 111 | System.out.println(findContinuousSequence(1)); 112 | System.out.println(findContinuousSequence(3)); 113 | System.out.println(findContinuousSequence(4)); 114 | System.out.println(findContinuousSequence(9)); 115 | System.out.println(findContinuousSequence(15)); 116 | System.out.println(findContinuousSequence(100)); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /剑指offer/Test42.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-14 6 | * Time: 10:40 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test42 { 10 | /** 11 | * 将data中start到end之间的数字反转 12 | * 13 | * @param data 14 | * @param start 15 | * @param end 16 | */ 17 | public static void reverse(char[] data, int start, int end) { 18 | if (data == null || data.length < 1 || start < 0 || end > data.length || start > end) { 19 | return; 20 | } 21 | 22 | while (start < end) { 23 | char tmp = data[start]; 24 | data[start] = data[end]; 25 | data[end] = tmp; 26 | 27 | start++; 28 | end--; 29 | } 30 | } 31 | 32 | /** 33 | * 题目一:输入一个英文句子,翻转句子中单词的顺序,但单词内字啊的顺序不变。 34 | * 为简单起见,标点符号和普通字母一样处理。 35 | * 36 | * @param data 37 | * @return 38 | */ 39 | public static char[] reverseSentence(char[] data) { 40 | if (data == null || data.length < 1) { 41 | return data; 42 | } 43 | 44 | reverse(data, 0, data.length - 1); 45 | 46 | int start = 0; 47 | int end = 0; 48 | 49 | while (start < data.length) { 50 | if (data[start] == ' ') { 51 | start++; 52 | end++; 53 | } else if (end == data.length || data[end] == ' ') { 54 | reverse(data, start, end - 1); 55 | end++; 56 | start = end; 57 | } else { 58 | end++; 59 | } 60 | } 61 | 62 | return data; 63 | } 64 | 65 | /** 66 | * 题目二:字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。 67 | * 请定义一个函数实现字符串左旋转操作的功能。 68 | * @param data 69 | * @param n 70 | * @return 71 | */ 72 | public static char[] leftRotateString(char[] data, int n) { 73 | if (data == null || n < 0 || n > data.length) { 74 | return data; 75 | } 76 | 77 | reverse(data, 0, data.length - 1); 78 | reverse(data, 0, data.length - n - 1); 79 | reverse(data, data.length - n, data.length - 1); 80 | 81 | return data; 82 | } 83 | 84 | 85 | public static void main(String[] args) { 86 | test01(); 87 | test02(); 88 | } 89 | 90 | private static void test01() { 91 | System.out.println(new String(reverseSentence("I am a student.".toCharArray()))); 92 | System.out.println(new String(reverseSentence("Wonderful".toCharArray()))); 93 | System.out.println(new String(reverseSentence("".toCharArray()))); 94 | System.out.println(new String(reverseSentence(" ".toCharArray()))); 95 | } 96 | 97 | private static void test02() { 98 | System.out.println(new String(leftRotateString("abcdefg".toCharArray(), 2))); 99 | System.out.println(new String(leftRotateString("abcdefg".toCharArray(), 1))); 100 | System.out.println(new String(leftRotateString("abcdefg".toCharArray(), 6))); 101 | System.out.println(new String(leftRotateString("abcdefg".toCharArray(), 7))); 102 | System.out.println(new String(leftRotateString("abcdefg".toCharArray(), 0))); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /剑指offer/Test43.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-14 6 | * Time: 12:01 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test43 { 10 | /** 11 | * 基于通归求解 12 | * 13 | * @param number 色子个数 14 | * @param max 色子的最大值 15 | */ 16 | public static void printProbability(int number, int max) { 17 | if (number < 1 || max < 1) { 18 | return; 19 | } 20 | 21 | int maxSum = number * max; 22 | int[] probabilities = new int[maxSum - number + 1]; 23 | probability(number, probabilities, max); 24 | 25 | double total = 1; 26 | for (int i = 0; i < number; i++) { 27 | total *= max; 28 | } 29 | 30 | for (int i = number; i <= maxSum; i++) { 31 | double ratio = probabilities[i - number] / total; 32 | System.out.printf("%-8.4f", ratio); 33 | } 34 | 35 | System.out.println(); 36 | 37 | } 38 | 39 | /** 40 | * @param number 色子个数 41 | * @param probabilities 不同色子数出现次数的计数数组 42 | * @param max 色子的最大值 43 | */ 44 | private static void probability(int number, int[] probabilities, int max) { 45 | for (int i = 1; i <= max; i++) { 46 | probability(number, number, i, probabilities, max); 47 | } 48 | } 49 | 50 | /** 51 | * @param original 总的色子数 52 | * @param current 当前处理的是第几个 53 | * @param sum 已经前面的色子数和 54 | * @param probabilities 不同色子数出现次数的计数数组 55 | * @param max 色子的最大值 56 | */ 57 | private static void probability(int original, int current, int sum, int[] probabilities, int max) { 58 | if (current == 1) { 59 | probabilities[sum - original]++; 60 | } else { 61 | for (int i = 1; i <= max; i++) { 62 | probability(original, current - 1, i + sum, probabilities, max); 63 | } 64 | } 65 | } 66 | 67 | /** 68 | * 基于循环求解 69 | * @param number 色子个数 70 | * @param max 色子的最大值 71 | */ 72 | public static void printProbability2(int number, int max) { 73 | if (number < 1 || max < 1) { 74 | return; 75 | } 76 | 77 | int[][] probabilities = new int[2][max * number + 1]; 78 | // 数据初始化 79 | for (int i = 0; i < max * number + 1; i++) { 80 | probabilities[0][i] = 0; 81 | probabilities[1][i] = 0; 82 | } 83 | 84 | // 标记当前要使用的是第0个数组还是第1个数组 85 | int flag = 0; 86 | 87 | // 抛出一个骰子时出现的各种情况 88 | for (int i = 1; i <= max; i++) { 89 | probabilities[flag][i] = 1; 90 | } 91 | 92 | // 抛出其它骰子 93 | for (int k = 2; k <= number; k++) { 94 | // 如果抛出了k个骰子,那么和为[0, k-1]的出现次数为0 95 | for (int i = 0; i < k; i++) { 96 | probabilities[1 - flag][i] = 0; 97 | } 98 | 99 | // 抛出k个骰子,所有和的可能 100 | for (int i = k; i <= max * k; i++) { 101 | probabilities[1 - flag][i] = 0; 102 | 103 | // 每个骰子的出现的所有可能的点数 104 | for (int j = 1; j <= i && j <= max; j++) { 105 | // 统计出和为i的点数出现的次数 106 | probabilities[1 - flag][i] += probabilities[flag][i - j]; 107 | } 108 | } 109 | 110 | flag = 1 - flag; 111 | } 112 | 113 | 114 | double total = 1; 115 | for (int i = 0; i < number; i++) { 116 | total *= max; 117 | } 118 | 119 | int maxSum = number * max; 120 | for (int i = number; i <= maxSum; i++) { 121 | double ratio = probabilities[flag][i] / total; 122 | System.out.printf("%-8.4f", ratio); 123 | } 124 | 125 | System.out.println(); 126 | } 127 | 128 | public static void main(String[] args) { 129 | test01(); 130 | test02(); 131 | } 132 | 133 | private static void test01() { 134 | printProbability(2, 4); 135 | } 136 | 137 | private static void test02() { 138 | printProbability2(2, 4); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /剑指offer/Test44.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * Author: 王俊超 7 | * Date: 2015-06-14 8 | * Time: 14:33 9 | * Declaration: All Rights Reserved !!! 10 | */ 11 | public class Test44 { 12 | /** 13 | * 题目:从扑克牌中随机抽5张牌,判断是不是一个顺子, 即这5张牌是不是连续的。 14 | * 2~10为数字本身, A为1。 J为11、Q为12、 为13。小王可以看成任意数字。 15 | * @param numbers 16 | * @return 17 | */ 18 | public static boolean isContinuous(int[] numbers) { 19 | if (numbers == null || numbers.length != 5) { 20 | return false; 21 | } 22 | 23 | // 对元素进行排序 24 | Arrays.sort(numbers); 25 | int numberOfZero = 0; 26 | int numberOfGap = 0; 27 | for (int i = 0; i < numbers.length && numbers[i] == 0; i++) { 28 | numberOfZero++; 29 | } 30 | 31 | // 第一个非0元素的位置 32 | int small = numberOfZero; 33 | int big = small + 1; 34 | 35 | while (big < numbers.length) { 36 | if (numbers[small] == numbers[big]) { 37 | return false; 38 | } 39 | 40 | numberOfGap += (numbers[big] - numbers[small] - 1); 41 | small = big; 42 | big++; 43 | } 44 | 45 | return numberOfGap <= numberOfZero; 46 | } 47 | 48 | 49 | public static void main(String[] args) { 50 | int[] numbers1 = {1, 3, 2, 5, 4}; 51 | System.out.println(isContinuous(numbers1)); 52 | int[] numbers2 = {1, 3, 2, 6, 4}; 53 | System.out.println(isContinuous(numbers2)); 54 | int[] numbers3 = {0, 3, 2, 6, 4}; 55 | System.out.println(isContinuous(numbers3)); 56 | int[] numbers4 = {0, 3, 1, 6, 4}; 57 | System.out.println(isContinuous(numbers4)); 58 | int[] numbers5 = {1, 3, 0, 5, 0}; 59 | System.out.println(isContinuous(numbers5)); 60 | int[] numbers6 = {1, 3, 0, 7, 0}; 61 | System.out.println(isContinuous(numbers6)); 62 | int[] numbers7 = {1, 0, 0, 5, 0}; 63 | System.out.println(isContinuous(numbers7)); 64 | int[] numbers8 = {1, 0, 0, 7, 0}; 65 | System.out.println(isContinuous(numbers8)); 66 | int[] numbers9 = {3, 0, 0, 0, 0}; 67 | System.out.println(isContinuous(numbers9)); 68 | int[] numbers10 = {0, 0, 0, 0, 0}; 69 | System.out.println(isContinuous(numbers10)); 70 | int[] numbers11 = {1, 0, 0, 1, 0}; 71 | System.out.println(isContinuous(numbers11)); 72 | 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /剑指offer/Test45.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | 6 | /** 7 | * Author: 王俊超 8 | * Date: 2015-06-14 9 | * Time: 14:57 10 | * Declaration: All Rights Reserved !!! 11 | */ 12 | public class Test45 { 13 | public static int lastRemaining(int n, int m) { 14 | if (n < 1 || m < 1) { 15 | return -1; 16 | } 17 | 18 | List list = new LinkedList<>(); 19 | for (int i = 0; i < n; i++) { 20 | list.add(i); 21 | } 22 | 23 | // 要删除元素的位置 24 | int idx = 0; 25 | // 开始计数的位置 26 | int start = 0; 27 | 28 | while (list.size() > 1) { 29 | 30 | // 只要移动m-1次就可以移动到下一个要删除的元素上 31 | for (int i = 1; i < m; i++) { 32 | idx = (idx + 1) % list.size(); // 【A】 33 | } 34 | 35 | list.remove(idx); 36 | 37 | // 确保idx指向每一轮的第一个位置 38 | // 下面的可以不用,【A】已经可以保证其正确性了,可以分析n=6,m=6的第一次删除情况 39 | // if (idx == list.size()) { 40 | // idx = 0; 41 | // } 42 | } 43 | 44 | return list.get(0); 45 | } 46 | 47 | public static int lastRemaining2(int n, int m) { 48 | if (n < 1 || m < 1) { 49 | return -1; 50 | } 51 | 52 | int last = 0; 53 | for (int i = 2; i <=n ; i++) { 54 | last = (last + m)%i; 55 | } 56 | 57 | return last; 58 | } 59 | 60 | public static void main(String[] args) { 61 | test01(); 62 | System.out.println(); 63 | test02(); 64 | } 65 | 66 | private static void test01() { 67 | System.out.println(lastRemaining(5, 3)); // 最后余下3 68 | System.out.println(lastRemaining(5, 2)); // 最后余下2 69 | System.out.println(lastRemaining(6, 7)); // 最后余下4 70 | System.out.println(lastRemaining(6, 6)); // 最后余下3 71 | System.out.println(lastRemaining(0, 0)); // 最后余下-1 72 | } 73 | 74 | private static void test02() { 75 | System.out.println(lastRemaining2(5, 3)); // 最后余下3 76 | System.out.println(lastRemaining2(5, 2)); // 最后余下2 77 | System.out.println(lastRemaining2(6, 7)); // 最后余下4 78 | System.out.println(lastRemaining2(6, 6)); // 最后余下3 79 | System.out.println(lastRemaining2(0, 0)); // 最后余下-1 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /剑指offer/Test47.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-14 6 | * Time: 16:07 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test47 { 10 | public static int add(int x, int y) { 11 | int sum; 12 | int carry; 13 | 14 | do { 15 | sum = x ^ y; //1。不进位加法 16 | // x&y的某一位是1说明,它是它的前一位的进位,所以向左移动一位 17 | carry = (x & y) << 1; //2。只有1&1才需要进位,等于0不需要进位 18 | x = sum; 19 | y = carry; 20 | } while (y != 0); 21 | 22 | return x; 23 | } 24 | 25 | // 1+1=2: 26 | // sum = 1^1= 0; 27 | // carry = (1&1)<<1 = 10; 28 | // carry != 0; 29 | // x=sum=0, y=carry=10; 30 | // sum = 0 ^ 10 = 10; 31 | // carry = (0&10)<<1=0; 32 | // carry == 0; 33 | public static void main(String[] args) { 34 | System.out.println(add(1, 2) + ", " + (1 + 2)); 35 | System.out.println(add(13, 34)+ ", " + (13 + 34)); 36 | System.out.println(add(19, 85)+ ", " + (19 + 95)); 37 | System.out.println(add(865, 245)+ ", " + (865 + 245)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /剑指offer/Test49.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-14 6 | * Time: 16:26 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test49 { 10 | 11 | /** 12 | * 题目:实现一个函数stringToInt,实现把字符串转换成整数这个功能, 13 | * 不能使用atoi或者其他类似的库函数。 14 | * 15 | * @param num 16 | * @return 17 | */ 18 | public static int stringToInt(String num) { 19 | 20 | if (num == null || num.length() < 1) { 21 | throw new NumberFormatException(num); 22 | } 23 | 24 | char first = num.charAt(0); 25 | if (first == '-') { 26 | return parseString(num, 1, false); 27 | } else if (first == '+') { 28 | return parseString(num, 1, true); 29 | } else if (first <= '9' && first >= '0') { 30 | return parseString(num, 0, true); 31 | } else { 32 | throw new NumberFormatException(num); 33 | } 34 | } 35 | 36 | /** 37 | * 判断字符是否是数字 38 | * 39 | * @param c 字符 40 | * @return true是,false否 41 | */ 42 | private static boolean isDigit(char c) { 43 | return c >= '0' && c <= '9'; 44 | } 45 | 46 | /** 47 | * 对字符串进行解析 48 | * 49 | * @param num 数字串 50 | * @param index 开始解析的索引 51 | * @param positive 是正数还是负数 52 | * @return 返回结果 53 | */ 54 | private static int parseString(String num, int index, boolean positive) { 55 | 56 | if (index >= num.length()) { 57 | throw new NumberFormatException(num); 58 | } 59 | 60 | int result; 61 | long tmp = 0; 62 | while (index < num.length() && isDigit(num.charAt(index))) { 63 | tmp = tmp * 10 + num.charAt(index) - '0'; 64 | // 保证求的得的值不超出整数的最大绝对值 65 | if (tmp > 0x8000_0000L) { 66 | throw new NumberFormatException(num); 67 | } 68 | index++; 69 | } 70 | 71 | if (positive) { 72 | if (tmp >= 0x8000_0000L) { 73 | throw new NumberFormatException(num); 74 | } else { 75 | result = (int) tmp; 76 | } 77 | } else { 78 | if (tmp == 0x8000_0000L) { 79 | result = 0x8000_0000; 80 | } else { 81 | result = (int) -tmp; 82 | } 83 | } 84 | 85 | return result; 86 | } 87 | 88 | public static void main(String[] args) { 89 | // System.out.println(Integer.parseInt(Integer.MIN_VALUE + "")); 90 | // System.out.println(0x8000_0000L); 91 | // System.out.println(stringToInt("")); 92 | System.out.println(stringToInt("123")); 93 | System.out.println(stringToInt("+123")); 94 | System.out.println(stringToInt("-123")); 95 | System.out.println(stringToInt("1a123")); 96 | System.out.println(stringToInt("+2147483647")); 97 | System.out.println(stringToInt("-2147483647")); 98 | System.out.println(stringToInt("+2147483648")); 99 | System.out.println(stringToInt("-2147483648")); 100 | // System.out.println(stringToInt("+2147483649")); 101 | // System.out.println(stringToInt("-2147483649")); 102 | // System.out.println(stringToInt("+")); 103 | // System.out.println(stringToInt("-")); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /剑指offer/Test50.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.Iterator; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | /** 8 | * Author: 王俊超 9 | * Date: 2015-06-14 10 | * Time: 17:06 11 | * Declaration: All Rights Reserved !!! 12 | */ 13 | public class Test50 { 14 | /** 15 | * 树的结点定义 16 | */ 17 | private static class TreeNode { 18 | int val; 19 | 20 | List children = new LinkedList<>(); 21 | 22 | 23 | public TreeNode() { 24 | } 25 | 26 | public TreeNode(int val) { 27 | this.val = val; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return val + ""; 33 | } 34 | } 35 | 36 | /** 37 | * 找结点的路径 38 | * 39 | * @param root 根结点 40 | * @param target 目标结点 41 | * @param path 从根结点到目标结点的路径 42 | */ 43 | public static void getNodePath(TreeNode root, TreeNode target, List path) { 44 | if (root == null) { 45 | return; 46 | } 47 | 48 | // 添加当前结点 49 | path.add(root); 50 | 51 | List children = root.children; 52 | // 处理子结点 53 | for (TreeNode node : children) { 54 | 55 | if (node == target) { 56 | path.add(node); 57 | return; 58 | } else { 59 | getNodePath(node, target, path); 60 | } 61 | } 62 | 63 | // 现场还原 64 | path.remove(path.size() - 1); 65 | } 66 | 67 | /** 68 | * 找两个路径中的最后一个共同的结点 69 | * 70 | * @param p1 路径1 71 | * @param p2 路径2 72 | * @return 共同的结点,没有返回null 73 | */ 74 | public static TreeNode getLastCommonNode(List p1, List p2) { 75 | Iterator ite1 = p1.iterator(); 76 | Iterator ite2 = p2.iterator(); 77 | TreeNode last = null; 78 | 79 | while (ite1.hasNext() && ite2.hasNext()) { 80 | TreeNode tmp = ite1.next(); 81 | if (tmp == ite2.next()) { 82 | last = tmp; 83 | } 84 | } 85 | 86 | return last; 87 | 88 | } 89 | 90 | /** 91 | * 找树中两个结点的最低公共祖先 92 | * @param root 树的根结点 93 | * @param p1 结点1 94 | * @param p2 结点2 95 | * @return 公共结点,没有返回null 96 | */ 97 | public static TreeNode getLastCommonParent(TreeNode root, TreeNode p1, TreeNode p2) { 98 | if (root == null || p1 == null || p2 == null) { 99 | return null; 100 | } 101 | List path1 = new LinkedList<>(); 102 | getNodePath(root, p1, path1); 103 | List path2 = new LinkedList<>(); 104 | getNodePath(root, p2, path2); 105 | 106 | return getLastCommonNode(path1, path2); 107 | } 108 | 109 | public static void main(String[] args) { 110 | test01(); 111 | System.out.println("=========="); 112 | test02(); 113 | System.out.println("=========="); 114 | test03(); 115 | } 116 | 117 | 118 | // 形状普通的树 119 | // 1 120 | // / \ 121 | // 2 3 122 | // / \ 123 | // 4 5 124 | // / \ / | \ 125 | // 6 7 8 9 10 126 | public static void test01() { 127 | TreeNode n1 = new TreeNode(1); 128 | TreeNode n2 = new TreeNode(2); 129 | TreeNode n3 = new TreeNode(3); 130 | TreeNode n4 = new TreeNode(4); 131 | TreeNode n5 = new TreeNode(5); 132 | TreeNode n6 = new TreeNode(6); 133 | TreeNode n7 = new TreeNode(7); 134 | TreeNode n8 = new TreeNode(8); 135 | TreeNode n9 = new TreeNode(9); 136 | TreeNode n10 = new TreeNode(10); 137 | 138 | n1.children.add(n2); 139 | n1.children.add(n3); 140 | 141 | n2.children.add(n4); 142 | 143 | n4.children.add(n6); 144 | n4.children.add(n7); 145 | 146 | n3.children.add(n5); 147 | 148 | n5.children.add(n8); 149 | n5.children.add(n9); 150 | n5.children.add(n10); 151 | 152 | System.out.println(getLastCommonParent(n1, n6, n8)); 153 | } 154 | 155 | // 树退化成一个链表 156 | // 1 157 | // / 158 | // 2 159 | // / 160 | // 3 161 | // / 162 | // 4 163 | // / 164 | // 5 165 | private static void test02() { 166 | TreeNode n1 = new TreeNode(1); 167 | TreeNode n2 = new TreeNode(2); 168 | TreeNode n3 = new TreeNode(3); 169 | TreeNode n4 = new TreeNode(4); 170 | TreeNode n5 = new TreeNode(5); 171 | 172 | n1.children.add(n2); 173 | n2.children.add(n3); 174 | n3.children.add(n4); 175 | n4.children.add(n5); 176 | 177 | System.out.println(getLastCommonParent(n1, n4, n5)); 178 | } 179 | 180 | // 树退化成一个链表,一个结点不在树中 181 | // 1 182 | // / 183 | // 2 184 | // / 185 | // 3 186 | // / 187 | // 4 188 | // / 189 | // 5 190 | private static void test03() { 191 | TreeNode n1 = new TreeNode(1); 192 | TreeNode n2 = new TreeNode(2); 193 | TreeNode n3 = new TreeNode(3); 194 | TreeNode n4 = new TreeNode(4); 195 | TreeNode n5 = new TreeNode(5); 196 | TreeNode n6 = new TreeNode(6); 197 | 198 | n1.children.add(n2); 199 | n2.children.add(n3); 200 | n3.children.add(n4); 201 | n4.children.add(n5); 202 | 203 | System.out.println(getLastCommonParent(n1, n5, n6)); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /剑指offer/Test51.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-15 6 | * Time: 08:36 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test51 { 10 | /** 11 | * 题目:在一个长度为n的数组里的所有数字都在0到n-1的范围内。数组中某些数字是重复的, 12 | * 但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。 13 | * 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者。 14 | * 15 | * @param number 16 | * @return 17 | */ 18 | public static int duplicate(int[] number) { 19 | if (number == null || number.length < 1) { 20 | return -1; 21 | } 22 | 23 | // 判断输入的是否在[0, number.length-1]之间 24 | for (int i : number) { 25 | if (i < 0 || i >= number.length) { 26 | return -1; 27 | } 28 | } 29 | 30 | for (int i = 0; i < number.length; i++) { 31 | // 当number[i]与i不相同的时候一直交换 32 | while (number[i] != i) { 33 | // 如果i位置与number[i]位置的数字相同,说明有重复数字 34 | if (number[i] == number[number[i]]) { 35 | return number[i]; 36 | } 37 | // 如果不同就交换 38 | else { 39 | swap(number, i, number[i]); 40 | } 41 | } 42 | } 43 | return -1; 44 | } 45 | 46 | private static void swap(int[] data, int x, int y) { 47 | int tmp = data[x]; 48 | data[x] = data[y]; 49 | data[y] = tmp; 50 | } 51 | 52 | public static void main(String[] args) { 53 | int[] numbers1 = {2, 1, 3, 1, 4}; 54 | System.out.println(duplicate(numbers1)); 55 | 56 | int[] numbers2 = {2, 4, 3, 1, 4}; 57 | System.out.println(duplicate(numbers2)); 58 | 59 | int[] numbers3 = {2, 4, 2, 1, 4}; 60 | System.out.println(duplicate(numbers3)); 61 | 62 | int[] numbers4 = {2, 1, 3, 0, 4}; 63 | System.out.println(duplicate(numbers4)); 64 | 65 | int[] numbers5 = {2, 1, 3, 5, 4}; 66 | System.out.println(duplicate(numbers5)); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /剑指offer/Test52.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * Author: 王俊超 7 | * Date: 2015-06-15 8 | * Time: 08:59 9 | * Declaration: All Rights Reserved !!! 10 | */ 11 | public class Test52 { 12 | public static double[] multiply(double[] data) { 13 | if (data == null || data.length < 2) { 14 | return null; 15 | } 16 | 17 | double[] result = new double[data.length]; 18 | 19 | // result[0]取1 20 | result[0] = 1; 21 | for (int i = 1; i < data.length; i++) { 22 | // 第一步每个result[i]都等于于data[0]*data[1]...data[i-1] 23 | // 当i=n-1时,此时result[n-1]的结果已经计算出来了【A】 24 | result[i] = result[i -1] * data[i - 1]; 25 | } 26 | 27 | // tmp保存data[n-1]*data[n-2]...data[i+1]的结果 28 | double tmp = 1; 29 | // 第二步求data[n-1]*data[n-2]...data[i+1] 30 | // 【A】result[n-1]的结果已经计算出来,所以从data.length-2开始操作 31 | for (int i = data.length - 2; i >= 0; i--) { 32 | tmp *= data[i + 1]; 33 | result[i] *= tmp; 34 | } 35 | 36 | return result; 37 | } 38 | 39 | public static void main(String[] args) { 40 | double[] array1 = {1, 2, 3, 4, 5}; 41 | System.out.println(Arrays.toString(multiply(array1))); // double expected[] = {120, 60, 40, 30, 24}; 42 | double[] array2 = {1, 2, 0, 4, 5}; 43 | System.out.println(Arrays.toString(multiply(array2))); // double expected[] = {0, 0, 40, 0, 0}; 44 | double[] array3 = {1, 2, 0, 4, 0}; 45 | System.out.println(Arrays.toString(multiply(array3))); // double expected[] = {0, 0, 0, 0, 0}; 46 | double[] array4 = {1, -2, 3, -4, 5}; 47 | System.out.println(Arrays.toString(multiply(array4))); // double expected[] = {120, -60, 40, -30, 24}; 48 | double[] array5 = {1, -2}; 49 | System.out.println(Arrays.toString(multiply(array5))); // double expected[] = {-2, 1}; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /剑指offer/Test53.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-15 6 | * Time: 09:19 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test53 { 10 | 11 | /** 12 | * 题目:请实现一个函数用来匹配包含‘.’和‘*’的正则表达式。模式中的字符'.'表示任意一个字符, 13 | * 而‘*’表示它前面的字符可以出现任意次(含0次)。本题中,匹配是指字符串的所有字符匹配整个模式。 14 | * 15 | * @param input 16 | * @param pattern 17 | * @return 18 | */ 19 | public static boolean match(String input, String pattern) { 20 | if (input == null || pattern == null) { 21 | return false; 22 | } 23 | 24 | return matchCore(input, 0, pattern, 0); 25 | } 26 | 27 | private static boolean matchCore(String input, int i, String pattern, int p) { 28 | 29 | // 匹配串和模式串都到达尾,说明成功匹配 30 | if (i >= input.length() && p >= pattern.length()) { 31 | return true; 32 | } 33 | 34 | // 只有模式串到达结尾,说明匹配失败 35 | if (i != input.length() && p >= pattern.length()) { 36 | return false; 37 | } 38 | 39 | // 模式串未结束,匹配串有可能结束有可能未结束 40 | 41 | // p位置的下一个字符中为*号 42 | if (p + 1 < pattern.length() && pattern.charAt(p + 1) == '*') { 43 | 44 | // 匹配串已经结束 45 | if (i >= input.length()) { 46 | return matchCore(input, i, pattern, p + 2); 47 | } 48 | // 匹配串还没有结束 49 | else { 50 | if (pattern.charAt(p) == input.charAt(i) || pattern.charAt(p) == '.') { 51 | return 52 | // 匹配串向后移动一个位置,模式串向后移动两个位置 53 | matchCore(input, i + 1, pattern, p + 2) 54 | // 匹配串向后移动一个位置,模式串不移动 55 | || matchCore(input, i + 1, pattern, p) 56 | // 匹配串不移动,模式串向后移动两个位置 57 | || matchCore(input, i, pattern, p + 2); 58 | } else { 59 | return matchCore(input, i, pattern, p + 2); 60 | } 61 | } 62 | } 63 | 64 | // 65 | 66 | // 匹配串已经结束 67 | if (i >= input.length()) { 68 | return false; 69 | } 70 | // 匹配串还没有结束 71 | else { 72 | if (input.charAt(i) == pattern.charAt(p) || pattern.charAt(p) == '.') { 73 | return matchCore(input, i + 1, pattern, p + 1); 74 | } 75 | } 76 | 77 | 78 | return false; 79 | } 80 | 81 | public static void main(String[] args) { 82 | System.out.println(match("", "") + "[" + true + "]"); 83 | System.out.println(match("", ".*") + "[" + false + "]"); 84 | System.out.println(match("", ".") + "[" + false + "]"); 85 | System.out.println(match("", "c*") + "[" + true + "]"); 86 | System.out.println(); 87 | 88 | System.out.println(match("a", ".*") + "[" + true + "]"); 89 | System.out.println(match("a", "a.") + "[" + false + "]"); 90 | System.out.println(match("a", "") + "[" + false + "]"); 91 | System.out.println(match("a", ".") + "[" + true + "]"); 92 | System.out.println(match("a", "ab*") + "[" + true + "]"); 93 | System.out.println(match("a", "ab*a") + "[" + false + "]"); 94 | System.out.println(); 95 | 96 | System.out.println(match("aa", "aa") + "[" + true + "]"); 97 | System.out.println(match("aa", "a*") + "[" + true + "]"); 98 | System.out.println(match("aa", ".*") + "[" + true + "]"); 99 | System.out.println(match("aa", ".") + "[" + false + "]"); 100 | System.out.println(); 101 | 102 | System.out.println(match("ab", ".*") + "[" + true + "]"); 103 | System.out.println(match("ab", ".*") + "[" + true + "]"); 104 | System.out.println(); 105 | 106 | System.out.println(match("aaa", "aa*") + "[" + true + "]"); 107 | System.out.println(match("aaa", "aa.a") + "[" + false + "]"); 108 | System.out.println(match("aaa", "a.a") + "[" + true + "]"); 109 | System.out.println(match("aaa", ".a") + "[" + false + "]"); 110 | System.out.println(match("aaa", "a*a") + "[" + true + "]"); 111 | System.out.println(match("aaa", "ab*a") + "[" + false + "]"); 112 | System.out.println(match("aaa", "ab*ac*a") + "[" + true + "]"); 113 | System.out.println(match("aaa", "ab*a*c*a") + "[" + true + "]"); 114 | System.out.println(match("aaa", ".*") + "[" + true + "]"); 115 | System.out.println(); 116 | 117 | System.out.println(match("aab", "c*a*b") + "[" + true + "]"); 118 | System.out.println(); 119 | 120 | System.out.println(match("aaca", "ab*a*c*a") + "[" + true + "]"); 121 | System.out.println(match("aaba", "ab*a*c*a") + "[" + false + "]"); 122 | System.out.println(match("bbbba", ".*a*a") + "[" + true + "]"); 123 | System.out.println(match("bcbbabab", ".*a*a") + "[" + false + "]"); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /剑指offer/Test54.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-15 6 | * Time: 13:42 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test54 { 10 | /** 11 | * 题目:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。 12 | * 13 | * @param string 14 | * @return 15 | */ 16 | public static boolean isNumeric(String string) { 17 | if (string == null || string.length() < 1) { 18 | return false; 19 | } 20 | int index = 0; 21 | 22 | if (string.charAt(index) == '+' || string.charAt(index) == '-') { 23 | index++; 24 | } 25 | 26 | // 已经到达字符串的末尾了 27 | if (index >= string.length()) { 28 | return false; 29 | } 30 | 31 | boolean numeric = true; 32 | index = scanDigits(string, index); 33 | // 还未到字符串的末尾 34 | if (index < string.length()) { 35 | // 如果是小数点 36 | if (string.charAt(index) == '.') { 37 | // 移动到下一个位置 38 | index++; 39 | index = scanDigits(string, index); 40 | 41 | // 已经到了字符串的末尾了 42 | if (index >= string.length()) { 43 | numeric = true; 44 | } 45 | // 还未到字符串结束位置 46 | else if (index < string.length() && (string.charAt(index) == 'e' || string.charAt(index) == 'E')) { 47 | numeric = isExponential(string, index); 48 | } else { 49 | numeric = false; 50 | } 51 | } 52 | // 如果是指数标识 53 | else if (string.charAt(index) == 'e' || string.charAt(index) == 'E') { 54 | numeric = isExponential(string, index); 55 | } else { 56 | numeric = false; 57 | } 58 | 59 | return numeric; 60 | } 61 | // 已经到了字符串的末尾了,说明其没有指数部分 62 | else { 63 | return true; 64 | } 65 | 66 | } 67 | 68 | /** 69 | * 判断是否是科学计数法的结尾部分,如E5,e5,E+5,e-5,e(E)后面接整数 70 | * 71 | * @param string 字符串 72 | * @param index 开始匹配的位置 73 | * @return 匹配的结果 74 | */ 75 | private static boolean isExponential(String string, int index) { 76 | 77 | if (index >= string.length() || (string.charAt(index) != 'e' && string.charAt(index) != 'E')) { 78 | return false; 79 | } 80 | 81 | // 移动到下一个要处理的位置 82 | index++; 83 | 84 | // 到达字符串的末尾,就返回false 85 | if (index >= string.length()) { 86 | return false; 87 | } 88 | 89 | if (string.charAt(index) == '+' || string.charAt(index) == '-') { 90 | index++; 91 | } 92 | 93 | // 到达字符串的末尾,就返回false 94 | if (index >= string.length()) { 95 | return false; 96 | } 97 | 98 | index = scanDigits(string, index); 99 | 100 | // 如果已经处理到了的数字的末尾就认为是正确的指数 101 | return index >= string.length(); 102 | } 103 | 104 | /** 105 | * 扫描字符串部分的数字部分 106 | * 107 | * @param string 字符串 108 | * @param index 开始扫描的位置 109 | * @return 从扫描位置开始第一个数字字符的位置 110 | */ 111 | private static int scanDigits(String string, int index) { 112 | while (index < string.length() && string.charAt(index) >= '0' && string.charAt(index) <= '9') { 113 | index++; 114 | } 115 | return index; 116 | } 117 | 118 | public static void main(String[] args) { 119 | System.out.println(isNumeric("100") + "[" + true + "]"); 120 | System.out.println(isNumeric("123.45e+6") + "[" + true + "]"); 121 | System.out.println(isNumeric("+500") + "[" + true + "]"); 122 | System.out.println(isNumeric("5e2") + "[" + true + "]"); 123 | System.out.println(isNumeric("3.1416") + "[" + true + "]"); 124 | System.out.println(isNumeric("600.") + "[" + true + "]"); 125 | System.out.println(isNumeric("-.123") + "[" + true + "]"); 126 | System.out.println(isNumeric("-1E-16") + "[" + true + "]"); 127 | System.out.println(isNumeric("100") + "[" + true + "]"); 128 | System.out.println(isNumeric("1.79769313486232E+308") + "[" + true + "]"); 129 | System.out.println(); 130 | 131 | System.out.println(isNumeric("12e") + "[" + false + "]"); 132 | System.out.println(isNumeric("1a3.14") + "[" + false + "]"); 133 | System.out.println(isNumeric("1+23") + "[" + false + "]"); 134 | System.out.println(isNumeric("1.2.3") + "[" + false + "]"); 135 | System.out.println(isNumeric("+-5") + "[" + false + "]"); 136 | System.out.println(isNumeric("12e+5.4") + "[" + false + "]"); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /剑指offer/Test55.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-15 6 | * Time: 14:54 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test55 { 10 | /** 11 | * 题目:请实现一个函数用来找出字符流中第一个只出现一次的字符。 12 | */ 13 | private static class CharStatistics { 14 | // 出现一次的标识 15 | private int index = 0; 16 | private int[] occurrence = new int[256]; 17 | 18 | public CharStatistics() { 19 | for (int i = 0; i < occurrence.length; i++) { 20 | occurrence[i] = -1; 21 | } 22 | } 23 | 24 | private void insert(char ch) { 25 | if (ch > 255) { 26 | throw new IllegalArgumentException( ch + "must be a ASCII char"); 27 | } 28 | 29 | // 只出现一次 30 | if (occurrence[ch] == -1) { 31 | occurrence[ch] = index; 32 | } else { 33 | // 出现了两次 34 | occurrence[ch] = -2; 35 | } 36 | 37 | index++; 38 | } 39 | 40 | public char firstAppearingOnce(String data) { 41 | if (data == null) { 42 | throw new IllegalArgumentException(data); 43 | } 44 | 45 | for (int i = 0; i < data.length(); i++) { 46 | insert(data.charAt(i)); 47 | } 48 | char ch = '\0'; 49 | // 用于记录最小的索引,对应的就是第一个不重复的数字 50 | int minIndex = Integer.MAX_VALUE; 51 | for (int i = 0; i < occurrence.length; i++) { 52 | if (occurrence[i] >= 0 && occurrence[i] < minIndex) { 53 | ch = (char) i; 54 | minIndex = occurrence[i]; 55 | } 56 | } 57 | 58 | return ch; 59 | } 60 | } 61 | 62 | public static void main(String[] args) { 63 | System.out.println(new CharStatistics().firstAppearingOnce("")); // '\0' 64 | System.out.println(new CharStatistics().firstAppearingOnce("g")); // 'g' 65 | System.out.println(new CharStatistics().firstAppearingOnce("go")); // 'g' 66 | System.out.println(new CharStatistics().firstAppearingOnce("goo")); // 'g' 67 | System.out.println(new CharStatistics().firstAppearingOnce("goog")); // '\0' 68 | System.out.println(new CharStatistics().firstAppearingOnce("googl")); // l 69 | System.out.println(new CharStatistics().firstAppearingOnce("google")); // l 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /剑指offer/Test56.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-16 6 | * Time: 08:49 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test56 { 10 | private static class ListNode { 11 | private int val; 12 | private ListNode next; 13 | 14 | public ListNode() { 15 | } 16 | 17 | public ListNode(int val) { 18 | this.val = val; 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | return val +""; 24 | } 25 | } 26 | 27 | public static ListNode meetingNode(ListNode head) { 28 | 29 | ListNode fast = head; 30 | ListNode slow = head; 31 | 32 | while (fast != null && fast.next != null) { 33 | fast = fast.next.next; 34 | slow = slow.next; 35 | if (fast == slow) { 36 | break; 37 | } 38 | } 39 | 40 | // 链表中没有环 41 | if (fast == null || fast.next == null) { 42 | return null; 43 | } 44 | 45 | // fast重新指向第一个结点 46 | fast = head; 47 | 48 | while (fast != slow) { 49 | fast = fast.next; 50 | slow = slow.next; 51 | } 52 | 53 | return fast; 54 | } 55 | 56 | public static void main(String[] args) { 57 | test01(); 58 | test02(); 59 | test03(); 60 | } 61 | 62 | // 1->2->3->4->5->6 63 | private static void test01() { 64 | ListNode n1 = new ListNode(1); 65 | ListNode n2 = new ListNode(2); 66 | ListNode n3 = new ListNode(3); 67 | ListNode n4 = new ListNode(4); 68 | ListNode n5 = new ListNode(5); 69 | ListNode n6 = new ListNode(6); 70 | 71 | n1.next = n2; 72 | n2.next = n3; 73 | n3.next = n4; 74 | n4.next = n5; 75 | n5.next = n6; 76 | 77 | System.out.println(meetingNode(n1)); 78 | } 79 | 80 | // 1->2->3->4->5->6 81 | // ^ | 82 | // | | 83 | // +--------+ 84 | private static void test02() { 85 | ListNode n1 = new ListNode(1); 86 | ListNode n2 = new ListNode(2); 87 | ListNode n3 = new ListNode(3); 88 | ListNode n4 = new ListNode(4); 89 | ListNode n5 = new ListNode(5); 90 | ListNode n6 = new ListNode(6); 91 | 92 | n1.next = n2; 93 | n2.next = n3; 94 | n3.next = n4; 95 | n4.next = n5; 96 | n5.next = n6; 97 | n6.next = n3; 98 | 99 | System.out.println(meetingNode(n1)); 100 | } 101 | 102 | // 1->2->3->4->5->6 <-+ 103 | // | | 104 | // +---+ 105 | private static void test03() { 106 | ListNode n1 = new ListNode(1); 107 | ListNode n2 = new ListNode(2); 108 | ListNode n3 = new ListNode(3); 109 | ListNode n4 = new ListNode(4); 110 | ListNode n5 = new ListNode(5); 111 | ListNode n6 = new ListNode(6); 112 | 113 | n1.next = n2; 114 | n2.next = n3; 115 | n3.next = n4; 116 | n4.next = n5; 117 | n5.next = n6; 118 | n6.next = n6; 119 | 120 | System.out.println(meetingNode(n1)); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /剑指offer/Test58.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-16 6 | * Time: 10:31 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test58 { 10 | private static class BinaryTreeNode { 11 | private int val; 12 | private BinaryTreeNode left; 13 | private BinaryTreeNode right; 14 | private BinaryTreeNode parent; 15 | 16 | public BinaryTreeNode() { 17 | } 18 | 19 | public BinaryTreeNode(int val) { 20 | this.val = val; 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return val + ""; 26 | } 27 | } 28 | 29 | public static BinaryTreeNode getNext(BinaryTreeNode node) { 30 | if (node == null) { 31 | return null; 32 | } 33 | 34 | // 保存要查找的下一个节点 35 | BinaryTreeNode target = null; 36 | 37 | if (node.right != null) { 38 | target = node.right; 39 | while (target.left != null) { 40 | target = target.left; 41 | } 42 | 43 | return target; 44 | } else if (node.parent != null){ 45 | target = node.parent; 46 | BinaryTreeNode cur = node; 47 | // 如果父新结点不为空,并且,子结点不是父结点的左孩子 48 | while (target != null && target.left != cur) { 49 | cur = target; 50 | target = target.parent; 51 | 52 | } 53 | 54 | return target; 55 | } 56 | 57 | return null; 58 | } 59 | 60 | private static void assemble(BinaryTreeNode node, 61 | BinaryTreeNode left, 62 | BinaryTreeNode right, 63 | BinaryTreeNode parent) { 64 | node.left = left; 65 | node.right = right; 66 | node.parent = parent; 67 | } 68 | public static void main(String[] args) { 69 | test01(); 70 | } 71 | 72 | // 1 73 | // 2 3 74 | // 4 5 6 7 75 | // 8 9 10 11 12 13 14 15 76 | public static void test01() { 77 | BinaryTreeNode n1 = new BinaryTreeNode(1); // 12 78 | BinaryTreeNode n2 = new BinaryTreeNode(2); // 10 79 | BinaryTreeNode n3 = new BinaryTreeNode(3); // 14 80 | BinaryTreeNode n4 = new BinaryTreeNode(4); // 9 81 | BinaryTreeNode n5 = new BinaryTreeNode(5); // 11 82 | BinaryTreeNode n6 = new BinaryTreeNode(6); // 13 83 | BinaryTreeNode n7 = new BinaryTreeNode(7); // 15 84 | BinaryTreeNode n8 = new BinaryTreeNode(8); // 4 85 | BinaryTreeNode n9 = new BinaryTreeNode(9); // 2 86 | BinaryTreeNode n10 = new BinaryTreeNode(10); // 5 87 | BinaryTreeNode n11 = new BinaryTreeNode(11); // 1 88 | BinaryTreeNode n12 = new BinaryTreeNode(12); // 6 89 | BinaryTreeNode n13 = new BinaryTreeNode(13); // 3 90 | BinaryTreeNode n14 = new BinaryTreeNode(14); // 7 91 | BinaryTreeNode n15 = new BinaryTreeNode(15); // null 92 | 93 | assemble(n1, n2, n3, null); 94 | assemble(n2, n4, n5, n1); 95 | assemble(n3, n6, n7, n1); 96 | assemble(n4, n8, n9, n2); 97 | assemble(n5, n10, n11, n2); 98 | assemble(n6, n12, n13, n3); 99 | assemble(n7, n14, n15, n3); 100 | assemble(n8, null, null, n4); 101 | assemble(n9, null, null, n4); 102 | assemble(n10, null, null, n5); 103 | assemble(n11, null, null, n5); 104 | assemble(n12, null, null, n6); 105 | assemble(n13, null, null, n6); 106 | assemble(n14, null, null, n7); 107 | assemble(n15, null, null, n7); 108 | 109 | System.out.println(getNext(n1)); 110 | System.out.println(getNext(n2)); 111 | System.out.println(getNext(n3)); 112 | System.out.println(getNext(n4)); 113 | System.out.println(getNext(n5)); 114 | System.out.println(getNext(n6)); 115 | System.out.println(getNext(n7)); 116 | System.out.println(getNext(n8)); 117 | System.out.println(getNext(n9)); 118 | System.out.println(getNext(n10)); 119 | System.out.println(getNext(n11)); 120 | System.out.println(getNext(n12)); 121 | System.out.println(getNext(n13)); 122 | System.out.println(getNext(n14)); 123 | System.out.println(getNext(n15)); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /剑指offer/Test59.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-16 6 | * Time: 11:08 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test59 { 10 | private static class BinaryTreeNode { 11 | private int val; 12 | private BinaryTreeNode left; 13 | private BinaryTreeNode right; 14 | 15 | public BinaryTreeNode() { 16 | } 17 | 18 | public BinaryTreeNode(int val) { 19 | this.val = val; 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return val + ""; 25 | } 26 | } 27 | 28 | public static boolean isSymmetrical(BinaryTreeNode root) { 29 | return isSymmetrical(root, root); 30 | } 31 | 32 | private static boolean isSymmetrical(BinaryTreeNode left, BinaryTreeNode right) { 33 | 34 | if (left == null && right == null) { 35 | return true; 36 | } 37 | 38 | if (left == null || right == null) { 39 | return false; 40 | } 41 | 42 | if (left.val != right.val ) { 43 | return false; 44 | } 45 | 46 | return isSymmetrical(left.left, right.right) && isSymmetrical(left.right, right.left); 47 | } 48 | 49 | public static void main(String[] args) { 50 | test01(); 51 | test02(); 52 | } 53 | 54 | private static void assemble(BinaryTreeNode node, 55 | BinaryTreeNode left, 56 | BinaryTreeNode right) { 57 | node.left = left; 58 | node.right = right; 59 | } 60 | 61 | // 1 62 | // 2 2 63 | // 4 6 6 4 64 | // 8 9 10 11 11 10 9 8 65 | public static void test01() { 66 | BinaryTreeNode n1 = new BinaryTreeNode(1); 67 | BinaryTreeNode n2 = new BinaryTreeNode(2); 68 | BinaryTreeNode n3 = new BinaryTreeNode(2); 69 | BinaryTreeNode n4 = new BinaryTreeNode(4); 70 | BinaryTreeNode n5 = new BinaryTreeNode(6); 71 | BinaryTreeNode n6 = new BinaryTreeNode(6); 72 | BinaryTreeNode n7 = new BinaryTreeNode(4); 73 | BinaryTreeNode n8 = new BinaryTreeNode(8); 74 | BinaryTreeNode n9 = new BinaryTreeNode(9); 75 | BinaryTreeNode n10 = new BinaryTreeNode(10); 76 | BinaryTreeNode n11 = new BinaryTreeNode(11); 77 | BinaryTreeNode n12 = new BinaryTreeNode(11); 78 | BinaryTreeNode n13 = new BinaryTreeNode(10); 79 | BinaryTreeNode n14 = new BinaryTreeNode(9); 80 | BinaryTreeNode n15 = new BinaryTreeNode(8); 81 | 82 | assemble(n1, n2, n3); 83 | assemble(n2, n4, n5); 84 | assemble(n3, n6, n7); 85 | assemble(n4, n8, n9); 86 | assemble(n5, n10, n11); 87 | assemble(n6, n12, n13); 88 | assemble(n7, n14, n15); 89 | assemble(n8, null, null); 90 | assemble(n9, null, null); 91 | assemble(n10, null, null); 92 | assemble(n11, null, null); 93 | assemble(n12, null, null); 94 | assemble(n13, null, null); 95 | assemble(n14, null, null); 96 | assemble(n15, null, null); 97 | 98 | System.out.println(isSymmetrical(n1)); 99 | 100 | } 101 | 102 | 103 | // 1 104 | // 2 2 105 | // 4 5 6 4 106 | // 8 9 10 11 11 10 9 8 107 | public static void test02() { 108 | BinaryTreeNode n1 = new BinaryTreeNode(1); 109 | BinaryTreeNode n2 = new BinaryTreeNode(2); 110 | BinaryTreeNode n3 = new BinaryTreeNode(2); 111 | BinaryTreeNode n4 = new BinaryTreeNode(4); 112 | BinaryTreeNode n5 = new BinaryTreeNode(5); 113 | BinaryTreeNode n6 = new BinaryTreeNode(6); 114 | BinaryTreeNode n7 = new BinaryTreeNode(4); 115 | BinaryTreeNode n8 = new BinaryTreeNode(8); 116 | BinaryTreeNode n9 = new BinaryTreeNode(9); 117 | BinaryTreeNode n10 = new BinaryTreeNode(10); 118 | BinaryTreeNode n11 = new BinaryTreeNode(11); 119 | BinaryTreeNode n12 = new BinaryTreeNode(11); 120 | BinaryTreeNode n13 = new BinaryTreeNode(10); 121 | BinaryTreeNode n14 = new BinaryTreeNode(9); 122 | BinaryTreeNode n15 = new BinaryTreeNode(8); 123 | 124 | assemble(n1, n2, n3); 125 | assemble(n2, n4, n5); 126 | assemble(n3, n6, n7); 127 | assemble(n4, n8, n9); 128 | assemble(n5, n10, n11); 129 | assemble(n6, n12, n13); 130 | assemble(n7, n14, n15); 131 | assemble(n8, null, null); 132 | assemble(n9, null, null); 133 | assemble(n10, null, null); 134 | assemble(n11, null, null); 135 | assemble(n12, null, null); 136 | assemble(n13, null, null); 137 | assemble(n14, null, null); 138 | assemble(n15, null, null); 139 | 140 | System.out.println(isSymmetrical(n1)); 141 | 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /剑指offer/Test60.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | 6 | /** 7 | * Author: 王俊超 8 | * Date: 2015-06-16 9 | * Time: 17:12 10 | * Declaration: All Rights Reserved !!! 11 | */ 12 | public class Test60 { 13 | private static class BinaryTreeNode { 14 | private int val; 15 | private BinaryTreeNode left; 16 | private BinaryTreeNode right; 17 | 18 | public BinaryTreeNode() { 19 | } 20 | 21 | public BinaryTreeNode(int val) { 22 | this.val = val; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return val + ""; 28 | } 29 | } 30 | 31 | /** 32 | * 题目:从上到下按层打印二叉树,同一层的结点按从左到右的顺序打印,每一层打印一行。 33 | * @param root 34 | */ 35 | public static void print(BinaryTreeNode root) { 36 | if (root == null) { 37 | return; 38 | } 39 | 40 | List list = new LinkedList<>(); 41 | BinaryTreeNode node; 42 | // 当前层的结点个数 43 | int current = 1; 44 | // 记录下一层的结点个数 45 | int next = 0; 46 | list.add(root); 47 | 48 | while (list.size() > 0) { 49 | node = list.remove(0); 50 | current--; 51 | System.out.printf("%-3d", node.val); 52 | 53 | if (node.left != null) { 54 | list.add(node.left); 55 | next++; 56 | } 57 | if (node.right != null) { 58 | list.add(node.right); 59 | next++; 60 | } 61 | 62 | if (current ==0) { 63 | System.out.println(); 64 | current = next; 65 | next = 0; 66 | } 67 | } 68 | } 69 | 70 | public static void main(String[] args) { 71 | BinaryTreeNode n1 = new BinaryTreeNode(1); 72 | BinaryTreeNode n2 = new BinaryTreeNode(2); 73 | BinaryTreeNode n3 = new BinaryTreeNode(3); 74 | BinaryTreeNode n4 = new BinaryTreeNode(4); 75 | BinaryTreeNode n5 = new BinaryTreeNode(5); 76 | BinaryTreeNode n6 = new BinaryTreeNode(6); 77 | BinaryTreeNode n7 = new BinaryTreeNode(7); 78 | BinaryTreeNode n8 = new BinaryTreeNode(8); 79 | BinaryTreeNode n9 = new BinaryTreeNode(9); 80 | 81 | n1.left = n2; 82 | n1.right = n3; 83 | n2.left = n4; 84 | n2.right = n5; 85 | n3.left = n6; 86 | n3.right = n7; 87 | n4.left = n8; 88 | n4.right = n9; 89 | 90 | print(n1); 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /剑指offer/Test61.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | 6 | /** 7 | * Author: 王俊超 8 | * Date: 2015-06-16 9 | * Time: 18:41 10 | * Declaration: All Rights Reserved !!! 11 | */ 12 | public class Test61 { 13 | private static class BinaryTreeNode { 14 | private int val; 15 | private BinaryTreeNode left; 16 | private BinaryTreeNode right; 17 | 18 | public BinaryTreeNode() { 19 | } 20 | 21 | public BinaryTreeNode(int val) { 22 | this.val = val; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return val + ""; 28 | } 29 | } 30 | 31 | public static void print(BinaryTreeNode root) { 32 | 33 | if (root == null) { 34 | return; 35 | } 36 | 37 | 38 | List current = new LinkedList<>(); 39 | List reverse = new LinkedList<>(); 40 | int flag = 0; 41 | BinaryTreeNode node; 42 | current.add(root); 43 | 44 | while (current.size() > 0) { 45 | 46 | // 从最后一个开始取 47 | node = current.remove(current.size() - 1); 48 | 49 | System.out.printf("%-3d", node.val); 50 | 51 | // 当前是从左往右打印的,那就按从左往右入栈 52 | if (flag == 0) { 53 | if (node.left != null) { 54 | reverse.add(node.left); 55 | } 56 | 57 | if (node.right != null) { 58 | reverse.add(node.right); 59 | } 60 | 61 | 62 | } 63 | // 当前是从右往左打印的,那就按从右往左入栈 64 | else { 65 | if (node.right != null) { 66 | reverse.add(node.right); 67 | } 68 | 69 | if (node.left != null) { 70 | reverse.add(node.left); 71 | } 72 | } 73 | 74 | if (current.size() == 0) { 75 | flag = 1 - flag; 76 | List tmp = current; 77 | current = reverse; 78 | reverse = tmp; 79 | System.out.println(); 80 | } 81 | } 82 | } 83 | 84 | public static void main(String[] args) { 85 | BinaryTreeNode n1 = new BinaryTreeNode(1); 86 | BinaryTreeNode n2 = new BinaryTreeNode(2); 87 | BinaryTreeNode n3 = new BinaryTreeNode(3); 88 | BinaryTreeNode n4 = new BinaryTreeNode(4); 89 | BinaryTreeNode n5 = new BinaryTreeNode(5); 90 | BinaryTreeNode n6 = new BinaryTreeNode(6); 91 | BinaryTreeNode n7 = new BinaryTreeNode(7); 92 | BinaryTreeNode n8 = new BinaryTreeNode(8); 93 | BinaryTreeNode n9 = new BinaryTreeNode(9); 94 | 95 | n1.left = n2; 96 | n1.right = n3; 97 | n2.left = n4; 98 | n2.right = n5; 99 | n3.left = n6; 100 | n3.right = n7; 101 | n4.left = n8; 102 | n4.right = n9; 103 | 104 | print(n1); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /剑指offer/Test62.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | 6 | /** 7 | * Author: 王俊超 8 | * Date: 2015-06-16 9 | * Time: 21:15 10 | * Declaration: All Rights Reserved !!! 11 | */ 12 | public class Test62 { 13 | private static class BinaryTreeNode { 14 | private int val; 15 | private BinaryTreeNode left; 16 | private BinaryTreeNode right; 17 | 18 | public BinaryTreeNode() { 19 | } 20 | 21 | public BinaryTreeNode(int val) { 22 | this.val = val; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return val + ""; 28 | } 29 | } 30 | 31 | public static void serialize(BinaryTreeNode root, List result) { 32 | 33 | List list = new LinkedList<>(); 34 | list.add(root); 35 | BinaryTreeNode node; 36 | while (list.size() > 0) { 37 | node = list.remove(0); 38 | if (node == null) { 39 | result.add(null); 40 | }else { 41 | result.add(node.val); 42 | list.add(node.left); 43 | list.add(node.right); 44 | } 45 | } 46 | } 47 | 48 | public static BinaryTreeNode deserialize(List result, int idx) { 49 | 50 | if (result.size() < 1 || idx < 0 || result.size() <= idx || result.get(idx) == null) { 51 | return null; 52 | } 53 | 54 | BinaryTreeNode root = new BinaryTreeNode(result.get(idx)); 55 | root.left = deserialize(result, idx * 2 + 1); 56 | root.right = deserialize(result, idx * 2 + 2); 57 | return root; 58 | 59 | } 60 | 61 | public static void main(String[] args) { 62 | test01(); 63 | } 64 | 65 | private static void test01() { 66 | BinaryTreeNode n1 = new BinaryTreeNode(1); 67 | BinaryTreeNode n2 = new BinaryTreeNode(2); 68 | BinaryTreeNode n3 = new BinaryTreeNode(3); 69 | BinaryTreeNode n4 = new BinaryTreeNode(4); 70 | BinaryTreeNode n5 = new BinaryTreeNode(5); 71 | BinaryTreeNode n6 = new BinaryTreeNode(6); 72 | BinaryTreeNode n7 = new BinaryTreeNode(7); 73 | BinaryTreeNode n8 = new BinaryTreeNode(8); 74 | BinaryTreeNode n9 = new BinaryTreeNode(9); 75 | 76 | n1.left = n2; 77 | n1.right = n3; 78 | n2.left = n4; 79 | n2.right = n5; 80 | n3.left = n6; 81 | n3.right = n7; 82 | n4.left = n8; 83 | n4.right = n9; 84 | 85 | List result = new LinkedList<>(); 86 | serialize(n1, result); 87 | System.out.println(result); 88 | System.out.println(); 89 | 90 | BinaryTreeNode root = deserialize(result, 0) ; 91 | print(root); 92 | 93 | } 94 | 95 | private static void print(BinaryTreeNode root) { 96 | if (root != null) { 97 | print(root.left); 98 | System.out.printf("%-3d", root.val); 99 | print(root.right); 100 | } 101 | } 102 | } 103 | 104 | -------------------------------------------------------------------------------- /剑指offer/Test63.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-16 6 | * Time: 21:39 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test63 { 10 | private static class BinaryTreeNode { 11 | private int val; 12 | private BinaryTreeNode left; 13 | private BinaryTreeNode right; 14 | 15 | public BinaryTreeNode() { 16 | } 17 | 18 | public BinaryTreeNode(int val) { 19 | this.val = val; 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return val + ""; 25 | } 26 | } 27 | 28 | public static BinaryTreeNode kthNode(BinaryTreeNode root, int k) { 29 | if (root == null || k < 1) { 30 | return null; 31 | } 32 | 33 | int[] tmp = {k}; 34 | return kthNodeCore(root, tmp); 35 | } 36 | 37 | private static BinaryTreeNode kthNodeCore(BinaryTreeNode root, int[] k) { 38 | BinaryTreeNode result = null; 39 | 40 | // 先成左子树中找 41 | if (root.left != null) { 42 | result = kthNodeCore(root.left, k); 43 | } 44 | 45 | // 如果在左子树中没有找到 46 | if (result == null) { 47 | // 说明当前的根结点是所要找的结点 48 | if(k[0] == 1) { 49 | result = root; 50 | } else { 51 | // 当前的根结点不是要找的结点,但是已经找过了,所以计数器减一 52 | k[0]--; 53 | } 54 | } 55 | 56 | // 根结点以及根结点的右子结点都没有找到,则找其右子树 57 | if (result == null && root.right != null) { 58 | result = kthNodeCore(root.right, k); 59 | } 60 | 61 | return result; 62 | } 63 | 64 | public static void main(String[] args) { 65 | BinaryTreeNode n1 = new BinaryTreeNode(1); 66 | BinaryTreeNode n2 = new BinaryTreeNode(2); 67 | BinaryTreeNode n3 = new BinaryTreeNode(3); 68 | BinaryTreeNode n4 = new BinaryTreeNode(4); 69 | BinaryTreeNode n5 = new BinaryTreeNode(5); 70 | BinaryTreeNode n6 = new BinaryTreeNode(6); 71 | BinaryTreeNode n7 = new BinaryTreeNode(7); 72 | BinaryTreeNode n8 = new BinaryTreeNode(8); 73 | BinaryTreeNode n9 = new BinaryTreeNode(9); 74 | 75 | n1.left = n2; 76 | n1.right = n3; 77 | n2.left = n4; 78 | n2.right = n5; 79 | n3.left = n6; 80 | n3.right = n7; 81 | n4.left = n8; 82 | n4.right = n9; 83 | 84 | print(n1); 85 | System.out.println(); 86 | 87 | for (int i = 0; i <= 10; i++) { 88 | System.out.printf(kthNode(n1, i) + ", "); 89 | } 90 | 91 | } 92 | 93 | /** 94 | * 中序遍历一棵树 95 | * @param root 96 | */ 97 | private static void print(BinaryTreeNode root) { 98 | if (root != null) { 99 | print(root.left); 100 | System.out.printf("%-3d", root.val); 101 | print(root.right); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /剑指offer/Test65.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | import java.util.Deque; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | /** 8 | * Author: 王俊超 9 | * Date: 2015-06-17 10 | * Time: 09:44 11 | * Declaration: All Rights Reserved !!! 12 | */ 13 | public class Test65 { 14 | private static List maxInWindows(List data, int size) { 15 | List windowMax = new LinkedList<>(); 16 | 17 | // 条件检查 18 | if (data == null || size < 1 || data.size() < 1) { 19 | return windowMax; 20 | } 21 | 22 | Deque idx = new LinkedList<>(); 23 | 24 | // 窗口还没有被填满时,找最大值的索引 25 | for (int i = 0; i < size && i < data.size(); i++) { 26 | // 如果索引对应的值比之前存储的索引值对应的值大或者相等,就删除之前存储的值 27 | while (!idx.isEmpty() && data.get(i) >= data.get(idx.getLast())) { 28 | idx.removeLast(); 29 | } 30 | 31 | // 添加索引 32 | idx.addLast(i); 33 | } 34 | 35 | // 窗口已经被填满了 36 | for (int i = size; i < data.size(); i++) { 37 | // 第一个窗口的最大值保存 38 | windowMax.add(data.get(idx.getFirst())); 39 | 40 | // 如果索引对应的值比之前存储的索引值对应的值大或者相等,就删除之前存储的值 41 | while (!idx.isEmpty() && data.get(i) >= data.get(idx.getLast())) { 42 | idx.removeLast(); 43 | } 44 | 45 | // 删除已经滑出窗口的数据对应的下标 46 | if (!idx.isEmpty() && idx.getFirst() <= (i - size)) { 47 | idx.removeFirst(); 48 | } 49 | 50 | // 可能的最大的下标索引入队 51 | idx.addLast(i); 52 | } 53 | 54 | // 最后一个窗口最大值入队 55 | windowMax.add(data.get(idx.getFirst())); 56 | 57 | return windowMax; 58 | 59 | } 60 | 61 | private static List arrayToCollection(int[] array) { 62 | List result = new LinkedList<>(); 63 | if (array != null) { 64 | for (int i : array) { 65 | result.add(i); 66 | } 67 | } 68 | 69 | return result; 70 | } 71 | 72 | public static void main(String[] args) { 73 | 74 | // expected {7}; 75 | List data1 = arrayToCollection(new int[]{1, 3, -1, -3, 5, 3, 6, 7}); 76 | System.out.println(data1 + "," + maxInWindows(data1, 10)); 77 | 78 | // expected {3, 3, 5, 5, 6, 7}; 79 | List data2 = arrayToCollection(new int[]{1, 3, -1, -3, 5, 3, 6, 7}); 80 | System.out.println(data2 + "," + maxInWindows(data2, 3)); 81 | 82 | // expected {7, 9, 11, 13, 15}; 83 | List data3 = arrayToCollection(new int[]{1, 3, 5, 7, 9, 11, 13, 15}); 84 | System.out.println(data3 + "," + maxInWindows(data3, 4)); 85 | 86 | // expected {16, 14, 12}; 87 | List data5 = arrayToCollection(new int[]{16, 14, 12, 10, 8, 6, 4}); 88 | System.out.println(data5 + "," + maxInWindows(data5, 5)); 89 | 90 | // expected {10, 14, 12, 11}; 91 | List data6 = arrayToCollection(new int[]{10, 14, 12, 11}); 92 | System.out.println(data6 + "," + maxInWindows(data6, 1)); 93 | 94 | // expected {14}; 95 | List data7 = arrayToCollection(new int[]{10, 14, 12, 11}); 96 | System.out.println(data7 + "," + maxInWindows(data7, 4)); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /剑指offer/Test66.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-17 6 | * Time: 10:53 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test66 { 10 | /** 11 | * 题目:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。 12 | * 路径可以从矩阵中任意一格开始,每一步可以在矩阵中间向左、右、上、下移动一格。 13 | * 如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。 14 | * 15 | * @param matrix 输入矩阵 16 | * @param rows 矩阵行数 17 | * @param cols 矩阵列数 18 | * @param str 要搜索的字符串 19 | * @return 是否找到 true是,false否 20 | */ 21 | public static boolean hasPath(char[] matrix, int rows, int cols, char[] str) { 22 | // 参数校验 23 | if (matrix == null || matrix.length != rows * cols || str == null || str.length < 1) { 24 | return false; 25 | } 26 | 27 | // 变量初始化 28 | boolean[] visited = new boolean[rows * cols]; 29 | for (int i = 0; i < visited.length; i++) { 30 | visited[i] = false; 31 | } 32 | 33 | // 记录结果的数组, 34 | int[] pathLength = {0}; 35 | // 以每一个点为起始进行搜索 36 | for (int i = 0; i < rows; i++) { 37 | for (int j = 0; j < cols; j++) { 38 | if (hasPathCore(matrix, rows, cols, str, visited, i, j, pathLength)) { 39 | return true; 40 | } 41 | } 42 | } 43 | 44 | return false; 45 | } 46 | 47 | /** 48 | * 回溯搜索算法 49 | * 50 | * @param matrix 输入矩阵 51 | * @param rows 矩阵行数 52 | * @param cols 矩阵列数 53 | * @param str 要搜索的字符串 54 | * @param visited 访问标记数组 55 | * @param row 当前处理的行号 56 | * @param col 当前处理的列号 57 | * @param pathLength 已经处理的str中字符个数 58 | * @return 是否找到 true是,false否 59 | */ 60 | private static boolean hasPathCore(char[] matrix, int rows, int cols, char[] str, boolean[] visited, 61 | int row, int col, int[] pathLength) { 62 | 63 | if (pathLength[0] == str.length) { 64 | return true; 65 | } 66 | 67 | boolean hasPath = false; 68 | 69 | // 判断位置是否合法 70 | if (row >= 0 && row < rows 71 | && col >= 0 && col < cols 72 | && matrix[row * cols + col] == str[pathLength[0]] 73 | && !visited[row * cols + col]) { 74 | 75 | visited[row * cols + col] = true; 76 | pathLength[0]++; 77 | 78 | // 按左上右下进行回溯 79 | hasPath = hasPathCore(matrix, rows, cols, str, visited, row, col - 1, pathLength) 80 | || hasPathCore(matrix, rows, cols, str, visited, row - 1, col, pathLength) 81 | || hasPathCore(matrix, rows, cols, str, visited, row, col + 1, pathLength) 82 | || hasPathCore(matrix, rows, cols, str, visited, row + 1, col, pathLength); 83 | 84 | if (!hasPath) { 85 | pathLength[0]--; 86 | visited[row * cols + col] = false; 87 | } 88 | 89 | } 90 | 91 | return hasPath; 92 | } 93 | 94 | public static void main(String[] args) { 95 | //ABCE //ABCCED 96 | //SFCS 97 | //ADEE 98 | System.out.println(hasPath("ABCESFCSADEE".toCharArray(), 3, 4, 99 | "ABCCED".toCharArray()) + "[true]");// true 100 | 101 | //ABCE //SEE 102 | //SFCS 103 | //ADEE 104 | System.out.println(hasPath("ABCESFCSADEE".toCharArray(), 3, 4, 105 | "SEE".toCharArray()) + "[true]");// true 106 | 107 | //ABCE //ABCB 108 | //SFCS 109 | //ADEE 110 | System.out.println(hasPath("ABCESFCSADEE".toCharArray(), 3, 4, 111 | "ABCB".toCharArray()) + "[false]");// false 112 | 113 | //ABCEHJIG //SLHECCEIDEJFGGFIE 114 | //SFCSLOPQ 115 | //ADEEMNOE 116 | //ADIDEJFM 117 | //VCEIFGGS 118 | System.out.println(hasPath("ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS".toCharArray(), 5, 8, 119 | "SLHECCEIDEJFGGFIE".toCharArray()) + "[true]");// true 120 | 121 | 122 | //ABCEHJIG //SGGFIECVAASABCEHJIGQEM 123 | //SFCSLOPQ // 124 | //ADEEMNOE 125 | //ADIDEJFM 126 | //VCEIFGGS 127 | System.out.println(hasPath("ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS".toCharArray(), 5, 8, 128 | "SGGFIECVAASABCEHJIGQEM".toCharArray()) + "[true]");// true 129 | 130 | //ABCEHJIG //SGGFIECVAASABCEEJIGOEM 131 | //SFCSLOPQ 132 | //ADEEMNOE 133 | //ADIDEJFM 134 | //VCEIFGGS 135 | System.out.println(hasPath("ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS".toCharArray(), 5, 8, 136 | "SGGFIECVAASABCEEJIGOEM".toCharArray()) + "[false]");// false 137 | 138 | 139 | //ABCEHJIG //SGGFIECVAASABCEHJIGQEMS 140 | //SFCSLOPQ 141 | //ADEEMNOE 142 | //ADIDEJFM 143 | //VCEIFGGS 144 | System.out.println(hasPath("ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS".toCharArray(), 5, 8, 145 | "SGGFIECVAASABCEHJIGQEMS".toCharArray()) + "[false]");// false 146 | 147 | //AAAA //AAAAAAAAAAAA 148 | //AAAA 149 | //AAAA 150 | System.out.println(hasPath("AAAAAAAAAAAA".toCharArray(), 3, 4, 151 | "AAAAAAAAAAAA".toCharArray()) + "[true]");// true 152 | 153 | //AAAA //AAAAAAAAAAAAA 154 | //AAAA 155 | //AAAA 156 | System.out.println(hasPath("AAAAAAAAAAAA".toCharArray(), 3, 4, 157 | "AAAAAAAAAAAAA".toCharArray()) + "[false]");// false 158 | 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /剑指offer/Test67.java: -------------------------------------------------------------------------------- 1 | package com.github.yuehsutong.剑指offer; 2 | 3 | /** 4 | * Author: 王俊超 5 | * Date: 2015-06-17 6 | * Time: 19:19 7 | * Declaration: All Rights Reserved !!! 8 | */ 9 | public class Test67 { 10 | /** 11 | * 题目:地上有个m行n列的方格。一个机器人从坐标(0,0)的格子开始移动, 12 | * 它每一次可以向左、右、上、下移动一格,但不能进入行坐标和列坐标的数 13 | * 位之和大于k的格子。例如,当k为18时,机器人能够进入方格(35,37), 14 | * 因为3+5+3+7=18.但它不能进入方格(35,38),因为3+5+3+8=19. 15 | * 请问该机器人能够达到多少格子? 16 | * 17 | * @param threshold 约束值 18 | * @param rows 方格的行数 19 | * @param cols 方格的列数 20 | * @return 最多可走的方格 21 | */ 22 | public static int movingCount(int threshold, int rows, int cols) { 23 | // 参数校验 24 | if (threshold < 0 || rows < 1 || cols < 1) { 25 | return 0; 26 | } 27 | 28 | // 变量初始化 29 | boolean[] visited = new boolean[rows * cols]; 30 | for (int i = 0; i < visited.length; i++) { 31 | visited[i] = false; 32 | } 33 | 34 | return movingCountCore(threshold, rows, cols, 0, 0, visited); 35 | } 36 | 37 | /** 38 | * 递归回溯方法 39 | * 40 | * @param threshold 约束值 41 | * @param rows 方格的行数 42 | * @param cols 方格的列数 43 | * @param row 当前处理的行号 44 | * @param col 当前处理的列号 45 | * @param visited 访问标记数组 46 | * @return 最多可走的方格 47 | */ 48 | private static int movingCountCore(int threshold, int rows, int cols, 49 | int row, int col, boolean[] visited) { 50 | 51 | int count = 0; 52 | 53 | if (check(threshold, rows, cols, row, col, visited)) { 54 | visited[row * cols + col] = true; 55 | count = 1 56 | + movingCountCore(threshold, rows, cols, row - 1, col, visited) 57 | + movingCountCore(threshold, rows, cols, row, col - 1, visited) 58 | + movingCountCore(threshold, rows, cols, row + 1, col, visited) 59 | + movingCountCore(threshold, rows, cols, row, col + 1, visited); 60 | } 61 | 62 | return count; 63 | } 64 | 65 | /** 66 | * 断机器人能否进入坐标为(row, col)的方格 67 | * 68 | * @param threshold 约束值 69 | * @param rows 方格的行数 70 | * @param cols 方格的列数 71 | * @param row 当前处理的行号 72 | * @param col 当前处理的列号 73 | * @param visited 访问标记数组 74 | * @return 是否可以进入,true是,false否 75 | */ 76 | private static boolean check(int threshold, int rows, int cols, 77 | int row, int col, boolean[] visited) { 78 | return col >= 0 && col < cols 79 | && row >= 0 && row < rows 80 | && !visited[row * cols + col] 81 | && (getDigitSum(col) + getDigitSum(row) <= threshold); 82 | } 83 | 84 | /** 85 | * 一个数字的数位之和 86 | * 87 | * @param number 数字 88 | * @return 数字的数位之和 89 | */ 90 | private static int getDigitSum(int number) { 91 | int result = 0; 92 | while (number > 0) { 93 | result += (number % 10); 94 | number /= 10; 95 | } 96 | 97 | return result; 98 | } 99 | 100 | public static void main(String[] args) { 101 | System.out.println(movingCount(5, 10, 10) + "[21]"); 102 | System.out.println(movingCount(15, 20, 20) + "[359]"); 103 | System.out.println(movingCount(10, 1, 100) + "[29]"); 104 | System.out.println(movingCount(10, 1, 10) + "[10]"); 105 | System.out.println(movingCount(15, 100, 1) + "[79]"); 106 | System.out.println(movingCount(15, 10, 1) + "[10]"); 107 | System.out.println(movingCount(5, 10, 10) + "[21]"); 108 | System.out.println(movingCount(12, 1, 1) + "[1]"); 109 | System.out.println(movingCount(-10, 10, 10) + "[0]"); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /操作系统/操作系统.md: -------------------------------------------------------------------------------- 1 | # 操作系统 2 | 3 | ## 内存管理 4 | 5 | ### 虚拟内存 6 | 7 | - 虚拟内存采用的是分页技术,也就是将地址空间划分成固定大小的页,每一页再与内存进行映射。 8 | 9 | ### 分页系统地址映射 10 | 11 | - 内存管理单元(MMU)管理着地址空间和物理内存的转换,其中的页表(Page table)存储着页(程序地址空间)和页框(物理内存空间)的映射表。 12 | 13 | ### 页面置换算法 14 | 15 | - 是什么 16 | 17 | - 在程序运行过程中,如果要访问的页面不在内存中,就发生缺页中断从而将该页调入内存中。此时如果内存已无空闲空间,系统必须从内存中调出一个页面到磁盘对换区中来腾出空间。 18 | 19 | - 有哪些 20 | 21 | - 1.最佳 22 | 23 | - 所选择的被换出的页面将是最长时间内不再被访问,通常可以保证获得最低的缺页率。是一种理论上的算法,因为无法知道一个页面多长时间不再被访问。 24 | 25 | - 2.最近最久未使用 26 | 27 | - LRU 将最近最久未使用的页面换出。为了实现 LRU,需要在内存中维护一个所有页面的链表。当一个页面被访问时,将这个页面移到链表表头。这样就能保证链表表尾的页面是最近最久未访问的。因为每次访问都需要更新链表,因此这种方式实现的 LRU 代价很高。 28 | 29 | - 3.最近未使用 30 | 31 | - 每个页面都有两个状态位:R 与 M,当页面被访问时设置页面的 R=1,当页面被修改时设置 M=1。其中 R 位会定时被清零。可以将页面分成以下四类: 32 | R=0,M=0 33 | R=0,M=1 34 | R=1,M=0 35 | R=1,M=1 36 | 当发生缺页中断时,NRU 算法随机地从类编号最小的非空类中挑选一个页面将它换出。 37 | NRU 优先换出已经被修改的脏页面(R=0,M=1),而不是被频繁使用的干净页面(R=1,M=0)。 38 | 39 | - 4.先进先出(FIFO) 40 | 41 | - 选择换出的页面是最先进入的页面。该算法会将那些经常被访问的页面也被换出,从而使缺页率升高。 42 | 43 | - 5.第二次机会算法 44 | 45 | - FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问题,对该算法做一个简单的修改: 46 | 当页面被访问 (读或写) 时设置该页面的 R 位为 1。需要替换的时候,检查最老页面的 R 位。如果 R 位是 0,那么这个页面既老又没有被使用,可以立刻置换掉;如果是 1,就将 R 位清 0,并把该页面放到链表的尾端,修改它的装入时间使它就像刚装入的一样,然后继续从链表的头部开始搜索。 47 | 48 | - 6.时钟算法 49 | 50 | - 第二次机会算法需要在链表中移动页面,降低了效率。时钟算法使用环形链表将页面连接起来,再使用一个指针指向最老的页面。 51 | 52 | ### 分段 53 | 54 | - 分段的做法是把每个表分成段,一个段构成一个独立的地址空间。每个段的长度可以不同,并且可以动态增长。 55 | 56 | ### 段页式 57 | 58 | - 程序的地址空间划分成多个拥有独立地址空间的段,每个段上的地址空间划分成大小相同的页。这样既拥有分段系统的共享和保护,又拥有分页系统的虚拟内存功能。 59 | 60 | ### 分页和分段的比较 61 | 62 | - 1.对程序员的透明性:分页透明,但是分段需要程序员显式划分每个段。 63 | 2.地址空间的维度:分页是一维地址空间,分段是二维的。 64 | 3.大小是否可以改变:页的大小不可变,段的大小可以动态改变。 65 | 4.出现的原因:分页主要用于实现虚拟内存,从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。 66 | 67 | ## 设备管理· 68 | 69 | ### 磁盘结构 70 | 71 | - 盘面(Platter):一个磁盘有多个盘面; 72 | 磁道(Track):盘面上的圆形带状区域,一个盘面可以有多个磁道; 73 | 扇区(Track Sector):磁道上的一个弧段,一个磁道可以有多个扇区,它是最小的物理储存单位,目前主要有 512 bytes 与 4 K 两种大小; 74 | 磁头(Head):与盘面非常接近,能够将盘面上的磁场转换为电信号(读),或者将电信号转换为盘面的磁场(写); 75 | 制动手臂(Actuator arm):用于在磁道之间移动磁头; 76 | 主轴(Spindle):使整个盘面转动。 77 | 78 | ### 磁盘调度算法 79 | 80 | - 是什么 81 | 82 | - 读写一个磁盘块的时间的影响因素有: 83 | 1.旋转时间(主轴转动盘面,使得磁头移动到适当的扇区上)2.寻道时间(制动手臂移动,使得磁头移动到适当的磁道上)3.实际的数据传输时间 84 | 其中,寻道时间最长,因此磁盘调度的主要目标是使磁盘的平均寻道时间最短。 85 | 86 | - 有哪些 87 | 88 | - 1.先来先服务 89 | 90 | - 按照磁盘请求的顺序进行调度。 91 | 优点是公平和简单。缺点也很明显,因为未对寻道做任何优化,使平均寻道时间可能较长。 92 | 93 | - 2.最短寻道时间优先 94 | 95 | - 优先调度与当前磁头所在磁道距离最近的磁道。 96 | 虽然平均寻道时间比较低,但是不够公平。如果新到达的磁道请求总是比一个在等待的磁道请求近,那么在等待的磁道请求会一直等待下去,也就是出现饥饿现象。具体来说,两端的磁道请求更容易出现饥饿现象。 97 | 98 | - 3.电梯算法 99 | 100 | - 电梯总是保持一个方向运行,直到该方向没有请求为止,然后改变运行方向。 101 | 电梯算法(扫描算法)和电梯的运行过程类似,总是按一个方向来进行磁盘调度,直到该方向上没有未完成的磁盘请求,然后改变方向。因为考虑了移动方向,因此所有的磁盘请求都会被满足,解决了 SSTF 的饥饿问题。 102 | 103 | ## 死锁 104 | 105 | ### 定义 106 | 107 | - 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。 108 | 109 | ### 死锁检测与死锁恢复 110 | 111 | - Java死锁检测 112 | 113 | - jps命令+jstack命令 114 | 115 | - 每种类型一个资源的死锁检测 116 | 117 | - 每种类型一个资源的死锁检测算法是通过检测有向图是否存在环来实现,从一个节点出发进行深度优先搜索,对访问过的节点进行标记,如果访问了已经标记的节点,就表示有向图存在环,也就是检测到死锁的发生。 118 | 119 | - 每种类型的多个资源的死锁检测 120 | 121 | - 进程 P1 和 P2 所请求的资源都得不到满足,只有进程 P3 可以,让 P3 执行,之后释放 P3 拥有的资源,此时 A = (2 2 2 0)。P2 可以执行,执行后释放 P2 拥有的资源,A = (4 2 2 1) 。P1 也可以执行。所有进程都可以顺利执行,没有死锁。 122 | 123 | - 死锁恢复 124 | 125 | - 利用抢占恢复 126 | 利用回滚恢复 127 | 通过杀死进程恢复 128 | 129 | ### 死锁预防 130 | 131 | - Java死锁预防 132 | 133 | - 1.以确定的顺序获取锁 134 | - 2.超时放弃 135 | 136 | - 破坏互斥条件 137 | 138 | - 例如假脱机打印机技术允许若干个进程同时输出,唯一真正请求物理打印机的进程是打印机守护进程。 139 | 140 | - 破坏占有和等待条件 141 | 142 | - 一种实现方式是规定所有进程在开始执行前请求所需要的全部资源。 143 | 144 | - 破坏不可抢占条件 145 | - 破坏环路等待 146 | 147 | - 给资源统一编号,进程只能按编号顺序来请求资源。 148 | 149 | ### 死锁避免 150 | 151 | - 安全状态 152 | 153 | - 如果没有死锁发生,并且即使所有进程突然请求对资源的最大需求,也仍然存在某种调度次序能够使得每一个进程运行完毕,则称该状态是安全的。 154 | 155 | - 单个资源的银行家算法 156 | 157 | - 一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额度,算法要做的是判断对请求的满足是否会进入不安全状态,如果是,就拒绝请求;否则予以分配。 158 | 159 | - 多个资源的银行家算法 160 | 161 | ## 进程管理 162 | 163 | ### 进程与线程 164 | 165 | - 进程 166 | 167 | - 进程是资源分配的基本单位。 168 | 169 | - 线程 170 | 171 | - 线程是CPU调度的基本单位。 172 | 173 | - 区别 174 | 175 | - Ⅰ 拥有资源 176 | 进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问隶属进程的资源。 177 | Ⅱ 调度 178 | 线程是独立调度的基本单位,在同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程中的线程时,会引起进程切换。 179 | Ⅲ 系统开销 180 | 由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程 CPU 环境的保存及新调度进程 CPU 环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。 181 | Ⅳ 通信方面 182 | 线程间可以通过直接读写同一进程中的数据进行通信,但是进程通信需要借助 IPC。 183 | 184 | ### 进程状态的切换 185 | 186 | - 就绪状态(ready):等待被调度 187 | 运行状态(running) 188 | 阻塞状态(waiting):等待资源 189 | 190 | ### 进程调度算法 191 | 192 | - 批处理系统 193 | 194 | - 1.先来先服务 195 | 196 | - 非抢占式的调度算法,按照请求的顺序进行调度。 197 | 有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。 198 | 199 | - 2.短作业优先 200 | 201 | - 非抢占式的调度算法,按照请求的顺序进行调度。 202 | 有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。 203 | 204 | - 3.最短剩余时间优先 205 | 206 | - 非抢占式的调度算法,按照请求的顺序进行调度。 207 | 有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。 208 | 209 | - 交互式系统 210 | 211 | - 1.时间片轮转 212 | 213 | - 将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。 214 | 215 | - 2.优先级调度 216 | 217 | - 为每个进程分配一个优先级,按优先级进行调度。 218 | 为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。 219 | 220 | - 3.多级反馈队列 221 | 222 | - 一个进程需要执行 100 个时间片,如果采用时间片轮转调度算法,那么需要交换 100 次。多级队列是为这种需要连续执行多个时间片的进程考虑,它设置了多个队列,每个队列时间片大小都不同,例如 1,2,4,8,..。进程在第一个队列没执行完,就会被移到下一个队列。这种方式下,之前的进程只需要交换 7 次。 223 | 每个队列优先权也不同,最上面的优先权最高。因此只有上一个队列没有进程在排队,才能调度当前队列上的进程。可以将这种调度算法看成是时间片轮转调度算法和优先级调度算法的结合。 224 | 225 | - 实时系统 226 | 227 | - 实时系统要求一个请求在一个确定时间内得到响应。 228 | 分为硬实时和软实时,前者必须满足绝对的截止时间,后者可以容忍一定的超时。 229 | 230 | ### 进程同步 231 | 232 | - 临界区 233 | 234 | - 为了互斥访问临界资源,每个进程在进入临界区之前,需要先进行检查。 235 | 236 | - 同步与互斥 237 | 238 | - 同步:多个进程因为合作产生的直接制约关系,使得进程有一定的先后执行关系。 239 | 互斥:多个进程在同一时刻只有一个进程能进入临界区。 240 | 241 | - 信号量 242 | 243 | - 信号量(Semaphore)是一个整型变量,可以对其执行 down 和 up 操作,也就是常见的 P 和 V 操作。 244 | down : 如果信号量大于 0 ,执行 -1 操作;如果信号量等于 0,进程睡眠,等待信号量大于 0; 245 | up :对信号量执行 +1 操作,唤醒睡眠的进程让其完成 down 操作。 246 | down 和 up 操作需要被设计成原语,不可分割,通常的做法是在执行这些操作的时候屏蔽中断。 247 | 如果信号量的取值只能为 0 或者 1,那么就成为了 互斥量(Mutex) ,0 表示临界区已经加锁,1 表示临界区解锁。 248 | 249 | - 管程 250 | 251 | - 使用信号量机制实现的生产者消费者问题需要客户端代码做很多控制,而管程把控制的代码独立出来,不仅不容易出错,也使得客户端代码调用更容易。 252 | 253 | ### 经典同步问题 254 | 255 | - 读者-写者问题 256 | 257 | - 允许多个进程同时对数据进行读操作,但是不允许读和写以及写和写操作同时发生。 258 | 259 | - 哲学家进餐问题 260 | 261 | - 必须同时拿起左右两根筷子; 262 | 只有在两个邻居都没有进餐的情况下才允许进餐。 263 | 264 | ### 进程通信 265 | 266 | - 管道 267 | 268 | - 进程同步与进程通信很容易混淆,它们的区别在于: 269 | 进程同步:控制多个进程按一定顺序执行; 270 | 进程通信:进程间传输信息。 271 | 272 | - FIFO 273 | 274 | - 进程同步与进程通信很容易混淆,它们的区别在于: 275 | 进程同步:控制多个进程按一定顺序执行; 276 | 进程通信:进程间传输信息。 277 | 278 | - 消息队列 279 | 280 | - 相比于 FIFO,消息队列具有以下优点: 281 | 消息队列可以独立于读写进程存在,从而避免了 FIFO 中同步管道的打开和关闭时可能产生的困难; 282 | 避免了 FIFO 的同步阻塞问题,不需要进程自己提供同步方法; 283 | 读进程可以根据消息类型有选择地接收消息,而不像 FIFO 那样只能默认地接收。 284 | 285 | - 信号量 286 | 287 | - 它是一个计数器,用于为多个进程提供对共享数据对象的访问。 288 | 289 | - 共享存储 290 | 291 | - 允许多个进程共享一个给定的存储区。因为数据不需要在进程之间复制,所以这是最快的一种 IPC。 292 | 需要使用信号量用来同步对共享存储的访问。 293 | 多个进程可以将同一个文件映射到它们的地址空间从而实现共享内存。另外 XSI 共享内存不是使用文件,而是使用内存的匿名段。 294 | 295 | - 套接字 296 | 297 | - 与其它通信机制不同的是,它可用于不同机器间的进程通信。 298 | 299 | 300 | 301 | -------------------------------------------------------------------------------- /操作系统/操作系统.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossDing/Pentagram/be12b225a29d18d2e024211c588100cb3bf54504/操作系统/操作系统.png -------------------------------------------------------------------------------- /数据库系统/数据库.md: -------------------------------------------------------------------------------- 1 | # 数据库 2 | 3 | ## 数据库系统 4 | 5 | ### 事务四大特性 6 | 7 | - 1. 原子性 8 | 9 | - 事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。 10 | 11 | - 2.一致性 12 | 13 | - 数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对一个数据的读取结果都是相同的。 14 | 15 | - 3.隔离性 16 | 17 | - 数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对一个数据的读取结果都是相同的。 18 | 19 | - 4.持久性 20 | 21 | - 一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。 22 | 23 | ### 并发一致性问题 24 | 25 | - 脏读 26 | 27 | - 一个事务读取另外一个事务还没有提交的数据叫脏读。 28 | 29 | - 不可重复度 30 | 31 | - 在一个事务中前后两次读取的结果并不一致,导致了不可重复读。 32 | 33 | - 幻读 34 | 35 | - 在两个连续的查询之间一个并发的事务修改了查询的数据集,导致这两个查询返回了不同的结果。 36 | 37 | ### 事务的隔离级别 38 | 39 | - 未提交读 40 | 41 | - 事务中的修改,即使没有提交,对其它事务也是可见的。 42 | 43 | - 已提交读 44 | 45 | - 一个事务只能读取已经提交的事务所做的修改。 46 | 47 | - 可重复读 48 | 49 | - 保证在同一个事务中多次读取同样数据的结果是一样的。 50 | MySQL默认的隔离级别 51 | 52 | - 可串行化 53 | 54 | - 强制事务串行执行。 55 | 需要加锁实现,而其它隔离级别通常不需要。 56 | 57 | ### 多版本并发控制 58 | 59 | - 多版本并发控制(MVCC)是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,用于实现已提交读和可重复读这两种隔离级别。 60 | 61 | - 版本号 62 | 63 | - 系统版本号:是一个递增的数字,每开始一个新的事务,系统版本号就会自动递增。 64 | 事务版本号:事务开始时的系统版本号。 65 | 66 | - 快照读 67 | 68 | - 使用 MVCC 读取的是快照中的数据,这样可以减少加锁所带来的开销。 69 | 70 | - 当前读 71 | 72 | - 使用 MVCC 读取的是快照中的数据,这样可以减少加锁所带来的开销。 73 | 74 | ### 范式 75 | 76 | - 第一范式 77 | 78 | - 所有字段值都是不可分解的原子值 79 | 80 | - 第二范式 81 | 82 | - 确保数据库表中每一列都和主键相关,而不能只与主键的某一部分相关。 83 | 84 | - 第三范式 85 | 86 | - 每一列数据都和主键直接相关,而不能间接相关。 87 | 88 | ### ER图 89 | 90 | - 有三个组成部分:实体、属性、联系。 91 | 92 | ## Redis 93 | 94 | ### 数据类型 95 | 96 | - String字符串 97 | - List列表 98 | - Set无序集合 99 | - HASH 100 | 101 | - 链地址法解决HASH冲突 102 | 103 | - ZSET有序集合 104 | 105 | - 跳跃表 106 | 107 | ### 使用场景 108 | 109 | - 计数器 110 | - 缓存 111 | - 查找表 112 | - 消息队列 113 | - 分布式锁 114 | 115 | - SETNX 116 | - RedLock 117 | 118 | ### Redis 与 Memcached 119 | 120 | - 数据类型 121 | 122 | - Memcached 仅支持字符串类型,而 Redis 支持五种不同的数据类型,可以更灵活地解决问题。 123 | 124 | - 数据持久化 125 | 126 | - Redis 支持两种持久化策略:RDB 快照和 AOF 日志,而 Memcached 不支持持久化。 127 | 128 | - 分布式 129 | 130 | - Memcached 不支持分布式,只能通过在客户端使用一致性哈希来实现分布式存储,这种方式在存储和查询时都需要先在客户端计算一次数据所在的节点。 131 | 132 | - 内存管理机制 133 | 134 | - 在 Redis 中,并不是所有数据都一直存储在内存中,可以将一些很久没用的 value 交换到磁盘,而 Memcached 的数据则会一直在内存中。 135 | 136 | ### 键的过期时间 137 | 138 | - Redis 可以为每个键设置过期时间,当键过期时,会自动删除该键。 139 | 140 | ### 数据淘汰策略 141 | 142 | - 从已设置过期时间的数据集中挑选最近最少使用的数据淘汰 143 | - 从已设置过期时间的数据集中挑选将要过期的数据淘汰 144 | - 从已设置过期时间的数据集中任意选择数据淘汰 145 | - 从所有数据集中挑选最近最少使用的数据淘汰 146 | - 从所有数据集中任意选择数据进行淘汰 147 | - 禁止驱逐数据 148 | 149 | ### 持久化 150 | 151 | - RDB持久化 152 | 153 | - 可以将快照复制到其它服务器从而创建具有相同数据的服务器副本。 154 | Redis调用fork(),产生一个子进程。子进程把数据写到一个临时的RDB文件。当子进程写完新的RDB文件后,把旧的RDB文件替换掉。 155 | 156 | - AOF 持久化 157 | 158 | - 将写命令添加到 AOF 文件(Append Only File)的末尾。实时。 159 | 160 | ### 事务 161 | 162 | - 一个事务包含了多个命令,服务器在执行事务期间,不会改去执行其它客户端的命令请求。 163 | 164 | ### 事件 165 | 166 | - 文件事件 167 | - 时间事件 168 | 169 | ### 复制 170 | 171 | - 主从复制:通过使用 slaveof host port 命令来让一个服务器成为另一个服务器的从服务器。 172 | 173 | ### 哨兵 174 | 175 | - Sentinel(哨兵)可以监听集群中的服务器,并在主服务器进入下线状态时,自动从从服务器中选举出新的主服务器。 176 | 177 | ### 分片 178 | 179 | - 分片是将数据划分为多个部分的方法,可以将数据存储到多台机器里面,这种方法在解决某些问题时可以获得线性级别的性能提升。 180 | 181 | ### 优势 182 | 183 | - 1.基于内存 184 | 2.单线程避免了频繁的上下文切换 185 | 3.非阻塞IO多路复用模型 186 | 187 | ## MySQL 188 | 189 | ### 索引 190 | 191 | - MySQL索引 192 | 193 | - B+Tree索引 194 | 195 | - B+Tree 196 | 197 | - 是一种多路搜索树,M路的B树最多能拥有M个孩子节点。 198 | 数据库中的索引一般是在磁盘上,数据量大的情况可能无法一次装入内存,B+树的设计可以允许数据分批加载,同时树的高度较低,提高查找效率。 199 | 叶子节点之间通过指针连接,方便查询多条数据。 200 | 201 | - 聚簇索引 202 | 203 | - InnoDB 的 B+Tree 索引分为主索引和辅助索引。主索引的叶子节点 data 域记录着完整的数据记录,这种索引方式被称为聚簇索引。因为无法把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。 204 | 205 | - 非聚簇索引 206 | 207 | - 辅助索引的叶子节点的 data 域记录着主键的值,因此在使用辅助索引进行查找时,需要先查找到主键值,然后再到主索引中进行查找。 208 | 209 | - 哈希索引 210 | 211 | - 哈希索引能以 O(1) 时间进行查找,但是失去了有序性: 212 | 无法用于排序与分组; 213 | 只支持精确查找,无法用于部分查找和范围查找。 214 | 215 | - 全文索引 216 | 217 | - MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而不是直接比较是否相等。 218 | 全文索引使用倒排索引实现,它记录着关键词到其所在文档的映射。 219 | 220 | - 空间数据索引 221 | 222 | - MyISAM 存储引擎支持空间数据索引(R-Tree),可以用于地理数据存储。空间数据索引会从所有维度来索引数据,可以有效地使用任意维度来进行组合查询。 223 | 224 | - 索引优化 225 | 226 | - 索引列不能是表达式的一部分,也不能是函数的参数,否则无法使用索引。 227 | - 在需要使用多个列作为条件进行查询时,使用多列索引比使用多个单列索引性能更好。 228 | - 让选择性最强的索引列放在前面。 229 | - 索引包含所有需要查询的字段的值。 230 | 231 | - 索引优点 232 | 233 | - 大大减少了服务器需要扫描的数据行数。 234 | - 帮助服务器避免进行排序和分组,以及避免创建临时表。 235 | 236 | - 索引使用条件 237 | 238 | - 对于非常小的表、大部分情况下简单的全表扫描比建立索引更高效; 239 | - 对于中到大型的表,索引就非常有效; 240 | - 但是对于特大型的表,建立和维护索引的代价将会随之增长。这种情况下,需要用到一种技术可以直接区分出需要查询的一组数据,而不是一条记录一条记录地匹配,例如可以使用分区技术。 241 | 242 | ### 查询性能优化 243 | 244 | - 使用 Explain 进行分析 245 | 246 | - select_type : 查询类型,有简单查询、联合查询、子查询等 247 | key : 使用的索引 248 | rows : 扫描的行数 249 | 250 | - 优化数据访问 251 | 252 | - 1. 减少请求的数据量 253 | 254 | - 只返回必要的列:最好不要使用 SELECT * 语句。 255 | 只返回必要的行:使用 LIMIT 语句来限制返回的数据。 256 | 缓存重复查询的数据 257 | 258 | - 2. 减少服务器端扫描的行数 259 | 260 | - 重构查询方式 261 | 262 | - 1. 切分大查询 263 | 264 | - 一个大查询如果一次性执行的话,可能一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。 265 | 266 | - 2. 分解大连接查询 267 | 268 | - 将一个大连接查询分解成对每一个表进行一次单表查询,然后在应用程序中进行关联。好处有: 269 | 让缓存更高效。减少锁竞争。应用层连接,扩展性更高。 270 | 271 | ### 存储引擎 272 | 273 | - InnoDB 274 | 275 | - 是 MySQL 默认的事务型存储引擎。 276 | 实现了四个标准的隔离级别,默认级别是可重复读(REPEATABLE READ)。在可重复读隔离级别下,通过多版本并发控制(MVCC)+ Next-Key Locking 防止幻影读。 277 | 主索引是聚簇索引,在索引中保存了数据,从而避免直接读取磁盘,因此对查询性能有很大的提升。 278 | 内部做了很多优化,包括从磁盘读取数据时采用的可预测性读、能够加快读操作并且自动创建的自适应哈希索引、能够加速插入操作的插入缓冲区等。 279 | 支持真正的在线热备份。其它存储引擎不支持在线热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取。 280 | 281 | - MyISAM 282 | 283 | - 设计简单,数据以紧密格式存储。对于只读数据,或者表比较小、可以容忍修复操作,则依然可以使用它。 284 | 提供了大量的特性,包括压缩表、空间数据索引等。 285 | 不支持事务。 286 | 不支持行级锁,只能对整张表加锁,读取时会对需要读到的所有表加共享锁,写入时则对表加排它锁。但在表有读取操作的同时,也可以往表中插入新的记录,这被称为并发插入(CONCURRENT INSERT)。 287 | 288 | - 比较 289 | 290 | - 事务:InnoDB 是事务型的,可以使用 Commit 和 Rollback 语句。 291 | 并发:MyISAM 只支持表级锁,而 InnoDB 还支持行级锁。 292 | 外键:InnoDB 支持外键。 293 | 备份:InnoDB 支持在线热备份。 294 | 崩溃恢复:MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。 295 | 其它特性:MyISAM 支持压缩表和空间数据索引。 296 | 297 | ### 切分 298 | 299 | - 水平切分 300 | 301 | - 水平切分又称为 Sharding,它是将同一个表中的记录拆分到多个结构相同的表中。 302 | - 策略 303 | 304 | - 哈希取模:hash(key) % N; 305 | 范围:可以是 ID 范围也可以是时间范围; 306 | 映射表:使用单独的一个数据库来存储映射关系。 307 | 308 | - 问题 309 | 310 | - 1. 事务问题 311 | 使用分布式事务来解决,比如 XA 接口。 312 | 2. 连接 313 | 可以将原来的连接分解成多个单表查询,然后在用户程序中进行连接。 314 | 3. ID 唯一性 315 | 使用全局唯一 ID(GUID) 316 | 为每个分片指定一个 ID 范围 317 | 分布式 ID 生成器 (如 Twitter 的 Snowflake 算法) 318 | 319 | - 垂直切分 320 | 321 | - 垂直切分是将一张表按列切分成多个表,通常是按照列的关系密集程度进行切分,也可以利用垂直切分将经常被使用的列和不经常被使用的列切分到不同的表中。 322 | 323 | ### 复制 324 | 325 | - 主从复制 326 | - 读写分离 327 | 328 | - 主服务器处理写操作以及实时性要求比较高的读操作,而从服务器处理读操作。 329 | 330 | ## SQL 331 | 332 | ### 视图 333 | 334 | - 视图是从一个或几个基本表(或视图)中导出的虚拟的表。 335 | 336 | ### 存储过程 337 | 338 | - 存储过程可以看成是对一系列 SQL 操作的批处理。预编译SQL语句。 339 | 340 | ### 游标 341 | 342 | - 实际上是一种能从包含多条数据记录的结果集中每次提取一条记录的机制。 343 | 344 | ### 触发器 345 | 346 | - 由事件触发的存储过程。 347 | 348 | *XMind: ZEN - Trial Version* -------------------------------------------------------------------------------- /数据库系统/数据库.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossDing/Pentagram/be12b225a29d18d2e024211c588100cb3bf54504/数据库系统/数据库.png -------------------------------------------------------------------------------- /计算机网络/计算机网络.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossDing/Pentagram/be12b225a29d18d2e024211c588100cb3bf54504/计算机网络/计算机网络.png -------------------------------------------------------------------------------- /设计模式/设计模式.md: -------------------------------------------------------------------------------- 1 | # 设计模式 2 | 3 | ## 创建型 4 | 5 | ### 单例模式 6 | 7 | - 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 8 | 9 | ### 工厂模式 10 | 11 | - 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。 12 | 13 | ### 抽象工厂模式 14 | 15 | - 围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。 16 | 17 | ### 建造者模式 18 | 19 | - 使用多个简单的对象一步一步构建成一个复杂的对象。一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。 20 | 21 | ### 原型模式 22 | 23 | - 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。 24 | 25 | ## 结构型 26 | 27 | ### 适配器模式 28 | 29 | - 将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 30 | 31 | ### 桥接模式 32 | 33 | - 用于把抽象化与实现化解耦,使得二者可以独立变化。 34 | 35 | ### 过滤器模式 36 | 37 | - 这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。 38 | 39 | ### 组合模式 40 | 41 | - 将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 42 | 43 | ### 装饰器模式 44 | 45 | - 允许向一个现有的对象添加新的功能,同时又不改变其结构。比如新建一个抽象类去继承接口,并通过扩展抽象类方法增加接口的新功能。 46 | 47 | ### 外观模式 48 | 49 | - 外观模式隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。 50 | 51 | ### 享元模式 52 | 53 | - 享元模式尝试重用现有的同类对象,减少创建对象的数量,以减少内存占用和提高性能。如果未找到匹配的对象,则创建新对象。 54 | 55 | ### 代理模式 56 | 57 | - 为其他对象提供一种代理以控制对这个对象的访问。 58 | 在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。 59 | 60 | ## 结构性 61 | 62 | ### 责任链模式 63 | 64 | - 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。 65 | 66 | ### 命令模式 67 | 68 | - 请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。是一种数据驱动的设计模式。 69 | 70 | ### 解释器模式 71 | 72 | - 解释器模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。 73 | 74 | ### 迭代器模式 75 | 76 | - 提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。也就是把在元素之间游走的责任交给迭代器,而不是聚合对象。 77 | 78 | ### 中介者模式 79 | 80 | - 用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。比如A调用B,B调用C。 81 | 82 | ### 备忘录模式 83 | 84 | - 所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。 85 | 86 | ### 观察者模式 87 | 88 | - 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 89 | 90 | ### 状态模式 91 | 92 | - 允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。 93 | 94 | ### 空对象模式 95 | 96 | - 一个空对象取代 NULL 对象实例的检查。Null 对象不是检查空值,而是反应一个不做任何动作的关系。这样的 Null 对象也可以在数据不可用的时候提供默认的行为。 97 | 98 | ### 策略模式 99 | 100 | - 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。 101 | 102 | ### 模板模式 103 | 104 | - 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 105 | 106 | ### 访问者模式 107 | 108 | - 使用一个访问者类,改变了元素类的执行算法。主要将数据结构与数据操作分离。 109 | 110 | ## J2EE模式 111 | 112 | ### MVC模式 113 | 114 | - MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。 115 | 116 | ### 业务代表模式 117 | 118 | - 业务代表模式(Business Delegate Pattern)用于对表示层和业务层解耦。它基本上是用来减少通信或对表示层代码中的业务层代码的远程查询功能。 119 | 120 | ### 组合实体模式 121 | 122 | - 组合实体模式(Composite Entity Pattern)用在 EJB 持久化机制中。一个组合实体是一个 EJB 实体 bean,代表了对象的图解。当更新一个组合实体时,内部依赖对象 beans 会自动更新,因为它们是由 EJB 实体 bean 管理的。 123 | 124 | ### 数据访问对象模式 125 | 126 | - 数据访问对象模式(Data Access Object Pattern)或 DAO 模式用于把低级的数据访问 API 或操作从高级的业务服务中分离出来。 127 | 128 | ### 前端控制器模式 129 | 130 | - 前端控制器模式(Front Controller Pattern)是用来提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理。该处理程序可以做认证/授权/记录日志,或者跟踪请求,然后把请求传给相应的处理程序。 131 | 132 | ### 拦截过滤器模式 133 | 134 | - 拦截过滤器模式(Intercepting Filter Pattern)用于对应用程序的请求或响应做一些预处理/后处理。定义过滤器,并在把请求传给实际目标应用程序之前应用在请求上。过滤器可以做认证/授权/记录日志,或者跟踪请求,然后把请求传给相应的处理程序。 135 | 136 | ### 服务定位器模式 137 | 138 | - 服务定位器模式(Service Locator Pattern)用在我们想使用 JNDI 查询定位各种服务的时候。考虑到为某个服务查找 JNDI 的代价很高,服务定位器模式充分利用了缓存技术。在首次请求某个服务时,服务定位器在 JNDI 中查找服务,并缓存该服务对象。当再次请求相同的服务时,服务定位器会在它的缓存中查找,这样可以在很大程度上提高应用程序的性能。 139 | 140 | ### 传输对象模式 141 | 142 | - 传输对象模式(Transfer Object Pattern)用于从客户端向服务器一次性传递带有多个属性的数据。传输对象也被称为数值对象。传输对象是一个具有 getter/setter 方法的简单的 POJO 类,它是可序列化的,所以它可以通过网络传输。它没有任何的行为。 143 | 144 | ## 六大原则 145 | 146 | ### 开闭原则 147 | 148 | - 对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,使用接口和抽象类实现一个热插拔的效果。 149 | 150 | ### 里氏替换原则 151 | 152 | - 任何基类可以出现的地方,子类一定可以出现。实现抽象的规范,实现子父类互相替换。 153 | 154 | ### 依赖倒转原则 155 | 156 | - 针对接口编程,依赖于抽象而不依赖于具体。 157 | 158 | ### 接口隔离原则 159 | 160 | - 降低耦合度,接口单独设计,互相隔离。 161 | 162 | ### 最少知道原则 163 | 164 | - 迪米特法则:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。 165 | 166 | ### 合成复用原则 167 | 168 | - 尽量使用聚合、组合的方式,而不是使用继承。 169 | 170 | *XMind: ZEN - Trial Version* -------------------------------------------------------------------------------- /设计模式/设计模式.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossDing/Pentagram/be12b225a29d18d2e024211c588100cb3bf54504/设计模式/设计模式.png --------------------------------------------------------------------------------