├── images ├── offer12.png ├── offer23.png ├── offer25.png ├── offer26.png ├── offer27.png ├── offer28.png ├── offer32.png ├── offer33.png ├── offer36.png ├── offer52.png ├── offer54.png ├── offer10-1.gif ├── offer35-1.png ├── offer35-2.png ├── offer35-3.png ├── offer35-4.png ├── offer8-1.png └── offer8-2.png ├── code └── Coding-Interviews │ ├── src │ └── com │ │ └── todorex │ │ ├── offer9 │ │ ├── BuildStack.java │ │ ├── BuildQueue.java │ │ └── BuildQueue1.java │ │ ├── offer22 │ │ ├── ListNode.java │ │ └── Solution.java │ │ ├── offer23 │ │ ├── ListNode.java │ │ └── Solution.java │ │ ├── offer24 │ │ ├── ListNode.java │ │ └── Solution.java │ │ ├── offer25 │ │ ├── ListNode.java │ │ └── Solution.java │ │ ├── offer52 │ │ ├── ListNode.java │ │ ├── Solution.java │ │ └── Solution1.java │ │ ├── offer18 │ │ ├── ListNode.java │ │ ├── Solution1.java │ │ └── Solution2.java │ │ ├── offer32 │ │ ├── BinaryTreeNode.java │ │ └── Solution.java │ │ ├── offer33 │ │ ├── BinaryTreeNode.java │ │ └── Solution.java │ │ ├── offer34 │ │ ├── BinaryTreeNode.java │ │ └── Solution.java │ │ ├── offer26 │ │ ├── BinaryTreeNode.java │ │ └── Solution.java │ │ ├── offer27 │ │ ├── BinaryTreeNode.java │ │ └── Solution.java │ │ ├── offer28 │ │ ├── BinaryTreeNode.java │ │ └── Solution.java │ │ ├── offer55 │ │ ├── BinaryTreeNode.java │ │ ├── Solution1.java │ │ ├── Solution2.java │ │ └── Solution3.java │ │ ├── offer6 │ │ ├── ListNode.java │ │ ├── Solution3.java │ │ ├── Solution2.java │ │ ├── Solution.java │ │ ├── Solution4.java │ │ └── Solution1.java │ │ ├── offer54 │ │ ├── TreeNode.java │ │ └── Solution.java │ │ ├── offer68 │ │ ├── TreeNode.java │ │ ├── Solution.java │ │ └── Solution1.java │ │ ├── offer7 │ │ ├── TreeNode.java │ │ └── Solution.java │ │ ├── offer21 │ │ ├── ReorderStrategy.java │ │ ├── OddEventReorderStategy.java │ │ ├── Solution1.java │ │ ├── Solution.java │ │ └── Solution2.java │ │ ├── offer36 │ │ ├── BinaryTreeNode.java │ │ └── Solution.java │ │ ├── offer37 │ │ ├── BinaryTreeNode.java │ │ └── Solution.java │ │ ├── offer35 │ │ ├── ComplexListNode.java │ │ └── Solution.java │ │ ├── offer8 │ │ ├── TreeLinkNode.java │ │ └── Solution.java │ │ ├── offer46 │ │ ├── Solution1.java │ │ └── Solution.java │ │ ├── offer10 │ │ ├── Solution.java │ │ ├── Solution1.java │ │ └── FrogSolution.java │ │ ├── offer17 │ │ ├── Solution.java │ │ ├── Solution2.java │ │ └── Solution1.java │ │ ├── offer16 │ │ ├── Solution.java │ │ ├── Solution1.java │ │ └── Solution2.java │ │ ├── offer20 │ │ ├── Solution2.java │ │ ├── Solution.java │ │ └── Solution1.java │ │ ├── offer64 │ │ └── Solution.java │ │ ├── offer65 │ │ └── Solution.java │ │ ├── offer15 │ │ ├── Solution.java │ │ └── Solution1.java │ │ ├── offer2 │ │ ├── Singleton1.java │ │ ├── Singleton.java │ │ ├── Singleton2.java │ │ └── Singleton3.java │ │ ├── offer66 │ │ └── Solution.java │ │ ├── offer3 │ │ ├── Duplicate.java │ │ ├── Duplicate1.java │ │ └── BinarySearch.java │ │ ├── offer63 │ │ └── Solution.java │ │ ├── offer42 │ │ └── Solution.java │ │ ├── offer30 │ │ └── Solution.java │ │ ├── offer5 │ │ ├── Solution.java │ │ └── Solution1.java │ │ ├── offer51 │ │ ├── Solution.java │ │ └── MergeSort.java │ │ ├── offer57 │ │ ├── Solution.java │ │ ├── Solution1.java │ │ └── Solution2.java │ │ ├── offer58 │ │ ├── Solution.java │ │ ├── Solution2.java │ │ └── Solution1.java │ │ ├── offer45 │ │ └── Solution.java │ │ ├── offer43 │ │ └── Solution.java │ │ ├── offer49 │ │ ├── Solution.java │ │ └── Solution1.java │ │ ├── offer4 │ │ └── Solution.java │ │ ├── offer14 │ │ ├── Solution1.java │ │ └── Solution.java │ │ ├── offer39 │ │ ├── Solution.java │ │ └── Solution1.java │ │ ├── offer50 │ │ ├── Solution1.java │ │ └── Solution.java │ │ ├── offer31 │ │ └── Solution.java │ │ ├── offer47 │ │ ├── Solution.java │ │ └── Solution1.java │ │ ├── offer48 │ │ └── Solution.java │ │ ├── offer38 │ │ └── Solution.java │ │ ├── offer56 │ │ ├── Solution1.java │ │ └── Solution.java │ │ ├── offer40 │ │ ├── Solution.java │ │ └── Solution2.java │ │ ├── offer53 │ │ ├── Solution2.java │ │ └── Solution.java │ │ ├── offer62 │ │ └── Solution.java │ │ ├── offer41 │ │ └── Solution.java │ │ ├── offer61 │ │ └── Solution.java │ │ ├── offer29 │ │ └── Solution.java │ │ ├── offer59 │ │ └── Solution.java │ │ ├── offer44 │ │ └── Solution.java │ │ ├── offer11 │ │ └── Solution.java │ │ ├── offer60 │ │ ├── Solution1.java │ │ └── Solution.java │ │ ├── offer19 │ │ └── Solution.java │ │ └── offer67 │ │ └── Solution.java │ └── .idea │ ├── kotlinc.xml │ ├── modules.xml │ └── inspectionProfiles │ └── Project_Default.xml └── notes ├── 求1+2+...+n.md ├── 不用加减乘除做加法.md ├── 二叉树的镜像.md ├── 构建乘积数组.md ├── 股票的最大利润.md ├── 反转链表.md ├── 包含min函数的栈.md ├── 合并两个排序的链表.md ├── 对称的二叉树.md ├── 圆圈中最后剩下的数字.md ├── 把数字翻译成字符串.md ├── 连续子数组的最大和.md ├── 把数组排成最小的数.md ├── 二叉搜索树的第K大的节点.md ├── 栈的压入、弹出序列.md ├── 从上到下打印二叉树.md ├── 二维数组中的查找.md ├── 序列化二叉树.md ├── 扑克牌中的顺子.md ├── 字符串的排列.md ├── 最长不含重复字符的子字符串.md ├── 二进制中1的个数.md ├── 链表中倒数第K个节点.md ├── 二叉搜索树与双向链表.md ├── 二叉树中和为某一值的路径.md ├── 队列的最大值.md ├── 1~n整数中1出现的次数.md ├── 顺时针打印矩阵.md ├── 二叉树的下一个节点.md ├── 用两个栈实现队列.md ├── 二叉搜索树的后序遍历序列.md ├── 数据流中的中位数.md ├── 树的子结构.md └── 第一个只出现一次的字符.md /images/offer12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer12.png -------------------------------------------------------------------------------- /images/offer23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer23.png -------------------------------------------------------------------------------- /images/offer25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer25.png -------------------------------------------------------------------------------- /images/offer26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer26.png -------------------------------------------------------------------------------- /images/offer27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer27.png -------------------------------------------------------------------------------- /images/offer28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer28.png -------------------------------------------------------------------------------- /images/offer32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer32.png -------------------------------------------------------------------------------- /images/offer33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer33.png -------------------------------------------------------------------------------- /images/offer36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer36.png -------------------------------------------------------------------------------- /images/offer52.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer52.png -------------------------------------------------------------------------------- /images/offer54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer54.png -------------------------------------------------------------------------------- /images/offer10-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer10-1.gif -------------------------------------------------------------------------------- /images/offer35-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer35-1.png -------------------------------------------------------------------------------- /images/offer35-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer35-2.png -------------------------------------------------------------------------------- /images/offer35-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer35-3.png -------------------------------------------------------------------------------- /images/offer35-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer35-4.png -------------------------------------------------------------------------------- /images/offer8-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer8-1.png -------------------------------------------------------------------------------- /images/offer8-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todorex/Coding-Interviews/HEAD/images/offer8-2.png -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer9/BuildStack.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer9; 2 | 3 | 4 | /** 5 | * 用两个队列实现栈 6 | * @Author rex 7 | * 2018/6/17 8 | */ 9 | public class BuildStack { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer22/ListNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer22; 2 | 3 | /** 4 | * @Author rex 5 | * 2018/7/26 6 | */ 7 | public class ListNode { 8 | int val; 9 | ListNode next = null; 10 | } 11 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer23/ListNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer23; 2 | 3 | /** 4 | * @Author rex 5 | * 2018/7/26 6 | */ 7 | public class ListNode { 8 | int val; 9 | ListNode next = null; 10 | } 11 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer24/ListNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer24; 2 | 3 | /** 4 | * @Author rex 5 | * 2018/7/26 6 | */ 7 | public class ListNode { 8 | int val; 9 | ListNode next = null; 10 | } 11 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer25/ListNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer25; 2 | 3 | /** 4 | * @Author rex 5 | * 2018/7/26 6 | */ 7 | public class ListNode { 8 | int val; 9 | ListNode next = null; 10 | } 11 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer52/ListNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer52; 2 | 3 | /** 4 | * 链表节点 5 | * @Author rex 6 | * 2018/9/4 7 | */ 8 | public class ListNode { 9 | int value; 10 | ListNode next; 11 | } 12 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer18/ListNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer18; 2 | 3 | /** 4 | * 链表节点 5 | * 6 | * @Author rex 7 | * 2018/7/23 8 | */ 9 | public class ListNode { 10 | int val; 11 | ListNode next = null; 12 | } 13 | -------------------------------------------------------------------------------- /code/Coding-Interviews/.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer32/BinaryTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer32; 2 | 3 | /** 4 | * 二叉树节点 5 | * 6 | * @Author rex 7 | * 2018/8/1 8 | */ 9 | public class BinaryTreeNode { 10 | int value; 11 | BinaryTreeNode left; 12 | BinaryTreeNode right; 13 | } 14 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer33/BinaryTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer33; 2 | 3 | /** 4 | * 二叉树节点 5 | * 6 | * @Author rex 7 | * 2018/8/1 8 | */ 9 | public class BinaryTreeNode { 10 | int value; 11 | BinaryTreeNode left; 12 | BinaryTreeNode right; 13 | } 14 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer34/BinaryTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer34; 2 | 3 | /** 4 | * 二叉树节点 5 | * 6 | * @Author rex 7 | * 2018/8/1 8 | */ 9 | public class BinaryTreeNode { 10 | int value; 11 | BinaryTreeNode left; 12 | BinaryTreeNode right; 13 | } 14 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer26/BinaryTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer26; 2 | 3 | /** 4 | * 二叉树节点 5 | * 6 | * @Author rex 7 | * 2018/8/1 8 | */ 9 | public class BinaryTreeNode { 10 | double value; 11 | BinaryTreeNode left; 12 | BinaryTreeNode right; 13 | } 14 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer27/BinaryTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer27; 2 | 3 | /** 4 | * 二叉树节点 5 | * 6 | * @Author rex 7 | * 2018/8/1 8 | */ 9 | public class BinaryTreeNode { 10 | double value; 11 | BinaryTreeNode left; 12 | BinaryTreeNode right; 13 | } 14 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer28/BinaryTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer28; 2 | 3 | /** 4 | * 二叉树节点 5 | * 6 | * @Author rex 7 | * 2018/8/1 8 | */ 9 | public class BinaryTreeNode { 10 | double value; 11 | BinaryTreeNode left; 12 | BinaryTreeNode right; 13 | } 14 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer55/BinaryTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer55; 2 | 3 | /** 4 | * 二叉树节点 5 | * 6 | * @Author rex 7 | * 2018/8/1 8 | */ 9 | public class BinaryTreeNode { 10 | double value; 11 | BinaryTreeNode left; 12 | BinaryTreeNode right; 13 | } 14 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer6/ListNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer6; 2 | 3 | /** 4 | * @Author rex 5 | * 2018/6/10 6 | */ 7 | public class ListNode { 8 | int val; 9 | ListNode next = null; 10 | 11 | ListNode(int val) { 12 | this.val = val; 13 | } 14 | ListNode(){} 15 | } 16 | -------------------------------------------------------------------------------- /code/Coding-Interviews/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer54/TreeNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer54; 2 | 3 | /** 4 | * 二叉树节点 5 | * @Author rex 6 | * 2018/6/11 7 | */ 8 | public class TreeNode { 9 | int val; 10 | TreeNode left; 11 | TreeNode right; 12 | 13 | TreeNode(int x) { 14 | val = x; 15 | } 16 | TreeNode(){} 17 | } -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer68/TreeNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer68; 2 | 3 | /** 4 | * 二叉树节点 5 | * @Author rex 6 | * 2018/6/11 7 | */ 8 | public class TreeNode { 9 | int val; 10 | TreeNode left; 11 | TreeNode right; 12 | 13 | TreeNode(int x) { 14 | val = x; 15 | } 16 | TreeNode(){} 17 | } 18 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer7/TreeNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer7; 2 | 3 | /** 4 | * 二叉树节点 5 | * @Author rex 6 | * 2018/6/11 7 | */ 8 | public class TreeNode { 9 | int val; 10 | TreeNode left; 11 | TreeNode right; 12 | 13 | TreeNode(int x) { 14 | val = x; 15 | } 16 | TreeNode(){} 17 | } 18 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer21/ReorderStrategy.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer21; 2 | 3 | /** 4 | * 重排序策略 5 | * @Author rex 6 | * 2018/7/25 7 | */ 8 | public interface ReorderStrategy { 9 | /** 10 | * 判断符合某种条件 11 | * @param array 12 | * @param index 13 | */ 14 | boolean reorderBySomething(int[] array, int index); 15 | } 16 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer36/BinaryTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer36; 2 | 3 | /** 4 | * 二叉树节点 5 | * 6 | * @Author rex 7 | * 2018/8/1 8 | */ 9 | public class BinaryTreeNode { 10 | int value; 11 | BinaryTreeNode left; 12 | BinaryTreeNode right; 13 | 14 | public BinaryTreeNode(int value) { 15 | this.value = value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer37/BinaryTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer37; 2 | 3 | /** 4 | * 二叉树节点 5 | * 6 | * @Author rex 7 | * 2018/8/1 8 | */ 9 | public class BinaryTreeNode { 10 | int value; 11 | BinaryTreeNode left; 12 | BinaryTreeNode right; 13 | 14 | public BinaryTreeNode(int value) { 15 | this.value = value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer35/ComplexListNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer35; 2 | 3 | /** 4 | * 复杂链表的节点 5 | * 6 | * @Author rex 7 | * 2018/8/10 8 | */ 9 | public class ComplexListNode { 10 | int value; 11 | ComplexListNode next; 12 | ComplexListNode random; 13 | 14 | public ComplexListNode(int value) { 15 | this.value = value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer8/TreeLinkNode.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer8; 2 | 3 | /** 4 | * 二叉树节点 5 | * @Author rex 6 | * 2018/6/13 7 | */ 8 | public class TreeLinkNode { 9 | int val; 10 | TreeLinkNode left = null; 11 | TreeLinkNode right = null; 12 | // 父节点 13 | TreeLinkNode parent = null; 14 | 15 | TreeLinkNode(int val) { 16 | this.val = val; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer46/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer46; 2 | 3 | /** 4 | * 把数字翻译成字符串 5 | * 6 | * @Author rex 7 | * 2018/8/29 8 | */ 9 | public class Solution1 { 10 | /** 11 | * 递归解题 12 | * 13 | * f(i)=f(i-1) + g(i,i-1)f(i-2) 14 | * 15 | * @param s 16 | * @return 17 | */ 18 | public int numDecodings(String s) { 19 | return 0; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer10/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer10; 2 | 3 | /** 4 | * 最简单的递归 5 | * 没有考虑时间复杂度 6 | * @Author rex 7 | * 2018/7/15 8 | */ 9 | public class Solution { 10 | // 很垃圾 11 | public int Fibonacci(int n) { 12 | if(n == 0) { 13 | return 0; 14 | } 15 | if(n == 1) { 16 | return 1; 17 | } 18 | return Fibonacci(n-1) + Fibonacci(n-2); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer17/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer17; 2 | 3 | /** 4 | * 打印从1到最大的n位数 5 | * 6 | * @Author rex 7 | * 2018/7/22 8 | */ 9 | public class Solution { 10 | /** 11 | * 已经掉入陷阱 12 | * @param n 13 | */ 14 | public void print1ToMaxOfNDigits(int n) { 15 | int number = (int) Math.pow(10, n); 16 | for (int i = 1; i < number; i++) { 17 | System.out.println(i); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer21/OddEventReorderStategy.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer21; 2 | 3 | /** 4 | * 按照奇偶重排序 5 | * @Author rex 6 | * 2018/7/25 7 | */ 8 | public class OddEventReorderStategy implements ReorderStrategy { 9 | /** 10 | * 返回是否为偶数 11 | * @param array 12 | * @param index 13 | * @return 14 | */ 15 | @Override 16 | public boolean reorderBySomething(int[] array, int index) { 17 | return (array[index] & 1) == 0; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer16/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer16; 2 | 3 | /** 4 | * 数值的整数次方 5 | * 6 | * @Author rex 7 | * 2018/7/21 8 | */ 9 | public class Solution { 10 | /** 11 | * 自己解题 12 | * 13 | * @param base 14 | * @param exponent 15 | * @return 16 | */ 17 | public double power(double base, int exponent) { 18 | double result = 1.0; 19 | for (int i = 1; i <= exponent; i++) { 20 | result *= base; 21 | } 22 | return result; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer20/Solution2.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer20; 2 | 3 | /** 4 | * 表示数值的字符串 5 | * 6 | * @Author rex 7 | * 2018/7/24 8 | */ 9 | public class Solution2 { 10 | /** 11 | * 正则表达式解法 12 | * 13 | * @param str 14 | * @return 15 | */ 16 | public boolean isNumeric(char[] str) { 17 | // 防止特殊输入 18 | if (str == null || str.length < 0) { 19 | return false; 20 | } 21 | return new String(str).matches("[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer55/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer55; 2 | 3 | /** 4 | * 二叉树的深度 5 | * 6 | * @Author rex 7 | * 2018/9/11 8 | */ 9 | public class Solution1 { 10 | /** 11 | * 递归 12 | * @param root 13 | * @return 14 | */ 15 | public int treeDepth(BinaryTreeNode root) { 16 | if (root == null) { 17 | return 0; 18 | } 19 | int left = treeDepth(root.left); 20 | int right = treeDepth(root.right); 21 | return Math.max(left, right) + 1; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer64/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer64; 2 | 3 | /** 4 | * 求1+2+...+n 5 | * 6 | * @Author rex 7 | * 2018/9/18 8 | */ 9 | public class Solution { 10 | 11 | public int sum_Solution(int n) { 12 | int sum = n; 13 | boolean temp = n > 0 && (sum += sum_Solution(n - 1)) > 0; 14 | return sum; 15 | } 16 | 17 | public static void main(String[] args) { 18 | Solution solution = new Solution(); 19 | System.out.println(solution.sum_Solution(5)); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer10/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer10; 2 | 3 | /** 4 | * 聪明的循环 5 | * 考虑时间复杂度 6 | * @Author rex 7 | * 2018/7/15 8 | */ 9 | public class Solution1 { 10 | public int Fibonacci(int n) { 11 | if (n < 2) { 12 | return n; 13 | } 14 | int pre1 = 0; 15 | int pre2 = 1; 16 | int fib = 0; 17 | for (int i = 2; i <=n; i++) { 18 | fib = pre1 + pre2; 19 | pre1 = pre2; 20 | pre2 = fib; 21 | } 22 | return fib; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer27/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer27; 2 | 3 | /** 4 | * 二叉树的镜像 5 | * 6 | * @Author rex 7 | * 2018/8/2 8 | */ 9 | public class Solution { 10 | /** 11 | * 自己解法 12 | * 递归 13 | * @param root 14 | */ 15 | public void mirror(BinaryTreeNode root) { 16 | if (root == null) { 17 | return; 18 | } 19 | if (root.left != null || root.right != null) { 20 | BinaryTreeNode temp = root.left; 21 | root.left = root.right; 22 | root.right = temp; 23 | mirror(root.left); 24 | mirror(root.right); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer10/FrogSolution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer10; 2 | 3 | /** 4 | * 青蛙台阶问题 5 | * @Author rex 6 | * 2018/7/15 7 | */ 8 | public class FrogSolution { 9 | 10 | /** 11 | * 采用斐波那契解法 12 | * @param target 13 | * @return 14 | */ 15 | public int JumpFloor(int target) { 16 | if (target < 3) { 17 | return target; 18 | } 19 | int pre1 = 1; 20 | int pre2 = 2; 21 | int fib = 0; 22 | for (int i = 3; i <= target; i++) { 23 | fib = pre1 + pre2; 24 | pre1 = pre2; 25 | pre2 = fib; 26 | } 27 | return fib; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer65/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer65; 2 | 3 | /** 4 | * 不用加减乘除做加法 5 | * 6 | * @Author rex 7 | * 2018/9/18 8 | */ 9 | public class Solution { 10 | /** 11 | * 位运算解题 12 | * 13 | * @param num1 14 | * @param num2 15 | * @return 16 | */ 17 | public int add(int num1,int num2) { 18 | int sum, carry; 19 | do { 20 | // 1. 不考虑进位加 21 | sum = num1 ^ num2; 22 | // 2. 算出进位的值 23 | carry = (num1 & num2) << 1; 24 | num1 = sum; 25 | num2 = carry; 26 | } while (carry != 0); 27 | // 3. 当没有进位的时候,num1就是最终的和 28 | return num1; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer15/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer15; 2 | 3 | /** 4 | * 二进制中1的个数 5 | * @Author rex 6 | * 2018/7/20 7 | */ 8 | public class Solution { 9 | /** 10 | * 惊喜解法 11 | * 12 | * 利用了把一个整数减去1之后再后原来的整数做位与运算,得到的结果相当于把整数的二进制表示中最右边的1变成0 13 | * 14 | * @param n 15 | * @return 16 | */ 17 | public int numberOf1(int n) { 18 | int count = 0; 19 | while (n != 0) { 20 | ++count; 21 | n = (n-1) & n; 22 | } 23 | return count; 24 | } 25 | 26 | public static void main(String[] args) { 27 | Solution solution = new Solution(); 28 | System.out.println(solution.numberOf1(-1));; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer2/Singleton1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer2; 2 | 3 | /** 4 | * 饿汉式单例模式 5 | * 线程安全 6 | * @Author rex 7 | * 2018/6/8 8 | */ 9 | public class Singleton1 { 10 | /** 11 | * 私有静态变量 12 | * 直接实例化 13 | * 丢失了延迟实例化带来的节约资源的优势 14 | */ 15 | private static Singleton1 singleton = new Singleton1(); 16 | 17 | /** 18 | * @Author rex 19 | * @Date 2018/6/7 下午9:39 20 | * @Description 私有构造函数 21 | */ 22 | private Singleton1() { 23 | } 24 | 25 | /** 26 | * @Author rex 27 | * @Date 2018/6/7 下午9:41 28 | * @Description 公有获取单例对象静态函数 29 | */ 30 | public static Singleton1 getSingleton() { 31 | return singleton; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer2/Singleton.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer2; 2 | 3 | /** 4 | * 实现Singleton模式 5 | * @Author rex 6 | * 2018/6/7. 7 | */ 8 | public class Singleton { 9 | 10 | /** 11 | * 私有静态变量 12 | */ 13 | private static Singleton singleton; 14 | 15 | /** 16 | * @Author rex 17 | * @Date 2018/6/7 下午9:39 18 | * @Description 私有构造函数 19 | */ 20 | private Singleton() { 21 | } 22 | 23 | /** 24 | * @Author rex 25 | * @Date 2018/6/7 下午9:41 26 | * @Description 公有获取单例对象函数 27 | */ 28 | public static Singleton getSingleton() { 29 | if (singleton == null) { 30 | singleton = new Singleton(); 31 | } 32 | return singleton; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer24/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer24; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 反转链表 7 | * 8 | * @Author rex 9 | * 2018/7/28 10 | */ 11 | public class Solution { 12 | /** 13 | * 自己解法 14 | * @param head 15 | * @return 16 | */ 17 | public ListNode ReverseList(ListNode head) { 18 | if (head == null) { 19 | return null; 20 | } 21 | ListNode currHead = head; 22 | ListNode temp = null; 23 | while (head.next != null) { 24 | temp = currHead; 25 | currHead = head.next; 26 | head.next = currHead.next; 27 | currHead.next = temp; 28 | } 29 | return currHead; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer66/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer66; 2 | 3 | /** 4 | * 构建乘积矩阵 5 | * 6 | * @Author rex 7 | * 2018/9/18 8 | */ 9 | public class Solution { 10 | /** 11 | * 保存中间过程很重要 12 | * @param A 13 | * @return 14 | */ 15 | public int[] multiply(int[] A) { 16 | int n = A.length; 17 | int[] B = new int[n]; 18 | // 从左往右累乘(但是保证B[i] = A[0]*...*A[i-1]) 19 | for (int i = 0, temp = 1; i < n; temp *= A[i], i++) { 20 | B[i] = temp; 21 | } 22 | // 从右往左累乘(但是保证B[i] = B[i] * A[n-1] * ... * A[i + 1]) 23 | for (int i = n - 1, temp = 1; i >= 0; temp *= A[i], i--) { 24 | B[i] *= temp; 25 | } 26 | return B; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer3/Duplicate.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer3; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * 数组中重复的数字 7 | * 利用HashMap 8 | * @Author rex 9 | * 2018/6/8 10 | */ 11 | public class Duplicate { 12 | /** 13 | * @Author rex 14 | * @Date 2018/6/8 下午10:03 15 | * @Description 找出数组中重复的数字 16 | */ 17 | public static int findDuplicate(int[] intArray) { 18 | // 最好设置HashMap的初始大小,防止扩容浪费时间 19 | Map map = new HashMap<>(intArray.length); 20 | for (int i : intArray) { 21 | if (map.get(i) == null) { 22 | map.put(i, 1); 23 | } else { 24 | return i; 25 | } 26 | } 27 | return -1; 28 | 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer9/BuildQueue.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer9; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * 构建队列的声明 7 | * @Author rex 8 | * 2018/6/17 9 | */ 10 | public class BuildQueue { 11 | Stack stack1 = new Stack(); 12 | Stack stack2 = new Stack(); 13 | 14 | public void push(int node) { 15 | stack1.push(node); 16 | } 17 | 18 | public int pop() { 19 | while(!stack1.isEmpty()) { 20 | int a = stack1.pop(); 21 | stack2.push(a); 22 | } 23 | int b = stack2.pop(); 24 | while(!stack2.isEmpty()) { 25 | int c = stack2.pop(); 26 | stack1.push(c); 27 | } 28 | return b; 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /notes/求1+2+...+n.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 求1+2+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C) 3 | 4 | # 测试用例 5 | * 功能测试(输入5、10求1+2+...+5和1+2+...+10) 6 | * 边界值测试(输入0和1) 7 | 8 | # 题目考点 9 | * 考察应聘者的发散思维能力。 10 | * 考察应聘者的知识面的广度和深度。 11 | 12 | # 解题思路 13 | 反正肯定是用递归,然后我们需要的就是怎么把返回条件确定好。 14 | 15 | 条件与 && 具有短路原则,即在第一个条件语句为 false 的情况下不会去执行第二个条件语句。利用这一特性,将递归的返回条件取非然后作为 && 的第一个条件语句,递归的主体转换为第二个条件语句,那么当递归的返回条件为 true 的情况下就不会执行递归的主体部分,递归返回。 16 | 17 | 18 | # 自己解题 19 | 发散思维不够呀 20 | # 参考解题 21 | ```Java 22 | /** 23 | * 求1+2+...+n 24 | * 25 | * @Author rex 26 | * 2018/9/18 27 | */ 28 | public class Solution { 29 | 30 | public int sum_Solution(int n) { 31 | int sum = n; 32 | boolean temp = n > 0 && (sum +=sum_Solution(n - 1)) > 0; 33 | return sum; 34 | } 35 | } 36 | ``` 37 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer15/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer15; 2 | 3 | /** 4 | * 二进制中1的个数 5 | * 6 | * @Author rex 7 | * 2018/7/20 8 | */ 9 | public class Solution1 { 10 | /** 11 | * 常规解法 12 | * 利用1左移,然后与n进行位与操作 13 | * 14 | * @param n 15 | * @return 16 | */ 17 | public int numberOf1(int n) { 18 | int count = 0; 19 | int index = 1; 20 | while (index != 0) { 21 | if ((n & index) > 0) { 22 | count ++; 23 | } 24 | index = index << 1; 25 | } 26 | return count; 27 | } 28 | 29 | public static void main(String[] args) { 30 | Solution1 solution1 = new Solution1(); 31 | System.out.println(solution1.numberOf1(-1));; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer9/BuildQueue1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer9; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * 用两个栈实现队列 7 | * 8 | * @Author rex 9 | * 2018/6/17 10 | */ 11 | public class BuildQueue1 { 12 | Stack stack1 = new Stack(); 13 | Stack stack2 = new Stack(); 14 | 15 | public void push(int node) { 16 | stack1.push(node); 17 | } 18 | 19 | public int pop() throws Exception { 20 | if (stack2.isEmpty()) { 21 | while (!stack1.isEmpty()) { 22 | stack2.push(stack1.pop()); 23 | } 24 | } 25 | 26 | if (stack2.isEmpty()) { 27 | throw new Exception("queue is empty"); 28 | 29 | } 30 | return stack2.pop(); 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer2/Singleton2.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer2; 2 | 3 | /** 4 | * 懒汉式双重校验锁单例模式 5 | * 线程安全 6 | * 7 | * @Author rex 8 | * 2018/6/7 9 | */ 10 | public class Singleton2 { 11 | /** 12 | * 私有静态变量 13 | */ 14 | private static Singleton2 singleton; 15 | 16 | /** 17 | * @Author rex 18 | * @Date 2018/6/7 下午9:39 19 | * @Description 私有构造函数 20 | */ 21 | private Singleton2() { 22 | 23 | } 24 | 25 | /** 26 | * @Author rex 27 | * @Date 2018/6/8 下午7:28 28 | * @Description 公有获取单例对象静态函数 29 | * 在方法级别上加锁 30 | * 当一个线程进入该方法之后,其它线程试图进入该方法都必须等待, 31 | * 因此性能上有一定的损耗 32 | */ 33 | public static synchronized Singleton2 getSingleton() { 34 | if (singleton == null) { 35 | singleton = new Singleton2(); 36 | } 37 | return singleton; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer63/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer63; 2 | 3 | /** 4 | * 股票的最大利润 5 | * 6 | * @Author rex 7 | * 2018/9/18 8 | */ 9 | public class Solution { 10 | /** 11 | * O(N)解题 12 | * @param prices 13 | * @return 14 | */ 15 | public int maxProfit(int[] prices) { 16 | // 防止特殊输入 17 | if (prices == null || prices.length < 2) { 18 | return 0; 19 | } 20 | // 保存最大利润 21 | int maxProfit = 0; 22 | // 保存之前的最小值 23 | int min = prices[0]; 24 | for (int i = 1; i < prices.length; i++) { 25 | if (prices[i] - min > maxProfit) { 26 | maxProfit = prices[i] - min; 27 | } 28 | if (prices[i] < min) { 29 | min = prices[i]; 30 | } 31 | } 32 | return maxProfit; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer25/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer25; 2 | 3 | /** 4 | * 合并两个排序的链表 5 | * 6 | * @Author rex 7 | * 2018/7/30 8 | */ 9 | public class Solution { 10 | /** 11 | * 自己解法 12 | * 递归实现 13 | * @param list1 14 | * @param list2 15 | * @return 16 | */ 17 | public ListNode Merge(ListNode list1,ListNode list2) { 18 | if (list1 == null) { 19 | return list2; 20 | } else if (list2 == null) { 21 | return list1; 22 | } 23 | ListNode resultNode = null; 24 | if (list1.val < list2.val) { 25 | resultNode = list1; 26 | resultNode.next = Merge(list1.next, list2); 27 | } else { 28 | resultNode = list2; 29 | resultNode.next = Merge(list1, list2.next); 30 | } 31 | return resultNode; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer42/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer42; 2 | 3 | 4 | 5 | /** 6 | * 连续子数组的最大和 7 | * 8 | * @Author rex 9 | * 2018/8/25 10 | */ 11 | public class Solution { 12 | /** 13 | * 找规律解法(思想还是动态规划的) 14 | * @param array 15 | * @return 16 | */ 17 | public int findGreatestSumOfSubArray(int[] array) { 18 | 19 | if (array == null || array.length == 0) { 20 | return 0; 21 | } 22 | int max = array[0]; 23 | 24 | int curMax = max; 25 | for (int i = 1; i < array.length; i++) { 26 | 27 | if (curMax <= 0) { 28 | curMax = array[i]; 29 | 30 | } else { 31 | curMax += array[i]; 32 | 33 | } 34 | if (curMax > max) { 35 | max = curMax; 36 | } 37 | 38 | } 39 | return max; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer28/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer28; 2 | 3 | /** 4 | * 对称的二叉树 5 | * 6 | * @Author rex 7 | * 2018/8/3 8 | */ 9 | public class Solution { 10 | /** 11 | * 理解完镜像 12 | * 递归解法 13 | * @param pRoot 14 | * @return 15 | */ 16 | boolean isSymmetrical(BinaryTreeNode pRoot) { 17 | if (pRoot == null) { 18 | return false; 19 | } 20 | return isSymmetrical(pRoot.left, pRoot.right); 21 | } 22 | 23 | boolean isSymmetrical(BinaryTreeNode pRoot1, BinaryTreeNode pRoot2) { 24 | if (pRoot1 == null && pRoot2 == null) { 25 | return true; 26 | } 27 | if (pRoot1 == null || pRoot2 == null || pRoot1.value != pRoot2.value) { 28 | return false; 29 | } 30 | return isSymmetrical(pRoot1.left, pRoot2.right) && isSymmetrical(pRoot1.right, pRoot2.left); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer6/Solution3.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer6; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Stack; 5 | 6 | /** 7 | * 从尾到图打印链表 8 | * 9 | * @Author rex 10 | * 2018/6/10 11 | */ 12 | public class Solution3 { 13 | /** 14 | * @Author rex 15 | * @Date 2018/6/10 下午8:09 16 | * @Description 从尾到头打印链表(使用递归) 17 | */ 18 | public static ArrayList printListFromTailToHead(ListNode listNode) { 19 | ArrayList result = new ArrayList(); 20 | 21 | if (listNode != null) { 22 | result.addAll(printListFromTailToHead(listNode.next)); 23 | result.add(listNode.val); 24 | } 25 | return result; 26 | } 27 | 28 | public static void main(String[] args) { 29 | ListNode listNode = new ListNode(2); 30 | System.out.println(printListFromTailToHead(listNode)); 31 | 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer30/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer30; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * 包含min函数的栈 7 | * 8 | * @Author rex 9 | * 2018/8/5 10 | */ 11 | public class Solution { 12 | /** 13 | * 存储数据 14 | */ 15 | Stack dataStack = new Stack<>(); 16 | /** 17 | * 存储每一次的最小值 18 | */ 19 | Stack minStack = new Stack<>(); 20 | 21 | public void push(int node) { 22 | dataStack.push(node); 23 | if (minStack.empty() || node < minStack.peek()) { 24 | minStack.push(node); 25 | } else { 26 | minStack.push(minStack.peek()); 27 | } 28 | } 29 | 30 | public void pop() { 31 | dataStack.pop(); 32 | minStack.pop(); 33 | 34 | } 35 | 36 | public int top() { 37 | return dataStack.peek(); 38 | } 39 | 40 | public int min() { 41 | return minStack.peek(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /notes/不用加减乘除做加法.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 写一个函数,求两个整数之和,要求在函数体内不得使用“+”、“-”、“\*”、“\/”四则运算符号。 3 | 4 | # 测试用例 5 | 输入整数、负数和0。 6 | 7 | # 题目考点 8 | * 考察应聘者的发散思维能力。 9 | * 考察应聘者对二进制和位运算的理解。 10 | 11 | # 解题思路 12 | 分为三步走: 13 | 1. 利用位异或运算不考虑进位地加两个数 14 | 2. 利用位与运算以及左移运算得到进制代表的值 15 | 3. 将上面1,2的结果相加(即重复1,2运算,直到进位为0) 16 | 17 | # 自己解题 18 | 位运算不熟!!! 19 | # 参考解题 20 | ```Java 21 | /** 22 | * 不用加减乘除做加法 23 | * 24 | * @Author rex 25 | * 2018/9/18 26 | */ 27 | public class Solution { 28 | /** 29 | * 位运算解题 30 | * 31 | * @param num1 32 | * @param num2 33 | * @return 34 | */ 35 | public int add(int num1,int num2) { 36 | int sum, carry; 37 | do { 38 | // 1. 不考虑进位加 39 | sum = num1 ^ num2; 40 | // 2. 算出进位的值 41 | carry = (num1 & num2) << 1; 42 | num1 = sum; 43 | num2 = carry; 44 | } while (carry != 0); 45 | // 3. 当没有进位的时候,num1就是最终的和 46 | return num1; 47 | } 48 | } 49 | ``` 50 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer2/Singleton3.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer2; 2 | 3 | /** 4 | * 实现懒汉式双重校验锁Singleton模式 5 | * 线程安全 6 | * 7 | * @Author rex 8 | * 2018/6/7 9 | */ 10 | public class Singleton3 { 11 | /** 12 | * 私有静态变量 13 | */ 14 | private static Singleton3 singleton; 15 | 16 | /** 17 | * @Author rex 18 | * @Date 2018/6/7 下午9:39 19 | * @Description 私有构造函数 20 | */ 21 | private Singleton3() { 22 | } 23 | 24 | /** 25 | * @Author rex 26 | * @Date 2018/6/7 下午9:46 27 | * @Description 公有获取单例对象静态函数 28 | * 加同步锁前后两次判断实例是否已存在 29 | * 缩小的同步代码块 30 | */ 31 | public static Singleton3 getSingleton() { 32 | if (singleton == null) { 33 | synchronized (Singleton3.class) { 34 | if (singleton == null) { 35 | singleton = new Singleton3(); 36 | } 37 | } 38 | } 39 | return singleton; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer5/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer5; 2 | 3 | /** 4 | * 替换空格 5 | * @Author rex 6 | * 2018/6/10 7 | */ 8 | public class Solution { 9 | 10 | /** 11 | * @Author rex 12 | * @Date 2018/6/10 下午4:24 13 | * @Description 替换空格 14 | * 利用StringBuffer本身的替换 15 | */ 16 | public static String replaceSpace(StringBuffer str) { 17 | if (str == null) { 18 | return null; 19 | } 20 | String string = str.toString(); 21 | int j = 0; 22 | for (int i = 0; i < string.length(); i++) { 23 | if (string.charAt(i) == ' ') { 24 | int index = i + j * 2; 25 | str.replace(index, index + 1, "%20"); 26 | j++; 27 | } 28 | } 29 | return str.toString(); 30 | } 31 | public static void main(String[] args) { 32 | System.out.println(replaceSpace(new StringBuffer(" "))); 33 | } 34 | 35 | } 36 | 37 | 38 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer51/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer51; 2 | 3 | /** 4 | * 数组中的逆序对 5 | * 6 | * @Author rex 7 | * 2018/9/3 8 | */ 9 | public class Solution { 10 | /** 11 | * low 12 | * 时间复杂度太大 13 | * @param array 14 | * @return 15 | */ 16 | public int inversePairs(int [] array) { 17 | int count = 0; 18 | if (array == null || array.length == 0) { 19 | return count; 20 | } 21 | 22 | 23 | for (int i = 0; i < array.length; i++) { 24 | for (int j = i + 1; j < array.length; j++) { 25 | if (array[i] > array[j]) { 26 | count++; 27 | } 28 | } 29 | } 30 | return count; 31 | } 32 | 33 | 34 | public static void main(String[] args) { 35 | Solution solution = new Solution(); 36 | int[] array = new int[]{7, 5, 6, 4}; 37 | System.out.println(solution.inversePairs(array)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer55/Solution2.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer55; 2 | 3 | /** 4 | * 平衡二叉树 5 | * 6 | * @Author rex 7 | * 2018/9/11 8 | */ 9 | public class Solution2 { 10 | /** 11 | * 自己解题 12 | * 太多重复遍历 19ms 13 | * @param root 14 | * @return 15 | */ 16 | public boolean isBalanced_Solution(BinaryTreeNode root) { 17 | if (root == null) { 18 | return true; 19 | } 20 | int left = treeDepth(root.left); 21 | int right = treeDepth(root.right); 22 | if (Math.abs(left-right) > 1) { 23 | return false; 24 | } 25 | return isBalanced_Solution(root.left) && isBalanced_Solution(root.right); 26 | } 27 | 28 | public int treeDepth(BinaryTreeNode root) { 29 | if (root == null) { 30 | return 0; 31 | } 32 | int left = treeDepth(root.left); 33 | int right = treeDepth(root.right); 34 | return Math.max(left, right) + 1; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer22/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer22; 2 | 3 | 4 | /** 5 | * 链表中的倒数第K个节点 6 | * @Author rex 7 | * 2018/7/26 8 | */ 9 | public class Solution { 10 | /** 11 | * 自己解法 12 | * @param head 13 | * @param k 14 | * @return 15 | */ 16 | public ListNode findKthToTail(ListNode head, int k) { 17 | // 防止空指针和k=0无意义 18 | if (head == null || k == 0) { 19 | return null; 20 | } 21 | // 第一个指针的步数 22 | int i = 0; 23 | ListNode firstNode = head; 24 | ListNode secondNode = head; 25 | while (firstNode.next != null) { 26 | firstNode = firstNode.next; 27 | i++; 28 | if (i > k-1) { 29 | // 保持两个指针距离为k-1 30 | secondNode = secondNode.next; 31 | } 32 | } 33 | // 还没走到k-1步就结束了 34 | // 表示链表的节点总数少于k 35 | if (i findNumbersWithSum(int [] array, int sum) { 20 | 21 | ArrayList result = new ArrayList(); 22 | // 非法输入 23 | if (array == null || array.length < 2) { 24 | return result; 25 | } 26 | 27 | for (int i = 0; i < array.length; i++) { 28 | for (int j = i + 1; j < array.length; j++) { 29 | if (array[i] + array[j] == sum) { 30 | result.add(array[i]); 31 | result.add(array[j]); 32 | return result; 33 | } 34 | } 35 | } 36 | return result; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer6/Solution2.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer6; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Stack; 5 | 6 | /** 7 | * 从尾到头打印链表 8 | * @Author rex 9 | * 2018/6/10 10 | */ 11 | public class Solution2 { 12 | /** 13 | * @Author rex 14 | * @Date 2018/6/10 下午8:09 15 | * @Description 从尾到头打印链表(使用栈) 16 | */ 17 | public static ArrayList printListFromTailToHead(ListNode listNode) { 18 | ArrayList result = new ArrayList(); 19 | 20 | Stack stack = new Stack<>(); 21 | while (listNode != null) { 22 | stack.push(listNode); 23 | listNode = listNode.next; 24 | } 25 | while (! stack.empty()) { 26 | result.add(stack.pop().val); 27 | } 28 | return result; 29 | } 30 | 31 | public static void main(String[] args) { 32 | ListNode listNode = new ListNode(2); 33 | System.out.println(printListFromTailToHead(listNode)); 34 | 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer18/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer18; 2 | 3 | 4 | /** 5 | * 在O(1)时间内删除链表节点 6 | * 7 | * @Author rex 8 | * 2018/7/23 9 | */ 10 | public class Solution1 { 11 | /** 12 | * 打破常规思维的链表删除方式 13 | * 14 | * @param head 15 | * @param toBeDeleted 16 | */ 17 | public void deleteNode(ListNode head, ListNode toBeDeleted) { 18 | // 两个指针否要考虑非法输入 19 | if (head == null || toBeDeleted == null) { 20 | return; 21 | } 22 | if (toBeDeleted.next != null) { 23 | // 要删除的节点不是尾节点 24 | ListNode next = toBeDeleted.next; 25 | toBeDeleted.val = next.val; 26 | toBeDeleted.next = next.next; 27 | toBeDeleted.next = null; 28 | } else { 29 | // 要删除的节点是尾节点(只能遍历) 30 | ListNode current = head; 31 | while (current.next != toBeDeleted) { 32 | current = current.next; 33 | } 34 | current.next = null; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer58/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer58; 2 | 3 | /** 4 | * 翻转单词顺序 5 | * 6 | * @Author rex 7 | * 2018/9/15 8 | */ 9 | public class Solution { 10 | /** 11 | * 需要额外空间 12 | * 13 | * @param str 14 | * @return 15 | */ 16 | public String reverseSentence(String str) { 17 | if (str == null || str.length() == 0) { 18 | return ""; 19 | } 20 | StringBuilder sb = new StringBuilder(); 21 | String[] temp = str.split(" "); 22 | if (temp.length == 0) { 23 | return str; 24 | } 25 | 26 | for (int i = temp.length - 1; i >= 0; i--) { 27 | sb.append(temp[i]); 28 | sb.append(" "); 29 | } 30 | sb.deleteCharAt(sb.length() - 1); 31 | return sb.toString(); 32 | } 33 | 34 | public static void main(String[] args) { 35 | Solution solution = new Solution(); 36 | String s = " "; 37 | System.out.println(solution.reverseSentence(s)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer45/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer45; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * 把数组排成最小的数 7 | * 8 | * @Author rex 9 | * 2018/8/28 10 | */ 11 | public class Solution { 12 | 13 | /** 14 | * 新的排序规则解题 15 | * @param numbers 16 | * @return 17 | */ 18 | public String printMinNumber(int [] numbers) { 19 | // 防止特殊输入 20 | if (numbers == null || numbers.length == 0) { 21 | return ""; 22 | } 23 | // 以防两个数相加溢出,将int 变成 string 24 | String[] numberStrings = new String[numbers.length]; 25 | for (int i = 0; i < numbers.length; i++) { 26 | numberStrings[i] = numbers[i] + ""; 27 | } 28 | 29 | // 利用新的排序规则排序 30 | Arrays.sort(numberStrings, (s1, s2) -> (s1 + s2).compareTo(s2 + s1)); 31 | 32 | StringBuilder result = new StringBuilder(); 33 | 34 | for (String s : numberStrings) { 35 | result.append(s); 36 | } 37 | 38 | return result.toString(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer43/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer43; 2 | 3 | /** 4 | * 1~n整数中1出现的次数 5 | * 6 | * @Author rex 7 | * 2018/8/26 8 | */ 9 | public class Solution { 10 | /** 11 | * 规律解法 12 | * 按数位可能出现1的次数统计 13 | * 14 | * @param n 15 | * @return 16 | */ 17 | public int numberOf1Between1AndN_Solution(int n) { 18 | int count = 0; 19 | for (int m = 1; m <= n; m *= 10) { 20 | int a = n / m; 21 | int b = n % m; 22 | 23 | // count += (a + 8) / 10 * m + (a % 10 == 1 ? b + 1 : 0); 24 | // 上面一句抵下面所有句 25 | 26 | int temp = a % 10; 27 | 28 | if (temp == 0) { 29 | // 情况1:当前数位为0 30 | count += a / 10 * m; 31 | } else if (temp == 1) { 32 | // 情况2:当前数位为1 33 | count += a / 10 * m + b + 1; 34 | } else { 35 | // 情况3:当前数位大于1 36 | count += (a / 10 + 1) * m; 37 | } 38 | } 39 | return count; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer8/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer8; 2 | 3 | /** 4 | * 二叉树的下一个节点 5 | * 6 | * @Author rex 7 | * 2018/6/13 8 | */ 9 | public class Solution { 10 | 11 | /** 12 | * 寻找二叉树的下一个节点 13 | * 14 | * @param pNode 15 | * @return 16 | */ 17 | public TreeLinkNode GetNext(TreeLinkNode pNode) { 18 | if (pNode == null) { 19 | return null; 20 | } 21 | // 第一种情况 22 | if (pNode.right != null) { 23 | TreeLinkNode rNode = pNode.right; 24 | while (rNode.left != null) { 25 | rNode = rNode.left; 26 | } 27 | return rNode; 28 | } else { 29 | // 第二种情况 30 | while (pNode.parent != null) { 31 | TreeLinkNode parentNode = pNode.parent; 32 | if (parentNode.left == pNode) { 33 | return parentNode; 34 | } 35 | pNode = pNode.parent; 36 | 37 | } 38 | 39 | } 40 | return null; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer32/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer32; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedList; 5 | import java.util.Queue; 6 | 7 | /** 8 | * 从上到下打印二叉树 9 | * 10 | * @Author rex 11 | * 2018/8/7 12 | */ 13 | public class Solution { 14 | /** 15 | * 用队列做中间缓存 16 | * 17 | * @param root 18 | * @return 19 | */ 20 | public ArrayList printFromTopToBottom(BinaryTreeNode root) { 21 | ArrayList result = new ArrayList<>(); 22 | if (root == null) { 23 | return result; 24 | } 25 | Queue queue = new LinkedList<>(); 26 | queue.offer(root); 27 | while (!queue.isEmpty()) { 28 | BinaryTreeNode node = queue.poll(); 29 | result.add(node.value); 30 | if (node.left != null) { 31 | queue.offer(node.left); 32 | } 33 | if (node.right != null) { 34 | queue.offer(node.right); 35 | } 36 | } 37 | return result; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer6/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer6; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 从尾到头打印链表 8 | * @Author rex 9 | * 2018/6/10 10 | */ 11 | public class Solution { 12 | /** 13 | * @Author rex 14 | * @Date 2018/6/10 下午8:09 15 | * @Description 从尾到头打印链表 16 | */ 17 | public static ArrayList printListFromTailToHead(ListNode listNode) { 18 | ArrayList result = new ArrayList(); 19 | if (listNode == null) { 20 | return result; 21 | } 22 | List list = new ArrayList(); 23 | 24 | while(listNode != null){ 25 | list.add(listNode.val); 26 | listNode = listNode.next; 27 | } 28 | for(int i = list.size()-1; i >= 0; i--) { 29 | result.add(list.get(i)); 30 | } 31 | return result; 32 | } 33 | 34 | public static void main(String[] args) { 35 | ListNode listNode = new ListNode(2); 36 | System.out.println(printListFromTailToHead(listNode)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer68/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer68; 2 | 3 | /** 4 | * 二叉搜索树中两个节点的最低公共祖先 5 | * 6 | * @Author rex 7 | * 2018/9/19 8 | */ 9 | public class Solution { 10 | /** 11 | * 获得二叉搜索树中两个节点的最低公共祖先 12 | * @param root 13 | * @param p 14 | * @param q 15 | * @return 16 | */ 17 | public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { 18 | // 防止特殊输入 19 | if (root == null || p == null || q == null) { 20 | return null; 21 | } 22 | // 最低公共祖先为从根节点往下第一个在两个输入节点之间的值的节点 23 | int min = Math.min(p.val, q.val); 24 | int max = Math.max(p.val, q.val); 25 | while (root != null) { 26 | if (root.val >= min && root.val <= max) { 27 | return root; 28 | } 29 | 30 | if (root.val > p.val && root.val > q.val) { 31 | root = root.left; 32 | } 33 | if (root.val < p.val && root.val < q.val) { 34 | root = root.right; 35 | } 36 | 37 | } 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer49/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer49; 2 | 3 | /** 4 | * 丑数 5 | * 6 | * @Author rex 7 | * 2018/9/1 8 | */ 9 | public class Solution { 10 | /** 11 | * 求出第index个丑数 12 | * @param index 13 | * @return 14 | */ 15 | public int getUglyNumber_Solution(int index) { 16 | // 防止特殊输入 17 | if (index <= 0) { 18 | return 0; 19 | } 20 | 21 | int count = 0; 22 | int base = 0; 23 | while (count != index) { 24 | base++; 25 | if (isUglyNumber(base)) { 26 | count++; 27 | } 28 | } 29 | return base; 30 | 31 | } 32 | 33 | /** 34 | * 判断是否为丑数 35 | * @param number 36 | * @return 37 | */ 38 | boolean isUglyNumber(int number) { 39 | while (number % 2 == 0) { 40 | number /= 2; 41 | } 42 | while (number % 3 == 0) { 43 | number /= 3; 44 | } 45 | while (number % 5 == 0) { 46 | number /= 5; 47 | } 48 | return number == 1; 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer4/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer4; 2 | 3 | /** 4 | * 二维数组中的查找 5 | * 从例子中找规律 6 | * @Author rex 7 | * 2018/6/9 8 | */ 9 | public class Solution { 10 | 11 | /** 12 | * @Author rex 13 | * @Date 2018/6/10 上午9:28 14 | * @Description 查找二维数组中的 15 | * @param target 要查找的数字 16 | * @param array 被查找的数组 17 | */ 18 | public static boolean find(int target, int[][] array) { 19 | if (array == null || array.length == 0 || array[0].length == 0) { 20 | return false; 21 | } 22 | int i = 0; 23 | int j = array[0].length - 1; 24 | while (j >= 0 && i <= array.length - 1) { 25 | if (array[i][j] == target) { 26 | return true; 27 | } else if (array[i][j] > target) { 28 | j--; 29 | } else { 30 | i++; 31 | } 32 | } 33 | return false; 34 | } 35 | } 36 | 37 | 38 | // public static void main(String[] args) { 39 | // System.out.println(Find(5,new int[][]{{1,2,8,9},{2,4,9,12},{4,7,10,13},{6,8,11,15}})); 40 | // } 41 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer14/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer14; 2 | 3 | /** 4 | * 剪绳子 5 | * 贪婪算法解法 6 | * @Author rex 7 | * 2018/7/20 8 | */ 9 | public class Solution1 { 10 | /** 11 | * 贪婪算法剪绳子 12 | * @param n 绳子长度 13 | * @return 14 | */ 15 | public int cutRopeByGA(int n) { 16 | //异常处理 17 | if(n < 0) { 18 | throw new IllegalArgumentException("Illegal Paramters"); 19 | } 20 | // 得到绳子长度为1-3的显然的最优解 21 | if(n < 2) { 22 | return 0; 23 | } 24 | if(n == 2) { 25 | return 1; 26 | } 27 | if(n == 3) { 28 | return 2; 29 | } 30 | 31 | int timesOfThree = n/3; 32 | 33 | 34 | if (n%3 == 1) { 35 | timesOfThree--; 36 | } 37 | int timesOfTwo = (n - timesOfThree * 3)/2; 38 | 39 | return (int) (Math.pow(3,timesOfThree) * Math.pow(2, timesOfTwo)); 40 | 41 | } 42 | 43 | public static void main(String[] args) { 44 | Solution1 solution = new Solution1(); 45 | System.out.println(solution.cutRopeByGA(9)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer55/Solution3.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer55; 2 | 3 | /** 4 | * 二叉平衡树 5 | * 6 | * @Author rex 7 | * 2018/9/11 8 | */ 9 | public class Solution3 { 10 | 11 | /** 12 | * 判断是否为二叉平衡树 13 | * @param root 14 | * @return 15 | */ 16 | public boolean isBalanced_Solution(BinaryTreeNode root) { 17 | if (root == null) { 18 | return true; 19 | } 20 | return treeDepth(root) != -1; 21 | 22 | } 23 | 24 | /** 25 | * 树的深度(后序遍历) 26 | * 27 | * @param root 28 | * @return -1:表示不是二叉平衡树 29 | */ 30 | public int treeDepth(BinaryTreeNode root) { 31 | if (root == null) { 32 | return 0; 33 | } 34 | int left = treeDepth(root.left); 35 | if (left == -1) { 36 | return -1; 37 | } 38 | int right = treeDepth(root.right); 39 | if (right == -1) { 40 | return -1; 41 | } 42 | if (Math.abs(left - right) > 1) { 43 | return -1; 44 | } else { 45 | return Math.max(left, right) + 1; 46 | } 47 | 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer46/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer46; 2 | 3 | 4 | 5 | /** 6 | * 把数字翻译成字符串 7 | * 8 | * @Author rex 9 | * 2018/8/29 10 | */ 11 | public class Solution { 12 | /** 13 | * 动态规划 14 | * @param s 15 | * @return 16 | */ 17 | public int numDecodings(String s) { 18 | 19 | // 防止特殊输入 20 | if (s == null || s.length() == 0) { 21 | return 0; 22 | } 23 | // 存储动态规划中间值 24 | int[] dp = new int[s.length()+1]; 25 | 26 | // 为了dp[2] += dp[0] 而设置 27 | dp[0] = 1; 28 | dp[1] = 1; 29 | 30 | for (int i = 2; i <= s.length(); i++) { 31 | // 1. 肯定会发生 32 | dp[i] += dp[i-1]; 33 | // 2. 当前位与前一位可以组成一个数字时 34 | if (Integer.parseInt(s.substring(i-2,i)) >=0 && Integer.parseInt(s.substring(i-2,i)) <=25) { 35 | dp[i] += dp[i-2]; 36 | } 37 | 38 | 39 | } 40 | return dp[s.length()]; 41 | } 42 | 43 | public static void main(String[] args) { 44 | Solution solution = new Solution(); 45 | System.out.println(solution.numDecodings("2")); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer6/Solution4.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer6; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | /** 9 | * 从尾到图打印链表 10 | * 11 | * @Author rex 12 | * 2018/6/10 13 | */ 14 | public class Solution4 { 15 | /** 16 | * @Author rex 17 | * @Date 2018/6/10 下午8:09 18 | * @Description 从尾到头打印链表(使用递归) 19 | */ 20 | public static ArrayList printListFromTailToHead(ListNode listNode) { 21 | ArrayList result = new ArrayList(); 22 | if (listNode == null) { 23 | return result; 24 | } 25 | 26 | while (listNode != null) { 27 | result.add(listNode.val); 28 | listNode = listNode.next; 29 | } 30 | Collections.reverse(result); 31 | return result; 32 | } 33 | 34 | public static void main(String[] args) { 35 | ListNode listNode = new ListNode(2); 36 | ListNode listNode1 = new ListNode(3); 37 | listNode.next = listNode1; 38 | System.out.println(printListFromTailToHead(listNode)); 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /notes/二叉树的镜像.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 请完成一个函数,输入一颗二叉树,请函数输出它的镜像。二叉树节点的定义如下: 3 | ```Java 4 | public class BinaryTreeNode { 5 | double value; 6 | BinaryTreeNode left; 7 | BinaryTreeNode right; 8 | } 9 | ``` 10 | ![offer27](https://github.com/todorex/Coding-Interviews/raw/master/images/offer27.png) 11 | 12 | # 测试用例 13 | * 功能测试(普通的二叉树;二叉树的所有节点都没有左子树或者右子树;只有一个节点的二叉树) 14 | * 特殊输入测试(二叉树的根节点为空指针) 15 | 16 | # 题目考点 17 | * 考察应聘者对二叉树的理解。 18 | * 考察应聘者的思维能力,可以通过画图使问题形象化。 19 | 20 | # 解题思路 21 | 我们递归交换左右节点即可。 22 | 23 | # 自己解题 24 | ```Java 25 | /** 26 | * 二叉树的镜像 27 | * 28 | * @Author rex 29 | * 2018/8/2 30 | */ 31 | public class Solution { 32 | /** 33 | * 自己解法 34 | * 递归 35 | * @param root 36 | */ 37 | public void mirror(BinaryTreeNode root) { 38 | if (root == null) { 39 | return; 40 | } 41 | if (root.left != null || root.right != null) { 42 | BinaryTreeNode temp = root.left; 43 | root.left = root.right; 44 | root.right = temp; 45 | mirror(root.left); 46 | mirror(root.right); 47 | } 48 | } 49 | } 50 | ``` 51 | 52 | # 参考解题 53 | 参考自己解题 54 | # 补充 55 | 思路比代码更重要,所以在写代码之前一定要先想请思路。 56 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer49/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer49; 2 | 3 | /** 4 | * 丑数 5 | * 6 | * @Author rex 7 | * 2018/9/1 8 | */ 9 | public class Solution1 { 10 | /** 11 | * 求出第index个丑数 12 | * 空间换时间 13 | * 14 | * @param index 15 | * @return 16 | */ 17 | public int getUglyNumber_Solution(int index) { 18 | // 防止特殊输入 19 | if (index <= 0) { 20 | return 0; 21 | } 22 | // 存放所有丑数 23 | int[] dp = new int[index]; 24 | dp[0] = 1; 25 | int nextUglyIndex = 1; 26 | // 存储乘以2、3、5的下标(当一个数乘完,下标加1) 27 | int i2 = 0, i3 = 0, i5 = 0; 28 | 29 | while (nextUglyIndex < index) { 30 | int min = Math.min(dp[i2] * 2, Math.min(dp[i3] * 3, dp[i5] *5)); 31 | dp[nextUglyIndex] = min; 32 | if (dp[i2] * 2 == min) { 33 | i2++; 34 | } 35 | if (dp[i3] * 3 == min) { 36 | i3++; 37 | } 38 | if (dp[i5] * 5 == min) { 39 | i5++; 40 | } 41 | nextUglyIndex++; 42 | } 43 | return dp[index - 1]; 44 | 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /notes/构建乘积数组.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 给定一个数组 A[0, 1,..., n-1],请构建一个数组 B[0, 1,..., n-1],其中 B 中的元素 B[i]=A[0]\*A[1]\*...\*A[i-1]\*A[i+1]\*...\*A[n-1]。不能使用除法。 3 | 4 | # 测试用例 5 | * 功能测试(输入数组包含整数、负数、一个0、多个0) 6 | * 边界值测试(输入数组的长度为0) 7 | 8 | # 题目考点 9 | * 考察应聘者的发散思维能力。 10 | * 考察应聘者对数组的理解和编程能力。 11 | 12 | # 解题思路 13 | 1. 从左往右累乘(但是保证B[i] = A[0] \*...\* A[i-1]) 14 | 2. 从右往左累乘(但是保证B[i] = B[i] \* A[n-1] \* ... \* A[i + 1]) 15 | 16 | # 自己解题 17 | 脑子只有时间复杂度为O(N2)的解法。 18 | 19 | # 参考解题 20 | ```java 21 | /** 22 | * 构建乘积矩阵 23 | * 24 | * @Author rex 25 | * 2018/9/18 26 | */ 27 | public class Solution { 28 | /** 29 | * 保存中间过程很重要 30 | * @param A 31 | * @return 32 | */ 33 | public int[] multiply(int[] A) { 34 | int n = A.length; 35 | int[] B = new int[n]; 36 | // 1. 从左往右累乘(但是保证B[i] = A[0]*...*A[i-1]) 37 | for (int i = 0, temp = 1; i < n; temp *= A[i], i++) { 38 | B[i] = temp; 39 | } 40 | // 2. 从右往左累乘(但是保证B[i] = B[i] * A[n-1] * ... * A[i + 1]) 41 | for (int i = n - 1, temp = 1; i >= 0; temp *= A[i], i--) { 42 | B[i] *= temp; 43 | } 44 | return B; 45 | } 46 | } 47 | ``` 48 | # 补充 49 | 通过保存或者利用中间过程是降低时间复杂度的可行方法。 50 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer39/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer39; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 数组中出现次数超过一半的数字 8 | * 9 | * @Author rex 10 | * 2018/8/15 11 | */ 12 | public class Solution { 13 | /** 14 | * 基于HashMap的解法 15 | * 时间复杂度为O(1) 16 | * 空间复杂度不为O(1) 17 | * @param array 18 | * @return 19 | */ 20 | public int moreThanHalfNum_Solution(int [] array) { 21 | if (array == null || array.length == 0) { 22 | return 0; 23 | } 24 | Map map = new HashMap<>(); 25 | int value; 26 | for (int i = 0; i < array.length; i++) { 27 | if (!map.containsKey(array[i])) { 28 | map.put(array[i],1); 29 | if (array.length == 1) { 30 | return array[0]; 31 | } 32 | } else { 33 | value = map.get(array[i]); 34 | value = map.put(array[i], value + 1); 35 | if (value + 1 > array.length/2) { 36 | return array[i]; 37 | } 38 | } 39 | } 40 | return 0; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer57/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer57; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * 和为s的两个数字 7 | * 8 | * @Author rex 9 | * 2018/9/14 10 | */ 11 | public class Solution1 { 12 | /** 13 | * 双指针解法 14 | * 15 | * @param array 16 | * @param sum 17 | * @return 18 | */ 19 | public ArrayList findNumbersWithSum(int [] array, int sum) { 20 | 21 | ArrayList result = new ArrayList(); 22 | // 非法输入 23 | if (array == null || array.length < 2) { 24 | return result; 25 | } 26 | // 定义首尾指针 27 | int smallIndex = 0; 28 | int bigIndex = array.length - 1; 29 | while (smallIndex < bigIndex) { 30 | int cur = array[smallIndex] + array[bigIndex]; 31 | if (cur == sum) { 32 | result.add(array[smallIndex]); 33 | result.add(array[bigIndex]); 34 | return result; 35 | } else if (cur > sum) { 36 | bigIndex--; 37 | } else { 38 | smallIndex++; 39 | } 40 | } 41 | return result; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer50/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer50; 2 | 3 | import java.util.Map; 4 | import java.util.TreeMap; 5 | 6 | /** 7 | * 第一个只出现一次的字符 8 | * 9 | * @Author rex 10 | * 2018/9/2 11 | */ 12 | public class Solution1 { 13 | /** 14 | * 利用数组构建的Map解题 15 | * @param str 16 | * @return 17 | */ 18 | public char firstNotRepeatingChar(String str) { 19 | // 防止特殊输入 20 | if (str == null || str.length() == 0) { 21 | return '\0'; 22 | } 23 | // 当字符都是数字和英文时,一个字符长度为8位,所以总共有256种可能 24 | final int TABLE_SIZE = 256; 25 | // 构建Map 26 | int[] hashTable = new int[TABLE_SIZE]; 27 | for (int i = 0; i < str.length(); i++) { 28 | hashTable[str.charAt(i)]++; 29 | } 30 | for (int i = 0; i < str.length(); i++) { 31 | if (hashTable[str.charAt(i)] == 1) { 32 | return str.charAt(i); 33 | } 34 | } 35 | return '\0'; 36 | } 37 | 38 | public static void main(String[] args) { 39 | 40 | Solution solution = new Solution(); 41 | System.out.println(solution.firstNotRepeatingChar("abaccdeff")); 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /notes/股票的最大利润.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少? 3 | 4 | 例如,一只股票在某些时间节点的价格为{9, 11, 8, 5, 7, 12, 16, 14}。如果我们能在价格为5的时候买入并在价格为16时卖出,则能收获最大的利润11。 5 | 6 | # 测试用例 7 | * 功能测试(存储股票价价格的数组无序、单调递增、单调递减) 8 | * 边界值测试(存储股票价格的数组中只有两个数字) 9 | * 特殊输入测试(指向数组的指针为空指针) 10 | 11 | # 题目考点 12 | * 考察应聘者的抽象建模能力。 13 | * 考察应聘者对数组的编程能力。 14 | 15 | # 解题思路 16 | 核心就是保存之前利润的最大值以及买入的最小值 17 | # 自己解题 18 | ```java 19 | /** 20 | * 股票的最大利润 21 | * 22 | * @Author rex 23 | * 2018/9/18 24 | */ 25 | public class Solution { 26 | /** 27 | * O(N)解题 28 | * @param prices 29 | * @return 30 | */ 31 | public int maxProfit(int[] prices) { 32 | // 防止特殊输入 33 | if (prices == null || prices.length < 2) { 34 | return 0; 35 | } 36 | // 保存最大利润 37 | int maxProfit = 0; 38 | // 保存之前的最小值 39 | int min = prices[0]; 40 | for (int i = 1; i < prices.length; i++) { 41 | if (prices[i] - min > maxProfit) { 42 | maxProfit = prices[i] - min; 43 | } 44 | if (prices[i] < min) { 45 | min = prices[i]; 46 | } 47 | } 48 | return maxProfit; 49 | } 50 | } 51 | ``` 52 | # 参考解题 53 | 见自己解题 54 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer21/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer21; 2 | 3 | 4 | /** 5 | * 调整数组顺序使奇数位于偶数前面 6 | * @Author rex 7 | * 2018/7/24 8 | */ 9 | public class Solution1 { 10 | /** 11 | * 只完成基本功能的解法 12 | * @param array 13 | */ 14 | public void reOrderArray(int [] array) { 15 | // 防止特殊输入 16 | if (array == null || array.length == 0) { 17 | return; 18 | } 19 | int i = 0; 20 | int j = array.length-1; 21 | while (i < j) { 22 | // 第一个指针,直到遇见偶数,然后开始准备交换 23 | while (i < j && (array[i] & 1) != 0) { 24 | i++; 25 | } 26 | // 第二个指针,直到遇见奇数,然后开始准备交换 27 | while (i < j && (array[j] & 1) == 0) { 28 | j--; 29 | } 30 | // 开始交换 31 | if (i < j) { 32 | swapArray(array, i, j); 33 | } 34 | } 35 | } 36 | 37 | /** 38 | * 交换数组的两个值 39 | * @param array 40 | * @param i 41 | * @param j 42 | */ 43 | public void swapArray(int[] array, int i, int j) { 44 | int temp = array[i]; 45 | array[i] = array[j]; 46 | array[j] = temp; 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer31/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer31; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * 栈的压入、弹出序列 7 | * @Author rex 8 | * 2018/8/6 9 | */ 10 | public class Solution { 11 | /** 12 | * 辅助栈 13 | */ 14 | Stack stack = new Stack<>(); 15 | 16 | /** 17 | * 字节解题 18 | * @param pushA 19 | * @param popA 20 | * @return 21 | */ 22 | public boolean isPopOrder(int [] pushA,int [] popA) { 23 | if (pushA == null || popA ==null) { 24 | return false; 25 | } 26 | int pushAIndex = 0; 27 | int popAIndex = 0; 28 | while (popAIndex < popA.length) { 29 | if ((stack.empty() || stack.peek() != popA[popAIndex])) { 30 | if (pushAIndex == pushA.length) { 31 | break; 32 | } 33 | stack.push(pushA[pushAIndex]); 34 | pushAIndex++; 35 | } else { 36 | stack.pop(); 37 | popAIndex++; 38 | } 39 | } 40 | if (popAIndex == popA.length) { 41 | return true; 42 | } else { 43 | return false; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer50/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer50; 2 | 3 | 4 | 5 | import java.util.Map; 6 | import java.util.TreeMap; 7 | 8 | /** 9 | * 第一个只出现一次的字符 10 | * 11 | * @Author rex 12 | * 2018/9/2 13 | */ 14 | public class Solution { 15 | 16 | /** 17 | * 利用TreeMap解题 18 | * @param str 19 | * @return 20 | */ 21 | public char firstNotRepeatingChar(String str) { 22 | if (str == null || str.length() == 0) { 23 | return '\0'; 24 | } 25 | Map map = new TreeMap<>(); 26 | for (int i = 0; i < str.length(); i++) { 27 | char c = str.charAt(i); 28 | if (map.containsKey(c)) { 29 | map.put(c, map.get(c) + 1); 30 | } else { 31 | map.put(c, 1); 32 | } 33 | } 34 | 35 | for(Character c : map.keySet()) { 36 | if (map.get(c) == 1) { 37 | return c; 38 | } 39 | } 40 | return '\0'; 41 | } 42 | 43 | public static void main(String[] args) { 44 | Solution solution = new Solution(); 45 | System.out.println(solution.firstNotRepeatingChar("abaccdeff")); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer14/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer14; 2 | 3 | /** 4 | * 剪绳子 5 | * 动态规划解法 6 | * @Author rex 7 | * 2018/7/20 8 | */ 9 | class Solution { 10 | /** 11 | * 动态规划剪绳子 12 | * @param n 绳子长度 13 | * @return 14 | */ 15 | public int cutRopeByDP(int n) { 16 | //异常处理 17 | if(n < 0) { 18 | throw new IllegalArgumentException("Illegal Paramters"); 19 | } 20 | // 得到绳子长度为1-3的显然的最优解 21 | if(n < 2) { 22 | return 0; 23 | } 24 | if(n == 2) { 25 | return 1; 26 | } 27 | if(n == 3) { 28 | return 2; 29 | } 30 | 31 | //创建数组存储子问题最优解 32 | int[] result = new int[n + 1]; 33 | for (int i = 0; i < 4; i++) { 34 | result[i] = i; 35 | } 36 | // 开始求解每一个绳子长度的最优解 37 | for (int i = 4; i <= n; i++) { 38 | int max = 0; 39 | for (int j = 1; j <= i/2; j++) { 40 | int temp = result[j] * result[i - j]; 41 | if (temp > max) { 42 | max = temp; 43 | } 44 | } 45 | result[i] = max; 46 | } 47 | return result[n]; 48 | } 49 | } -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer6/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer6; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 从尾到头打印链表 8 | * @Author rex 9 | * 2018/6/10 10 | */ 11 | public class Solution1 { 12 | /** 13 | * @Author rex 14 | * @Date 2018/6/10 下午8:09 15 | * @Description 从尾到头打印链表(使用头插法将链表反转) 16 | */ 17 | public static ArrayList printListFromTailToHead(ListNode listNode) { 18 | ArrayList result = new ArrayList(); 19 | 20 | if (listNode == null) { 21 | return result; 22 | } 23 | ListNode head = new ListNode(); 24 | while (listNode != null) { 25 | ListNode temp = listNode.next; 26 | listNode.next = head.next; 27 | head.next = listNode; 28 | listNode = temp; 29 | } 30 | head = head.next; 31 | while (head != null) { 32 | result.add(head.val); 33 | head = head.next; 34 | } 35 | return result; 36 | } 37 | 38 | public static void main(String[] args) { 39 | ListNode listNode = new ListNode(2); 40 | System.out.println(printListFromTailToHead(listNode)); 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer47/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer47; 2 | 3 | /** 4 | * 礼物的最大价值 5 | * 6 | * @Author rex 7 | * 2018/8/30 8 | */ 9 | public class Solution { 10 | 11 | /** 12 | * 递归解题 13 | * @param board 14 | * @return 15 | */ 16 | public int getMost(int[][] board) { 17 | int i = board.length; 18 | int j = board[0].length; 19 | return getMost(board, i-1, j-1); 20 | } 21 | 22 | /** 23 | * 计算当前坐标下的礼物最大值 24 | * @param board 25 | * @param i 26 | * @param j 27 | * @return 28 | */ 29 | public int getMost(int[][] board, int i, int j) { 30 | 31 | if (i == 0 && j == 0) { 32 | return board[i][j]; 33 | } 34 | int left = 0; 35 | int top = 0; 36 | if (i >= 1) { 37 | top = getMost(board, i - 1, j); 38 | } 39 | if (j >= 1) { 40 | left = getMost(board, i, j - 1 ); 41 | } 42 | return Math.max(top,left) + board[i][j]; 43 | } 44 | 45 | public static void main(String[] args) { 46 | int[][] a = new int[][]{{1,2},{3,4}}; 47 | Solution solution = new Solution(); 48 | int b = solution.getMost(a); 49 | System.out.println(b); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer36/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer36; 2 | 3 | /** 4 | * 二叉搜索树与双向链表 5 | * 6 | * @Author rex 7 | * 2018/8/11 8 | */ 9 | public class Solution { 10 | /** 11 | * 关键 12 | */ 13 | private BinaryTreeNode lastNode; 14 | 15 | /** 16 | * 参考解题 17 | * @param pRootOfTree 18 | * @return 19 | */ 20 | public BinaryTreeNode convert(BinaryTreeNode pRootOfTree) { 21 | 22 | convertChild(pRootOfTree); 23 | BinaryTreeNode firstNode = lastNode; 24 | while (lastNode != null && firstNode.left !=null) { 25 | firstNode = firstNode.left; 26 | } 27 | return firstNode; 28 | 29 | } 30 | 31 | public void convertChild(BinaryTreeNode pRootOfTree) { 32 | if (pRootOfTree == null) { 33 | return; 34 | } 35 | BinaryTreeNode current = pRootOfTree; 36 | if (current.left != null) { 37 | convertChild(current.left); 38 | } 39 | // 关键 40 | current.left = lastNode; 41 | if (lastNode != null) { 42 | lastNode.right = current; 43 | } 44 | lastNode = current; 45 | if (current.right != null) { 46 | convertChild(current.right); 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer48/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer48; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * 最长不含重复字符的子字符串 7 | * 8 | * @Author rex 9 | * 2018/8/31 10 | */ 11 | public class Solution { 12 | 13 | /** 14 | * 动态规划解题 15 | * @param s 16 | * @return 17 | */ 18 | public int longestSubsingWithoutDuplication(String s) { 19 | // 关键(动态规划的体现) 20 | int curLength = 0; 21 | 22 | int maxLength = 0; 23 | 24 | // 存放下标 25 | Map indexMap = new HashMap<>(); 26 | 27 | for (int i = 0; i < s.length(); i++) { 28 | // 数组下标 29 | Integer index = indexMap.get(s.charAt(i)); 30 | // 如果第i个字符之前没有出现过 或者 出现的下标不在当前不包含重复字符的子字符串内 31 | if (index == null || (i - index) > curLength) { 32 | curLength++; 33 | } else { 34 | // 如果第i个字符出现的下标在当前不包含重复字符的子字符串内 35 | // 与之前最长的字符串比较 36 | maxLength = Math.max(maxLength, curLength); 37 | // 调整为两个重复字符串的距离 38 | curLength = i - index; 39 | } 40 | indexMap.put(s.charAt(i),i); 41 | } 42 | maxLength = Math.max(maxLength, curLength); 43 | 44 | return maxLength; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer38/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer38; 2 | 3 | /** 4 | * 字符串的排列 5 | * 6 | * @Author rex 7 | * 2018/8/14 8 | */ 9 | public class Solution { 10 | /** 11 | * 字符串排序 12 | * 13 | * @param str 14 | */ 15 | void permutation(String str) { 16 | if (str == null || str.length() == 0) { 17 | return; 18 | } 19 | char[] ca = str.toCharArray(); 20 | permutation(ca, 0); 21 | } 22 | 23 | /** 24 | * 字符数组排序 25 | * 递归方法 26 | * 27 | * @param ca 28 | * @param begin 29 | */ 30 | void permutation(char[] ca, int begin) { 31 | if (begin == ca.length) { 32 | System.out.println(String.valueOf(ca)); 33 | } else { 34 | for (int i = begin; i < ca.length; i++) { 35 | // 和后面一个字符交换位置 36 | swap(ca, i, begin); 37 | permutation(ca, begin + 1); 38 | // 换回来 39 | swap(ca, i, begin); 40 | } 41 | } 42 | 43 | } 44 | 45 | /** 46 | * 交换位置 47 | * @param ca 48 | * @param i 49 | * @param j 50 | */ 51 | void swap(char[] ca, int i, int j) { 52 | char temp = ca[i]; 53 | ca[i] = ca[j]; 54 | ca[j] = temp; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer57/Solution2.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer57; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * 和为s的连续正数序列 7 | * 8 | * @Author rex 9 | * 2018/9/14 10 | */ 11 | public class Solution2 { 12 | /** 13 | * 双指针解法 14 | * @param sum 15 | * @return 16 | */ 17 | public ArrayList> findContinuousSequence(int sum) { 18 | ArrayList> result = new ArrayList>(); 19 | // 非法输入 20 | if (sum <= 0) { 21 | return result; 22 | } 23 | // 定义连续正数序列的首尾指针,都是递增的 24 | int small = 1; 25 | int big = 2; 26 | int cur = small + big; 27 | while (small <= sum/2 && small < big) { 28 | if (cur == sum) { 29 | ArrayList list = new ArrayList<>(); 30 | for (int i = small; i <= big; i++) { 31 | list.add(i); 32 | } 33 | result.add(list); 34 | big++; 35 | cur +=big; 36 | } else if (cur > sum) { 37 | cur -= small; 38 | small++; 39 | } else { 40 | big++; 41 | cur += big; 42 | } 43 | 44 | } 45 | return result; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer52/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer52; 2 | 3 | import java.util.ArrayDeque; 4 | import java.util.Deque; 5 | 6 | 7 | /** 8 | * 两个链表的第一个公共节点 9 | * 10 | * @Author rex 11 | * 2018/9/4 12 | */ 13 | public class Solution { 14 | /** 15 | * 用栈解题 16 | * @param pHead1 17 | * @param pHead2 18 | * @return 19 | */ 20 | public ListNode findFirstCommonNode(ListNode pHead1, ListNode pHead2) { 21 | ListNode commonNode = null; 22 | if (pHead1 == null || pHead2 == null) { 23 | return null; 24 | } 25 | // JDK建议双向队列Deque优先于Stack 26 | Deque stack1 = new ArrayDeque<>(); 27 | Deque stack2 = new ArrayDeque<>(); 28 | // 两个链表入栈 29 | while (pHead1 != null) { 30 | stack1.push(pHead1); 31 | pHead1 = pHead1.next; 32 | } 33 | while (pHead2 != null) { 34 | stack2.push(pHead2); 35 | pHead2 = pHead2.next; 36 | } 37 | // 不断出栈比较 38 | while (!stack1.isEmpty() && !stack2.isEmpty()) { 39 | if (stack1.peek() == stack2.peek()) { 40 | commonNode = stack1.peek(); 41 | } 42 | stack1.pop(); 43 | stack2.pop(); 44 | } 45 | return commonNode; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /notes/反转链表.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。 3 | 4 | 链表节点定义如下: 5 | ```Java 6 | public class ListNode { 7 | int val; 8 | ListNode next = null; 9 | } 10 | ``` 11 | # 测试用例 12 | * 功能测试(输入的链表含有多个节点;链表中只有一个节点) 13 | * 特殊功能测试(链表头节点为空指针) 14 | 15 | # 题目考点 16 | * 考察应聘者对链表、指针的编程能力 17 | * 考察应聘者思维的全面性及写出来的代码的鲁棒性 18 | 19 | # 解题思路 20 | 主要的过程有以下三步: 21 | 1. 定义一个将指向反转链表的头指针和一个保存临时节点的指针 22 | 2. 循环头节点的下一个节点,如果不为空,则将它的下一个节点作为新的头结点,并用临时节点保存之前头节点 23 | 3. 然后就是链表的重排问题了,首先原链表的头结点,将指向的新的头节点的下一个节点,新的头节点将指向原来的头结点 24 | 25 | # 自己解题 26 | ```Java 27 | /** 28 | * 反转链表 29 | * 30 | * @Author rex 31 | * 2018/7/28 32 | */ 33 | public class Solution { 34 | /** 35 | * 自己解法 36 | * @param head 37 | * @return 38 | */ 39 | public ListNode ReverseList(ListNode head) { 40 | if (head == null) { 41 | return null; 42 | } 43 | ListNode currHead = head; 44 | ListNode temp = null; 45 | while (head.next != null) { 46 | temp = currHead; 47 | currHead = head.next; 48 | head.next = head.next.next; 49 | currHead.next = temp; 50 | } 51 | return currHead; 52 | } 53 | } 54 | ``` 55 | # 参考解题 56 | 同自己解题 57 | 58 | # 补充 59 | 自已多花时间找出问题并修正问题比在面试官找出问题再去慌慌张张修改代码要好很多。**其实面试官检查应聘者所写代码的方法也是用他事先准备好的测试用例来进行测试。如果应聘者能够想到这些测试用例**,并用它们来测试自己的代码,就能保证有备无患了。 60 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer56/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer56; 2 | 3 | /** 4 | * 数组中唯一只出现一次的数字 5 | * 6 | * @Author rex 7 | * 2018/9/13 8 | */ 9 | public class Solution1 { 10 | /** 11 | * 数组中唯一只出现一次的数字(其他数字都出现三次) 12 | * @param numbers 13 | * @return 14 | * @throws Exception 15 | */ 16 | public int findNumberAppearingOnce(int[] numbers) throws Exception { 17 | // 非法输入 18 | if (numbers == null || numbers.length == 0) { 19 | throw new Exception("invalid input"); 20 | } 21 | int[] bitSum = new int[32]; 22 | for (int i = 0; i < numbers.length; i++) { 23 | for (int j = 0; j < 32; j++) { 24 | if ((numbers[i] & 1) == 1) { 25 | bitSum[j]++; 26 | } 27 | numbers[i] = numbers[i] >> 1; 28 | } 29 | } 30 | int result = 0; 31 | for (int i = 31; i >=0; i--) { 32 | result = result << 1; 33 | result += bitSum[i] % 3; 34 | } 35 | return result; 36 | } 37 | 38 | public static void main(String[] args) throws Exception { 39 | int[] number = new int[]{1,2,1,1,3,3,3}; 40 | Solution1 solution1 = new Solution1(); 41 | System.out.println(solution1.findNumberAppearingOnce(number)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer40/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer40; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | 8 | import static java.util.stream.Collectors.toList; 9 | 10 | /** 11 | * 最小的 k 个数 12 | * 13 | * @Author rex 14 | * 2018/8/16 15 | */ 16 | public class Solution { 17 | /** 18 | * 直接用StreamAPI 19 | * @param input 20 | * @param k 21 | * @return 22 | */ 23 | public ArrayList getLeastNumbers_Solution(int [] input, int k) { 24 | 25 | ArrayList result = new ArrayList<>(); 26 | if (input == null || input.length == 0) { 27 | return result; 28 | } 29 | if (k < 0 || k > input.length) { 30 | return result; 31 | } 32 | List list = Arrays.stream(input).boxed().sorted().limit(k).collect(Collectors.toList()); 33 | 34 | return (ArrayList) list; 35 | 36 | } 37 | 38 | public static void main(String[] args) { 39 | Solution solution = new Solution(); 40 | int[] a = new int[]{4,5,1,6,2,7,3,8}; 41 | ArrayList list = solution.getLeastNumbers_Solution(a,4); 42 | for (int i = 0; i < list.size(); i++) { 43 | System.out.println(list.get(i)); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer21/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer21; 2 | 3 | /** 4 | * 调整数组顺序使奇数位于偶数前面 5 | * @Author rex 6 | * 2018/7/24 7 | */ 8 | public class Solution { 9 | /** 10 | * 调整数组顺序 11 | * @param array 12 | */ 13 | public void reOrderArray(int [] array) { 14 | // 防止特殊输入 15 | if (array == null || array.length == 0) { 16 | return; 17 | } 18 | int i = 0; 19 | int j = array.length-1; 20 | while (j - i != 1) { 21 | if ((array[i] & 1) == 0) { 22 | // 为偶数 23 | swapArray(array, i, j); 24 | j--; 25 | } else { 26 | i++; 27 | } 28 | 29 | } 30 | } 31 | 32 | /** 33 | * 交换数组的两个值 34 | * @param array 35 | * @param i 36 | * @param j 37 | */ 38 | public void swapArray(int[] array, int i, int j) { 39 | int temp = array[i]; 40 | array[i] = array[j]; 41 | array[j] = temp; 42 | } 43 | 44 | 45 | public static void main(String[] args) { 46 | int[] array = new int[] {2,4,4,6,8,10,1,3,5}; 47 | Solution solution = new Solution(); 48 | solution.reOrderArray(array); 49 | for (int i = 0; i < array.length; i++) { 50 | System.out.println(array[i]); 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer58/Solution2.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer58; 2 | 3 | /** 4 | * 左旋字符串 5 | * 6 | * @Author rex 7 | * 2018/9/15 8 | */ 9 | public class Solution2 { 10 | public String leftRotateString(String str,int n) { 11 | // 非法输入 12 | if (str == null || str.length() <= 0) { 13 | return ""; 14 | } 15 | if (n < 0 || n > str.length() - 1) { 16 | return str; 17 | } 18 | char[] chars = str.toCharArray(); 19 | // 1. 反转整个字符串 20 | reverse(chars, 0, str.length() - 1); 21 | // 2. 分别反转左右两部分 22 | reverse(chars, 0, str.length() - 1 - n); 23 | reverse(chars, str.length() - n , str.length() - 1); 24 | return new String(chars); 25 | } 26 | 27 | 28 | /** 29 | * 反转char数组 30 | * @param chars 31 | * @param start 32 | * @param end 33 | */ 34 | public void reverse(char[] chars, int start, int end) { 35 | while (start < end) { 36 | swap(chars, start, end); 37 | start++; 38 | end--; 39 | } 40 | } 41 | 42 | /** 43 | * 交换char数组两个位置的值 44 | * @param chars 45 | * @param i 46 | * @param j 47 | */ 48 | public void swap(char[] chars, int i, int j) { 49 | char temp = chars[i]; 50 | chars[i] = chars[j]; 51 | chars[j] = temp; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /notes/包含min函数的栈.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 定义栈的数据,请在该类型中实现一个能够找到栈的最小元素的min函数。在该栈中,调用min、push及pop的时间复杂度都是O(1)。 3 | 4 | # 测试用例 5 | * 新压入栈的数字比之前的最小值大。 6 | * 新压入栈的数字比之前的最小值小。 7 | * 弹出栈的数字不是最小元素。 8 | * 弹出栈的数字是最小元素。 9 | 10 | # 题目考点 11 | * 考察应聘者分析复杂问题的思维能力——举例分析。 12 | * 考察应聘者对栈的理解。 13 | 14 | # 解题思路 15 | 这里只限制了时间复杂度,没限制空间复杂度,我们可能会想到用辅助栈。 16 | 17 | 然后我们就会想到用一个栈来保存最小值,具体怎么保存? 18 | 19 | 我们可以每次在辅助栈都压入当前数据栈的最小值。在执行min函数的时候直接取到辅助栈的栈顶元素即可,当执行push函数时,如果压入的元素比当前最小值小则压入辅助栈,不然再压入一个当前最小值(为了pop的方便),在执行pop函数时,我们只要直接弹出数据栈与辅助栈的栈顶元素就好。 20 | 21 | # 自己解题 22 | 没有思路。 23 | 24 | # 参考解题 25 | ```Java 26 | /** 27 | * 包含min函数的栈 28 | * 29 | * @Author rex 30 | * 2018/8/5 31 | */ 32 | public class Solution { 33 | /** 34 | * 存储数据 35 | */ 36 | Stack dataStack = new Stack<>(); 37 | /** 38 | * 存储每一次的最小值 39 | */ 40 | Stack minStack = new Stack<>(); 41 | 42 | public void push(int node) { 43 | dataStack.push(node); 44 | if (minStack.empty() || node < minStack.peek()) { 45 | minStack.push(node); 46 | } else { 47 | minStack.push(minStack.peek()); 48 | } 49 | } 50 | 51 | public void pop() { 52 | dataStack.pop(); 53 | minStack.pop(); 54 | 55 | } 56 | 57 | public int top() { 58 | return dataStack.peek(); 59 | } 60 | 61 | public int min() { 62 | return minStack.peek(); 63 | } 64 | 65 | } 66 | ``` 67 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer5/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer5; 2 | 3 | /** 4 | * 替换空格 5 | * 从后往前(降低时间复杂度) 6 | * @Author rex 7 | * 2018/6/10 8 | */ 9 | public class Solution1 { 10 | 11 | /** 12 | * @Author rex 13 | * @Date 2018/6/10 下午4:24 14 | * @Description 替换空格 15 | * 利用两个指针从后往前替换 16 | * @param str 输入字符串 17 | */ 18 | public static String replaceSpace(StringBuffer str) { 19 | if (str == null) { 20 | return null; 21 | } 22 | int oldLength = str.length(); 23 | // 扩展StringBuffer 24 | for (int i = 0; i < oldLength; i++) { 25 | if (str.charAt(i) == ' ') { 26 | str.append(" "); 27 | } 28 | } 29 | int newLength = str.length(); 30 | int p1 = oldLength - 1; 31 | int p2 = newLength - 1; 32 | while (p1 != p2) { 33 | if (str.charAt(p1) == ' ') { 34 | str.setCharAt(p2--, '0'); 35 | str.setCharAt(p2--, '2'); 36 | str.setCharAt(p2--, '%'); 37 | p1--; 38 | } else { 39 | str.setCharAt(p2--, str.charAt(p1--)); 40 | } 41 | } 42 | return str.toString(); 43 | } 44 | 45 | public static void main(String[] args) { 46 | System.out.println(replaceSpace(new StringBuffer("we are happy."))); 47 | } 48 | 49 | } 50 | 51 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer47/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer47; 2 | 3 | /** 4 | * 礼物的最大价值 5 | * 6 | * @Author rex 7 | * 2018/8/30 8 | */ 9 | public class Solution1 { 10 | 11 | /** 12 | * 动态规划解题 13 | * 14 | * @param board 15 | * @return 16 | */ 17 | public int getMost(int[][] board) { 18 | if (board == null || board.length == 0 || board[0].length == 0) { 19 | return 0; 20 | } 21 | int rows = board.length; 22 | int columns = board[0].length; 23 | // 辅助数组,如果可以破坏原数组,则可直接对原数组重新赋值 24 | int[][] temp = new int[rows][columns]; 25 | for (int i = 0; i< rows; i++) { 26 | for (int j = 0; j < columns; j++) { 27 | int value = board[i][j]; 28 | if (i == 0 && j == 0) { 29 | // 存入当前值 30 | temp[i][j] = value; 31 | } else if (i == 0){ 32 | // 往右走 33 | temp[i][j] = temp[i][j-1] + value; 34 | } else if (j == 0) { 35 | // 往下走 36 | temp[i][j] = temp[i-1][j] + value; 37 | } else { 38 | // 挑一个最大的路径走 39 | temp[i][j] = Math.max(temp[i][j-1], temp[i-1][j]) + value; 40 | } 41 | } 42 | } 43 | 44 | return temp[rows - 1][columns-1]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer35/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer35; 2 | 3 | 4 | /** 5 | * 复杂链表的复制 6 | * 7 | * @Author rex 8 | * 2018/8/10 9 | */ 10 | public class Solution { 11 | /** 12 | * 参考解题 13 | * @param pHead 14 | * @return 15 | */ 16 | public ComplexListNode clone(ComplexListNode pHead) { 17 | if (pHead == null) { 18 | return null; 19 | } 20 | ComplexListNode current = pHead; 21 | 22 | // 1. 插入新节点 23 | while (current != null) { 24 | ComplexListNode clone = new ComplexListNode(current.value); 25 | clone.next = current.next; 26 | current.next = clone; 27 | current = clone.next; 28 | } 29 | 30 | // 2. 设置复制节点的random指针 31 | current = pHead; 32 | while (current != null) { 33 | ComplexListNode clone = current.next; 34 | if (current.random != null) { 35 | clone.random = current.random.next; 36 | } 37 | current = clone.next; 38 | } 39 | 40 | // 3. 将原链表拆分成原始链表和复制链表 41 | current = pHead; 42 | ComplexListNode pCloneHead = pHead.next; 43 | while (current.next != null) { 44 | ComplexListNode next = current.next; 45 | current.next = next.next; 46 | current = next; 47 | } 48 | 49 | return pCloneHead; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer40/Solution2.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer40; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | import java.util.TreeSet; 6 | 7 | /** 8 | * 最小的 k 个数 9 | * 10 | * @Author rex 11 | * 2018/8/23 12 | */ 13 | public class Solution2 { 14 | /** 15 | * 利用基于红黑树实现的TreeSet 16 | * 17 | * @param input 18 | * @param k 19 | * @return 20 | */ 21 | public ArrayList getLeastNumbers_Solution(int[] input, int k) { 22 | ArrayList list = new ArrayList(); 23 | // 防止特殊输入 24 | if (input == null || input.length <= 0 || input.length < k || k < 1) { 25 | return list; 26 | } 27 | // 利用TreeSet构建一颗k大小的黑红树 28 | TreeSet treeset = new TreeSet(); 29 | for (int i = 0; i < k; i++) { 30 | treeset.add(input[i]); 31 | } 32 | 33 | // 之后的数与红黑树最大值的比较,如果比最大值大,替换数中最大值 34 | for (int i = k; i < input.length; i++) { 35 | // 拿到树中的最大值 36 | int max = treeset.last(); 37 | if (input[i] < max) { 38 | treeset.remove(max); 39 | treeset.add(input[i]); 40 | } 41 | } 42 | 43 | // 包装成list 44 | Iterator it = treeset.iterator(); 45 | while (it.hasNext()) { 46 | list.add(it.next()); 47 | } 48 | return list; 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer53/Solution2.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer53; 2 | 3 | /** 4 | * 0 ~ n-1 缺失的数字 5 | * 6 | * @Author rex 7 | * 2018/9/7 8 | */ 9 | public class Solution2 { 10 | /** 11 | * 12 | * @param array 数组 13 | * @param n 数组程度 14 | * @return 15 | */ 16 | public int getMissingNumber(int[] array, int n) { 17 | if (array == null || array.length == 0) { 18 | return -1; 19 | } 20 | return binarySearch(array, 0, n - 1); 21 | 22 | } 23 | 24 | public int binarySearch(int[] array, int start, int end) { 25 | if (start > end) { 26 | return -1; 27 | } 28 | // 当缺失的数字在最右边 29 | if (array.length == end) { 30 | return end; 31 | } 32 | int middleIndex = (start + end) >> 1; 33 | if (middleIndex == array[middleIndex]) { 34 | return binarySearch(array, middleIndex + 1, end); 35 | } else { 36 | if ( array[middleIndex - 1] + 2 == array[middleIndex]) { 37 | return array[middleIndex] - 1; 38 | } 39 | return binarySearch(array, start, middleIndex -1); 40 | } 41 | } 42 | 43 | public static void main(String[] args) { 44 | Solution2 solution = new Solution2(); 45 | int[] array = new int[] {0,1}; 46 | int ret = solution.getMissingNumber(array,3); 47 | System.out.println(ret); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /notes/合并两个排序的链表.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 输入两个单调递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。例如下图中的链表1和链表2,则合并之后的升序链表如链表3所示。链表的节点定义如下: 3 | ```Java 4 | public class ListNode { 5 | int val; 6 | ListNode next = null; 7 | } 8 | ``` 9 | ![offer25](https://github.com/todorex/Coding-Interviews/raw/master/images/offer25.png) 10 | 11 | # 测试用例 12 | * 功能测试(输入的两个链表有多个节点;节点的值互不相同或者存在值相等的多个节点) 13 | * 特殊输入测试(两个节点的一个或者连个节点为空指针;两个链表中只有一个节点) 14 | 15 | # 题目考点 16 | * 考察应聘者分析问题的能力。 17 | * 考察应聘者能不能写出鲁棒的代码。 18 | 19 | # 解题思路 20 | 当我们得到两个链表中较小的头节点并把它连接到新链表,两个链表剩余的节点依然是有序的,因此合并的步骤和之前啊的步骤是一样的,这就是典型的递归过程。所以我们只要用递归的方法解决就好了。 21 | 22 | # 自己解题 23 | ```Java 24 | /** 25 | * 合并两个排序的链表 26 | * 27 | * @Author rex 28 | * 2018/7/30 29 | */ 30 | public class Solution { 31 | /** 32 | * 自己解法 33 | * 递归实现 34 | * @param list1 35 | * @param list2 36 | * @return 37 | */ 38 | public ListNode Merge(ListNode list1,ListNode list2) { 39 | if (list1 == null) { 40 | return list2; 41 | } else if (list2 == null) { 42 | return list1; 43 | } 44 | ListNode resultNode = null; 45 | if (list1.val < list2.val) { 46 | resultNode = list1; 47 | resultNode.next = Merge(list1.next, list2); 48 | } else { 49 | resultNode = list2; 50 | resultNode.next = Merge(list1, list2.next); 51 | } 52 | return resultNode; 53 | } 54 | } 55 | ``` 56 | # 参考解题 57 | 同自己解法 58 | # 补充 59 | 循环 or 递归 60 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer62/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer62; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 圆圈中最后剩下的数字 8 | * 9 | * @Author rex 10 | * 2018/9/18 11 | */ 12 | public class Solution { 13 | /** 14 | * 环形链表解题 15 | * @param n 16 | * @param m 17 | * @return 18 | */ 19 | public int lastRemaining_Solution(int n, int m) { 20 | // 防止特殊输入 21 | if (n < 0 || m < 1) { 22 | return -1; 23 | } 24 | // 构造链表 25 | List list = new ArrayList<>(); 26 | for (int i = 0; i < n; i++) { 27 | list.add(i); 28 | } 29 | int current = 0; 30 | while (list.size() > 1) { 31 | for (int i = 1; i < m; i++) { 32 | current++; 33 | if (current == list.size()) { 34 | current = 0; 35 | } 36 | } 37 | // 删除之后,注意边界 38 | list.remove(current); 39 | if (current > list.size() - 1) { 40 | current = 0; 41 | } 42 | } 43 | return list.get(current); 44 | 45 | } 46 | 47 | 48 | public static void main(String[] args) { 49 | Solution solution = new Solution(); 50 | int n = 400; 51 | int m = 977; 52 | int result = solution.lastRemaining_Solution(n, m); 53 | System.out.println(result); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer37/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer37; 2 | 3 | /** 4 | * 序列化二叉树 5 | * 6 | * @Author rex 7 | * 2018/8/13 8 | */ 9 | public class Solution { 10 | 11 | int index; 12 | 13 | /** 14 | * 序列化成String(前序遍历) 15 | * 节点之间的分隔符是 ',' 16 | * 空节点为'$' 17 | * @param root 18 | * @return 19 | */ 20 | String serialize(BinaryTreeNode root) { 21 | if (root == null) { 22 | return "$,"; 23 | } 24 | return root.value + "," + serialize(root.left) + serialize(root.right); 25 | } 26 | 27 | /** 28 | * 反序列化 29 | * @param str 30 | * @return 31 | */ 32 | BinaryTreeNode deserialize(String str) { 33 | if (str == null || str.length() == 0) { 34 | return null; 35 | } 36 | index = -1; 37 | String[] strNode = str.split(","); 38 | return deserialize(strNode); 39 | } 40 | 41 | /** 42 | * 真正反序列化 43 | * (前序遍历) 44 | * @param strNode 45 | * @return 46 | */ 47 | BinaryTreeNode deserialize(String[] strNode) { 48 | index++; 49 | BinaryTreeNode treeNode = null; 50 | if (! strNode[index].equals("$")) { 51 | treeNode = new BinaryTreeNode(Integer.valueOf(strNode[index])); 52 | treeNode.left = deserialize(strNode); 53 | treeNode.right = deserialize(strNode); 54 | } 55 | return treeNode; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer41/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer41; 2 | 3 | import java.util.PriorityQueue; 4 | 5 | /** 6 | * 数据流中的中位数 7 | * 8 | * @Author rex 9 | * 2018/8/24 10 | */ 11 | public class Solution { 12 | /** 13 | * 大顶堆,存储左半边元素 (需重写比较器) 14 | **/ 15 | private PriorityQueue left = new PriorityQueue<>((o1, o2) -> o2 - o1); 16 | /** 17 | * 小顶堆,存储右半边元素,并且右半边元素都大于左半边 18 | **/ 19 | private PriorityQueue right = new PriorityQueue<>(); 20 | 21 | /** 22 | * 当前数据流读入的元素个数,用于判断奇偶 23 | **/ 24 | private int count = 0; 25 | 26 | /** 27 | * 读取数据流 28 | * 29 | * @param num 30 | */ 31 | public void insert(Integer num) { 32 | // 为了实现平衡分配,可以在数据的总数目是偶数时把数据插入最小堆,否则插入最大堆 33 | if ((count & 1) == 0) { 34 | // 在要把数据插入最小堆的时候,如果该数比最大堆的数还要小 35 | // 那么需要先插入最大堆,把最大堆的最大值拿出来出入到最小堆 36 | // 反之亦然 37 | left.add(num); 38 | right.add(left.poll()); 39 | } else { 40 | right.add(num); 41 | left.add(right.poll()); 42 | } 43 | count++; 44 | } 45 | 46 | /** 47 | * 获取当前读取数据的中位数 48 | * 49 | * @return 50 | */ 51 | public Double getMedian() { 52 | if ((count & 1) == 0) { 53 | return (left.peek() + right.peek()) / 2.0; 54 | } else { 55 | return (double)right.peek(); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer34/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer34; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * 二叉树中和为某一值的路径 7 | * 8 | * @Author rex 9 | * 2018/8/9 10 | */ 11 | public class Solution { 12 | /** 13 | * 参考解题 14 | * 15 | * 例子分析 16 | * @param root 17 | * @param target 18 | * @return 19 | */ 20 | public ArrayList> findPath(BinaryTreeNode root, int target) { 21 | ArrayList> result = new ArrayList<>(); 22 | ArrayList singleResult = new ArrayList<>(); 23 | if (root == null) { 24 | return result; 25 | } 26 | findPath(root, target, result, singleResult); 27 | return result; 28 | 29 | } 30 | 31 | public void findPath(BinaryTreeNode node, int target, ArrayList> result, ArrayList singleResult) { 32 | target -= node.value; 33 | singleResult.add(node.value); 34 | // 判断是否等于目标值且是叶节点 35 | if (target == 0 && node.left == null && node.right == null) { 36 | result.add(new ArrayList<>(singleResult)); 37 | } 38 | if (node.left != null) { 39 | findPath(node.left, target, result, singleResult); 40 | } 41 | if (node.right != null) { 42 | findPath(node.right, target, result, singleResult); 43 | } 44 | // 删除当前节点的值 45 | singleResult.remove(singleResult.size()-1); 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer33/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer33; 2 | 3 | /** 4 | * 二叉搜索树的后序遍历序列 5 | * 6 | * @Author rex 7 | * 2018/8/8 8 | */ 9 | public class Solution { 10 | /** 11 | * 字节解题 12 | * @param sequence 13 | * @return 14 | */ 15 | public boolean verifySquenceOfBST(int [] sequence) { 16 | if (sequence == null || sequence.length == 0) { 17 | return true; 18 | } 19 | 20 | return verfy(sequence, 0, sequence.length - 1); 21 | 22 | 23 | } 24 | 25 | /** 26 | * 验证子树 27 | * @param sequence 28 | * @param first 29 | * @param last 30 | * @return 31 | */ 32 | public boolean verfy(int[] sequence, int first, int last) { 33 | if (first - last == 0) { 34 | return true; 35 | } 36 | int right = first; 37 | while (sequence[right] < sequence[last] && right < last) { 38 | right++; 39 | } 40 | int left = right; 41 | while (left < last) { 42 | if (sequence[left] < sequence[last]) { 43 | return false; 44 | } 45 | left++; 46 | } 47 | boolean leftTree = true; 48 | if (right - 1 >= first) { 49 | leftTree = verfy(sequence, first, right-1); 50 | } 51 | boolean rightTree = true; 52 | if (last - 1 > right) { 53 | rightTree = verfy(sequence, right, last-1); 54 | } 55 | return leftTree && rightTree; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer26/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer26; 2 | 3 | /** 4 | * 树的子结构 5 | * 递归 6 | * 7 | * @Author rex 8 | * 2018/8/1 9 | */ 10 | public class Solution { 11 | /** 12 | * 判断树1是否存在树2这样的子树 13 | * @param root1 14 | * @param root2 15 | * @return 16 | */ 17 | public boolean hasSubtree(BinaryTreeNode root1, BinaryTreeNode root2) { 18 | if (root1 == null || root2 == null) { 19 | return false; 20 | } 21 | 22 | return doesTree1HaveTree2(root1, root2) || doesTree1HaveTree2(root1.left, root2) || doesTree1HaveTree2(root1.right, root2); 23 | 24 | } 25 | 26 | /** 27 | * 判断树1是否存在树2这样的子树核心 28 | * @param root1 29 | * @param root2 30 | * @return 31 | */ 32 | public boolean doesTree1HaveTree2(BinaryTreeNode root1, BinaryTreeNode root2) { 33 | if (root2 == null) { 34 | return true; 35 | } 36 | if (root1 == null) { 37 | return false; 38 | } 39 | if (equal(root1.value, root2.value)) { 40 | return doesTree1HaveTree2(root1.left, root2.left) && doesTree1HaveTree2(root1.right, root2.right); 41 | } 42 | return false; 43 | } 44 | 45 | /** 46 | * 判断两个double类型是否相等 47 | * @param i 48 | * @param j 49 | * @return 50 | */ 51 | public boolean equal(double i, double j) { 52 | if (Math.abs(i-j) < 0.0000001) { 53 | return true; 54 | } else { 55 | return false; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /notes/对称的二叉树.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 请实现一个函数,用来判断一颗二叉树是不是对称的。如果一颗二叉树和它的镜像一样,那么它是对称的。如下图: 3 | 4 | ![offer28](https://github.com/todorex/Coding-Interviews/raw/master/images/offer28.png) 5 | 6 | 二叉树节点定义如下: 7 | ```Java 8 | public class BinaryTreeNode { 9 | double value; 10 | BinaryTreeNode left; 11 | BinaryTreeNode right; 12 | } 13 | ``` 14 | 15 | # 测试用例 16 | * 功能测试(对称的二叉树;因结构而不对称的二叉树;结构对称但节点值不对称的二叉树) 17 | * 特殊输入测试(二叉树的根节点为空指针;只有一个节点的二叉树;所有节点的值都相同的二叉树) 18 | 19 | # 题目考点 20 | * 考察应聘者对二叉树的理解。 21 | * 考察应聘者的思维能力,应聘者可以通过画图把抽象问题形象化。 22 | 23 | # 解题思路 24 | 采用递归 25 | 26 | 只要左子树的右子树 和 右子树的左子树相同 27 | 左子树的左子树 和 右子树的右子树相同即可 28 | 29 | 30 | 如果采用栈或者队列,请需要考虑所有节点的值都相同的二叉树这种特殊输入测试。 31 | 32 | # 自己解题 33 | 没有理解对称的判断。 34 | 35 | # 参考解题 36 | ```Java 37 | /** 38 | * 对称的二叉树 39 | * 40 | * @Author rex 41 | * 2018/8/3 42 | */ 43 | public class Solution { 44 | /** 45 | * 理解完镜像 46 | * 递归解法 47 | * @param pRoot 48 | * @return 49 | */ 50 | boolean isSymmetrical(BinaryTreeNode pRoot) { 51 | if (pRoot == null) { 52 | return false; 53 | } 54 | return isSymmetrical(pRoot.left, pRoot.right); 55 | } 56 | 57 | boolean isSymmetrical(BinaryTreeNode pRoot1, BinaryTreeNode pRoot2) { 58 | if (pRoot1 == null && pRoot2 == null) { 59 | return true; 60 | } 61 | if (pRoot1 == null || pRoot2 == null || pRoot1.value != pRoot2.value) { 62 | return false; 63 | } 64 | return isSymmetrical(pRoot1.left, pRoot2.right) && isSymmetrical(pRoot1.right, pRoot2.left); 65 | } 66 | } 67 | ``` 68 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer17/Solution2.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer17; 2 | 3 | /** 4 | * 打印从1到最大的n位数 5 | * 6 | * @Author rex 7 | * 2018/7/22 8 | */ 9 | public class Solution2 { 10 | /** 11 | * 把问题转成数字排列的解法 12 | * 相当于回溯法 13 | * @param n 14 | */ 15 | public void print1ToMaxOfNDigits(int n) { 16 | if (n <= 0) { 17 | return; 18 | } 19 | char[] number = new char[n]; 20 | print1ToMaxOfNDigitsRecursively(number, n, 0); 21 | 22 | } 23 | 24 | /** 25 | * 递归核心 26 | * @param number 27 | * @param length 28 | * @param index 字符串的第几位 29 | */ 30 | public void print1ToMaxOfNDigitsRecursively(char[] number, int length, int index) { 31 | if (index > length - 1) { 32 | printNumber(number); 33 | return; 34 | } 35 | for (int i = 0; i < 10; i++) { 36 | number[index] = (char) (i +'0'); 37 | print1ToMaxOfNDigitsRecursively(number, length, index + 1); 38 | } 39 | } 40 | /** 41 | * 根据字符串打印出数字 42 | * @param number 43 | */ 44 | private void printNumber(char[] number) { 45 | // 默认字符串不以0开始 46 | boolean isBegining0 = true; 47 | 48 | for (int i = 0; i < number.length; i++) { 49 | if (isBegining0 && number[i] != '0') { 50 | isBegining0 = false; 51 | } 52 | if (!isBegining0) { 53 | System.out.print(number[i]); 54 | } 55 | } 56 | System.out.println(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer18/Solution2.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer18; 2 | 3 | /** 4 | * 删除链表重复的节点 5 | * @Author rex 6 | * 2018/7/23 7 | */ 8 | public class Solution2 { 9 | /** 10 | * 全面的删除 11 | * @param pHead 12 | * @return 13 | */ 14 | public ListNode deleteDuplication(ListNode pHead) { 15 | // 防止非法输入 16 | if (pHead == null || pHead.next == null) { 17 | return pHead; 18 | } 19 | // 两个指针 20 | ListNode preNode = null; 21 | ListNode node = pHead; 22 | while (node != null) { 23 | ListNode next = node.next; 24 | // 删除标志位 25 | boolean needDeleted = false; 26 | if (next != null && next.val == node.val) { 27 | needDeleted = true; 28 | } 29 | if (!needDeleted) { 30 | preNode = node; 31 | node = node.next; 32 | } else { 33 | int value = node.val; 34 | ListNode toBeDelete = node; 35 | while (toBeDelete != null && toBeDelete.val == value) { 36 | next = toBeDelete.next; 37 | toBeDelete = next; 38 | // 考虑头指针是否存在 39 | if (preNode == null) { 40 | pHead = next; 41 | } else { 42 | preNode.next = next; 43 | } 44 | node = next; 45 | } 46 | } 47 | } 48 | return pHead; 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /notes/圆圈中最后剩下的数字.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 0,1,...,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈剩下的最后一个数字。 3 | 4 | **约瑟夫环问题** 5 | # 测试用例 6 | * 功能测试(输入的m小于n,比如从最初有5个数字的圆圈中每次删除第2、3个数字;输入的m大于或者等于n,比如从最初有6个数字的圆圈中每次删除第6、7个数字) 7 | * 特殊输入测试(圆圈中有0个数字) 8 | * 性能测试(从最初有400个数字的圆圈中每次删除第997个数字) 9 | 10 | # 题目考点 11 | * 考察应聘者的抽象建模能力。 12 | * 考察应聘者对环形链表的理解及应用能力。 13 | * 考察应聘者的数学功底及逻辑思维能力。 14 | 15 | # 解题思路 16 | 解题思路一:利用链表(可以模拟的)来循环遍历 17 | 18 | 解题思路二:利用数学推导出一个递归公式,见书本。这里就不展示了。 19 | # 自己解题 20 | 最普通的链表(模拟链表)遍历 21 | 22 | ```Java 23 | /** 24 | * 圆圈中最后剩下的数字 25 | * 26 | * @Author rex 27 | * 2018/9/18 28 | */ 29 | public class Solution { 30 | /** 31 | * 环形链表解题 32 | * @param n 33 | * @param m 34 | * @return 35 | */ 36 | public int lastRemaining_Solution(int n, int m) { 37 | // 防止特殊输入 38 | if (n < 0 || m < 1) { 39 | return -1; 40 | } 41 | // 构造链表 42 | List list = new ArrayList<>(); 43 | for (int i = 0; i < n; i++) { 44 | list.add(i); 45 | } 46 | int current = 0; 47 | while (list.size() > 1) { 48 | for (int i = 1; i < m; i++) { 49 | current++; 50 | if (current == list.size()) { 51 | current = 0; 52 | } 53 | } 54 | // 删除之后,注意边界 55 | list.remove(current); 56 | if (current > list.size() - 1) { 57 | current = 0; 58 | } 59 | } 60 | return list.get(current); 61 | 62 | } 63 | } 64 | ``` 65 | # 参考解题 66 | 1. 见自己解题 67 | 2. 见书本数学推导递归求解 68 | -------------------------------------------------------------------------------- /notes/把数字翻译成字符串.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 给定一个数字,我们按照如下规则翻译成字符串:0 翻译成“a”,1 翻译成“b”... 25 翻译成“z”。一个数字有多种翻译可能,例如 12258 一共有 5 种,分别是 bccfi,bwfi,bczi,mcfi,mzi。实现一个函数,用来计算一个数字有多少种不同的翻译方法。 3 | 4 | # 测试用例 5 | * 功能测试(只有一位数字;包含多位数字) 6 | * 特殊输入测试(输入的数字字符串指针为空指针) 7 | 8 | # 题目考点 9 | * 考察应聘者分析问题的能力。应聘者能够从问题中分析出递归的表达式`[f(i)=f(i-1) + g(i,i-1)f(i-2)]`是解决这个问题的关键。 10 | * 考察应聘者对递归及时间效率的理解。面试官期待应聘者能够用基于循环的代码(动态规划)来避免不必要的重复计算。 11 | 12 | # 解题思路 13 | **动态规划** 14 | 15 | 假设数组dp[i]表示从头到字符串的第i位(不是字符串的下标),一共有多少种解码方法的话 16 | 17 | 首先我们一定能从第i-1位的解码方法上继续解码。那么就是dp[i] += dp[i-1]; 18 | 19 | 除此之外如果字符串的第i-1位和第i位能组成一个10到25的数字,说明我们可以是在第i-2位的解码方法上继续解码。那么就是dp[i] += dp[i-2]; 20 | 21 | # 自己解题 22 | 没有抽象出来,抽象能力太差!! 23 | 24 | # 参考解题 25 | ```java 26 | /** 27 | * 把数字翻译成字符串 28 | * 29 | * @Author rex 30 | * 2018/8/29 31 | */ 32 | public class Solution { 33 | /** 34 | * 动态规划 35 | * @param s 36 | * @return 37 | */ 38 | public int numDecodings(String s) { 39 | 40 | // 防止特殊输入 41 | if (s == null || s.length() == 0) { 42 | return 0; 43 | } 44 | // 存储动态规划中间值 45 | int[] dp = new int[s.length()+1]; 46 | 47 | // 为了dp[2] += dp[0] 而设置 48 | dp[0] = 1; 49 | dp[1] = 1; 50 | 51 | for (int i = 2; i <= s.length(); i++) { 52 | // 1. 肯定会发生 53 | dp[i] += dp[i-1]; 54 | // 2. 当前位与前一位可以组成一个数字时 55 | if (Integer.parseInt(s.substring(i-2,i)) >=0 && Integer.parseInt(s.substring(i-2,i)) <=25) { 56 | dp[i] += dp[i-2]; 57 | } 58 | 59 | 60 | } 61 | return dp[s.length()]; 62 | } 63 | } 64 | ``` 65 | # 补充 66 | 动态规划 67 | -------------------------------------------------------------------------------- /notes/连续子数组的最大和.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。 3 | 4 | # 测试用例 5 | * 功能测试(输入的数组汇中有正数也有负数;输入的数组中全是正数;输入的数组中全是负数) 6 | * 特殊输入测试(表示数组的指针为空指针) 7 | 8 | # 题目考点 9 | * 考察应聘者对时间复杂度的理解。**这道题如果应聘者给出时间复杂度为O(n2)甚至O(n3)的算法,则是不能通过面试的。** 10 | * 考察应聘者对动态规划的理解。 11 | * 考察应聘者思维的全面性。能否合理地处理无效的输入。 12 | 13 | # 解题思路 14 | 动态规划思想 15 | 16 | 我们用函数f(i)来表示以第i个数字结尾的子数组的最大和,那么我们需要求出max[f(i)],其中求出max[f(i)],其中0<=i 0 f(i) > 0 20 | ``` 21 | 22 | 如果我们用一个数组来表示f(i),那么最后我们又要排序,所以又在无形之见加入了时间复杂度,所以在进行上述过程中就需要加入比较逻辑,当上诉过程完成之后,最大值也得到了。 23 | 24 | # 自己解题 25 | ```Java 26 | /** 27 | * 连续子数组的最大和 28 | * 29 | * @Author rex 30 | * 2018/8/25 31 | */ 32 | public class Solution { 33 | /** 34 | * 找规律解法(思想还是动态规划的) 35 | * @param array 36 | * @return 37 | */ 38 | public int findGreatestSumOfSubArray(int[] array) { 39 | 40 | if (array == null || array.length == 0) { 41 | return 0; 42 | } 43 | int max = array[0]; 44 | 45 | int curMax = max; 46 | for (int i = 1; i < array.length; i++) { 47 | 48 | if (curMax <= 0) { 49 | curMax = array[i]; 50 | 51 | } else { 52 | curMax += array[i]; 53 | 54 | } 55 | if (curMax > max) { 56 | max = curMax; 57 | } 58 | 59 | } 60 | return max; 61 | } 62 | } 63 | ``` 64 | 20ms,还是很快的!! 65 | # 参考解题 66 | 见自己解题。 67 | # 补充 68 | 虽然通常我们用递归的方式来分析动态规划的问题,但最终都会基于循环去编码。 69 | -------------------------------------------------------------------------------- /notes/把数组排成最小的数.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组 {3,32,321},则打印出这三个数字能排成的最小数字为 321323。 3 | # 测试用例 4 | * 功能测试(输入的数组中有多个数字;输入的素组中的数字有重复的数位;输入的数组中只有一个数字) 5 | * 特殊输入测试(输入数组的指针为空指针) 6 | 7 | # 题目考点 8 | * 本题有两个难点:第一个难点是相处一种新的比较规则来排序一个数组;第二个难点是证明这个比较规则是有效的,并且证明根据这个规则排序之后,把数组中所有数字拼接起来得到的拼接起来得到的数字是最小的。这需要很强的数学功底和逻辑思维能力。 9 | * 考察应聘者解决大数问题的能力。应聘者在面试的时候要意识到,把两个int型的整数拼接起来得到的数字可能会超过int型数字能够表达的范围,从而导致数字溢出。我们可以用字符串表示数字,这样就能简洁地解决大数问题。 10 | 11 | # 解题思路 12 | 可以看成是一个排序问题,在比较两个字符串 S1 和 S2 的大小时,应该比较的是 S1+S2 和 S2+S1 的大小,如果 S1+S2 < S2+S1,那么应该把 S1 排在前面,否则应该把 S2 排在前面。 13 | 14 | 证明参考《剑指Offer》 15 | 16 | # 自己解题 17 | 全排列N!,No,So,大写的不会!! 18 | 19 | # 参考解题 20 | ```java 21 | /** 22 | * 把数组排成最小的数 23 | * 24 | * @Author rex 25 | * 2018/8/28 26 | */ 27 | public class Solution { 28 | 29 | /** 30 | * 新的排序规则解题 31 | * @param numbers 32 | * @return 33 | */ 34 | public String printMinNumber(int [] numbers) { 35 | // 防止特殊输入 36 | if (numbers == null || numbers.length == 0) { 37 | return ""; 38 | } 39 | // 以防两个数相加溢出,将int 变成 string 40 | String[] numberStrings = new String[numbers.length]; 41 | for (int i = 0; i < numbers.length; i++) { 42 | numberStrings[i] = numbers[i] + ""; 43 | } 44 | 45 | // 利用新的排序规则排序 46 | Arrays.sort(numberStrings, (s1, s2) -> (s1 + s2).compareTo(s2 + s1)); 47 | 48 | StringBuilder result = new StringBuilder(); 49 | 50 | for (String s : numberStrings) { 51 | result.append(s); 52 | } 53 | 54 | return result.toString(); 55 | } 56 | } 57 | ``` 58 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer16/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer16; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * 数值的整数次方 7 | * 8 | * @Author rex 9 | * 2018/7/21 10 | */ 11 | public class Solution1 { 12 | 13 | private boolean g_invalid_input = false; 14 | 15 | /** 16 | * 全面但不够高效的解法 17 | * 18 | * @param base 19 | * @param exponent 20 | * @return 21 | */ 22 | public double power(double base, int exponent) { 23 | // 0的0次方没有意义 24 | if (doubleCompare(base, 0.0) == 0 && exponent == 0) { 25 | g_invalid_input = false; 26 | return 0.0; 27 | } 28 | int absExponent = Math.abs(exponent); 29 | double result = powerWithPositiveExponent(base, absExponent); 30 | if (exponent < 0) { 31 | result = 1.0/result; 32 | } 33 | return result; 34 | } 35 | 36 | 37 | 38 | /** 39 | * 指数为正时,得到的整数次方 40 | * 41 | * @param base 42 | * @param exponent 43 | * @return 44 | */ 45 | public double powerWithPositiveExponent(double base, int exponent) { 46 | double result = 1.0; 47 | for (int i = 1; i <= exponent; i++) { 48 | result *= base; 49 | } 50 | return result; 51 | } 52 | 53 | /** 54 | * 比较两个浮点型大小 55 | * @param a 56 | * @param b 57 | * @return 58 | */ 59 | public int doubleCompare(double a, double b) { 60 | BigDecimal data1 = new BigDecimal(a); 61 | BigDecimal data2 = new BigDecimal(b); 62 | return data1.compareTo(data2); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /code/Coding-Interviews/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 36 | -------------------------------------------------------------------------------- /notes/二叉搜索树的第K大的节点.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 给定一棵二叉搜索树,请找出其中的第k小的结点。例如,在下图的二叉搜索树里,按节点数值大小顺序,第三大节点的值是4。 3 | 4 | ![offer54](https://github.com/todorex/Coding-Interviews/raw/master/images/offer54.png) 5 | 6 | # 测试用例 7 | * 功能测试(各种形态不同的二叉搜索树) 8 | * 边界值测试(输入k为0、1、二叉搜索树的节点数、二叉树搜索树的节点数+1) 9 | * 特殊输入测试(指向二叉搜索树根节点的指针为空指针) 10 | 11 | # 题目考点 12 | * 考察应聘者的只是迁移能力,利用中序遍历解题。 13 | * 考察应聘者对二叉搜索树和中序遍历的特点的理解。 14 | 15 | # 解题思路 16 | 按照中序遍历的顺序遍历一个二叉搜索树。 17 | 18 | 只要熟悉中序遍历的写法,那么这道题就不难了。 19 | # 自己解题 20 | ```java 21 | /** 22 | * 二叉搜索树的第K大节点 23 | * 24 | * @Author rex 25 | * 2018/9/8 26 | */ 27 | public class Solution { 28 | private int k; 29 | 30 | /** 31 | * 二叉搜索树的第K大节点 32 | * 33 | * @param pRoot 34 | * @param k 35 | * @return 36 | */ 37 | TreeNode kthNode(TreeNode pRoot, int k) { 38 | // 防止特殊输入 39 | if (pRoot == null || k == 0) { 40 | return null; 41 | } 42 | this.k = k; 43 | return kthNodeCore(pRoot); 44 | 45 | } 46 | 47 | /** 48 | * 中序遍历 49 | * 50 | * @param pRoot 51 | * @return 52 | */ 53 | TreeNode kthNodeCore(TreeNode pRoot) { 54 | TreeNode target = null; 55 | 56 | if (pRoot.left != null) { 57 | target = kthNodeCore(pRoot.left); 58 | } 59 | // 核心 60 | if (target == null) { 61 | if (k == 1) { 62 | target = pRoot; 63 | } 64 | k--; 65 | } 66 | 67 | if (target == null && pRoot.right != null) { 68 | target = kthNodeCore(pRoot.right); 69 | } 70 | 71 | return target; 72 | 73 | } 74 | } 75 | ``` 76 | # 参考解题 77 | 见自己解题。 78 | -------------------------------------------------------------------------------- /notes/栈的压入、弹出序列.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是或否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。 3 | 4 | # 测试用例 5 | * 功能测试(输入的两个数组含有多个数字或者只有一个数字;第二个数组是或者不是第一个数组表示的压入序列对应的栈的弹出序列) 6 | * 特殊输入测试(输入两个空指针) 7 | 8 | # 题目考点 9 | * 考察应聘者对栈的理解 10 | * 考察应聘者用举例分析复杂问题的能力 11 | 12 | # 解题思路 13 | 1. 建立一个辅助栈 14 | 2. 当栈为空或者栈的栈顶元素不为弹出序列需要的弹出元素时,将压入序列继续压入栈直到栈顶为弹出元素 15 | 3. 当栈顶元素为弹出元素时,则弹出该元素 16 | 4. 当压入序列的所有元素都压入栈时,依然找不到匹配的弹出元素,则不是弹出序列 17 | 18 | # 自己解题 19 | ```Java 20 | /** 21 | * 栈的压入、弹出序列 22 | * @Author rex 23 | * 2018/8/6 24 | */ 25 | public class Solution { 26 | /** 27 | * 辅助栈 28 | */ 29 | Stack stack = new Stack<>(); 30 | 31 | /** 32 | * 字节解题 33 | * @param pushA 34 | * @param popA 35 | * @return 36 | */ 37 | public boolean isPopOrder(int [] pushA,int [] popA) { 38 | if (pushA == null || popA ==null) { 39 | return false; 40 | } 41 | int pushAIndex = 0; 42 | int popAIndex = 0; 43 | while (popAIndex < popA.length) { 44 | if ((stack.empty() || stack.peek() != popA[popAIndex])) { 45 | if (pushAIndex == pushA.length) { 46 | break; 47 | } 48 | stack.push(pushA[pushAIndex]); 49 | pushAIndex++; 50 | } else { 51 | stack.pop(); 52 | popAIndex++; 53 | } 54 | } 55 | if (popAIndex == popA.length) { 56 | return true; 57 | } else { 58 | return false; 59 | } 60 | } 61 | } 62 | ``` 63 | # 参考解题 64 | 参考自己解题 65 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer54/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer54; 2 | 3 | /** 4 | * 二叉搜索树的第K大节点 5 | * 6 | * @Author rex 7 | * 2018/9/8 8 | */ 9 | public class Solution { 10 | private int k; 11 | 12 | /** 13 | * 二叉搜索树的第K大节点 14 | * 15 | * @param pRoot 16 | * @param k 17 | * @return 18 | */ 19 | TreeNode kthNode(TreeNode pRoot, int k) { 20 | // 防止特殊输入 21 | if (pRoot == null || k == 0) { 22 | return null; 23 | } 24 | this.k = k; 25 | return kthNodeCore(pRoot); 26 | 27 | } 28 | 29 | /** 30 | * 中序遍历 31 | * 32 | * @param pRoot 33 | * @return 34 | */ 35 | TreeNode kthNodeCore(TreeNode pRoot) { 36 | TreeNode target = null; 37 | 38 | if (pRoot.left != null) { 39 | target = kthNodeCore(pRoot.left); 40 | } 41 | // 核心 42 | if (target == null) { 43 | if (k == 1) { 44 | target = pRoot; 45 | } 46 | k--; 47 | } 48 | 49 | if (target == null && pRoot.right != null) { 50 | target = kthNodeCore(pRoot.right); 51 | } 52 | 53 | return target; 54 | 55 | } 56 | 57 | 58 | public static void main(String[] args) { 59 | Solution solution = new Solution(); 60 | TreeNode t1 = new TreeNode(8); 61 | TreeNode t2 = new TreeNode(6); 62 | TreeNode t3 = new TreeNode(5); 63 | TreeNode t4 = new TreeNode(7); 64 | t1.left = t2; 65 | t2.left = t3; 66 | t2.right = t4; 67 | System.out.println(solution.kthNode(t1, 2).val); 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer39/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer39; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 数组中出现次数超过一半的数字 8 | * 9 | * @Author rex 10 | * 2018/8/15 11 | */ 12 | public class Solution1 { 13 | /** 14 | * 基于数组特点解法 15 | * 投票问题 16 | * @param array 17 | * @return 18 | */ 19 | public int moreThanHalfNum_Solution(int [] array) { 20 | if (array == null || array.length == 0) { 21 | return 0; 22 | } 23 | int result = array[0]; 24 | int times = 1; 25 | for (int i = 1; i < array.length; i ++) { 26 | if (times == 0) { 27 | result = array[i]; 28 | times = 1; 29 | } 30 | 31 | if (array[i] == result) { 32 | times++; 33 | } else { 34 | times--; 35 | } 36 | } 37 | if (checkMoreThanHalf(array,result)) { 38 | return result; 39 | } else { 40 | return 0; 41 | } 42 | 43 | 44 | } 45 | 46 | /** 47 | * 检查result是不是在array出现次数超过一半 48 | * @param array 49 | * @param result 50 | * @return 51 | */ 52 | public boolean checkMoreThanHalf(int[] array, int result) { 53 | boolean isMoreThanHalf = false; 54 | int times = 0; 55 | for (int i = 0; i < array.length; i++) { 56 | if (array[i] == result) { 57 | times++; 58 | } 59 | } 60 | if (times > (array.length >> 1)) { 61 | isMoreThanHalf =true; 62 | } 63 | return isMoreThanHalf; 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer61/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer61; 2 | 3 | import java.util.Arrays; 4 | 5 | 6 | /** 7 | * 扑克牌中的顺子 8 | * 9 | * @Author rex 10 | * 2018/9/18 11 | */ 12 | public class Solution { 13 | /** 14 | * 参考解题 15 | * @param numbers 0表示大小王 16 | * @return 17 | */ 18 | public boolean isContinuous(int [] numbers) { 19 | // 防止特殊输入测试 20 | if (numbers == null || numbers.length < 1) { 21 | return false; 22 | } 23 | // 1. 数组排序 24 | Arrays.sort(numbers); 25 | // 2. 统计数组中0的个数 26 | int zeroCount = 0; 27 | for (int i = 0; i < numbers.length; i++) { 28 | if (numbers[i] == 0) { 29 | zeroCount++; 30 | } else { 31 | break; 32 | } 33 | } 34 | // 3. 统计排序之后的数组中相邻数字之间的空缺总数 35 | int lackCount = 0; 36 | int temp = numbers[zeroCount]; 37 | for (int i = zeroCount; i < numbers.length - 1; ) { 38 | if (numbers[i + 1] - temp == 1) { 39 | i++; 40 | } else if (numbers[i + 1] - temp == 0) { 41 | return false; 42 | } else { 43 | lackCount++; 44 | } 45 | temp++; 46 | } 47 | // 4. 比较0的个数和空缺总数 48 | if (lackCount > zeroCount) { 49 | return false; 50 | } 51 | return true; 52 | 53 | } 54 | 55 | public static void main(String[] args) { 56 | Solution solution = new Solution(); 57 | int[] test = new int[]{1, 3, 2, 6, 4}; 58 | System.out.println(solution.isContinuous(test)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /notes/从上到下打印二叉树.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 从上到下打印二叉树的每个节点,同一层的节点按照从左到右的顺序打印。例如下图,则依次打印出1,2,3,4,5,6,7。 3 | 4 | ![offer32](https://github.com/todorex/Coding-Interviews/raw/master/images/offer32.png) 5 | 6 | 二叉树节点如下: 7 | ```java 8 | public class BinaryTreeNode { 9 | int value; 10 | BinaryTreeNode left; 11 | BinaryTreeNode right; 12 | } 13 | ``` 14 | 15 | # 测试用例 16 | * 功能测试(完全二叉树;所有节点只有左子树的二叉树;所有节点只有右子树的二叉树) 17 | * 特殊功能测试(二叉树根节点为空指针;只有一个节点的二叉树) 18 | 19 | # 题目考点 20 | * 考察应聘者的思维能力,想到用队列处理按层遍历。 21 | * 考察应聘者对二叉树及队列的理解。 22 | 23 | # 解题思路 24 | 这道题如果能想到队列,编码还是很简单的。不逼逼了!!! 25 | 26 | 1. 先将头节点放入队列 27 | 2. 当队列不为空的时候,从队列取出一个节点输出 28 | 3. 判断取出的节点左右节点是否为空,不为空则将他们加入队列 29 | 4. 循环至队列为空跳出 30 | 31 | # 自己解题 32 | 没有思路。 33 | 34 | # 参考解题 35 | ```java 36 | /** 37 | * 从上到下打印二叉树 38 | * 39 | * @Author rex 40 | * 2018/8/7 41 | */ 42 | public class Solution { 43 | /** 44 | * 用队列做中间缓存 45 | * 46 | * @param root 47 | * @return 48 | */ 49 | public ArrayList printFromTopToBottom(BinaryTreeNode root) { 50 | ArrayList result = new ArrayList<>(); 51 | if (root == null) { 52 | return result; 53 | } 54 | Queue queue = new LinkedList<>(); 55 | queue.offer(root); 56 | while (!queue.isEmpty()) { 57 | BinaryTreeNode node = queue.poll(); 58 | result.add(node.value); 59 | if (node.left != null) { 60 | queue.offer(node.left); 61 | } 62 | if (node.right != null) { 63 | queue.offer(node.right); 64 | } 65 | } 66 | return result; 67 | } 68 | } 69 | ``` 70 | # 补充 71 | 不管是广度优先遍历一副有向图还是一颗树,都要用到队列或者栈。 72 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer58/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer58; 2 | 3 | /** 4 | * 翻转单词顺序 5 | * 6 | * @Author rex 7 | * 2018/9/15 8 | */ 9 | public class Solution1 { 10 | /** 11 | * 翻转单词顺序 12 | * @param str 13 | * @return 14 | */ 15 | public String reverseSentence(String str) { 16 | if (str == null || str.length() == 0) { 17 | return ""; 18 | } 19 | char[] chars = str.toCharArray(); 20 | // 1. 反转整个字符串 21 | reverse(chars, 0, chars.length - 1); 22 | // 2. 反转每个单词 23 | int i = 0, j = 0; 24 | while (j <= chars.length - 1) { 25 | if (j == chars.length -1 || chars[j] == ' ') { 26 | reverse(chars, i, j - 1); 27 | i = j + 1; 28 | } 29 | j++; 30 | } 31 | return new String(chars); 32 | } 33 | 34 | /** 35 | * 反转char数组 36 | * @param chars 37 | * @param start 38 | * @param end 39 | */ 40 | public void reverse(char[] chars, int start, int end) { 41 | while (start < end) { 42 | swap(chars, start, end); 43 | start++; 44 | end--; 45 | } 46 | } 47 | 48 | /** 49 | * 交换char数组两个位置的值 50 | * @param chars 51 | * @param i 52 | * @param j 53 | */ 54 | public void swap(char[] chars, int i, int j) { 55 | char temp = chars[i]; 56 | chars[i] = chars[j]; 57 | chars[j] = temp; 58 | } 59 | 60 | public static void main(String[] args) { 61 | Solution solution = new Solution(); 62 | String s = "Wonderful"; 63 | System.out.println(solution.reverseSentence(s)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer56/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer56; 2 | 3 | /** 4 | * 数组中值出现一次的两个数字 5 | * 6 | * @Author rex 7 | * 2018/9/13 8 | */ 9 | public class Solution { 10 | 11 | /** 12 | * 找出数组中值出现一次的两个数字 13 | * @param array 14 | * @param num1 15 | * @param num2 16 | */ 17 | public void findNumsAppearOnce(int [] array,int num1[] , int num2[]) { 18 | if (array == null || array.length < 2) { 19 | return; 20 | } 21 | int resultExclusiveOR = 0; 22 | // 1. 得到数组各个数字异或的结果 23 | for (int i = 0; i < array.length; i++) { 24 | resultExclusiveOR ^= array[i]; 25 | } 26 | // 2. 找出异或结果二进制位为1的位 27 | int indexBit = findFirstBitIs1(resultExclusiveOR); 28 | // 3. 根据二进制位为1分成两部分分别异或,得到两个只出现一次的数字 29 | for (int i = 0; i < array.length; i++) { 30 | if (isBit1(array[i], indexBit)) { 31 | num1[0] ^= array[i]; 32 | } else { 33 | num2[0] ^= array[i]; 34 | } 35 | } 36 | } 37 | 38 | /** 39 | * 在整数num的二进制表示中找到右边是1的位 40 | * @param num 41 | * @return 42 | */ 43 | public int findFirstBitIs1(int num) { 44 | int indexBit = 0; 45 | // int为32位 46 | while ((num & 1) == 0 && indexBit < 32) { 47 | num = num >> 1; 48 | indexBit++; 49 | } 50 | return indexBit; 51 | 52 | } 53 | 54 | /** 55 | * 判断num的二进制从右边数起的第indexBit是不是1 56 | * @param num 57 | * @param indexBit 从0开始 58 | * @return 59 | */ 60 | public boolean isBit1(int num, int indexBit) { 61 | return ((num >> indexBit) & 1) == 1; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /notes/二维数组中的查找.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 3 | 4 | 例如下面的二维数组就是每行、每列都是递增排序,如果在这个数组中查找数字7,则放回true;如果查找数字5,由于数组不含该数字,则返回false。 5 | ``` 6 | 1 2 8 9 7 | 2 4 6 12 8 | 4 7 10 13 9 | 6 8 11 15 10 | ``` 11 | # 测试用例 12 | * 二维数组中包含查找的数字(查找的数字是数组中的最大值和最小值;查找的数字介于数组中的最大值和最小值之间) 13 | * 二维数组中没有查找的数字(查找的数字大于数组中的最大值;查找的数字小于数组中的最小值,查找的数字在数组的最大值与最小值之间但数组汇总没有这个数字) 14 | * 特殊输入测试(输入数组一维长度或者二维长度为0) 15 | 16 | # 题目考点 17 | * 考察应聘者对二维数组的理解及编程能力。二维数组在内存中占据连续的空间。 18 | * 考察应聘者分析问题的能力。当应聘者发现问题比较复杂时,能不能 **通过具体的例子找出其中的规律**,是能否解决这个问题的关键。 19 | 20 | 21 | # 解题思路 22 | 首先选取数组中右上角(左下角也可以)的数字,如果该数字等于要查找的数字,那么查找过程结束;如果该数字大于要查找的数字,则剔除这个数字所在的列,因为它所在的列的所有值都比要查找的数字大;如果该数字小于要查找的数字,则剔除这个数字所在的行,因为它所在的列的所有值都比要查找的数字小;也就是说,如果查找的数字不在数组(新查找范围)的右上角,则每次都能剔除一行或者一列,每次都可以缩小查找的范围,直到找到要查找的数字,或者查找不到。 23 | 24 | # 自己解题 25 | ```java 26 | /** 27 | * 二维数组中的查找 28 | * 从例子中找规律 29 | * @Author rex 30 | * 2018/6/9 31 | */ 32 | public class Solution { 33 | 34 | /** 35 | * @Author rex 36 | * @Date 2018/6/10 上午9:28 37 | * @Description 查找二维数组中的 38 | * @param target 要查找的数字 39 | * @param array 被查找的数组 40 | */ 41 | public static boolean find(int target, int[][] array) { 42 | if (array == null || array.length == 0 || array[0].length == 0) { 43 | return false; 44 | } 45 | int i = 0; 46 | int j = array[0].length - 1; 47 | while (j >= 0 && i <= array.length - 1) { 48 | if (array[i][j] == target) { 49 | return true; 50 | } else if (array[i][j] > target) { 51 | j--; 52 | } else { 53 | i++; 54 | } 55 | } 56 | return false; 57 | } 58 | } 59 | ``` 60 | # 正确解题 61 | 同自己解题。 62 | -------------------------------------------------------------------------------- /notes/序列化二叉树.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 请实现两个函数,分别用来序列化和反序列化二叉树。 3 | # 测试用例 4 | * 功能测试(输入的二叉树是完全二叉树;所有节点都没有左/右子树的二叉树;只有一个节点的二叉树;所有节点的值都相同的二叉树) 5 | * 特殊输入测试(指向二叉树根节点的指针为空指针) 6 | 7 | # 题目考点 8 | * 考察应聘者分析复杂问题的能力。 9 | * 考察应聘者对二叉树遍历的理解及编程能力。 10 | 11 | # 解题思路 12 | 确定序列化规则 13 | 14 | 如果我们根据前序遍历进行序列化,那么我们只要根据前序遍历解序列化就可以啦!! 15 | 16 | # 自己解题 17 | 什么鬼? 18 | 19 | # 参考解题 20 | ```Java 21 | /** 22 | * 序列化二叉树 23 | * 24 | * @Author rex 25 | * 2018/8/13 26 | */ 27 | public class Solution { 28 | 29 | int index; 30 | 31 | /** 32 | * 序列化成String(前序遍历) 33 | * 节点之间的分隔符是 ',' 34 | * 空节点为'$' 35 | * @param root 36 | * @return 37 | */ 38 | String serialize(BinaryTreeNode root) { 39 | if (root == null) { 40 | return "$,"; 41 | } 42 | return root.value + "," + serialize(root.left) + serialize(root.right); 43 | } 44 | 45 | /** 46 | * 反序列化 47 | * @param str 48 | * @return 49 | */ 50 | BinaryTreeNode deserialize(String str) { 51 | if (str == null || str.length() == 0) { 52 | return null; 53 | } 54 | index = -1; 55 | String[] strNode = str.split(","); 56 | return deserialize(strNode); 57 | } 58 | 59 | /** 60 | * 真正反序列化 61 | * (前序遍历) 62 | * @param strNode 63 | * @return 64 | */ 65 | BinaryTreeNode deserialize(String[] strNode) { 66 | index++; 67 | BinaryTreeNode treeNode = null; 68 | if (! strNode[index].equals("$")) { 69 | treeNode = new BinaryTreeNode(Integer.valueOf(strNode[index])); 70 | treeNode.left = deserialize(strNode); 71 | treeNode.right = deserialize(strNode); 72 | } 73 | return treeNode; 74 | } 75 | } 76 | ``` 77 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer29/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer29; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * 顺时针打印矩阵 7 | * 8 | * @Author rex 9 | * 2018/8/4 10 | */ 11 | public class Solution { 12 | /** 13 | * 参考解法 14 | * 注重边界条件 15 | * @param matrix 16 | * @return 17 | */ 18 | public ArrayList printMatrix(int [][] matrix) { 19 | ArrayList list = new ArrayList(); 20 | int row = matrix.length; 21 | int column = matrix[0].length; 22 | // 计算要遍历的圈数 23 | int circles = (Math.min(row,column)-1)/2+1; 24 | int start = 0; 25 | while (start < circles) { 26 | int endX = column - 1 - start; 27 | int endY = row - 1 - start; 28 | // 开始一圈圈打印矩阵 29 | // 从左到右打印一行 30 | for (int i = start; i <= endX; i ++) { 31 | list.add(matrix[start][i]); 32 | } 33 | // 从上到下打印一列(需要考虑时候有) 34 | if (start < endY) { 35 | for (int i = start + 1; i <= endY; i++) { 36 | list.add(matrix[i][endX]); 37 | } 38 | } 39 | // 从右到左打印一列(需要考虑时候有) 40 | if (start < endY && start < endX) { 41 | for (int i = endX -1; i >= start; i--) { 42 | list.add(matrix[endY][i]); 43 | } 44 | } 45 | // 从下到上打印一列(需要考虑时候有) 46 | if (start < endX && start < endY -1) { 47 | for (int i = endY - 1; i > start; i--) { 48 | list.add(matrix[i][start]); 49 | } 50 | } 51 | start++; 52 | } 53 | return list; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /notes/扑克牌中的顺子.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王可以看成任意数字。 3 | 4 | # 测试用例 5 | * 功能测试(抽出的牌中有一个或者多个大、小王;抽出的牌中没有大、小王;抽出牌有对子) 6 | * 特殊输入测试(输入空指针) 7 | 8 | # 题目考点 9 | 考察应聘者的抽象建模能力。 10 | 11 | 我这个小伙子抽象能力不行呀!!! 12 | 13 | # 解题思路 14 | 1. 数组排序 15 | 2. 统计数组中0的个数 16 | 3. 统计排序之后的数组中相邻数字之间的空缺总数 17 | 4. 比较0的个数和空缺总数 18 | 19 | # 自己解题 20 | 没有好的抽象,只有超级暴力解题。 21 | 22 | 23 | # 参考解题 24 | ```Java 25 | /** 26 | * 扑克牌中的顺子 27 | * 28 | * @Author rex 29 | * 2018/9/18 30 | */ 31 | public class Solution { 32 | /** 33 | * 参考解题 34 | * @param numbers 0表示大小王 35 | * @return 36 | */ 37 | public boolean isContinuous(int [] numbers) { 38 | // 防止特殊输入测试 39 | if (numbers == null || numbers.length < 1) { 40 | return false; 41 | } 42 | // 1. 数组排序 43 | Arrays.sort(numbers); 44 | // 2. 统计数组中0的个数 45 | int zeroCount = 0; 46 | for (int i = 0; i < numbers.length; i++) { 47 | if (numbers[i] == 0) { 48 | zeroCount++; 49 | } else { 50 | break; 51 | } 52 | } 53 | // 3. 统计排序之后的数组中相邻数字之间的空缺总数 54 | int lackCount = 0; 55 | int temp = numbers[zeroCount]; 56 | for (int i = zeroCount; i < numbers.length - 1; ) { 57 | if (numbers[i + 1] - temp == 1) { 58 | i++; 59 | } else if (numbers[i + 1] - temp == 0) { 60 | return false; 61 | } else { 62 | lackCount++; 63 | } 64 | temp++; 65 | } 66 | // 4. 比较0的个数和空缺总数 67 | if (lackCount > zeroCount) { 68 | return false; 69 | } 70 | return true; 71 | 72 | } 73 | } 74 | ``` 75 | -------------------------------------------------------------------------------- /notes/字符串的排列.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 输入一个字符串,打印出该字符串中字符的所有排列。例如,输入字符串 abc,则打印出由字符 a, b, c 所能排列出来的所有字符串 abc, acb, bac, bca, cab 和 cba。 3 | 4 | # 测试用例 5 | * 功能测试(输入的字符串中有一个或者多个字符) 6 | * 特殊输入测试(输入的字符串的内容为空或者空指针) 7 | 8 | # 题目考点 9 | * 考察应聘者的思维能力。 10 | * 考察应聘者对递归的理解和编程能力。 11 | 12 | # 解题思路 13 | 通过将字符串分成两部分,从而把大问题分解成小问题,然后使用递归解决。 14 | 15 | 1. 求所有可能出现在第一位置的字符,即把第一个字符和后面所有的字符交换。(记得为了下一次的交换,要把之前交换的东西交换回来) 16 | 2. 固定第一个字符,求后面所有字符的排列。 17 | 3. 这时候我们仍把后面的所有字符分成两个部分:后面字符的第一个字符,以及这个字符之后的所有字符。重复过程1,2。 18 | 19 | # 自己解题 20 | 不会 21 | 22 | # 参考解题 23 | ```Java 24 | /** 25 | * 字符串的排列 26 | * 27 | * @Author rex 28 | * 2018/8/14 29 | */ 30 | public class Solution { 31 | /** 32 | * 字符串排序 33 | * 34 | * @param str 35 | */ 36 | void permutation(String str) { 37 | if (str == null || str.length() == 0) { 38 | return; 39 | } 40 | char[] ca = str.toCharArray(); 41 | permutation(ca, 0); 42 | } 43 | 44 | /** 45 | * 字符数组排序 46 | * 递归方法 47 | * 48 | * @param ca 49 | * @param begin 50 | */ 51 | void permutation(char[] ca, int begin) { 52 | if (begin == ca.length) { 53 | System.out.println(String.valueOf(ca)); 54 | } else { 55 | for (int i = begin; i < ca.length; i++) { 56 | // 和后面一个字符交换位置 57 | swap(ca, i, begin); 58 | permutation(ca, begin + 1); 59 | // 换回来(为了下一次交换) 60 | swap(ca, i, begin); 61 | } 62 | } 63 | 64 | } 65 | 66 | /** 67 | * 交换位置 68 | * @param ca 69 | * @param i 70 | * @param j 71 | */ 72 | void swap(char[] ca, int i, int j) { 73 | char temp = ca[i]; 74 | ca[i] = ca[j]; 75 | ca[j] = temp; 76 | } 77 | 78 | } 79 | ``` 80 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer52/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer52; 2 | 3 | 4 | /** 5 | * 两个链表的第一个公共节点 6 | * 7 | * @Author rex 8 | * 2018/9/4 9 | */ 10 | public class Solution1 { 11 | /** 12 | * 规律解题 13 | * 14 | * @param pHead1 15 | * @param pHead2 16 | * @return 17 | */ 18 | public ListNode findFirstCommonNode(ListNode pHead1, ListNode pHead2) { 19 | if (pHead1 == null || pHead2 == null) { 20 | return null; 21 | } 22 | int length1 = getLinkedListLength(pHead1); 23 | int length2 = getLinkedListLength(pHead2); 24 | 25 | if (length1 > length2) { 26 | pHead1 = walkStep(pHead1, length1 - length2); 27 | } else { 28 | pHead2 = walkStep(pHead2, length2 - length1); 29 | } 30 | while (pHead1 != null) { 31 | if (pHead1 == pHead2) { 32 | return pHead1; 33 | } 34 | pHead1 = pHead1.next; 35 | pHead2 = pHead2.next; 36 | } 37 | return null; 38 | } 39 | 40 | /** 41 | * 得到链表长度 42 | * @param pHead 43 | * @return 44 | */ 45 | public int getLinkedListLength(ListNode pHead) { 46 | int length = 0; 47 | if (pHead == null) { 48 | return length; 49 | } 50 | while (pHead != null) { 51 | length++; 52 | pHead = pHead.next; 53 | } 54 | return length; 55 | } 56 | 57 | /** 58 | * 链表走几步 59 | * @param pHead 60 | * @param step 61 | * @return 62 | */ 63 | public ListNode walkStep(ListNode pHead, int step) { 64 | while (step > 0) { 65 | pHead = pHead.next; 66 | step--; 67 | } 68 | return pHead; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer53/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer53; 2 | 3 | /** 4 | * 在排序数组中查找数字 5 | * @Author rex 6 | * 2018/9/6 7 | */ 8 | public class Solution { 9 | /** 10 | * 垃圾算法,还是和普通遍历一样的复杂度 11 | * @param array 12 | * @param k 13 | * @return 14 | */ 15 | public int getNumberOfK(int [] array , int k) { 16 | int count = 0; 17 | if (array == null || array.length == 0) { 18 | return count; 19 | } 20 | int index = getNumberOfKCore(array, k, 0, array.length - 1); 21 | int temp = index; 22 | while (temp >=0 && array[temp] == k) { 23 | count++; 24 | temp--; 25 | } 26 | temp = index + 1; 27 | while (temp <= array.length - 1 && array[temp] == k) { 28 | count++; 29 | temp++; 30 | } 31 | return count; 32 | } 33 | 34 | /** 35 | * 二分查找 36 | * @param array 37 | * @param k 38 | * @param start 39 | * @param end 40 | * @return 41 | */ 42 | public int getNumberOfKCore(int[] array, int k, int start, int end) { 43 | if (start == end) { 44 | return start; 45 | } 46 | int mid = (start + end) >> 1; 47 | if (array[mid] < k) { 48 | return getNumberOfKCore(array, k, start, mid - 1); 49 | } else if (array[mid] > k){ 50 | return getNumberOfKCore(array, k, mid + 1, end); 51 | } else { 52 | return mid; 53 | } 54 | } 55 | 56 | public static void main(String[] args) { 57 | int[] array = new int[]{1,3,3,3,3,4,5}; 58 | Solution solution = new Solution(); 59 | int count = solution.getNumberOfK(array, 2); 60 | System.out.println(count); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer20/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer20; 2 | 3 | /** 4 | * 表示数值的字符串 5 | * 6 | * @Author rex 7 | * 2018/7/24 8 | */ 9 | public class Solution { 10 | /** 11 | * 全靠debug暴力出来 12 | * 13 | * @param str 14 | * @return 15 | */ 16 | public boolean isNumeric(char[] str) { 17 | boolean haveE = false; 18 | boolean havaDot = false; 19 | boolean isNumber = false; 20 | int length = str.length; 21 | int i; 22 | for (i = 0; i < length; ) { 23 | if (i == length - 1) { 24 | if ((str[i] >= '0' && str[i] <= '9')) { 25 | i++; 26 | } else { 27 | return isNumber; 28 | } 29 | } else if (i == 0) { 30 | if (str[i] == '+' || str[i] == '-' || (str[i] >= '0' && str[i] <= '9')) { 31 | i++; 32 | } else { 33 | return isNumber; 34 | } 35 | } else if ((str[i] >= '0' && str[i] <= '9')) { 36 | i++; 37 | } else if (str[i] == 'e' || str[i] == 'E') { 38 | haveE = true; 39 | if (i + 1 < length && (str[i + 1] == '-' || str[i + 1] == '+' || (str[i + 1] >= '0' && str[i + 1] <= '9'))) { 40 | i += 2; 41 | } else { 42 | return isNumber; 43 | } 44 | 45 | } else if (!haveE && !havaDot && !haveE && str[i] == '.') { 46 | i++; 47 | havaDot = true; 48 | } else { 49 | return isNumber; 50 | } 51 | 52 | } 53 | if (i == length) { 54 | isNumber = true; 55 | 56 | } 57 | return isNumber; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer20/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer20; 2 | 3 | /** 4 | * 表示数值的字符串 5 | * @Author rex 6 | * 2018/7/24 7 | */ 8 | public class Solution1 { 9 | /** 10 | * 定义str的变量的位置 11 | */ 12 | private int i = 0; 13 | 14 | /** 15 | * 非正则表达式解法 16 | * 17 | * 利用模式分析为A[.[B]][e|EC]或者.B[e|EC] 18 | * @param str 19 | * @return 20 | */ 21 | public boolean isNumeric(char[] str) { 22 | // 防止特殊输入 23 | if (str == null || str.length < 0) { 24 | return false; 25 | } 26 | // 1. 扫描A 27 | 28 | boolean numberic = scanInteger(str); 29 | // 2. 扫描B 30 | if (i < str.length && str[i] == '.') { 31 | i++; 32 | // 小数后面可以没有数字 33 | numberic = scanUnSignedInteger(str) || numberic; 34 | } 35 | // 3. 扫描C 36 | if (i < str.length && (str[i] == 'e' || str[i] == 'E')) { 37 | i++; 38 | // 出现e/E 后面必须跟数字 39 | numberic = scanInteger(str) && numberic; 40 | } 41 | 42 | return numberic && i == str.length; 43 | } 44 | 45 | /** 46 | * 扫描整数部分(有可能在起始处有‘+’或者‘-’) 47 | * @param str 48 | * @return 49 | */ 50 | private boolean scanInteger(char[] str) { 51 | if (i < str.length && (str[i] == '+' || str[i] == '-')) { 52 | i++; 53 | } 54 | return scanUnSignedInteger(str); 55 | } 56 | 57 | /** 58 | * 扫描无符号整数部分(在起始处不可能有‘+’或者‘-’) 59 | * @param str 60 | * @return 61 | */ 62 | private boolean scanUnSignedInteger(char[] str) { 63 | // 用于对比是否符合 64 | int temp = i; 65 | while (i < str.length && str[i] >= '0' && str[i] <= '9') { 66 | i++; 67 | } 68 | return i > temp; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer16/Solution2.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer16; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * 数值的整数次方 7 | * 8 | * @Author rex 9 | * 2018/7/21 10 | */ 11 | public class Solution2 { 12 | 13 | private boolean g_invalid_input = false; 14 | 15 | /** 16 | * 全面但不够高效的解法 17 | * 18 | * @param base 19 | * @param exponent 20 | * @return 21 | */ 22 | public double power(double base, int exponent) { 23 | // 0的0次方没有意义 24 | if (doubleCompare(base, 0.0) == 0 && exponent == 0) { 25 | g_invalid_input = false; 26 | return 0.0; 27 | } 28 | int absExponent = Math.abs(exponent); 29 | double result = powerWithPositiveExponent(base, absExponent); 30 | if (exponent < 0) { 31 | result = 1.0/result; 32 | } 33 | return result; 34 | } 35 | 36 | 37 | 38 | /** 39 | * 指数为正时,得到的整数次方 40 | * 41 | * @param base 42 | * @param exponent 43 | * @return 44 | */ 45 | public double powerWithPositiveExponent(double base, int exponent) { 46 | if (exponent == 0) { 47 | return 1; 48 | } 49 | if (exponent == 1) { 50 | return base; 51 | } 52 | double result = powerWithPositiveExponent(base, exponent >> 1); 53 | result *= result; 54 | if ((exponent & 0x1) == 1) { 55 | result *= base; 56 | } 57 | return result; 58 | } 59 | 60 | /** 61 | * 比较两个浮点型大小 62 | * @param a 63 | * @param b 64 | * @return 65 | */ 66 | public int doubleCompare(double a, double b) { 67 | BigDecimal data1 = new BigDecimal(a); 68 | BigDecimal data2 = new BigDecimal(b); 69 | return data1.compareTo(data2); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer3/Duplicate1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer3; 2 | 3 | 4 | /** 5 | * 数组中重复的数字 6 | * 利用数组下标 7 | * 8 | * @Author rex 9 | * 2018/6/8 10 | */ 11 | public class Duplicate1 { 12 | 13 | /** 14 | * @param intArray 输入数组 15 | * @param duplicaiton 将首次找到的重复数字利用duplicaiton[0] = ?存入数组 16 | * @return 如果输入数组无效返回false,duplicaiton[0]=-1 17 | * @Author rex 18 | * @Date 2018/6/8 下午10:08 19 | * @Description 找出数组中重复的数字 20 | */ 21 | public static boolean findDuplicate(int[] intArray, int[] duplicaiton) { 22 | // 杜绝数组为空 23 | if (intArray.length == 0) { 24 | duplicaiton[0] = -1; 25 | return false; 26 | } 27 | // 杜绝数组有非法数字 28 | for (int i = 0; i < intArray.length; i++) { 29 | if (intArray[i] < 0 || intArray[i] > intArray.length - 1) { 30 | duplicaiton[0] = -1; 31 | return false; 32 | } 33 | } 34 | for (int i = 0; i < intArray.length; i++) { 35 | 36 | while (intArray[i] != i) { 37 | if (intArray[i] == intArray[intArray[i]]) { 38 | duplicaiton[0] = intArray[i]; 39 | return true; 40 | } 41 | int temp = intArray[i]; 42 | intArray[i] = intArray[temp]; 43 | intArray[temp] = temp; 44 | } 45 | 46 | 47 | } 48 | duplicaiton[0] = -1; 49 | return false; 50 | 51 | } 52 | 53 | } 54 | 55 | // public static void main(String[] args) { 56 | // int[] duplicaiton = new int[1]; 57 | // int[] intArray = new int[]{2, 3, 1, 0, 2, 5}; 58 | // System.out.println(Duplicate1.findDuplicate(intArray,duplicaiton)); 59 | // System.out.println(duplicaiton[0]); 60 | // 61 | // } 62 | -------------------------------------------------------------------------------- /notes/最长不含重复字符的子字符串.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。假设字符串只包含 'a'~'z' 的字符的,例如在字符串“arabcacfr”中,最长不含重复字符的子字符串为 “acfr”,长度为 4。 3 | 4 | # 测试用例 5 | * 功能测试(包含多个字符的字符串;只要一个字符的字符串;所有都唯一的字符串;所有字符都相同的字符串) 6 | * 特殊输入测试(空字符串) 7 | 8 | # 题目考点 9 | * 考察应聘者用动态规划分析问题的能力。 10 | * 考察应聘者对递归及时间效率的理解。 11 | 12 | # 解题思路 13 | 定义函数f(i)表示以第i个字符结尾的不包含重复字符的子字符串的最长长度。它有以下几种情况: 14 | 1. 如果第i个字符之前没有出现过 或者 出现的下标不在当前不包含重复字符的子字符串内:那么f(i) = f(i-1) + 1 15 | 2. 如果第i个字符出现的下标在当前不包含重复字符的子字符串内:则需要比较当前不包含重复字符的子字符串的长度与之前最长的不包含重复字符的子字符串谁比较长,如果比之前的长,那么需要调整之前最长的字符串的长度, 不管怎样,这时f(i) = d(两个重复字符串的距离) 16 | 17 | # 自己解题 18 | ```java 19 | /** 20 | * 最长不含重复字符的子字符串 21 | * 22 | * @Author rex 23 | * 2018/8/31 24 | */ 25 | public class Solution { 26 | 27 | /** 28 | * 动态规划解题 29 | * @param s 30 | * @return 31 | */ 32 | public int longestSubsingWithoutDuplication(String s) { 33 | // 关键(动态规划的体现) 34 | int curLength = 0; 35 | 36 | int maxLength = 0; 37 | 38 | // 存放下标 39 | Map indexMap = new HashMap<>(); 40 | 41 | for (int i = 0; i < s.length(); i++) { 42 | // 数组下标 43 | Integer index = indexMap.get(s.charAt(i)); 44 | // 如果第i个字符之前没有出现过 或者 出现的下标不在当前不包含重复字符的子字符串内 45 | if (index == null || (i - index) > curLength) { 46 | curLength++; 47 | } else { 48 | // 如果第i个字符出现的下标在当前不包含重复字符的子字符串内 49 | // 与之前最长的字符串比较 50 | maxLength = Math.max(maxLength, curLength); 51 | // 调整为两个重复字符串的距离 52 | curLength = i - index; 53 | } 54 | // 更新下标 55 | indexMap.put(s.charAt(i),i); 56 | } 57 | maxLength = Math.max(maxLength, curLength); 58 | 59 | return maxLength; 60 | } 61 | 62 | } 63 | ``` 64 | # 参考解题 65 | 参考自己解题 66 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer68/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer68; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 普通树中两个节点的最低公共祖先 8 | * 9 | * @Author rex 10 | * 2018/9/19 11 | */ 12 | public class Solution1 { 13 | /** 14 | * 求二叉树中两个节点的最低公共祖先 15 | * 16 | * @param root 17 | * @param p 18 | * @param q 19 | * @return 20 | */ 21 | public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { 22 | // 防止特殊输入 23 | if (root == null || p == null || q == null) { 24 | return null; 25 | } 26 | ArrayList pPath = new ArrayList(); 27 | ArrayList qPath = new ArrayList(); 28 | // 寻找p、q路径 29 | findPath(root, p, pPath); 30 | findPath(root, q, qPath); 31 | // 寻找p、q路径的最后一个公共节点 32 | int minLength = Math.min(pPath.size(), qPath.size()); 33 | int LCA = 0; 34 | for (int i = 0; i < minLength; i++) { 35 | if (pPath.get(i) == qPath.get(i)) { 36 | LCA = i; 37 | } 38 | } 39 | return pPath.get(LCA); 40 | 41 | } 42 | 43 | /** 44 | * 查找从根节点到目标节点的路径 45 | * 46 | * @param root 47 | * @param target 48 | * @param path 49 | * @return 50 | */ 51 | public boolean findPath(TreeNode root, TreeNode target, List path) { 52 | if (root == null) { 53 | return false; 54 | } 55 | path.add(root); 56 | if (root == target) { 57 | return true; 58 | } 59 | if (findPath(root.left, target, path) || findPath(root.right, target, path)) { 60 | return true; 61 | } 62 | path.remove(path.size() - 1); 63 | return false; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /notes/二进制中1的个数.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。例如,把9表示成二进制是1001,有两位是1.因此,如果输入9,则该函数输出2。 3 | 4 | # 测试用例 5 | * 正数(包括边界值1、0x7FFFFFFF) 6 | * 负数(包括边界值0x80000000、0xFFFFFFFF) 7 | * 0 8 | 9 | # 题目考点 10 | * 考察应聘者对二进制及位运算的理解。 11 | 12 | # 解题思路 13 | ## 常规解法 14 | 首先把n和1做与运算,判断n的最低位是不是为1,接着把1左移一位得到2,再和n做运算,就能判断n的次低位是不是1...这样反复左移,每次都能判断n的其中一位是不是1。 15 | ## 常规解法 16 | 我们基于这样一个**事实**:把一个整数减去1之后再后原来的整数做位与运算,得到的结果相当于把整数的二进制表示中最右边的1变成0。例子如下: 17 | ``` 18 | n : 10110100 19 | n-1 : 10110011 20 | n&(n-1) : 10110000 21 | ``` 22 | 那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。 23 | ## 自带API解法 24 | ```java 25 | Integer.bitCount(n); 26 | ``` 27 | 28 | # 自己解题 29 | 对二进制操作不了解。 30 | # 参考解题 31 | ## 常规解法 32 | Java中没有无符号整形,所以这里忽略。 33 | ## 惊喜解法 34 | ```java 35 | /** 36 | * 二进制中1的个数 37 | * @Author rex 38 | * 2018/7/20 39 | */ 40 | public class Solution { 41 | /** 42 | * 惊喜解法 43 | * 44 | * 利用了把一个整数减去1之后再后原来的整数做位与运算,得到的结果相当于把整数的二进制表示中最右边的1变成0 45 | * 46 | * @param n 47 | * @return 48 | */ 49 | public int numberOf1(int n) { 50 | int count = 0; 51 | while (n != 0) { 52 | ++count; 53 | n = (n-1) & n; 54 | } 55 | return count; 56 | } 57 | 58 | } 59 | ``` 60 | # 补充 61 | **位运算只有5中运算:与、或、异或、左移和右移。** 62 | 63 | 与、或、异或运算就是简单的真值表。 64 | 65 | 左移运算符 m<>n 表示把 m 右移 n位。在右移 n 位的时候,最右边的 n 位将被丢弃,但右移时处理最左边位情形有两种情况,如果数字是无符号数字,则用0填补最左边的 n 位;如果数字是一个有符号数值,则用数字的符号位填补最左边的 n 位。也就是说,如果数字原先是一个正数,则右移之后再最左边填补 n 个0;如果数字原先是负数,则右移之后在最左边补 n 个1。 68 | 69 | 70 | **负数的二进制表示(计算机用补码表示负数)** 71 | 1. 变成原码:将绝对值转换成二进制数,然后在最高位补1 72 | 2. 变成反码:对原码除符号位外各位取反 73 | 3. 变成补码:在反码最后一位加1 74 | 75 | **问题:** 把整数右移一位和把整数除以2在数学上是等价的吗? 76 | 77 | 不是,除法的效率比一位运算效率低得多,在实际编程中应尽可能得用一位运算符代替乘除法。 78 | 79 | 80 | **值得注意的地方:** 81 | 82 | **把一个整数减去1之后再后原来的整数做位与运算,得到的结果相当于把整数的二进制表示中最右边的1变成0。** 很多二进制问题都可以用这种思路解决。 83 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer23/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer23; 2 | 3 | 4 | import java.util.List; 5 | 6 | /** 7 | * 链表中环的入口节点 8 | * 9 | * @Author rex 10 | * 2018/7/27 11 | */ 12 | public class Solution { 13 | 14 | /** 15 | * 获得入口节点 16 | * @param head 17 | * @return 18 | */ 19 | public ListNode EntryNodeOfLoop(ListNode head) { 20 | ListNode meetingNode = meetingNode(head); 21 | if (meetingNode == null) { 22 | return null; 23 | } 24 | 25 | // 得到环中节点的数目 26 | int nodesInLoop = 1; 27 | ListNode node = meetingNode; 28 | while (node.next != meetingNode) { 29 | node = node.next; 30 | nodesInLoop++; 31 | } 32 | ListNode p1 = head; 33 | ListNode p2 = head; 34 | // 先移动p1,次数为环中节点的个数 35 | for (int i = 0; i < nodesInLoop; i++) { 36 | p1 = p1.next; 37 | } 38 | // 再同时移动p1,p2,当p1 = p2时,即为入口节点 39 | while (p1 != p2) { 40 | p1 = p1.next; 41 | p2 = p2.next; 42 | } 43 | return p1; 44 | } 45 | 46 | /** 47 | * 在存在环的前提下找到一快一慢两个指针相遇的节点 48 | * 没有找到则代码不存在环 49 | * @param head 50 | * @return 51 | */ 52 | public ListNode meetingNode(ListNode head) { 53 | if (head == null || head.next == null) { 54 | return null; 55 | } 56 | 57 | ListNode slow = head.next; 58 | ListNode fast = slow.next; 59 | while (slow != null && fast != null) { 60 | if (slow == fast) { 61 | return fast; 62 | } 63 | slow = slow.next; 64 | fast = fast.next; 65 | // 增强鲁棒性 66 | if (fast.next != null) { 67 | fast = fast.next; 68 | } 69 | } 70 | return null; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer59/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer59; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedList; 5 | 6 | /** 7 | * 活动窗口的最大值 8 | * 9 | * @Author rex 10 | * 2018/9/17 11 | */ 12 | public class Solution { 13 | /** 14 | * 双端队列解题 15 | * @param num 16 | * @param size 17 | * @return 18 | */ 19 | public ArrayList maxInWindows(int [] num, int size) { 20 | ArrayList maxInWindows = new ArrayList<>(); 21 | if (num.length >= size && size >= 1) { 22 | // 双端队列 23 | LinkedList index = new LinkedList<>(); 24 | // 第一个滑动窗口,保持队首为最大值 25 | for (int i = 0; i < size; i++) { 26 | // 保证队列中后面的元素比前面小 27 | while (!index.isEmpty() && num[i] >= num[index.getLast()]) { 28 | index.pollLast(); 29 | } 30 | index.offer(i); 31 | } 32 | for (int i = size; i < num.length; i++) { 33 | maxInWindows.add(num[index.getFirst()]); 34 | while (!index.isEmpty() && num[i] >= num[index.getLast()]) { 35 | index.pollLast(); 36 | } 37 | // 如果队首不在滑动窗口内,则从队首移除 38 | if (!index.isEmpty() && index.getFirst() <= (i-size)) { 39 | index.pollFirst(); 40 | } 41 | index.offer(i); 42 | } 43 | // 最后一个滑动窗口 44 | maxInWindows.add(num[index.pollFirst()]); 45 | } 46 | return maxInWindows; 47 | } 48 | 49 | public static void main(String[] args) { 50 | Solution solution = new Solution(); 51 | int[] num = new int[]{2,3,4,2,6,2,5,1}; 52 | ArrayList list = solution.maxInWindows(num,3); 53 | 54 | for(Integer integer : list) { 55 | System.out.println(integer); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /notes/链表中倒数第K个节点.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 输入一个链表,输出该链表中倒数第k个结点。 3 | 4 | 节点定义如下: 5 | ```Java 6 | public class ListNode { 7 | int val; 8 | ListNode next = null; 9 | } 10 | ``` 11 | # 测试用例 12 | * 功能测试(第k个节点在链表的中间;第k个节点是链表的头结点;第k个节点是链表的尾结点) 13 | * 特殊输入测试(链表头结点为空指针;链表的节点总数少于k;k=0) 14 | 15 | # 题目考点 16 | * 考查应聘者对链表的理解。 17 | * 考查应聘者所写代码的**鲁棒性**。 18 | 19 | # 解题思路 20 | 假设整个链表有n个节点,那么倒数第k个节点就是从头开始的第n-k+1个节点。 21 | 22 | 我们定义两个指针。第一个指针从链表的头指针开始遍历向前走k-1步,第二个指针保持不动;从第k步开始,第二个指针也开始从链表的头指针开始遍历。由于**两个指针的距离保持在k-1步**,当第一个指针到达链表的尾节点时,第二个指针正好指向倒数第k个节点。 23 | 24 | 但是为了保持程序的鲁棒性,需要考虑下面三个问题: 25 | 1. 输入头节点为空指针 26 | 2. 链表的节点总数少于k 27 | 3. 输入k为0 28 | 29 | # 自己解题 30 | ```Java 31 | /** 32 | * 链表中的倒数第K个节点 33 | * @Author rex 34 | * 2018/7/26 35 | */ 36 | public class Solution { 37 | /** 38 | * 自己解法 39 | * @param head 40 | * @param k 41 | * @return 42 | */ 43 | public ListNode findKthToTail(ListNode head, int k) { 44 | // 防止空指针和k=0无意义 45 | if (head == null || k == 0) { 46 | return null; 47 | } 48 | // 第一个指针的步数 49 | int i = 0; 50 | ListNode firstNode = head; 51 | ListNode secondNode = head; 52 | while (firstNode.next != null) { 53 | firstNode = firstNode.next; 54 | i++; 55 | if (i > k-1) { 56 | // 保持两个指针距离为k-1 57 | secondNode = secondNode.next; 58 | } 59 | } 60 | // 还没走到k-1步就结束了 61 | // 表示链表的节点总数少于k 62 | if (i> 1; 20 | // 辅助数组 21 | int[] temp = new int[array.length]; 22 | mergeSort(array, low, mid); 23 | mergeSort(array, mid + 1, high); 24 | merge(array, temp, low, mid, high); 25 | } 26 | return array; 27 | } 28 | 29 | /** 30 | * 归并 31 | * @param array 32 | * @param temp 33 | * @param low 34 | * @param mid 35 | * @param high 36 | */ 37 | public void merge(int[] array, int[] temp, int low, int mid, int high) { 38 | 39 | // 代表左边下标 40 | int i = low; 41 | // 代表右边下标 42 | int j = mid + 1; 43 | // 代表辅助数组的下标 44 | int k = 0; 45 | while (i <= mid && j <= high) { 46 | if (array[i] < array[j]) { 47 | temp[k++] = array[i++]; 48 | } else { 49 | temp[k++] = array[j++]; 50 | } 51 | } 52 | // 如果左边有剩余元素,移入辅助数组 53 | while (i <= mid) { 54 | temp[k++] = array[i++]; 55 | } 56 | // 如果右边有剩余元素,移入辅助数组 57 | while (j <= high) { 58 | temp[k++] = array[j++]; 59 | } 60 | // 临时数组覆盖原数组 61 | System.arraycopy(temp, 0, array, low, high - low + 1); 62 | 63 | } 64 | 65 | 66 | public static void main(String[] args) { 67 | MergeSort mergeSort = new MergeSort(); 68 | int[] a = new int[]{5, 4, 3, 2, 1}; 69 | mergeSort.mergeSort(a, 0, a.length - 1); 70 | for (Integer i : a) { 71 | System.out.println(i); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer44/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer44; 2 | 3 | /** 4 | * 数字序列中某一位的数字 5 | * 6 | * @Author rex 7 | * 2018/8/27 8 | */ 9 | public class Solution { 10 | /** 11 | * 求出数字序列中某一位的数字 12 | * @param index 13 | * @return 14 | */ 15 | public int digitAtIndex(int index) { 16 | if (index < 0) { 17 | return -1; 18 | } 19 | int digits = 1; 20 | while (true) { 21 | int numbers = countOfIntegers(digits); 22 | if (index < numbers * digits) { 23 | return digitAtIndex(index, digits); 24 | } 25 | index -= numbers * digits; 26 | digits++; 27 | } 28 | } 29 | 30 | /** 31 | * 当我们知道要找的哪一位数字位于某m位数之中后,使用下面的函数找出那位数字 32 | * @param index 33 | * @param digits 34 | */ 35 | public int digitAtIndex(int index, int digits) { 36 | int number = beginNumber(digits) + index / digits; 37 | // 从左到右第m位,就是从右到左的第digits-m位 38 | int indexFromRight = digits - index % digits; 39 | for (int i = 1; i < indexFromRight; i++) { 40 | number /= 10; 41 | } 42 | return number % 10; 43 | } 44 | 45 | /** 46 | * 求出m位的数字的个数 47 | * @param digits 位数 48 | * @return 49 | */ 50 | public int countOfIntegers(int digits) { 51 | if (digits == 1) { 52 | return 10; 53 | } 54 | int count = (int) Math.pow(10,digits - 1); 55 | return 9 * count; 56 | 57 | } 58 | 59 | /** 60 | * 求出m位的第一个数字 61 | * @param digits 62 | * @return 63 | */ 64 | public int beginNumber(int digits) { 65 | if (digits == 1) { 66 | return 0; 67 | } 68 | return (int) Math.pow(10, digits - 1); 69 | } 70 | 71 | 72 | public static void main(String[] args) { 73 | Solution solution = new Solution(); 74 | // 输出7 75 | System.out.println(solution.digitAtIndex(1001)); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /notes/二叉搜索树与双向链表.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 输入一颗二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的节点,只能调整树中的指针的指向,如下图: 3 | 4 | ![offer36](https://github.com/todorex/Coding-Interviews/raw/master/images/offer36.png) 5 | 6 | 二叉树节点的定义如下: 7 | ```java 8 | public class BinaryTreeNode { 9 | int value; 10 | BinaryTreeNode left; 11 | BinaryTreeNode right; 12 | } 13 | ``` 14 | # 测试用例 15 | * 功能测试(输入的二叉树是完全二叉树;所有节点都没有左/右子树的二叉树;只有一个节点的二叉树) 16 | * 特殊输入测试(指向一个二叉树根节点的指针为空指针) 17 | 18 | # 题目考点 19 | * 考察应聘者分析问题复制问题的能力。解决这个问题的关键在于把一个大的问题分解成几个小问题,并递归得解决小问题。 20 | * 考察应聘者对二叉树和双向链表的理解及编程能力。 21 | 22 | # 解题思路 23 | 首先我们一定要想到用中序遍历二叉树。 24 | 25 | 1. 关键:设置一个末尾节点指针用来串联根节点的两侧连接 26 | 2. 先将左子树遍历完,然后末尾节点指针将指向左子树的最右侧,然后根节点的左指针就指向了末尾节点 27 | 3. 当右子树的最左侧遍历完之后,右子树的最左侧的节点的左指针就将指向根节点(此时的末尾节点) 28 | 29 | # 自己解题 30 | 不会,理不直,气也壮 31 | # 参考解题 32 | ```java 33 | /** 34 | * 二叉搜索树与双向链表 35 | * 36 | * @Author rex 37 | * 2018/8/11 38 | */ 39 | public class Solution { 40 | /** 41 | * 关键 42 | */ 43 | private BinaryTreeNode lastNode; 44 | 45 | /** 46 | * 参考解题 47 | * @param pRootOfTree 48 | * @return 49 | */ 50 | public BinaryTreeNode convert(BinaryTreeNode pRootOfTree) { 51 | 52 | convertChild(pRootOfTree); 53 | BinaryTreeNode firstNode = lastNode; 54 | while (lastNode != null && firstNode.left !=null) { 55 | firstNode = firstNode.left; 56 | } 57 | return firstNode; 58 | 59 | } 60 | 61 | public void convertChild(BinaryTreeNode pRootOfTree) { 62 | if (pRootOfTree == null) { 63 | return; 64 | } 65 | BinaryTreeNode current = pRootOfTree; 66 | if (current.left != null) { 67 | convertChild(current.left); 68 | } 69 | // 关键 70 | current.left = lastNode; 71 | if (lastNode != null) { 72 | // 关键 73 | lastNode.right = current; 74 | } 75 | lastNode = current; 76 | if (current.right != null) { 77 | convertChild(current.right); 78 | } 79 | } 80 | 81 | } 82 | ``` 83 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer11/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer11; 2 | 3 | /** 4 | * 旋转数组的最小数字 5 | * @Author rex 6 | * 2018/7/19 7 | */ 8 | 9 | public class Solution { 10 | /** 11 | * 获取旋转数组的最小数字 12 | * @param array 13 | * @return 14 | */ 15 | public static int minNumberInRotateArray(int [] array) { 16 | int length = array.length; 17 | if(length == 0) { 18 | return 0; 19 | } 20 | // i永远为前一个非递减的数组指针 21 | int i= 0; 22 | // j永远为后一个非递减的数组指针 23 | int j= length-1; 24 | // middle永远指向中间的值,当旋转数据就是原数组时,返回第1个数字 25 | int middle = i; 26 | while (array[i] >= array[j]) { 27 | if ((j - i) == 1) { 28 | middle = j; 29 | break; 30 | } 31 | middle = (j + i) / 2; 32 | // 如果下标i, j, middle 指向的第三个数字相等,就只能顺序查找(这个也需要技巧) 33 | if (array[middle] == array[i] && array[middle] == array[j]) { 34 | return minNumberArray(array, i, j); 35 | } 36 | 37 | if (array[middle] >= array[i]) { 38 | i = middle; 39 | } else if (array[middle] <= array[j]){ 40 | j = middle; 41 | } 42 | } 43 | 44 | return array[middle]; 45 | } 46 | 47 | 48 | /** 49 | * 顺序找出旋转数组中最小的值 50 | * 因为有序,只要找到第一个递减的值就可以返回了 51 | * @param array 52 | * @param index1 53 | * @param index2 54 | * @return 55 | */ 56 | public static int minNumberArray(int [] array, int index1, int index2) { 57 | int result = array[index1]; 58 | for (int i = index1; i < index2-1; i++) { 59 | result = array[i]; 60 | if (result > array[i+1]) { 61 | return array[i+1]; 62 | } 63 | } 64 | return result; 65 | } 66 | 67 | public static void main(String[] args) { 68 | int[] array = new int[] {1,1,1,0,1}; 69 | System.out.println(minNumberInRotateArray(array)); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /notes/二叉树中和为某一值的路径.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 输入一颗二叉树和一个整数,打印出二叉树中节点值的和为输入整数的**所有路径**。从树的根节点开始往下直到 **叶节点** 所经过的节点形成一条路径。二叉树节点的定义如下: 3 | ```java 4 | public class BinaryTreeNode { 5 | int value; 6 | BinaryTreeNode left; 7 | BinaryTreeNode right; 8 | } 9 | ``` 10 | # 测试用例 11 | * 功能测试(二叉树中有一条、多条符合要求的路径;二叉树中没有符合要求的路径) 12 | * 特殊输入测试(指向二叉树根节点的指针是空指针) 13 | 14 | # 题目考点 15 | * 考察应聘者用例子分析复杂问题的能力。 16 | * 考察应聘者对二叉树前序遍历的理解。 17 | 18 | # 解题思路 19 | 对于从节点开始的题目,我们需要想到先序遍历。 20 | 21 | 1. 当用前序遍历的方式访问到某一节点时,就把该节点添加到路径数组中。 22 | 2. 如果是叶节点且刚好是我们想要的数,则将它的路径保存起来一份。 23 | 3. 如果当前节点不是叶节点,则继续访问它的子节点。 24 | 4. 当递归函数退出的时候会返回到它的父节点,所以我们需要在删除路径数组的最后一个节点。 25 | 26 | # 自己解题 27 | 只是想到用栈,却不知道模拟栈。 28 | # 参考解题 29 | ```java 30 | /** 31 | * 二叉树中和为某一值的路径 32 | * 33 | * @Author rex 34 | * 2018/8/9 35 | */ 36 | public class Solution { 37 | /** 38 | * 参考解题 39 | * 40 | * 例子分析 41 | * @param root 42 | * @param target 43 | * @return 44 | */ 45 | public ArrayList> findPath(BinaryTreeNode root, int target) { 46 | ArrayList> result = new ArrayList<>(); 47 | ArrayList singleResult = new ArrayList<>(); 48 | if (root == null) { 49 | return result; 50 | } 51 | findPath(root, target, result, singleResult); 52 | return result; 53 | 54 | } 55 | 56 | public void findPath(BinaryTreeNode node, int target, ArrayList> result, ArrayList singleResult) { 57 | target -= node.value; 58 | singleResult.add(node.value); 59 | // 判断是否等于目标值且是叶节点 60 | if (target == 0 && node.left == null && node.right == null) { 61 | result.add(new ArrayList<>(singleResult)); 62 | } 63 | if (node.left != null) { 64 | findPath(node.left, target, result, singleResult); 65 | } 66 | if (node.right != null) { 67 | findPath(node.right, target, result, singleResult); 68 | } 69 | // 删除当前节点的值 70 | singleResult.remove(singleResult.size()-1); 71 | 72 | } 73 | } 74 | ``` 75 | -------------------------------------------------------------------------------- /notes/队列的最大值.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 滑动窗口的最大值 3 | 4 | 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。 5 | 6 | 例如,如果输入数组 {2, 3, 4, 2, 6, 2, 5, 1} 及滑动窗口的大小 3,那么一共存在 6 个滑动窗口,他们的最大值分别为 {4, 4, 6, 6, 6, 5}。 7 | 8 | 9 | # 测试用例 10 | * 功能测试(输入数组的数字大小无序;输入数组额数字单调递增;输入数组的数字单调递减) 11 | * 边界值测试(滑动窗口的大小为0、1、等于输入数组的长度、大于输入数组的长度) 12 | * 特殊输入测试(输入数组为空) 13 | 14 | # 题目考点 15 | * 考察应聘者分析问题的能力。 16 | 17 | # 解题思路 18 | 利用一个双端队列(存数组下标),并永远保持队首为最大值。 19 | 20 | 21 | 在过程中需要保证两点: 22 | 1. 队列中后面的元素可以比前面小,因为当前面的元素在滑出窗口的时候,后面小的元素可能是最大值。 23 | 2. 队列中的数组首尾存的下标的差值不能大于等于活动窗口大小,因为滑动窗口在那。 24 | 25 | 示例见书本 26 | 27 | # 自己解题 28 | 你能想到的暴力解题,时间复杂度为O(nk) 29 | 30 | ## 参考解题 31 | ```Java 32 | /** 33 | * 活动窗口的最大值 34 | * 35 | * @Author rex 36 | * 2018/9/17 37 | */ 38 | public class Solution { 39 | /** 40 | * 双端队列解题 41 | * @param num 42 | * @param size 43 | * @return 44 | */ 45 | public ArrayList maxInWindows(int [] num, int size) { 46 | ArrayList maxInWindows = new ArrayList<>(); 47 | if (num.length >= size && size >= 1) { 48 | // 双端队列 49 | LinkedList index = new LinkedList<>(); 50 | // 第一个滑动窗口,保持队首为最大值 51 | for (int i = 0; i < size; i++) { 52 | // 保证队列中后面的元素比前面小 53 | while (!index.isEmpty() && num[i] >= num[index.getLast()]) { 54 | index.pollLast(); 55 | } 56 | index.offer(i); 57 | } 58 | for (int i = size; i < num.length; i++) { 59 | maxInWindows.add(num[index.getFirst()]); 60 | while (!index.isEmpty() && num[i] >= num[index.getLast()]) { 61 | index.pollLast(); 62 | } 63 | // 如果队首不在滑动窗口内,则从队首移除 64 | if (!index.isEmpty() && index.getFirst() <= (i-size)) { 65 | index.pollFirst(); 66 | } 67 | index.offer(i); 68 | } 69 | // 最后一个滑动窗口 70 | maxInWindows.add(num[index.pollFirst()]); 71 | } 72 | return maxInWindows; 73 | } 74 | } 75 | ``` 76 | -------------------------------------------------------------------------------- /notes/1~n整数中1出现的次数.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 输入一个整数n,求1\~n这n个整数的十进制表示中1出现的次数。例如,输入12,1\~12这些整数中包含1的数字有1、10、11和12,1一共出现了5次。 3 | 4 | # 测试用例 5 | * 功能测试(输入5、10、55、99) 6 | * 边界值测试(输入0、1等) 7 | * 性能测试(输入较大的数字,如10000、21235等) 8 | 9 | # 题目考点 10 | * 考察应聘者优化的激情和能力。最原始的方法大部分应聘者都能想到。当面试官提示还有更快的方法之后,应聘者千万不要轻易放弃尝试。必要的时候可以要求面试官给出提示。 11 | * 考察应聘者面对复杂问题的分析能力,可以通过分析具体例子一步步找到通用的规律。 12 | 13 | # 解题思路 14 | 主要思路:分别对每个数位(如个位、十位、百位等)上可能出现1的数进行统计 15 | 16 | 根据设定的整位,对n进行分割,分为两部分,高位n/i,低位n%i 17 | 18 | 情况1: 19 | 20 | 当i表示百位,且百位对应的数为0,如n=31056,i=100,则a=310,b=56,此时百位为1的次数有a/10*100=31000 21 | 22 | 情况2: 23 | 24 | 当i表示百位,且百位对应的数为1,如n=31156,i=100,则a=311,b=56,此时百位对应的就是1,则共有a%10=31次是包含100个连续点,当最高两位为31(即a=311),本次只对应局部点00~56,共b+1次,所有点加起来共有a/10*100+(b+1),这些点百位对应为1 25 | 26 | 情况3: 27 | 28 | 当i表示百位,且百位对应的数>=2,如n=31456,i=100,则a=314,b=56,此时百位为1的次数有a/10+1=32(最高两位0~31),每一次都包含100个连续的点,即共有(a/10+1)\*100个点的百位为1 29 | 30 | 31 | 32 | # 自己解题 33 | 暴力解题就不放出来了!! 34 | 35 | # 参考解题 36 | ```java 37 | /** 38 | * 1~n整数中1出现的次数 39 | * 40 | * @Author rex 41 | * 2018/8/26 42 | */ 43 | public class Solution { 44 | /** 45 | * 规律解法 46 | * 按数位可能出现1的次数统计 47 | * 48 | * @param n 49 | * @return 50 | */ 51 | public int numberOf1Between1AndN_Solution(int n) { 52 | int count = 0; 53 | for (int m = 1; m <= n; m *= 10) { 54 | int a = n / m; 55 | int b = n % m; 56 | 57 | // count += (a + 8) / 10 * m + (a % 10 == 1 ? b + 1 : 0); 58 | // 上面一句抵下面所有句 59 | 60 | int temp = a % 10; 61 | 62 | if (temp == 0) { 63 | // 情况1:当前数位为0 64 | count += a / 10 * m; 65 | } else if (temp == 1) { 66 | // 情况2:当前数位为1 67 | count += a / 10 * m + b + 1; 68 | } else { 69 | // 情况3:当前数位大于1 70 | count += (a / 10 + 1) * m; 71 | } 72 | } 73 | return count; 74 | } 75 | } 76 | ``` 77 | # 补充 78 | 1. [牛客网](https://www.nowcoder.com/questionTerminal/bd7f978302044eee894445e244c7eee6) 79 | 2. 当发现利用各种名算法以及数据结构不能解题的时候,很久可能就是利用规律来解题了(考察逻辑思维以及数据思维)。 80 | -------------------------------------------------------------------------------- /notes/顺时针打印矩阵.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。例如输入以下矩阵: 3 | ``` 4 | 1 2 3 4 5 | 5 6 7 8 6 | 9 10 11 12 7 | 13 14 15 16 8 | ``` 9 | 依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10。 10 | # 测试用例 11 | 数组中有多行数列;数组中只有一行;数组中只有一列等。 12 | # 题目考点 13 | 画图找规律,更应该是找出边界条件和各种可能发生的情况。 14 | # 解题思路 15 | 1. 首先我们要确定我们将一圈一圈遍历 16 | 2. 计算出要遍历的圈数 17 | 3. 在遍历一圈的时候,我们要分析各种**不同的圈(不同情况,如只有一行,只有一列,只有两列)** ,然后根据不同的情况限定不同的条件。 18 | 19 | # 自己解题 20 | 没有分析出最里圈的各种情况。 21 | 22 | # 参考解题 23 | ```Java 24 | /** 25 | * 顺时针打印矩阵 26 | * 27 | * @Author rex 28 | * 2018/8/4 29 | */ 30 | public class Solution { 31 | /** 32 | * 参考解法 33 | * 注重边界条件 34 | * @param matrix 35 | * @return 36 | */ 37 | public ArrayList printMatrix(int [][] matrix) { 38 | ArrayList list = new ArrayList(); 39 | int row = matrix.length; 40 | int column = matrix[0].length; 41 | // 计算要遍历的圈数 42 | int circles = (Math.min(row,column)-1)/2+1; 43 | int start = 0; 44 | while (start < circles) { 45 | int endX = column - 1 - start; 46 | int endY = row - 1 - start; 47 | // 开始一圈圈打印矩阵 48 | // 从左到右打印一行 49 | for (int i = start; i <= endX; i ++) { 50 | list.add(matrix[start][i]); 51 | } 52 | // 从上到下打印一列(需要考虑时候有) 53 | if (start < endY) { 54 | for (int i = start + 1; i <= endY; i++) { 55 | list.add(matrix[i][endX]); 56 | } 57 | } 58 | // 从右到左打印一列(需要考虑时候有) 59 | if (start < endY && start < endX) { 60 | for (int i = endX -1; i >= start; i--) { 61 | list.add(matrix[endY][i]); 62 | } 63 | } 64 | // 从下到上打印一列(需要考虑时候有) 65 | if (start < endX && start < endY -1) { 66 | for (int i = endY - 1; i > start; i--) { 67 | list.add(matrix[i][start]); 68 | } 69 | } 70 | start++; 71 | } 72 | return list; 73 | } 74 | } 75 | ``` 76 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer60/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer60; 2 | 3 | import java.util.AbstractMap; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * n个骰子的点数 10 | * 11 | * @Author rex 12 | * 2018/9/18 13 | */ 14 | public class Solution1 { 15 | /** 16 | * 骰子最大值 17 | */ 18 | int maxValue = 6; 19 | 20 | /** 21 | * 动态规划解题 22 | * @param n 23 | * @return 24 | */ 25 | public List> dicesSum(int n) { 26 | List> result = new ArrayList>(); 27 | if (n <= 0) { 28 | return result; 29 | } 30 | 31 | // 这里一定要用long型,不能用int型,数大了之后会造成int型溢出 32 | // 还折腾了一会 33 | long[][] probabilities = new long[2][maxValue * n + 1]; 34 | 35 | // 数组转换标志 36 | int flag = 0; 37 | 38 | // 用第一个骰子初始化数组 39 | for (int i = 1; i <= maxValue; i++) { 40 | probabilities[flag][i] = 1; 41 | } 42 | for (int k = 2; k <= n; k++) { 43 | // 清空不可能出现的位 44 | for (int i = 0; i < k; i++) { 45 | probabilities[1 - flag][i] = 0; 46 | } 47 | for (int i = k; i <= maxValue * k; i++) { 48 | probabilities[1 - flag][i] = 0; 49 | for (int j = 1; j <= i && j <= maxValue; j++) { 50 | probabilities[1 - flag][i] += probabilities[flag][i - j]; 51 | } 52 | } 53 | flag = 1 - flag; 54 | 55 | } 56 | 57 | double total = Math.pow(maxValue, n); 58 | for (int i = n; i <= maxValue * n; i++) { 59 | result.add(new AbstractMap.SimpleEntry(i, probabilities[flag][i] / total)); 60 | } 61 | return result; 62 | 63 | } 64 | 65 | public static void main(String[] args) { 66 | Solution1 solution = new Solution1(); 67 | int n = 15; 68 | List> result = solution.dicesSum(n); 69 | int a = 1; 70 | 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer60/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer60; 2 | 3 | import java.util.AbstractMap; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * n个骰子的点数 10 | * 11 | * @Author rex 12 | * 2018/9/18 13 | */ 14 | public class Solution { 15 | /** 16 | * 骰子最大值 17 | */ 18 | int maxValue = 6; 19 | 20 | /** 21 | * 递归解法 22 | * @param n 23 | * @return 24 | */ 25 | public List> dicesSum(int n) { 26 | List> result = new ArrayList>(); 27 | if (n <= 0) { 28 | return result; 29 | } 30 | // 结果次数 31 | int[] resultArray = new int[maxValue * n - n + 1]; 32 | // 可能情况 33 | int total = (int)Math.pow(maxValue, n); 34 | // 算出次数 35 | probability(n, resultArray); 36 | for (int i = 0; i < resultArray.length; i++) { 37 | result.add(new AbstractMap.SimpleEntry(i + n, (double)resultArray[i] / total)); 38 | } 39 | return result; 40 | } 41 | 42 | /** 43 | * 递归开始 44 | * @param n 45 | * @param resultArray 46 | */ 47 | public void probability(int n, int[] resultArray) { 48 | for (int i = 1; i <= maxValue; i++) { 49 | probability(n, i, n, resultArray); 50 | } 51 | } 52 | 53 | /** 54 | * 递归核心 55 | * @param original 56 | * @param sum 57 | * @param current 58 | * @param resultArray 59 | */ 60 | public void probability(int original, int sum, int current, int[] resultArray) { 61 | if (current == 1) { 62 | resultArray[sum - original]++; 63 | } else { 64 | for (int i = 1; i <= maxValue; i++) { 65 | probability(original, sum + i, current - 1, resultArray); 66 | } 67 | } 68 | 69 | } 70 | 71 | public static void main(String[] args) { 72 | Solution solution = new Solution(); 73 | int n = 12; 74 | List> result = solution.dicesSum(n); 75 | int a = 1; 76 | 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer19/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer19; 2 | 3 | /** 4 | * 正则表达式匹配 5 | * 6 | * @Author rex 7 | * 2018/7/23 8 | */ 9 | public class Solution { 10 | /** 11 | * 正则表达式匹配 12 | * (递归) 13 | * 14 | * @param str 15 | * @param pattern 16 | * @return 17 | */ 18 | public boolean match(char[] str, char[] pattern) { 19 | if (str == null || pattern == null) { 20 | return false; 21 | } 22 | return matchCore(str, pattern, 0, 0); 23 | 24 | } 25 | 26 | /** 27 | * 正则表达式匹配分步 28 | * 29 | * @param str 字符串 30 | * @param pattern 模式 31 | * @param i str的下标 32 | * @param j patter的下标 33 | * @return 34 | */ 35 | public boolean matchCore(char[] str, char[] pattern, int i, int j) { 36 | // 终止条件 37 | if (i == str.length && j == pattern.length) { 38 | return true; 39 | } 40 | // 当模式不足以配置字符串时 41 | if (i < str.length && j == pattern.length) { 42 | return false; 43 | } 44 | // 当模式中的第二个字符是“*”时: 45 | if (j + 1 < pattern.length && pattern[j + 1] == '*') { 46 | // 如果字符串第一个字符跟模式第一个字符匹配,可以有3种匹配方式 47 | if (i != str.length && pattern[j] == str[i] || (pattern[j] == '.' && i != str.length)) { 48 | return 49 | // 字符串后移1字符,模式后移2字符 50 | matchCore(str, pattern, i + 1, j + 2) 51 | // 字符串后移1字符,模式不变,即继续匹配字符下一位,因为*可以匹配多位 52 | || matchCore(str, pattern, i + 1, j) 53 | // 模式后移2字符,相当于x*被忽略 54 | || matchCore(str, pattern, i, j + 2); 55 | } else { 56 | return matchCore(str, pattern, i, j + 2); 57 | } 58 | 59 | } 60 | // 如果字符串第一个字符和模式中的第一个字符相匹配,那么字符串和模式都后移一个字符,然后匹配剩余的 61 | if ((i != str.length && str[i] == pattern[j]) || (pattern[j] == '.' && i != str.length)) { 62 | return matchCore(str, pattern, i + 1, j + 1); 63 | } 64 | // 如果字符串第一个字符和模式中的第一个字符相不匹配,直接返回false 65 | return false; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /notes/二叉树的下一个节点.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 给定一个二叉树和其中的一个节点,如何找出中序遍历序列的下一个节点?。注意,树中的节点不仅包含左右子节点,同时包含指向父节点的指针。 3 | 4 | 节点代码如下: 5 | ```java 6 | /** 7 | * 二叉树节点 8 | * @Author rex 9 | * 2018/6/13 10 | */ 11 | public class TreeLinkNode { 12 | int val; 13 | TreeLinkNode left = null; 14 | TreeLinkNode right = null; 15 | // 父节点 16 | TreeLinkNode parent = null; 17 | 18 | TreeLinkNode(int val) { 19 | this.val = val; 20 | } 21 | } 22 | ``` 23 | # 测试用例 24 | * 普通二叉树(完全二叉树;不完全二叉树)。 25 | * 特殊二叉树(所有的节点都没有右子节点的二叉树;所有节点都没有左子节点的二叉树;只有一个节点的二叉树;二叉树的根节点指针为null)。 26 | * 不同位置的节点的下一个节点(下一个节点为当前节点的右子节点、右字数的最左子节点、父节点、跨层的父节点等;当前节点没有下一个节点) 27 | 28 | # 题目考点 29 | * 考察应聘者对二叉树中序遍历的理解程度。 30 | * 考察应聘者分析复杂问题的能力。应聘者只有画出二叉树的结构图、通过具体的例子找出中序遍历下一个节点的规律,才有可能设计出可行的算法。(感觉上找规律很重要) 31 | 32 | # 解题思路 33 | 通过对下一个节点的情况分析,主要有以下两种情况: 34 | * 如果一个节点的右子树不为空,那么该节点的下一个节点是右子树的最左节点。如下图: 35 | ![demo1](https://raw.githubusercontent.com/todorex/Coding-Interviews/master/images/offer8-1.png) 36 | 37 | * 如果一个节点的右子树为空,那么需要沿着父节点的指针一直向上遍历,知道找到一个是它父节点的左子节点的节点。**如果这样的节点存在**,那么这个节点的父节点就是我们要找的下一个节点。如下图: 38 | ![demo2](https://raw.githubusercontent.com/todorex/Coding-Interviews/master/images/offer8-2.png) 39 | # 自己解题 40 | 没有进行详细的分析,没有找规律,没有做出来!!!!!! 41 | # 参考解题 42 | ```java 43 | /** 44 | * 二叉树的下一个节点 45 | * 46 | * @Author rex 47 | * 2018/6/13 48 | */ 49 | public class Solution { 50 | 51 | /** 52 | * 寻找二叉树的下一个节点 53 | * 54 | * @param pNode 55 | * @return 56 | */ 57 | public TreeLinkNode GetNext(TreeLinkNode pNode) { 58 | if (pNode == null) { 59 | return null; 60 | } 61 | // 第一种情况 62 | if (pNode.right != null) { 63 | TreeLinkNode rNode = pNode.right; 64 | while (rNode.left != null) { 65 | rNode = rNode.left; 66 | } 67 | return rNode; 68 | } else { 69 | // 第二种情况 70 | while (pNode.parent != null) { 71 | TreeLinkNode parentNode = pNode.parent; 72 | if (parentNode.left == pNode) { 73 | return parentNode; 74 | } 75 | pNode = pNode.parent; 76 | 77 | } 78 | 79 | } 80 | return null; 81 | } 82 | } 83 | ``` 84 | -------------------------------------------------------------------------------- /notes/用两个栈实现队列.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 用两个栈来实现一个队列,完成队列的 Push 和 Pop 操作。 3 | 4 | 队列的声明如下: 5 | 6 | ```Java 7 | /** 8 | * 构建队列的声明 9 | * @Author rex 10 | * 2018/6/17 11 | */ 12 | public class BuildQueue { 13 | Stack stack1 = new Stack(); 14 | Stack stack2 = new Stack(); 15 | 16 | public void push(int node) { 17 | } 18 | 19 | public int pop() { 20 | } 21 | } 22 | ``` 23 | 24 | # 测试用例 25 | * 往空的队列添加、删除元素。 26 | * 往非空的队列、添加删除元素。 27 | * 连续删除元素直至队列为空。 28 | 29 | # 题目考点 30 | * 考察应聘者对栈和队列的理解。 31 | * 考察应聘者分析复杂问题的能力。(找规律) 32 | 33 | # 解题思路 34 | 假设元素先进stack1栈,如果这个时候需要弹出元素时,分为两种情况:当stack2栈为空时,我们把stack1栈的元素逐个弹出并压入stack2栈,此时我们会发现最先进入的元素已经在stack2栈顶,可以直接弹出;当stack2栈不为空,在stack2中的栈顶元素就是最先进入队列的元素,可以弹出。 35 | 36 | # 自己解题 37 | ```Java 38 | /** 39 | * 用两个栈实现队列 40 | * @Author rex 41 | * 2018/6/17 42 | */ 43 | public class BuildQueue { 44 | Stack stack1 = new Stack(); 45 | Stack stack2 = new Stack(); 46 | 47 | public void push(int node) { 48 | stack1.push(node); 49 | } 50 | 51 | public int pop() { 52 | while(!stack1.isEmpty()) { 53 | int a = stack1.pop(); 54 | stack2.push(a); 55 | } 56 | int b = stack2.pop(); 57 | while(!stack2.isEmpty()) { 58 | int c = stack2.pop(); 59 | stack1.push(c); 60 | } 61 | return b; 62 | 63 | } 64 | } 65 | ``` 66 | 考虑缺点: 67 | 68 | 很暴力,没有分析当栈2不为空时可以直接弹出的情况。 69 | 70 | # 参考解题 71 | ```java 72 | /** 73 | * 用两个栈实现队列 74 | * @Author rex 75 | * 2018/6/17 76 | */ 77 | public class BuildQueue1 { 78 | Stack stack1 = new Stack(); 79 | Stack stack2 = new Stack(); 80 | 81 | public void push(int node) { 82 | stack1.push(node); 83 | } 84 | 85 | public int pop() throws Exception { 86 | if (stack2.isEmpty()) { 87 | while (!stack1.isEmpty()) { 88 | stack2.push(stack1.pop()); 89 | } 90 | } 91 | 92 | if (stack1.isEmpty()) { 93 | throw new Exception("queue is empty"); 94 | 95 | } 96 | return stack2.pop(); 97 | 98 | } 99 | } 100 | ``` 101 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer67/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer67; 2 | 3 | /** 4 | * 把字符串转换成整数 5 | * 6 | * @Author rex 7 | * 2018/9/19 8 | */ 9 | public class Solution { 10 | /** 11 | * 把字符串转换成整数 12 | * 13 | * @param str 14 | * @return 15 | */ 16 | public int strToInt(String str) { 17 | // 表示10进制转换 18 | int radix = 10; 19 | 20 | // 防止特殊输入 21 | if (str == null) { 22 | throw new NumberFormatException("null"); 23 | } 24 | // 定义返回值 25 | int result = 0; 26 | // 正负数标志位 27 | boolean negative = false; 28 | 29 | int i = 0, len = str.length(); 30 | 31 | if (len > 0) { 32 | // 处理首字符为+、-号的情况 33 | // 取出第一个字符判断是否为+、- 34 | char firstChar = str.charAt(0); 35 | // + 、 - 的ASCII码都比0小 36 | if (firstChar < '0') { 37 | if (firstChar == '-') { 38 | negative = true; 39 | } else if (firstChar != '+') { 40 | throw new NumberFormatException("invalid first char"); 41 | } 42 | if (len == 1) { 43 | throw new NumberFormatException("invalid string"); 44 | } 45 | i++; 46 | } 47 | 48 | while (i < len) { 49 | if (str.charAt(i) >= '0' && str.charAt(i) <= '9') { 50 | result = result * radix + str.charAt(i) - '0'; 51 | // 处理数字溢出 52 | if ((!negative && result > Integer.MAX_VALUE) || (negative && result < Integer.MIN_VALUE)) { 53 | throw new NumberFormatException("invalid string"); 54 | } 55 | // 处理非法字符 56 | } else { 57 | throw new NumberFormatException("invalid char"); 58 | } 59 | i++; 60 | } 61 | // 处理空串 62 | } else { 63 | throw new NumberFormatException("invalid string"); 64 | } 65 | return negative ? -result : result; 66 | } 67 | 68 | 69 | public static void main(String[] args) { 70 | Solution solution = new Solution(); 71 | System.out.println(solution.strToInt("+123s")); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer3/BinarySearch.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer3; 2 | 3 | /** 4 | * 二分查找法 5 | * @Author rex 6 | * 2018/6/9 7 | */ 8 | public class BinarySearch { 9 | /** 10 | * 使用递归的二分查找 11 | * 12 | * @param arr 有序数组 13 | * @param key 待查找关键字 14 | * @return 找到的位置 15 | */ 16 | public static int recursionBinarySearch(int[] arr, int key, int low, int high) { 17 | if (arr.length == 0) { 18 | return -1; 19 | } 20 | if (key < arr[low] || key > arr[high] || low > high) { 21 | return -1; 22 | } 23 | int middle = (high - low) >> 2; 24 | if (arr[middle] > key) { 25 | //比关键字大则关键字在左区域 26 | return recursionBinarySearch(arr, key, low, middle - 1); 27 | } else if (arr[middle] < key) { 28 | //比关键字小则关键字在右区域 29 | return recursionBinarySearch(arr, key, middle + 1, high); 30 | } else { 31 | return middle; 32 | } 33 | } 34 | 35 | /** 36 | * 不使用递归的二分查找 37 | * @param arr 38 | * @param key 39 | * @return 关键字位置 40 | */ 41 | public static int commonBinarySearch(int[] arr, int key) { 42 | int low = 0; 43 | int high = arr.length - 1; 44 | 45 | if (key < arr[low] || key > arr[high] || low > high) { 46 | return -1; 47 | } 48 | 49 | while (low <= high) { 50 | int middle = (low + high) >> 2; 51 | if (arr[middle] > key) { 52 | //比关键字大则关键字在左区域 53 | high = middle - 1; 54 | } else if (arr[middle] < key) { 55 | //比关键字小则关键字在右区域 56 | low = middle + 1; 57 | } else { 58 | return middle; 59 | } 60 | } 61 | 62 | return -1; 63 | } 64 | 65 | } 66 | 67 | // public static void main(String[] args) { 68 | // 69 | // int[] arr = {1,3,5,7,9,11}; 70 | // int key = 3; 71 | //// int position = recursionBinarySearch(arr,key,0,arr.length - 1); 72 | // 73 | // int position = commonBinarySearch(arr, key); 74 | // 75 | // if(position == -1){ 76 | // System.out.println("查找的是"+key+",序列中没有该数!"); 77 | // }else{ 78 | // System.out.println("查找的是"+key+",找到位置为:"+position); 79 | // } 80 | // 81 | // } 82 | -------------------------------------------------------------------------------- /notes/二叉搜索树的后序遍历序列.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结构。如果是则返回true,否则返回false。假设输入数组的任意两个两个数组都互不相同,例如输入数组 {5,7,6,9,11,10,8} ,则返回true,因为这个整数序列是下图二叉搜索树的后序遍历结果。如果输入的数组是 {7,4,6,5},由于没有哪颗二叉搜索树的后序遍历结果是这个序列,因此返回false。 3 | ![offer33](https://github.com/todorex/Coding-Interviews/raw/master/images/offer33.png) 4 | 5 | # 测试用例 6 | * 功能测试(输入的后序遍历序列对应一颗二叉树,包括完全二叉树、所有节点都没有左/右子树的二叉树、只有一个节点的二叉树;输入的后序遍历没有对应的一颗二叉树) 7 | * 特殊输入测试(指向后序遍历序列的指针为空指针) 8 | 9 | # 题目考点 10 | * 考察应聘者分子具体例子寻找规律的能力。 11 | * 考察应聘者对二叉树后序遍历的理解。 12 | 13 | # 解题思路 14 | 1. 先找到数组最后一个数字(根节点的值),然后就可以将数组分成两部分:第一部分是左子树,他们都比根节点的值小;第二部分是右子树,他们都比根节点的值大。 15 | 2. 然后就是递归验证左右子树合法(满足二叉搜索树的条件)即可 16 | 17 | # 自己解题 18 | ```Java 19 | /** 20 | * 二叉搜索树的后序遍历序列 21 | * 22 | * @Author rex 23 | * 2018/8/8 24 | */ 25 | public class Solution { 26 | /** 27 | * 字节解题 28 | * @param sequence 29 | * @return 30 | */ 31 | public boolean verifySquenceOfBST(int [] sequence) { 32 | if (sequence == null || sequence.length == 0) { 33 | return true; 34 | } 35 | 36 | return verfy(sequence, 0, sequence.length - 1); 37 | 38 | 39 | } 40 | 41 | /** 42 | * 验证子树 43 | * @param sequence 44 | * @param first 45 | * @param last 46 | * @return 47 | */ 48 | public boolean verfy(int[] sequence, int first, int last) { 49 | if (first - last == 0) { 50 | return true; 51 | } 52 | int right = first; 53 | while (sequence[right] < sequence[last] && right < last) { 54 | right++; 55 | } 56 | int left = right; 57 | while (left < last) { 58 | if (sequence[left] < sequence[last]) { 59 | return false; 60 | } 61 | left++; 62 | } 63 | // 保证不越界 64 | boolean leftTree = true; 65 | if (right - 1 >= first) { 66 | leftTree = verfy(sequence, first, right-1); 67 | } 68 | boolean rightTree = true; 69 | if (last - 1 > right) { 70 | rightTree = verfy(sequence, right, last-1); 71 | } 72 | return leftTree && rightTree; 73 | } 74 | 75 | } 76 | ``` 77 | # 参考解题 78 | 参考自己解题。 79 | 80 | # 补充 81 | 1. 二叉搜索树 82 | 83 | 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值 84 | 85 | 2. 举一反三 86 | 87 | 如果面试题要求处理一颗二叉树的遍历序列,则可以找到二叉树的根节点,再基于根节点把整棵树的遍历序列拆分成左子序列和右子序列,接下来再递归地处理这两个子序列。 88 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer17/Solution1.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer17; 2 | 3 | /** 4 | * 打印从1到最大的n位数 5 | * 6 | * @Author rex 7 | * 2018/7/22 8 | */ 9 | public class Solution1 { 10 | /** 11 | * 在字符串上模拟数字加法的解法 12 | * 13 | * @param n 14 | */ 15 | public void print1ToMaxOfNDigits(int n) { 16 | // 防止非法输入 17 | if (n <= 0) { 18 | return; 19 | } 20 | char[] number = new char[n]; 21 | initCharArray(number); 22 | while (!increment(number)) { 23 | printNumber(number); 24 | } 25 | } 26 | 27 | 28 | /** 29 | * 字符串表示的数字上模拟加法 30 | * @param number 31 | * @return 32 | */ 33 | private boolean increment(char[] number) { 34 | // 是否超出999.... 35 | boolean isOverflow = false; 36 | // 进位数 37 | int takeOver = 0; 38 | for (int i = number.length-1; i >= 0; i--) { 39 | int sum = number[i] - '0' + takeOver; 40 | if (i == number.length-1) { 41 | sum++; 42 | } 43 | if (sum >= 10) { 44 | if (i == 0) { 45 | isOverflow = true; 46 | } else { 47 | sum -= 10; 48 | takeOver = 1; 49 | number[i] = (char) ('0' + sum); 50 | } 51 | } else { 52 | number[i] = (char) ('0' + sum); 53 | break; 54 | } 55 | } 56 | return isOverflow; 57 | } 58 | 59 | /** 60 | * 根据字符串打印出数字 61 | * 这个我一会也想出来 62 | * @param number 63 | */ 64 | private void printNumber(char[] number) { 65 | // 默认字符串不以0开始 66 | boolean isBegining0 = true; 67 | 68 | for (int i = 0; i < number.length; i++) { 69 | if (isBegining0 && number[i] != '0') { 70 | isBegining0 = false; 71 | } 72 | if (!isBegining0) { 73 | System.out.print(number[i]); 74 | } 75 | } 76 | System.out.println(); 77 | } 78 | 79 | /** 80 | * 初始化字符数组 81 | * 82 | * 使其每个字符初始为'0' 83 | * @param chars 84 | */ 85 | public void initCharArray(char[] chars) { 86 | for (int i = 0; i < chars.length; i++) { 87 | chars[i] = '0'; 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /notes/数据流中的中位数.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。 3 | 4 | # 测试用例 5 | * 功能测试(从数据流中读出奇数个数字;从数据流中读出偶数个数字)。 6 | * 边界值测试(从数据流中读出0个、1个、2个数字) 7 | 8 | # 题目考点 9 | * 考察应聘者对时间复杂度的分析能力。 10 | * 考察应聘者对数据结构的理解程度。应聘者只有对各个常用数据结构容器的特点非常了解,知道他们的优缺点及适用场景,才能找出最优的解法。 11 | 12 | # 解题思路 13 | 其实我们要解决的这样一个问题:**选择数据容器**,不容的数据容器的插入和查询时间复杂度对于这道题是不一样的。总结如下,具体的实现思路可以参考《剑指Offer》: 14 | 15 | | 数据结构 | 插入的时间复杂度 | 得到中位数的时间复杂度 | 16 | | ------ | ------ | ------ | 17 | | 没有排序的数组 | O(1) | O(n) 利用了Partition函数 | 18 | | 排序的数组 | O(n) | O(1) | 19 | | 排序的链表 | O(n) | O(1) | 20 | | 二叉搜索树 | 平均O(logn),最差O(n) | 平均O(logn),最差O(n) | 21 | | AVL树 | O(logn) | O(1) | 22 | | 大顶堆和小顶堆 | O(logn) | O(1) | 23 | 24 | 我们这里以大顶堆和小顶堆解题,具体的实现思路如下: 25 | 26 | 将整个数据容器分为两部分,要求左右数据的数目之差不能超过1。左右部分不要求排序,但左边的需要都比右边的小即可。所以左边用大顶堆表示,右边用小顶堆表示。 27 | 28 | 为了实现平衡分配,可以在数据的总数目是偶数时把数据插入最小堆,否则插入最大堆。 29 | 30 | 在要把数据插入最小堆的时候,如果该数比最大堆的数还要小,那么需要先插入最大堆,把最大堆的最大值拿出来出入到最小堆。反之亦然。 31 | 32 | # 自己解题 33 | 这里用优先级队列来模拟大顶堆和小顶堆。 34 | 35 | ```java 36 | /** 37 | * 数据流中的中位数 38 | * 39 | * @Author rex 40 | * 2018/8/24 41 | */ 42 | public class Solution { 43 | /** 44 | * 大顶堆,存储左半边元素 (需重写比较器) 45 | **/ 46 | private PriorityQueue left = new PriorityQueue<>((o1, o2) -> o2 - o1); 47 | /** 48 | * 小顶堆,存储右半边元素,并且右半边元素都大于左半边 49 | **/ 50 | private PriorityQueue right = new PriorityQueue<>(); 51 | 52 | /** 53 | * 当前数据流读入的元素个数,用于判断奇偶 54 | **/ 55 | private int count = 0; 56 | 57 | /** 58 | * 读取数据流 59 | * 60 | * @param num 61 | */ 62 | public void insert(Integer num) { 63 | // 为了实现平衡分配,可以在数据的总数目是偶数时把数据插入最小堆,否则插入最大堆 64 | if ((count & 1) == 0) { 65 | // 在要把数据插入最小堆的时候,如果该数比最大堆的数还要小 66 | // 那么需要先插入最大堆,把最大堆的最大值拿出来出入到最小堆 67 | // 反之亦然 68 | left.add(num); 69 | right.add(left.poll()); 70 | } else { 71 | right.add(num); 72 | left.add(right.poll()); 73 | } 74 | count++; 75 | } 76 | 77 | /** 78 | * 获取当前读取数据的中位数 79 | * 80 | * @return 81 | */ 82 | public Double getMedian() { 83 | if ((count & 1) == 0) { 84 | return (left.peek() + right.peek()) / 2.0; 85 | } else { 86 | return (double)right.peek(); 87 | } 88 | } 89 | } 90 | ``` 91 | # 参考解题 92 | 见参考解题。 93 | -------------------------------------------------------------------------------- /code/Coding-Interviews/src/com/todorex/offer7/Solution.java: -------------------------------------------------------------------------------- 1 | package com.todorex.offer7; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * 重建二叉树 7 | * @Author rex 8 | * 2018/6/11 9 | */ 10 | public class Solution { 11 | 12 | /** 13 | * 利用map便于直接根据先序遍历的值定位到中序遍历的索引 14 | */ 15 | private static Map map = new HashMap<>(); 16 | 17 | /** 18 | * @Author rex 19 | * @Date 2018/6/12 下午9:21 20 | * @Description 构建树 21 | * @param pre 先序遍历序列 22 | * @param in 后序遍历序列 23 | */ 24 | public static TreeNode reConstructBinaryTree(int[] pre, int[] in) { 25 | if (pre == null || pre.length == 0 || in == null || in.length == 0) { 26 | return null; 27 | } 28 | for (int i = 0; i < in.length; i++) { 29 | map.put(in[i], i); 30 | } 31 | TreeNode root = reConstructBinaryTree(pre, 0, pre.length - 1, in, 0, in.length - 1); 32 | return root; 33 | 34 | } 35 | 36 | /** 37 | * @Author rex 38 | * @Date 2018/6/12 下午9:20 39 | * @Description 递归建立树 40 | * @param pre 先序遍历序列 41 | * @param startPreIndex 子 先序遍历序列开始索引 42 | * @param endPreIndex 子 先序遍历序列结束索引 43 | * @param pre 后序遍历序列 44 | * @param startPreIndex 子 后序遍历序列开始索引 45 | * @param endPreIndex 子 后序遍历序列结束索引 46 | */ 47 | private static TreeNode reConstructBinaryTree(int[] pre, int startPreIndex, int endPreIndex, 48 | int[] in, int startInIndex, int endInIndex) { 49 | if (startPreIndex > endPreIndex) { 50 | return null; 51 | } 52 | int rootValue = pre[startPreIndex]; 53 | TreeNode root = new TreeNode(rootValue); 54 | int rootValueIndex = map.get(rootValue); 55 | // 需要多验证几步确定参数(用相对个数来确定索引,先序遍历序列不要直接去使用中序遍历的索引) 56 | root.left = reConstructBinaryTree(pre, startPreIndex + 1, rootValueIndex - startInIndex + startPreIndex, in, startInIndex, rootValueIndex - 1); 57 | root.right = reConstructBinaryTree(pre, rootValueIndex - startInIndex + startPreIndex + 1, endPreIndex, in, rootValueIndex + 1, endInIndex); 58 | return root; 59 | } 60 | 61 | public static void main(String[] args) { 62 | int[] a = new int[]{1, 2, 4, 7, 3, 5, 6, 8}; 63 | int[] b = new int[]{4, 7, 2, 1, 5, 3, 8, 6}; 64 | System.out.println(reConstructBinaryTree(a, b)); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /notes/树的子结构.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 输入两棵二叉树A和B,判断B是不是A的子结构,二叉树的节点定义如下: 3 | ```Java 4 | /** 5 | * 二叉树节点 6 | * 7 | * @Author rex 8 | * 2018/8/1 9 | */ 10 | public class BinaryTreeNode { 11 | double value; 12 | BinaryTreeNode left; 13 | BinaryTreeNode right; 14 | } 15 | ``` 16 | 17 | ![offer26](https://github.com/todorex/Coding-Interviews/raw/master/images/offer26.png) 18 | # 测试用例 19 | * 功能测试(树A和树B都是普通的二叉树;树B是或者不是树A的子结构) 20 | * 特殊输入测试(两颗二叉树的一个或者两个根节点为空指针;二叉树的所有节点都没有左子树或者右子树) 21 | 22 | # 题目考点 23 | * 考察应聘者对二叉树的便宜算法的理解及**递归编程能力** 24 | * 考察应聘者所写代码的鲁棒性 25 | 26 | # 解题思路 27 | 代码自明(递归的方式) 28 | # 自己解题 29 | ```Java 30 | /** 31 | * 树的子结构 32 | * 递归 33 | * 34 | * @Author rex 35 | * 2018/8/1 36 | */ 37 | public class Solution { 38 | /** 39 | * 判断树1是否存在树2这样的子树 40 | * @param root1 41 | * @param root2 42 | * @return 43 | */ 44 | public boolean hasSubtree(BinaryTreeNode root1, BinaryTreeNode root2) { 45 | if (root1 == null || root2 == null) { 46 | return false; 47 | } 48 | 49 | return doesTree1HaveTree2(root1, root2) || doesTree1HaveTree2(root1.left, root2) || doesTree1HaveTree2(root1.right, root2); 50 | 51 | } 52 | 53 | /** 54 | * 判断树1是否存在树2这样的子树核心 55 | * @param root1 56 | * @param root2 57 | * @return 58 | */ 59 | public boolean doesTree1HaveTree2(BinaryTreeNode root1, BinaryTreeNode root2) { 60 | if (root2 == null) { 61 | return true; 62 | } 63 | if (root1 == null) { 64 | return false; 65 | } 66 | if (equal(root1.value, root2.value)) { 67 | return doesTree1HaveTree2(root1.left, root2.left) && doesTree1HaveTree2(root1.right, root2.right); 68 | } 69 | return false; 70 | } 71 | 72 | /** 73 | * 判断两个double类型是否相等 74 | * @param i 75 | * @param j 76 | * @return 77 | */ 78 | public boolean equal(double i, double j) { 79 | if (Math.abs(i-j) < 0.0000001) { 80 | return true; 81 | } else { 82 | return false; 83 | } 84 | } 85 | } 86 | ``` 87 | # 参考解题 88 | 参考自己解题 89 | # 补充 90 | 与二叉树相关的代码有大量的指针操作,在每次使用指针的时候,我们都要问自己这个指针有没有可能是空指针,如果是空指针则该怎么处理。 91 | 92 | 由于计算机表示小数(包括float和double型小数)都有误差,我们不能直接用等号判断两个小数是否相等。如果两个小数的差的绝对值很小,如小于0.0000001,就可以任务它们相等。 93 | 94 | 从规范性、完整性和鲁棒性3个方面提高代码质量 95 | * 规范性:书写清晰、布局合理、命名合理 96 | * 完整性:完成基本功能、考虑边界条件、做好错误处理 97 | * 鲁棒性:采取防御性编程、处理无效的输入 98 | -------------------------------------------------------------------------------- /notes/第一个只出现一次的字符.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 字符串中第一个只出现一次的字符。 3 | 4 | 在字符串找出第一个只出现一次的字符。如输入“abaccdeff”,则输出'b'。 5 | 6 | # 测试用例 7 | * 功能测试(字符串中存在只出现一次的字符;字符串中不存在只出现一次的字符;字符串中所有字符都只出现一次) 8 | * 特殊输出测试(字符串为空指针) 9 | 10 | # 题目考点 11 | * 考察应聘者对数组和字符串的编程能力。 12 | * 考察应聘者对哈希表的理解及运用。 13 | * 考察应聘者对时间效率和空间效率的分析能力。 14 | 15 | # 解题思路 16 | 利用哈希表,它可以是语言自带的API也可以是自己构建的Hash表,我们定义哈希表的键值是字符,而值为该字符出现的次数。 17 | 18 | 第一次扫描,每扫到一个字符,就在哈希表的对应项中把次数+1; 19 | 20 | 第二次扫描,每扫描到一个字符,就能从哈希表中得到该字符的出现次数。这样,第一个只出现一次的字符就是符合要求的输出。 21 | 22 | # 自己解题 23 | ```Java 24 | /** 25 | * 第一个只出现一次的字符 26 | * 27 | * @Author rex 28 | * 2018/9/2 29 | */ 30 | public class Solution1 { 31 | /** 32 | * 利用数组构建的Map解题 33 | * @param str 34 | * @return 35 | */ 36 | public char firstNotRepeatingChar(String str) { 37 | // 防止特殊输入 38 | if (str == null || str.length() == 0) { 39 | return '\0'; 40 | } 41 | // 当字符都是数字和英文时,一个字符长度为8位,所以总共有256种可能 42 | final int TABLE_SIZE = 256; 43 | // 构建Map 44 | int[] hashTable = new int[TABLE_SIZE]; 45 | for (int i = 0; i < str.length(); i++) { 46 | hashTable[str.charAt(i)]++; 47 | } 48 | for (int i = 0; i < str.length(); i++) { 49 | if (hashTable[str.charAt(i)] == 1) { 50 | return str.charAt(i); 51 | } 52 | } 53 | return '\0'; 54 | } 55 | } 56 | ``` 57 | 好处:使用TreeMap,第二次扫描可以不用扫描数组,可用于读取流。 58 | 59 | 坏处:时间效率和空间效率略差。 60 | 61 | # 参考解题 62 | ```Java 63 | /** 64 | * 第一个只出现一次的字符 65 | * 66 | * @Author rex 67 | * 2018/9/2 68 | */ 69 | public class Solution1 { 70 | /** 71 | * 利用数组构建的Map解题 72 | * @param str 73 | * @return 74 | */ 75 | public char firstNotRepeatingChar(String str) { 76 | // 防止特殊输入 77 | if (str == null || str.length() == 0) { 78 | return '\0'; 79 | } 80 | // 当字符都是数字和英文时,一个字符长度为8位,所以总共有256种可能 81 | final int TABLE_SIZE = 256; 82 | // 构建Map 83 | int[] hashTable = new int[TABLE_SIZE]; 84 | for (int i = 0; i < str.length(); i++) { 85 | hashTable[str.charAt(i)]++; 86 | } 87 | for (int i = 0; i < str.length(); i++) { 88 | if (hashTable[str.charAt(i)] == 1) { 89 | return str.charAt(i); 90 | } 91 | } 92 | return '\0'; 93 | } 94 | } 95 | ``` 96 | # 补充 97 | 如果需要判断多个字符是不是在某个字符串里出现过或者统计多个字符在某个字符串出现的次数,那么我们可以考虑**基于数组创建一个简单的哈希表**,这样可以用很小的空间消耗换来时间效率的提升。 98 | --------------------------------------------------------------------------------