├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── scut │ │ └── carson_ho │ │ └── algorithmlearning │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── scut │ │ │ └── carson_ho │ │ │ └── algorithmlearning │ │ │ ├── Algorithm │ │ │ ├── Exam_1.java │ │ │ ├── Exam_10.java │ │ │ ├── Exam_11.java │ │ │ ├── Exam_12.java │ │ │ ├── Exam_13.java │ │ │ ├── Exam_14.java │ │ │ ├── Exam_15.java │ │ │ ├── Exam_16.java │ │ │ ├── Exam_17.java │ │ │ ├── Exam_18.java │ │ │ ├── Exam_19.java │ │ │ ├── Exam_2.java │ │ │ ├── Exam_20.java │ │ │ ├── Exam_21.java │ │ │ ├── Exam_22.java │ │ │ ├── Exam_23.java │ │ │ ├── Exam_24.java │ │ │ ├── Exam_25.java │ │ │ ├── Exam_26.java │ │ │ ├── Exam_27.java │ │ │ ├── Exam_28.java │ │ │ ├── Exam_29.java │ │ │ ├── Exam_3.java │ │ │ ├── Exam_30.java │ │ │ ├── Exam_31.java │ │ │ ├── Exam_32.java │ │ │ ├── Exam_33.java │ │ │ ├── Exam_34.java │ │ │ ├── Exam_35.java │ │ │ ├── Exam_36.java │ │ │ ├── Exam_37.java │ │ │ ├── Exam_38.java │ │ │ ├── Exam_38_2.java │ │ │ ├── Exam_39.java │ │ │ ├── Exam_4.java │ │ │ ├── Exam_40.java │ │ │ ├── Exam_41Solution.java │ │ │ ├── Exam_41Test.java │ │ │ ├── Exam_42.java │ │ │ ├── Exam_43.java │ │ │ ├── Exam_44.java │ │ │ ├── Exam_45.java │ │ │ ├── Exam_46.java │ │ │ ├── Exam_47.java │ │ │ ├── Exam_48.java │ │ │ ├── Exam_49.java │ │ │ ├── Exam_5.java │ │ │ ├── Exam_50.java │ │ │ ├── Exam_50_2.java │ │ │ ├── Exam_51.java │ │ │ ├── Exam_52.java │ │ │ ├── Exam_53.java │ │ │ ├── Exam_53_1.java │ │ │ ├── Exam_53_2.java │ │ │ ├── Exam_54.java │ │ │ ├── Exam_55.java │ │ │ ├── Exam_55_1.java │ │ │ ├── Exam_56.java │ │ │ ├── Exam_56_1.java │ │ │ ├── Exam_57.java │ │ │ ├── Exam_57_1.java │ │ │ ├── Exam_58.java │ │ │ ├── Exam_58_1.java │ │ │ ├── Exam_59.java │ │ │ ├── Exam_59_1.java │ │ │ ├── Exam_6 │ │ │ │ ├── Exam_6.java │ │ │ │ └── UnidirectionalLinkedList.java │ │ │ ├── Exam_60.java │ │ │ ├── Exam_61.java │ │ │ ├── Exam_62.java │ │ │ ├── Exam_63.java │ │ │ ├── Exam_64.java │ │ │ ├── Exam_65.java │ │ │ ├── Exam_66.java │ │ │ ├── Exam_67.java │ │ │ ├── Exam_68.java │ │ │ ├── Exam_7.java │ │ │ ├── Exam_8.java │ │ │ └── Exam_9.java │ │ │ ├── BinaryTree │ │ │ ├── BinaryTree_NonRecursion.java │ │ │ ├── BinaryTree_Recursion.java │ │ │ └── Node.java │ │ │ ├── Graph │ │ │ ├── GraphEdge.java │ │ │ ├── GraphNode.java │ │ │ └── MyGraph.java │ │ │ ├── MainActivity.java │ │ │ ├── Search │ │ │ └── BinarySearch.java │ │ │ └── Sort │ │ │ ├── BubbleSort.java │ │ │ ├── ChooseSort.java │ │ │ ├── HeapSort.java │ │ │ ├── InsertSort.java │ │ │ ├── MergeSort.java │ │ │ ├── QuickSort.java │ │ │ └── ShellSort.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── scut │ └── carson_ho │ └── algorithmlearning │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── 具体讲解 ├── 0~n-1中缺失的数字(53-2).md ├── 1~n 整数中1出现的次数(43).md ├── 2个栈实现队列、2个队列实现栈(9).md ├── n个骰子的点数(60).md ├── 不用加减乘除 实现 加法(65).md ├── 丑数(49).md ├── 两个链表的第一个公共节点(52).md ├── 二叉搜索树与双向链表(36).md ├── 二叉搜索树最接近值查找.md ├── 二叉搜索树的后序遍历、前序遍历.md ├── 二叉搜索树的第k大节点(54).md ├── 二叉树中和为某一值的路径(34).md ├── 二叉树的下一个节点(8).md ├── 二叉树的深度(55).md ├── 二叉树的镜像(27).md ├── 二维数组中的查找(4).md ├── 二进制中 1的个数(15).md ├── 从上到下打印二叉树:不分行、分行 & 之字形(32).md ├── 从尾到头打印链表(6).md ├── 删除链表的节点(重复 : 不重复)(18).md ├── 判断1个字符串是否表示数值(20).md ├── 判断2个二叉树是否一样.md ├── 前、中、后、层序遍历.md ├── 剪绳子(14).md ├── 包含min函数的栈:得到栈的最小元素(30).md ├── 合并两个排序的链表(25).md ├── 和为 s 的数字(57).md ├── 和为 s 的连续正数序列(57-1).md ├── 图:广度优先遍历(BFS.md ├── 图:最小生成树算法-克鲁斯卡尔算法(Kruskal).md ├── 图:最小生成树算法-普利姆算法(Prim).md ├── 图:最短路径算法.md ├── 图:深度优先遍历( DFS ).md ├── 圆圈中最后剩下的数字(约瑟夫环问题)(62).md ├── 复杂链表的复制(35).md ├── 大数加、减、乘法.md ├── 字符串的排列(38).md ├── 字符串的组合 : 子集(38-2).md ├── 实现单例模式(2).md ├── 对称的二叉树(28).md ├── 平衡二叉树(55-2).md ├── 序列化二叉树(37).md ├── 扑克牌的顺子(61).md ├── 打印从1到最大的n位数(17).md ├── 找出旋转数组的最小数字(11).md ├── 把字符串转换成整数(67).md ├── 把数字翻译成字符串(46).md ├── 把数组的数字排成最小数(45).md ├── 排序算法:冒泡排序.md ├── 排序算法:堆排序.md ├── 排序算法:希尔排序.md ├── 排序算法:归并排序.md ├── 排序算法:快速排序.md ├── 排序算法:直接插入排序.md ├── 排序算法:简单选择排序.md ├── 数值的整数次方(16).md ├── 数字序列中某1位的数字(44).md ├── 数据流中的中位数(41).md ├── 数组中出现次数超过一半的数字(39).md ├── 数组中只出现一次的两个数字(56).md ├── 数组中唯一出现一次的数字(56-1).md ├── 数组中数值与下标相等的元素(53-3).md ├── 数组中的逆序对(51).md ├── 数组中重复的数字(可修改 & 不可修改数组)(3).md ├── 斐波那契数列(10).md ├── 替换 字符串中的空格(5).md ├── 最小的k个数(40).md ├── 最长不含重复字符的子字符串(48).md ├── 机器人的运动范围(13).md ├── 构建乘积数组(66).md ├── 栈的压入、弹出序列(31).md ├── 树中两个节点的最低公共祖先(68).md ├── 树的子结构(26).md ├── 正则表达式匹配(19).md ├── 求1+2+....+n(64).md ├── 滑动窗口的最大值(59).md ├── 矩阵中的最短路径(12).md ├── 礼物的最大价值(47).md ├── 第一个只出现一次的字符(50).md ├── 算法导航(最新).md ├── 统计 数字在排序数组中出现的次数(53).md ├── 翻转字符串 之 左旋转字符串(58-1).md ├── 翻转字符串 之 翻转单词顺序(58).md ├── 翻转链表(24).md ├── 股票的最大利润(63).md ├── 调整数组顺序,使奇数位于偶数前面(21).md ├── 赋值运算符函数(1).md ├── 连续子数组的最大和(42).md ├── 重建二叉树(7).md ├── 链表中倒数第k个节点 : 中间节点(22).md ├── 链表中环的入口节点(23).md ├── 队列的最大值(59-1).md ├── 面试技巧.md └── 顺时针打印矩阵(29).md /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 请看我以下博客 2 | 3 | # CSDN博客 4 | http://blog.csdn.net/carson_ho 5 | 6 | # 简书博客 7 | 8 | http://www.jianshu.com/u/383970bef0a0 9 | 10 | # 稀土掘金 11 | 12 | https://juejin.im/user/58d4d9781b69e6006ba65edc -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "scut.carson_ho.algorithmlearning" 8 | minSdkVersion 19 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.3.1' 28 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 29 | testCompile 'junit:junit:4.12' 30 | } 31 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Carson_Ho/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/scut/carson_ho/algorithmlearning/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("scut.carson_ho.algorithmlearning", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_1.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/12/4. 5 | */ 6 | 7 | public class Exam_1 { 8 | private int tintl; 9 | private int tint2; 10 | private int tint3; 11 | /** 12 | * 测试用例 13 | */ 14 | public static void main(String[] args) { 15 | 16 | // 功能测试1:赋值 17 | MyString s1 = new MyString("a"); 18 | MyString s2 = new MyString("b"); 19 | System.out.println(s1.assign(s2)); 20 | 21 | // 功能测试2:连续赋值 22 | MyString s3 = new MyString("c"); 23 | MyString s4 = new MyString("d"); 24 | MyString s5 = new MyString("e"); 25 | System.out.println("s3:" + s3.assign(s4).assign(s5)); 26 | System.out.println("s4:" + s4); 27 | System.out.println("s5:" + s5); 28 | } 29 | 30 | 31 | 32 | 33 | /** 34 | * 解题算法 35 | */ 36 | public static class MyString{ 37 | 38 | private String data; 39 | 40 | // 构造函数 41 | public MyString(String data) { 42 | this.data = data; 43 | } 44 | 45 | // 赋值运算符函数 46 | // 考察1:需将函数的返回类型 声明为该类型的引用 47 | // 考察2:需将传入参数的类型 声明为常量引用 48 | public MyString assign(final MyString another){ 49 | 50 | // 考察3:需判断传入参数 & 当前实例是否为1个实例 51 | // 若是,则不需进行赋值操作,直接返回,提高函数效率 52 | if(this == another || this.data.equals(another.data)) 53 | return this; 54 | else{ 55 | // 若不是,则进行赋值操作 56 | this.data = another.data; 57 | return this; 58 | } 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | return "MyString{" + 64 | "data='" + data + '\'' + 65 | '}'; 66 | } 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_10.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/10/25. 5 | */ 6 | 7 | public class Exam_10 { 8 | 9 | public static void main(String[] args) { 10 | 11 | System.out.println(Fibonacci(5)); 12 | System.out.println(Fibonacci2(5)); 13 | } 14 | 15 | 16 | /** 17 | * 递归实现 18 | */ 19 | public static int Fibonacci(int n){ 20 | if(n<=1) 21 | return n; 22 | return Fibonacci(n-1)+Fibonacci(n-2); 23 | } 24 | 25 | /** 26 | * 循环实现 27 | */ 28 | public static int Fibonacci2(int n){ 29 | if(n<=1) 30 | return n; 31 | 32 | int fibOne = 1; 33 | int fibTwo = 0; 34 | int sum = 0; 35 | 36 | for (int i = 2; i <= n; i++) { 37 | sum = fibOne + fibTwo; 38 | fibTwo = fibOne; 39 | fibOne = sum; 40 | } 41 | return sum; 42 | } 43 | 44 | /** 45 | * 青蛙跳台阶(基础) 46 | */ 47 | public int jumpFloor(int number) { 48 | 49 | if(number <=2 ) 50 | return number; 51 | 52 | int jumpone=2; // 离所求的number的距离为1步的情况,有多少种跳法 53 | int jumptwo=1; // 离所求的number的距离为2步的情况,有多少种跳法 54 | int sum=0; 55 | 56 | for( int i=3;i <= number; i++ ){ 57 | 58 | sum = jumptwo + jumpone; 59 | jumptwo = jumpone; 60 | jumpone= sum; 61 | } 62 | 63 | return sum; 64 | } 65 | 66 | /** 67 | * 青蛙跳台阶(变式) 68 | */ 69 | public int jumpFloor2(int num) { 70 | if( num <= 2) 71 | return num; 72 | 73 | int jumpone=2; // 前面一级台阶的总跳法数 74 | 75 | int sum=0; 76 | 77 | for(int i=3; i<=num ;i++){ 78 | 79 | sum = 2*jumpone; 80 | jumpone = sum; 81 | 82 | } 83 | return sum; 84 | } 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_11.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/10/25. 5 | */ 6 | 7 | public class Exam_11 { 8 | 9 | /** 10 | * 解题算法 11 | * 原理:二分查找,时间复杂度为O(logn)。 12 | * 注意对特殊情况的处理:特殊情况1 (旋转数组= 有序)、特殊情况2(两指针指向元素 与 中间元素 相等) 13 | * @param arr 14 | * @return 15 | */ 16 | public static int minNumberInRotateArray(int[] arr){ 17 | 18 | // 1. 设置2个指针的初始值 19 | int index1 = 0; // 第1个指针设置为:旋转数组的第1个元素 = 前子数组的第1个元素 20 | int index2 = arr.length-1; // 第2个指针设置为:旋转数组的最后1个元素 = 后子数组的最后1个元素 21 | 22 | // 2. 初始化中间元素值 23 | // 为了兼顾 特殊情况1(旋转数组= 有序),故将中间元素初始化为第1个元素 24 | int mid = 0; 25 | // 一旦发现数组第1个数字 < 最后1个数字,即说明旋转数组 = 有序 26 | // 则直接跳出循环,直接输出第1个数字 27 | while( arr[index1] >= arr[index2] ){ 28 | // 若2个指针距离 = 1,即相邻时,则代表 29 | // a. 第1个指针指向的元素 = 前子数组的最后1个元素 30 | // b. 第2个指针指向的元素 = 后子数组的第1个元素,即,数组中最小的元素,此时直接输出第2个指针元素,并跳出循环 31 | if( index2 - index1 == 1 ){ 32 | mid = index2; 33 | break; 34 | } 35 | 36 | // 若2个指针距离 ≠ 1,即开始解题算法 37 | // 3. 找出中间元素 38 | mid = index1 + (index2-index1) / 2; 39 | 40 | // 为了兼顾特殊情况2(两指针指向元素 与 中间元素 相等),则只能顺序查找 41 | if( arr[index1]==arr[index2] && arr[mid]==arr[index1] ){ 42 | return inInOrder(arr,index1,index2); 43 | } 44 | 45 | // 4. 开始比较中间元素 与 指针元素 的大小 46 | if( arr[mid]>=arr[index1] ){ 47 | index1 = mid; 48 | }else if( arr[mid]<=arr[index2] ){ 49 | index2 = mid; 50 | } 51 | } 52 | 53 | // 最终返回中间元素,此时中间元素 = 第2个指针指向的元素 54 | return arr[mid]; 55 | } 56 | 57 | /** 58 | * 辅助算法:顺序查找算法 59 | */ 60 | public static int inInOrder(int[] arr, int index1, int index2) { 61 | int min = arr[index1]; 62 | for(int i = index1+1;i<=index2;i++){ 63 | if(min>arr[i]) 64 | min = arr[i]; 65 | } 66 | return min; 67 | } 68 | 69 | /** 70 | * 测试用例 71 | */ 72 | public static void main(String[] args) { 73 | 74 | // 输入旋转后的数组 75 | int[] src = new int[]{3,4,5,1,2}; 76 | // 输出结果 77 | System.out.println(minNumberInRotateArray(src)); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_12.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/10/25. 5 | */ 6 | 7 | public class Exam_12 { 8 | 9 | /** 10 | * 解题算法 11 | * 实现原理 = 回溯法,采用递归实现 12 | * @param matrix = 矩阵 13 | * param row = 矩阵行数 14 | * param cols = 矩阵列数 15 | * @param str = 需寻找的字符串路径 16 | * @return boolean = 矩阵中是否存在要寻找的字符串路径 17 | */ 18 | 19 | public static boolean hasPath(char[] matrix, int rows, int cols, char[] str) { 20 | 21 | // 判断输入数据的合法性 22 | if(matrix == null || str == null || matrix.length == 0 || str.length == 0) 23 | return false; 24 | 25 | // 定义1个矩阵,用于标识路径是否已进入每个格子 26 | // 0表示未访问、1表示访问过 27 | int flag[] = new int[matrix.length]; 28 | 29 | // 通过遍历比较矩阵中的值 & 字符串路径中的值是否相等 30 | for (int i = 0; i < rows; i++) { 31 | for (int j = 0; j < cols; j++) { 32 | // 通过递归判断是否相等 33 | if (helper(matrix, rows, cols, i, j, str, 0, flag)) 34 | return true; 35 | } 36 | } 37 | return false; 38 | } 39 | 40 | /** 41 | * 辅助算法:判断 矩阵中是否存在于需寻找的字符串路径 42 | * 实现原理 = 递归 43 | * @param matrix = 矩阵 44 | * param row = 矩阵行数 45 | * param cols = 矩阵列数 46 | * param i = 当前行 47 | * param j = 当前列 48 | * param str = 需寻找的字符串路径 49 | * param k = 字符串路径下标 50 | * param flag = 路径标识矩阵 51 | * @return boolean = 矩阵中是否存在要寻找的字符串路径 52 | */ 53 | private static boolean helper(char[] matrix, int rows, int cols, int i, int j, char[] str, int k, int[] flag) { 54 | 55 | // 当前矩阵比较元素 56 | int index = i * cols + j; 57 | 58 | // 判断矩阵值是否与路径中的值相等 59 | if (i < 0 || i >= rows || j < 0 || j >= cols || matrix[index] != str[k] || flag[index] == 1) 60 | return false; 61 | 62 | // 结束条件:即比较完毕 63 | if(k == str.length - 1) 64 | return true; 65 | 66 | flag[index] = 1; 67 | // 分上下左右四个方向位置通过递归继续判断矩阵中的值是否与字符串路径中下1个值相等 68 | if (helper(matrix, rows, cols, i - 1, j, str, k + 1, flag) 69 | || helper(matrix, rows, cols, i + 1, j, str, k + 1, flag) 70 | || helper(matrix, rows, cols, i, j - 1, str, k + 1, flag) 71 | || helper(matrix, rows, cols, i, j + 1, str, k + 1, flag)) { 72 | return true; 73 | } 74 | 75 | flag[index] = 0; 76 | return false; 77 | } 78 | 79 | /** 80 | * 测试用例 81 | */ 82 | public static void main(String[] args){ 83 | char[] matrix = { 'a', 'b', 't', 'g', 84 | 'c', 'f', 'c', 's', 85 | 'j', 'd', 'e', 'h'}; 86 | int rows = 3; 87 | int cols = 4; 88 | 89 | // 测试输出结果 90 | System.out.println(hasPath(matrix, rows, cols, new char[] {'b','f','c','e'})); //true 91 | System.out.println(hasPath(matrix, rows, cols, new char[] {'a','b','f','b'})); //false,访问过的位置不能再访问 92 | 93 | 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_13.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/10/25. 5 | */ 6 | 7 | public class Exam_13 { 8 | 9 | /** 10 | * 解题算法 11 | * 实现原理 = 递归 12 | * @param threshold = 临界值K 13 | * @param rows = 矩阵行数 14 | * @param cols = 矩阵列数 15 | * @return boolean = 矩阵中是否存在要寻找的字符串路径 16 | */ 17 | public static int movingCount(int threshold, int rows, int cols) { 18 | // 定义1个矩阵,用于标识路径是否已进入每个格子 19 | // 0表示未访问、1表示访问过 20 | int flag[][] = new int[rows][cols]; 21 | //通过计算到达格子次数 22 | return helper(0, 0, rows, cols, flag, threshold); 23 | } 24 | 25 | /** 26 | * 辅助算法:计算到达格子次数 实现 27 | * 实现原理 = 递归 28 | */ 29 | private static int helper(int i, int j, int rows, int cols, int[][] flag, int threshold) { 30 | // 1. 需判断机器人是否可以进入当前坐标 31 | // 判断条件 = 横纵坐标数位之和是否>k,小于则可以进入 32 | if (i < 0 || i >= rows || j < 0 || j >= cols || numSum(i) + numSum(j) > threshold || flag[i][j] == 1) return 0; 33 | // 若可进入,则标记该位置已被访问 34 | flag[i][j] = 1; 35 | // 通过递归,继续向4个方向走 36 | return (1+helper(i - 1, j, rows, cols, flag, threshold) 37 | + helper(i + 1, j, rows, cols, flag, threshold) 38 | + helper(i, j - 1, rows, cols, flag, threshold) 39 | + helper(i, j + 1, rows, cols, flag, threshold) 40 | ); 41 | } 42 | 43 | /** 44 | * 辅助算法:计算横、纵坐标数位用于相加的值 45 | */ 46 | public static int numSum(int number){ 47 | int sum=0; 48 | while (number>0){ 49 | sum += number%10; 50 | number/=10; 51 | } 52 | return sum; 53 | } 54 | 55 | /** 56 | * 测试用例 57 | */ 58 | 59 | public static void main(String[] args){ 60 | // 测试1:3行4列、k = 0 61 | System.out.println(movingCount(0,3,4)); // 1 62 | 63 | // 测试2:3行4列、k = 1 64 | System.out.println(movingCount(1,3,4)); // 3 65 | 66 | // 测试3:2行20列、k = 9 67 | System.out.println(movingCount(9,2,20)); // 36 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_14.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/10/26. 5 | */ 6 | 7 | public class Exam_14 { 8 | 9 | /** 10 | * 解法1:动态规划 11 | */ 12 | public static int maxCutting(int length){ 13 | 14 | /** 15 | * 特殊情况考虑:绳子长度 <2、=2、=3时 16 | */ 17 | // a. 当绳子长度<2时,无法剪,最大乘积 = 0 18 | if(length<2) return 0; 19 | 20 | // b. 当绳子长度 = 2时,只能剪成长度 = 1的两段,因此f(2)=1 21 | if(length==2) return 1; 22 | 23 | // c. 当绳子长度 = 3时,可剪成长度 = 1 / 2的两段 or 长度 =1 的三段,由于1x2>1x1x1,因此f(3)=2 24 | if(length==3) return 2; 25 | 26 | // 1. 定义1数组用于存储子问题的最优解 27 | // f[n] = 把长度为n的绳子剪成若干段后 各段长度乘积的最大值 28 | int[] f = new int[length+1]; 29 | f[0]=0; 30 | f[1]=1; 31 | f[2]=2; 32 | f[3]=3; 33 | int max = 0; 34 | int temp = 0; 35 | 36 | // 2. 通过for循环,自下而上计算子问题的最优解 37 | for(int i=4;i<=length;i++){ 38 | max = 0; 39 | // 3. 通过for循环,通过计算 & 比较的方式,求出子问题的最优解 40 | for(int j=1;j<=i/2;j++){ 41 | 42 | // 先计算出 f(i)* f(n-i)所有值、再通过比较,从而求出最大值 43 | temp = f[j]*f[i-j]; 44 | if(temp>max) 45 | max = temp; 46 | } 47 | // 4. 将子问题的最优解存储在数组中 48 | f[i] = max; 49 | } 50 | // 5. 最终返回结果 51 | return f[length]; 52 | } 53 | 54 | 55 | 56 | /** 57 | * 解法2:贪婪算法 58 | */ 59 | public static int maxCuttingGreedy(int length) { 60 | 61 | /** 62 | * 特殊情况考虑:绳子长度 <2、=2、=3时 63 | */ 64 | 65 | // 1. 在绳子长度<2、=2、3时,采用和动态规划同样的处理 66 | // a. 当绳子长度<2时,无法剪,最大乘积 = 0 67 | if(length<2) return 0; 68 | 69 | // b. 当绳子长度 = 2时,只能剪成长度 = 1的两段,因此f(2)=1 70 | if(length==2) return 1; 71 | 72 | // c. 当绳子长度 = 3时,可剪成长度 = 1 / 2的两段 or 长度 =1 的三段,由于1x2>1x1x1,因此f(3)=2 73 | if(length==3) return 2; 74 | 75 | 76 | // 2. 尽可能多地剪长度为3的绳子 77 | int timesOf3 = length/3; 78 | 79 | // 3. 当剩下的绳子长度 = 4时,不能再剪去长度 = 3的绳子段 80 | // 而是,剪成2-2 81 | if (length%3==1) 82 | timesOf3-=1; 83 | int timesOf2=(length-timesOf3*3)/2; 84 | 85 | // 计算最终的乘积最大值 86 | // Math.pow(a,b)的作用 = 返回a^b的值 87 | return (int)(Math.pow(3,timesOf3))*(int)(Math.pow(2,timesOf2)); 88 | } 89 | 90 | /** 91 | * 测试用例 92 | */ 93 | public static void main(String[] args){ 94 | // // 功能测试:绳子长度 = 10 95 | // System.out.println("绳子长度 = 10 的最大值:"+maxCutting(10)); 96 | // 97 | // // 边界值测试:绳子长度 = 0、1、2、3、4 98 | // for(int i=0;i<5;i++){ 99 | // System.out.println("长度 = "+i+"的最大值:"+maxCutting(i)); 100 | // } 101 | 102 | 103 | 104 | // 功能测试:绳子长度 = 10 105 | System.out.println("绳子长度 = 10 的最大值:"+maxCuttingGreedy(10)); 106 | 107 | // 边界值测试:绳子长度 = 0、1、2、3、4 108 | for(int i=0;i<5;i++){ 109 | System.out.println("长度 = "+i+"的最大值:"+maxCuttingGreedy(i)); 110 | } 111 | } 112 | 113 | 114 | } 115 | 116 | 117 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_15.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/10/26. 5 | */ 6 | 7 | public class Exam_15 { 8 | 9 | /** 10 | * 解法1:右移判断 11 | */ 12 | public static int numberOfOne1(int n){ 13 | int count=0; 14 | while(n!=0){ 15 | // 不断右移相与 16 | if((n&1)!=0) 17 | count++; 18 | n>>>=1; 19 | } 20 | return count; 21 | } 22 | 23 | /** 24 | * 解法2:用1个辅助数与其 相与 判断 25 | */ 26 | public static int numberOfOne2(int n){ 27 | int count=0; 28 | int flag=1; 29 | while(flag!=0){ 30 | if((n&flag)!=0) 31 | count++; 32 | // 辅助数右移 33 | flag<<=1; 34 | } 35 | return count; 36 | } 37 | 38 | /** 39 | * 解法3:通过观察总结的规律求解 40 | * 规律如下: 41 | * 1. 把1个整数减去1后,再和原来的整数作 与运算 42 | * 2. 上述得到的结果 = 把原整数的二进制表示的最右边的1变成0 43 | * 3. 进行了多少次上述操作,则代表原整数的二进制表示有多少个1 44 | */ 45 | public static int numberOfOne3(int n){ 46 | int count=0; 47 | 48 | while(n!=0){ 49 | n = n&(n-1); 50 | count++; 51 | } 52 | return count; 53 | } 54 | 55 | /** 56 | * 测试用例 57 | */ 58 | public static void main(String[] args){ 59 | // 对3种解法进行测试 60 | System.out.println(numberOfOne1(3)); 61 | System.out.println(numberOfOne1(-3)); 62 | 63 | System.out.println(numberOfOne2(3)); 64 | System.out.println(numberOfOne2(-3)); 65 | 66 | System.out.println(numberOfOne3(3)); 67 | System.out.println(numberOfOne3(-3)); 68 | } 69 | 70 | } 71 | 72 | 73 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_16.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/10/27. 5 | */ 6 | 7 | public class Exam_16 { 8 | 9 | /** 10 | * 常规解法,时间复杂度为O(n) 11 | * @param base = 底数 12 | * @param exponent = 幂 13 | * @return 14 | */ 15 | public static double Power(double base, int exponent) { 16 | 17 | double result = 1.0; 18 | for(int i = 1;i <= exponent;i++){ 19 | result *= base; 20 | } 21 | return result; 22 | } 23 | 24 | /** 25 | * 完整解法,时间复杂度为O(logN) 26 | * 完善点:问题的完整性 & 乘方效率 27 | * @param base = 底数 28 | * @param exponent = 幂 29 | * @return 结果 30 | */ 31 | 32 | public static double PowerEst(double base, int exponent) { 33 | 34 | // 1. 特殊情况考虑 35 | // a. 指数 = 负数 & 底数 = 0时,通过 抛出异常 提示错误 36 | if(base == 0 && exponent <0) 37 | throw new RuntimeException("不合法输入:指数 = 负数 & 底数 = 0"); 38 | // return 0; // 也可通过返回0 提示错误 39 | 40 | // b. 指数 = 负数情况:对指数求绝对值 41 | int n = exponent ; 42 | if(exponent <0) 43 | n = Math.abs(exponent); 44 | 45 | // 2. 求幂次方 46 | double result = powerWithUnsignedExponent(base,n); 47 | 48 | // 3. 当指数 = 负数时,将最后结果取倒数 49 | if(exponent < 0) 50 | result = 1/result; 51 | 52 | // 4. 返回最后结果 53 | return result; 54 | } 55 | 56 | 57 | /** 58 | * 辅助算法:求一个数的正整数次幂 59 | * 60 | * @param base 底数 61 | * @param exponent 幂 62 | * @return 结果 63 | */ 64 | public static double powerWithUnsignedExponent(double base, long exponent) { 65 | 66 | // 1. 先求特殊情况: 67 | // a. 若指数 = 0,返回1 68 | if (exponent == 0) { 69 | return 1; 70 | } 71 | 72 | // b. 若指数 = 1,返回底数本身 73 | if (exponent == 1) { 74 | return base; 75 | } 76 | 77 | // 2. 使用公式 提高乘方效率(使用递归实现,即求一半的值) 78 | // 注:使用右移运算符 代替 除以2,以提高效率 79 | double result = powerWithUnsignedExponent(base, exponent >> 1); 80 | 81 | // 3. 求出最终的值 82 | result *= result; 83 | 84 | // 4. 判断最终值的奇偶:通过求余判断(用位与运算符 代替 求余运算符) 85 | // 若是奇数,就还要乘多1次底数 86 | if((exponent & 0x1)==1) 87 | result *= base; 88 | 89 | // 5. 返回结果 90 | return result; 91 | } 92 | 93 | /** 94 | * 测试用例 95 | */ 96 | public static void main(String[] args) { 97 | 98 | // 测试用例 99 | System.out.println(PowerEst(2, 4)); 100 | System.out.println(PowerEst(2, -4)); 101 | System.out.println(PowerEst(2, 0)); 102 | System.out.println(PowerEst(-2, 3)); 103 | System.out.println(PowerEst(0, 0)); 104 | System.out.println(PowerEst(0, 3)); 105 | System.out.println(PowerEst(0, -1)); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_17.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/10/27. 5 | */ 6 | 7 | public class Exam_17 { 8 | 9 | /** 10 | * 解题算法:用数组表达大数 11 | * @param num 输入的n位数 12 | */ 13 | 14 | public static void print1ToMaxOfNDigits(int num) { 15 | 16 | // 1. 判断输入数据的合法性 17 | if (num <= 0) 18 | return; 19 | 20 | // 2. 设置1数组用于表达数字(大小 = n位) 21 | int[] number = new int[num]; 22 | 23 | // 3. 初始化数组 24 | for (int i = 0; i < num; i++) 25 | number[i]= 0; 26 | 27 | 28 | // 4. 在数组上模拟加法,每次加1都输出结果 29 | // 关于在数组上模拟加法 & 打印输出数字 请看具体函数注释 30 | while (!increment(number)) { 31 | 32 | printNumber(number); 33 | // 每次输出1个数后,用空格隔开 34 | System.out.println(); 35 | } 36 | } 37 | 38 | /** 39 | * 辅助算法:在数组上模拟加法 40 | * @param number 输入的数组 41 | */ 42 | 43 | public static boolean increment(int[] number){ 44 | 45 | // 1. 检查输入数组长度的合法性 46 | if(number.length<1) 47 | throw new RuntimeException("invalid lenth of array"); 48 | 49 | // 最高位产生进位标志,true时表示数组中的数为最大的n位整数 50 | boolean isOverFlow=false; 51 | 52 | // 进位位 53 | int carry=0; 54 | 55 | // 无产生进位时,直接+1,循环只运行1次; 56 | // 每产生一次进位,循环多运行一次 57 | for(int i=number.length-1;i>=0;i--){ 58 | 59 | // 用于存储最终表示数字的每一位数 60 | int sum=number[i]+carry; 61 | 62 | // 数组的最高位+1,即 表示数字的最低位+1 63 | if(i == number.length-1) 64 | sum++; 65 | 66 | // 发生进位时 67 | if(sum>=10){ 68 | // a. 若最高位产生进位,则代表已经增加到了数组中的数为最大的n位整数 69 | if(i==0) 70 | isOverFlow=true; 71 | // b. 若只是普通位产生进位,将当前位数设置为0,sum设置为0 72 | else{ 73 | carry=1; 74 | number[i]=0; 75 | sum=0; 76 | } 77 | // 若无发生进位 78 | }else{ 79 | // +1后的结果保存到数组中,并退出循环 80 | number[i]=sum; 81 | break; 82 | } 83 | } 84 | return isOverFlow; 85 | } 86 | 87 | /** 88 | * 辅助算法:打印输出数字 89 | * @param number 输入的字符串 90 | */ 91 | public static void printNumber(int[] number){ 92 | 93 | boolean isBeginning=true; 94 | 95 | for(int i=0; i2->3->4 68 | ListNode head = new ListNode(1); 69 | ListNode node2 = new ListNode(2); 70 | ListNode node3 = new ListNode(3); 71 | ListNode node4 = new ListNode(4); 72 | ListNode node5 = new ListNode(5); 73 | ListNode node6 = new ListNode(6); 74 | head.next = node2; 75 | node2.next = node3; 76 | node3.next = node4; 77 | node4.next = node5; 78 | node5.next = node6; 79 | 80 | // 功能测试:倒数第1个节点(尾结点)、倒数第6个节点(头节点)、中间节点 81 | System.out.println(findKthToTail(head,1).val); 82 | System.out.println(findKthToTail(head,3).val); 83 | System.out.println(findKthToTail(head,6).val); 84 | 85 | // 异常情况测试:链表头节点 为空、、链表长度< k、k< 0 86 | findKthToTail(null,1); 87 | findKthToTail(head,8); 88 | findKthToTail(head,0); 89 | 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_24.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/1. 5 | */ 6 | 7 | public class Exam_24 { 8 | 9 | /** 10 | * 设置结点结构 11 | */ 12 | 13 | public static class ListNode { 14 | int val; 15 | ListNode next = null; 16 | 17 | ListNode(int val) { 18 | this.val = val; 19 | } 20 | } 21 | 22 | /** 23 | * 反转链表 24 | * 解题思路:反转链表中节点的指针方向 25 | */ 26 | 27 | public static ListNode reverseList(ListNode head) { 28 | 29 | // 1. 异常情况判断 30 | // a. 若链表头节点为空 返回空 31 | if (head == null) { 32 | System.out.println("链表头节点为空"); 33 | return null; 34 | } 35 | 36 | // b. 若链表只有1个节点的情况,就返回头结点 37 | if (head.next == null) { 38 | System.out.println(head.val); 39 | return head; 40 | } 41 | 42 | // 2. 定义3个指针:当前节点、当前节点的前1节点、当前节点的后1节点 43 | ListNode pre = null; 44 | ListNode cur = head; 45 | ListNode post = head.next; 46 | 47 | // 3. 通过3个指针配合,反转链表节点的指针方向 48 | while (true) { 49 | 50 | // 翻转当前节点 51 | cur.next = pre;// 将 当前结点 的下1个指针设置为 前1节点 52 | 53 | // 继续翻转下1个节点 54 | pre = cur; // 将 前1节点 设置为当前结点 55 | cur = post;// 将 当前节点 设置为后1节点 56 | 57 | // 判断已反转到最后节点:后1节点是否为空 58 | if (post != null) 59 | post = post.next;// 将 后后1节点 设置为后1节点 60 | else { 61 | // 返回最后1个节点 62 | System.out.println(pre.val); 63 | return pre; 64 | } 65 | } 66 | } 67 | 68 | 69 | /** 70 | * 测试用例 71 | */ 72 | public static void main(String[] args) { 73 | // 功能测试:输入链表有多个节点 74 | // 链表 = 1->2->3->4->5->6 75 | ListNode head = new ListNode(1); 76 | ListNode node2 = new ListNode(2); 77 | ListNode node3 = new ListNode(3); 78 | ListNode node4 = new ListNode(4); 79 | ListNode node5 = new ListNode(5); 80 | ListNode node6 = new ListNode(6); 81 | head.next = node2; 82 | node2.next = node3; 83 | node3.next = node4; 84 | node4.next = node5; 85 | node5.next = node6; 86 | 87 | reverseList(head); 88 | 89 | // 异常情况测试1:输入链表有1个节点 90 | // 链表 = 1 91 | ListNode head2 = new ListNode(1); 92 | reverseList(head2); 93 | 94 | // 异常情况测试2:输入链表为空 95 | ListNode head3 = null; 96 | reverseList(head3); 97 | 98 | } 99 | } 100 | 101 | 102 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_27.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | 4 | import java.util.LinkedList; 5 | import java.util.Queue; 6 | 7 | /** 8 | * Created by Carson_Ho on 17/11/2. 9 | */ 10 | 11 | public class Exam_27 { 12 | 13 | /** 14 | * 设置结点结构 15 | */ 16 | 17 | public static class TreeNode { 18 | int val = 0; // 二叉树的结点数据 19 | TreeNode left = null; // 二叉树的左子树(左孩子) 20 | TreeNode right = null; // 二叉树的右子树(右孩子) 21 | 22 | public TreeNode(int val) { 23 | this.val = val; 24 | 25 | } 26 | 27 | } 28 | 29 | /** 30 | * 解题算法 31 | */ 32 | public static void Mirror(TreeNode root){ 33 | // 代码的鲁棒性:二叉树的头节点 = 空 34 | if(root == null) 35 | return; 36 | 37 | // 1. 判断前序遍历到的节点是否有左、右节点 38 | if(root.left == null && root.right == null) 39 | return; 40 | 41 | // 2. 若有,则交换2个节点 42 | TreeNode temp = root.left; 43 | root.left = root.right; 44 | root.right = temp; 45 | 46 | // 3. 通过递归继续前序遍历节点 47 | Mirror(root.left); 48 | Mirror(root.right); 49 | } 50 | 51 | /** 52 | * 测试用例 53 | */ 54 | public static void main(String[] args){ 55 | // 构造二叉树 56 | // 8 57 | // 6 10 58 | // 5 7 9 11 59 | TreeNode root = new TreeNode(8); 60 | root.left = new TreeNode(6); 61 | root.right = new TreeNode(10); 62 | root.left.left = new TreeNode(5); 63 | root.left.right = new TreeNode(7); 64 | root.right.left = new TreeNode(9); 65 | root.right.right = new TreeNode(11); 66 | // 通过层序遍历输出二叉树结构 67 | System.out.println("初始二叉树结构"); 68 | levelTravel(root); 69 | 70 | // 测试 71 | System.out.println("功能测试"); 72 | Mirror(root); 73 | levelTravel(root); 74 | 75 | } 76 | 77 | /** 78 | * 内容:层序遍历 79 | * 方式:非递归(采用队列) 80 | * 作用:本方法只用于测试时输出数组,与本解题算法无关 81 | */ 82 | public static void levelTravel(TreeNode root){ 83 | // 创建队列 84 | Queue q=new LinkedList(); 85 | 86 | // 步骤1:判断当前结点是否为空;若是,则返回空操作 87 | if(root==null) 88 | return; 89 | // 步骤2:入队当前结点 90 | q.add(root); 91 | 92 | // 步骤3:判断当前队列是否为空,若为空则跳出循环 93 | while(!q.isEmpty()){ 94 | 95 | // 步骤4:出队队首元素 96 | root = q.poll(); 97 | 98 | // 步骤5:输出 出队元素 99 | printNode(root); 100 | 101 | // 步骤5:若出队元素有左孩子,则入队其左孩子 102 | if(root.left!=null) q.add(root.left); 103 | 104 | // 步骤6:若出队元素有右孩子,则入队其右孩子 105 | if(root.right!=null) q.add(root.right); 106 | } 107 | } 108 | 109 | /** 110 | * 输出结点值 111 | * 作用:本方法只用于测试时输出数组,与本解题算法无关 112 | */ 113 | public static void printNode(TreeNode node){ 114 | System.out.print(node.val); 115 | } 116 | 117 | 118 | 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_30.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * Created by Carson_Ho on 17/11/4. 7 | */ 8 | 9 | public class Exam_30 { 10 | 11 | /** 12 | * 测试用例 13 | */ 14 | public static void main(String[] args){ 15 | StackWithMin stack = new StackWithMin(); 16 | stack.push(3); 17 | stack.push(4); 18 | stack.push(2); 19 | stack.push(1); 20 | System.out.println(stack.min()); 21 | stack.pop(); 22 | System.out.println(stack.min()); 23 | stack.pop(); 24 | System.out.println(stack.min()); 25 | stack.pop(); 26 | System.out.println(stack.min()); 27 | stack.pop(); 28 | System.out.println(stack.min()); 29 | } 30 | 31 | } 32 | 33 | /** 34 | * 解题类:含min函数的栈结构(类) 35 | */ 36 | class StackWithMin { 37 | 38 | // 1. 定义2个栈:数据栈、辅助栈 39 | private Stack stackData = new Stack(); 40 | private Stack stackMin = new Stack(); 41 | 42 | // 2. 定义入栈规则 43 | public void push(int data){ 44 | // 对于数据栈:正常入栈 45 | stackData.push(data); 46 | 47 | // 对于辅助栈: 48 | // 若入栈元素 < 栈顶元素(最小元素),正常入栈; 49 | // 若入栈元素 > 栈顶元素(最小元素),再次入栈 栈顶元素 50 | if(stackMin.isEmpty()) 51 | stackMin.push(data); 52 | 53 | else{ 54 | int temp = stackMin.peek(); 55 | if(temp < data) 56 | stackMin.push(temp); 57 | else 58 | stackMin.push(data); 59 | } 60 | } 61 | 62 | // 3. 定义出栈规则 63 | public void pop(){ 64 | // 数据栈 & 辅助栈都正常出栈 65 | // 注:要判断空栈的情况 66 | if(!(stackMin.isEmpty())) { 67 | stackMin.pop(); 68 | } 69 | 70 | if(!(stackData.isEmpty())) { 71 | stackData.pop(); 72 | } 73 | 74 | } 75 | 76 | // 4. 获得当前数据栈最小元素(不是出栈) 77 | // 根据上述入栈规则后,辅助栈保证了在与数据栈同步出栈时,每次出栈元素均为当前数据栈中的最小元素 78 | // 故,若需获得当前数据栈最小元素,直接出栈辅助栈元素即可 79 | public int min(){ 80 | // 注:要判断空栈的情况 81 | if(stackMin.isEmpty()) 82 | return -1; 83 | 84 | int num = stackMin.pop(); 85 | stackMin.push(num); 86 | return num; 87 | } 88 | 89 | // 5. 获取数据栈栈顶元素 90 | // 注:不是出栈 91 | public int top(){ 92 | // 注:要判断空栈的情况 93 | if(stackData.isEmpty()) 94 | return -1; 95 | 96 | int num = stackData.pop(); 97 | stackData.push(num); 98 | return num; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_31.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * Created by Carson_Ho on 17/11/5. 7 | */ 8 | 9 | public class Exam_31 { 10 | 11 | /** 12 | * 解题算法 13 | */ 14 | public static boolean isPopOrder(int[] pushSeq,int[] popSeq){ 15 | 16 | // 判断输入数据的合法性:传入参数是否为null,压入序列与弹出序列长度是否一致 17 | if(pushSeq == null || popSeq == null|| pushSeq.length != popSeq.length) 18 | return false; 19 | 20 | // 设置2指针分别指向压入序列、弹出序列 21 | int pushSeqIndex=0,popSeqIndex=0; 22 | 23 | // 创建1个栈 用于辅助判断 24 | Stack stack = new Stack<>(); 25 | 26 | // 若指向弹出序列的指针已到序列结尾,则代表被测序列 = 弹出序列 27 | // 即,循环跳出 28 | while (popSeqIndex < popSeq.length){ 29 | 30 | // 若栈顶元素 ≠ 弹出序列指针指向元素时,入栈 压栈序列指针指向的元素,指针后移1位 31 | if(stack.isEmpty()||stack.peek()!=popSeq[popSeqIndex]) { 32 | if(pushSeqIndex < pushSeq.length ) { 33 | stack.push(pushSeq[pushSeqIndex]); 34 | pushSeqIndex++; 35 | 36 | }else 37 | // 若指向压栈序列的指针p1已到序列结尾,则代表被测序列 ≠ 弹出序列 38 | return false; 39 | } 40 | // 否则,出栈 栈顶元素,弹出序列指针向后移1位 41 | else{ 42 | stack.pop(); 43 | popSeqIndex++; 44 | } 45 | } 46 | return true; 47 | } 48 | 49 | /** 50 | * 测试用例 51 | */ 52 | public static void main(String[] args){ 53 | // 压入序列 54 | int[] push = {1,2,3,4,5}; 55 | 56 | // 判断弹出序列 57 | int[] pop1 = {4,5,3,2,1}; 58 | int[] pop2 = {4,3,5,1,2}; 59 | System.out.println(isPopOrder(push,pop1)); 60 | System.out.println(isPopOrder(push,pop2)); 61 | // 特殊输入测试 62 | System.out.println(isPopOrder(null,null)); 63 | } 64 | 65 | 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_33.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/7. 5 | */ 6 | 7 | public class Exam_33 { 8 | 9 | /** 10 | * 解题算法 11 | * @param sequence 需判断的某二叉搜索树的后序遍历的结果 12 | * @return true:该数组是某二叉搜索树的后序遍历的结果。false:不是 13 | */ 14 | public static boolean verifySequenceOfBST(int[] sequence) { 15 | 16 | // 1. 判断输入的数据合法性:输入的数组不能为空,并且有数据 17 | if (sequence == null || sequence.length <= 0) { 18 | return false; 19 | } 20 | 21 | // 2. 调用辅助方法进行检验 22 | return verifySequenceOfBST(sequence, 0, sequence.length - 1); 23 | } 24 | 25 | /** 26 | * 辅助算法(递归使用) 27 | * @param sequence 某二叉搜索树的后序遍历的结果 28 | * @param start 处理的开始位置 29 | * @param end 处理的结束位置 30 | * @return true:该数组是某二叉搜索树的后序遍历的结果。false:不是 31 | */ 32 | public static boolean verifySequenceOfBST(int[] sequence, int start, int end) { 33 | 34 | // 若要处理的数据只有1个 or 已无数据要处理(start>end)就返回true 35 | if (start >= end) { 36 | return true; 37 | } 38 | 39 | // 1. 寻找二叉搜索树的左子树节点 40 | // 即,从左向右找第1个 ≤ 根结点的元素的位置 41 | // 根节点 = 数组最后1个元素 = sequence[end] 42 | int index = start; 43 | while (index < end - 1 && sequence[index] < sequence[end]) { 44 | index++; 45 | // 跳出循环后,[start, index-1]的元素都是小于根结点的(sequence[end]) 46 | // 即,[start, index-1]范围的节点 = 根结点的左子树 47 | } 48 | 49 | // 2. 寻找二叉搜索树的右子树节点 50 | // 即,从左向右找第1个 ≥ 根结点的元素的位置 51 | // 由于上面已寻找到左子树节点,故只需从从最后1个≤根结点的元素开始,找第1个≥根结点的元素 52 | 53 | int right = index;// right用于记录第一个 ≥ 根结点的元素的位置 54 | while (index < end - 1 && sequence[index] > sequence[end]) { 55 | index++; 56 | // 跳出循环后,[index, end-1]的元素都是大于根结点的(sequence[end]) 57 | // [index, end-1]范围的节点 = 根结点的左子树 58 | } 59 | 60 | // 3. 判断上述2部分是否符合后序遍历序列的规则 61 | // 若第2部分满足规则,那么一定有index=end-1, 62 | // 若不满足,即说明根结点的右子树[index, end-1]中有≤根结点的元素,即不符合二叉搜索树的定义,返回false 63 | if (index != end - 1) { 64 | return false; 65 | } 66 | 67 | // 若执行到此处,则说明当前序列符合后序遍历规则 68 | // 即,[start, index-1] = 根结点左子树的位置、[index, end-1] = 根结点右子树的位置 69 | // 通过递归方式,继续判断2部分内部是否仍然满足后序遍历规则 70 | index = right; 71 | return verifySequenceOfBST(sequence, start, index - 1) && verifySequenceOfBST(sequence, index, end - 1); 72 | } 73 | 74 | /** 75 | * 测试用例 76 | */ 77 | public static void main(String[] args){ 78 | 79 | // 功能测试 80 | int[] data = {5,7,6,9,11,10,8}; 81 | int[] data1 = {7,4,6,5}; 82 | System.out.println(verifySequenceOfBST(data)); 83 | System.out.println(verifySequenceOfBST(data1)); 84 | 85 | // 特殊输入测试 86 | System.out.println(verifySequenceOfBST(null)); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_38_2.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | /** 8 | * Created by Carson_Ho on 17/11/9. 9 | */ 10 | 11 | public class Exam_38_2 { 12 | 13 | /** 14 | * 解题算法 15 | */ 16 | public static List stringCombination(String str) { 17 | // 1. 判断数据的合法性 18 | if(str == null || str.trim().length() == 0) { 19 | System.out.println("输入的字符串为空"); 20 | return new ArrayList<>(); 21 | } 22 | 23 | char chars[] = str.toCharArray();// 将字符串转换成数组便于处理 24 | StringBuilder sb = new StringBuilder(); 25 | int index = 0; 26 | 27 | // 2. 创建1链表用于存储排列 28 | List result = new LinkedList<>(); 29 | 30 | // 3. 通过 循环 求出字符串的组合 31 | for(int i = 1; i <= str.length(); ++i) { 32 | stringCombination(chars, i,result,sb,index); 33 | } 34 | 35 | return result; 36 | } 37 | 38 | /** 39 | * 辅助算法 40 | */ 41 | private static void stringCombination(char[] chars, int length,List result,StringBuilder sb,int index) { 42 | if(length == 0) { 43 | result.add(sb.toString()); 44 | return; 45 | } 46 | if(chars.length - index < length) { 47 | return; 48 | } 49 | // 选择第一个,从剩下的中选择length-1个 50 | sb.append(chars[index]); 51 | ++index; 52 | stringCombination(chars, length - 1,result,sb,index); 53 | sb.deleteCharAt(sb.length() - 1); 54 | 55 | // 不选择第一个,从剩下的中选择length个 56 | stringCombination(chars, length,result,sb,index); 57 | --index; 58 | } 59 | 60 | /** 61 | * 测试用例 62 | */ 63 | public static void main(String[] args) { 64 | 65 | // 功能测试:无重复字符 字符串 66 | System.out.println("功能测试1:无重复字符 字符串"); 67 | System.out.println(stringCombination("abc")); 68 | 69 | // 特殊输入测试:输入字符为空 70 | System.out.println("特殊输入测试:输入字符为空"); 71 | System.out.println(stringCombination(null)); 72 | 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_4.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/10/19. 5 | */ 6 | 7 | public class Exam_4 { 8 | 9 | /** 10 | * 在二维数组中查找 11 | * @param array = 二维数组 12 | * @param target = 需查找的整数 13 | * @return 返回false代表该数组无该整数;返回true代表该数组有该整数 14 | */ 15 | 16 | private static boolean answer(int[][] array,int target){ 17 | // 1. 判断输入数据 是否合法 18 | if(array==null) { 19 | System.out.print("输入数据不合法"); 20 | return false; 21 | } 22 | 23 | // 2. 设置查找范围初始右上角整数的二维坐标 = 最右上角元素 24 | int row = 0; // 第1行 25 | int col = array[0].length-1; // 最后列 26 | 27 | int numRow = array.length; // 二维数组的行数 28 | 29 | // 3. 比较查找范围右上角整数 与 需查找的整数 30 | while(row < numRow && col>=0){ 31 | 32 | // 若 需查找的整数 < 右上角整数 ,则在当前查找范围中删除当前列 33 | if(target < array[row][col]) 34 | col--; 35 | 36 | // 若 需查找的整数 > 右上角整数 ,则在当前查找范围中删除当前行 37 | else if (target > array[row][col]) { 38 | row++; 39 | 40 | }else { 41 | // 若 需查找的整数 = 右上角整数表示二维数组存在该整数 42 | System.out.print("数组中存在整数" + target); 43 | return true; 44 | } 45 | } 46 | System.out.print("数组中不存在整数" + target); 47 | return false; 48 | } 49 | 50 | /** 51 | * 递归解法 52 | * @param array = 二维数组 53 | * @param target = 需查找的整数 54 | * @return 返回false代表该数组无该整数;返回true代表该数组有该整数 55 | */ 56 | 57 | private static boolean answer1(int[][] array,int target) { 58 | // 1. 判断输入数据 是否合法 59 | if (array == null) { 60 | System.out.print("输入数据不合法"); 61 | return false; 62 | } 63 | 64 | return valueHelper(array,target,0,array[0].length-1); 65 | 66 | 67 | } 68 | // 递归辅助算法 69 | private static boolean valueHelper(int[][] array,int target,int row,int col) { 70 | // 1. 判断输入数据 是否合法 71 | if (row >= array.length || col<0) { 72 | System.out.print("遍历完毕"); 73 | return false; 74 | } 75 | 76 | if (target ==array[row][col]) return true; 77 | 78 | if (target maxH = new TreeSet<>(Collections.reverseOrder()); 17 | TreeSet minH = new TreeSet<>(); 18 | 19 | /** 20 | * 插入数据到堆里 21 | * 注:需保证数据平均分到2个堆里 & (最大堆)左部分数据 < (最小堆)右部分的数据 22 | */ 23 | public void Insert(Integer num) { 24 | // 需保证数据平均分到2个堆里 & (最大堆)左部分数据 < (最小堆)右部分的数据 25 | // 当偶数时,将数据插入最大堆 & 将最大堆最大的数据插入到最小堆中 26 | // 当奇数时,将数据插入最小堆 & 将最小堆最小的数据插入到最大堆中 27 | if(((maxH.size() + minH.size()) & 1) == 0) { 28 | maxH.add(num); 29 | minH.add(maxH.pollFirst()); 30 | } 31 | else { 32 | minH.add(num); 33 | maxH.add(minH.pollFirst()); 34 | } 35 | } 36 | 37 | /** 38 | * 获取中位数 39 | * 根据 左部分最大数据(最大堆的堆顶数据) & 右部分最小数据(最小堆的堆顶数据) ,从而获得中位数 40 | * 数据总数 = 奇数时取后者、偶数时取2者的平均 41 | * 42 | */ 43 | public Double GetMedian() { 44 | if(maxH.size() == 0 && minH.size() == 0) 45 | return new Double(0.0); 46 | 47 | // 偶数时,取平均 48 | if(((maxH.size() + minH.size()) & 1) == 0) { 49 | return (double)(minH.first() + maxH.first()) / 2; 50 | } 51 | else { 52 | // 奇数时,取最小堆顶元素 53 | return (double)(minH.first()); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_41Test.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/13. 5 | */ 6 | 7 | public class Exam_41Test { 8 | 9 | /** 10 | * 测试用例 11 | */ 12 | public static void main(String[] args){ 13 | 14 | // 测试用例 15 | // 功能测试 16 | System.out.println("功能测试"); 17 | Exam_41Solution test = new Exam_41Solution(); 18 | // 通过循环插入,代表动态从数据流获取数据 19 | for(int i = 0; i < 10; ++i) { 20 | test.Insert(i); 21 | System.out.print(test.GetMedian() + " "); 22 | } 23 | System.out.println(); 24 | 25 | // 特殊输入测试:无数据输入 26 | System.out.println("特殊输入测试"); 27 | Exam_41Solution test1 = new Exam_41Solution(); 28 | System.out.print(test1.GetMedian() + " "); 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_42.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/13. 5 | */ 6 | 7 | public class Exam_42 { 8 | 9 | /** 10 | * 解题算法:动态规划 11 | */ 12 | public static int findGreatestSumOfSumArrays(int[] array){ 13 | // 1. 判断输入数据的合法性 14 | if(array==null || array.length==0){ 15 | System.out.println("输入的数组为空"); 16 | return 0; 17 | } 18 | 19 | // 2. 定义所需变量 20 | int dp = array[0]; // dp[i]:以array[i]为结尾元素的连续数组的最大和(初始化为第1个元素) 21 | int maxdp = dp; // maxdp:当前dp的最大值 22 | 23 | // 3. 通过遍历数组进行动态规划 24 | for(int i=1;i < array.length;i++){ 25 | 26 | // 3.1 当以 第( i - 1) 个数字结尾的子数组中所有数字的和 > 0时,最大值 = 等于 二者的和 27 | if( dp>0 ) 28 | dp += array[i]; 29 | else 30 | // 3.2 当以 第( i - 1) 个数字结尾的子数组中所有数字的和 < 0时,最大值 = 其本身 31 | dp = array[i]; 32 | // 3.3 当前dp的最大值 33 | if(dp > maxdp) 34 | maxdp = dp; 35 | } 36 | return maxdp; 37 | } 38 | 39 | /** 40 | * 测试用例 41 | */ 42 | public static void main(String[] args){ 43 | 44 | // 功能测试:数组中无相同数字 45 | int[] array = {1,-2,3,10,-4,7,2,-5}; 46 | System.out.println(findGreatestSumOfSumArrays(array)); 47 | 48 | // 特殊输入测试:数组为空 49 | int[] array2 = null; 50 | System.out.println(findGreatestSumOfSumArrays(array2)); 51 | 52 | 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_43.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/13. 5 | */ 6 | 7 | public class Exam_43 { 8 | 9 | /** 10 | * 解题算法:找规律 11 | */ 12 | public static int NumberOf1Between1AndN_Solution(int n){ 13 | 14 | // 1. 判断 输入数据的合法性 15 | if(n<=0) { 16 | System.out.println("输入的数据不合法"); 17 | return 0; 18 | } 19 | 20 | int count = 0 ;// 记录出现1的次数 21 | 22 | // 2. 通过遍历 进行记录 23 | for(int i = 1;i<=n;i*=10){ 24 | 25 | // 表示当前分析的是哪个数位(个、十、百...),分割该数位 26 | int a = n/i; // 高位 27 | int b = n%i; // 低位 28 | 29 | // 2.1 当 i 对应的数为1时 30 | if(a%10==1){ 31 | count = count+ (a+8)/10*i + (b+1); 32 | }else{ 33 | // 2.2 当i位对应的数为 ≥2 或 为0时 34 | count = count + (a+8)/10*i; 35 | } 36 | } 37 | return count; 38 | 39 | } 40 | 41 | /** 42 | * 测试用例 43 | */ 44 | public static void main(String[] args) { 45 | // 功能测试 46 | System.out.println(NumberOf1Between1AndN_Solution(12)); 47 | 48 | // 性能测试:数字很大 49 | System.out.println(NumberOf1Between1AndN_Solution(12222)); 50 | 51 | // 特殊输入测试 52 | System.out.println(NumberOf1Between1AndN_Solution(0)); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_44.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/13. 5 | */ 6 | 7 | public class Exam_44 { 8 | 9 | /** 10 | * 解题算法 11 | */ 12 | 13 | public static int digitAtIndex(int index){ 14 | // 判断输入数据的合法性 15 | if(index<0) { 16 | System.out.println("输入的数据不合法"); 17 | return -1; 18 | } 19 | 20 | /** 21 | * 1. 确定该数字 属于几位数 22 | * 若 n < (10 + 2*9*(10^i) ),那么该数字 属于 i 位数 23 | */ 24 | // 个位数 25 | if(index<10) 26 | return index; 27 | // 2位以上 28 | int curIndex = 10; 29 | int length = 2; // 表示多少位数 30 | int boundNum = 10; 31 | 32 | while (curIndex + lengthSum(length)(){ 39 | @Override 40 | public int compare(String s1, String s2) { 41 | String c1 = s1 + s2; 42 | String c2 = s2 + s1; 43 | return c1.compareTo(c2); 44 | } 45 | }); 46 | 47 | 48 | // 3. 通过遍历字符串数组,拼接数组元素,从而成为最终最小的1个数字 49 | StringBuilder sb = new StringBuilder(); // 用于存储拼接后的数字 50 | 51 | for(int i = 0; i < numbers.length; i++){ 52 | sb.append(str[i]); 53 | } 54 | 55 | // 最终反馈 56 | return sb.toString(); 57 | } 58 | 59 | /** 60 | * 测试用例 61 | */ 62 | public static void main(String[] args) { 63 | int[] data = {3,32,321}; 64 | 65 | // 功能测试 66 | System.out.println(PrintMinNumber(data)); 67 | 68 | // 特殊输入测试 69 | System.out.println(PrintMinNumber(null)); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_46.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/14. 5 | */ 6 | 7 | public class Exam_46 { 8 | 9 | /** 10 | * 解题算法 11 | */ 12 | public static int getTranslationCount(int number){ 13 | 14 | // 1. 检查输入数据的合法性 15 | if(number < 0) 16 | return 0; 17 | 18 | if(number == 1) 19 | return 1; 20 | 21 | // 2. 递归计算翻译数 22 | // 为了便于转换,故将数字转成字符串 23 | return getTranslationCount(Integer.toString(number)); 24 | } 25 | /** 26 | * 辅助算法 27 | * 作用 = 递归计算翻译数 28 | * 原理 = 动态规划、从右到左计算:f(i-2) = f(i-1)+g(i-2,i-1)*f(i)、 29 | */ 30 | 31 | public static int getTranslationCount(String number) { 32 | 33 | // f(n) = 0、f(n-1)=1 34 | 35 | int f1 = 0,f2 = 1,g = 0; 36 | int temp; 37 | 38 | // 从右到左计算 39 | for( int i = number.length()-2; i>=0; i-- ){ 40 | 41 | // 当第 i 位 和 第(i+1)位 2个数字拼接起来的数字在10~25的范围内时,g (i,i+1) =1 42 | // 注:通过""拼接起来 43 | if(Integer.parseInt( number.charAt(i)+""+number.charAt(i+1) )<26) 44 | g = 1; 45 | else 46 | g = 0; 47 | 48 | temp = f2; 49 | 50 | // 计算f (i-2) = f (i-1)+ g (i-2,i-1) x f (i) 51 | f2 = f2+g*f1; 52 | f1 = temp; 53 | } 54 | 55 | return f2; 56 | } 57 | 58 | /** 59 | * 测试用例 60 | */ 61 | public static void main(String[] args) { 62 | 63 | // 功能测试 64 | System.out.println(getTranslationCount(12258)); 65 | 66 | // 特殊输入测试:负数、0 67 | System.out.println(getTranslationCount(-10)); 68 | System.out.println(getTranslationCount(0)); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_47.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/15. 5 | */ 6 | 7 | public class Exam_47 { 8 | 9 | /** 10 | * 解题算法 11 | */ 12 | public static int getMaxVaule(int[][] data){ 13 | 14 | // 1. 检查输入数据的合法性 15 | if(data == null || data.length==0 || data[0].length==0) 16 | return 0; 17 | 18 | // 2. 使用1个辅助的2维数组(长度 = 棋盘 的列数n) 19 | // 由于第i行的值 仅与 第 i 行 & 第(i-1行)有关 20 | // 故 仅用数组中的2行用于缓存中间结果,,即data [2][] 即可完成状态的记录 & 更新 21 | int[][] dp = new int[2][data[0].length]; 22 | 23 | int curRowIndex = 0; // 用于记录当前行 24 | int preRowIndex = 0; // 用于记录上1行 25 | 26 | // 通过 循环 实现递归 27 | for(int row=0;row= dp[curRowIndex][col-1]) 40 | dp[curRowIndex][col] = dp[preRowIndex][col]+data[row][col]; 41 | else 42 | dp[curRowIndex][col] = dp[curRowIndex][col-1]+data[row][col]; 43 | } 44 | } 45 | } 46 | 47 | return dp[(data.length-1)&1][data[0].length-1]; 48 | } 49 | 50 | /** 51 | * 测试用例 52 | */ 53 | 54 | public static void main(String[] args){ 55 | // 功能测试 56 | int[][] data = { 57 | {1,10,3,8}, 58 | {12,2,9,6}, 59 | {5,7,4,11}, 60 | {3,7,16,5}}; 61 | System.out.println(getMaxVaule(data)); 62 | 63 | // 只有1行 64 | int[][] data1 = { 65 | {1,10,3,8}, 66 | }; 67 | 68 | System.out.println(getMaxVaule(data1)); 69 | 70 | // 只有1列 71 | int[][] data2 = { 72 | {1}, 73 | {12}, 74 | {5}, 75 | {3}, 76 | }; 77 | System.out.println(getMaxVaule(data2)); 78 | 79 | // 特殊输入测试 80 | System.out.println(getMaxVaule(null)); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_49.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/15. 5 | */ 6 | 7 | public class Exam_49 { 8 | 9 | /** 10 | * 解题算法 11 | */ 12 | public static int getUglyNumber(int num){ 13 | // 检查数据的合法性 14 | if(num <= 0 ) 15 | return 0; 16 | 17 | // 因1-6都是丑数,所以当丑数数量<7个时直接返回数量 18 | if (num < 7) 19 | return num; 20 | 21 | // 1. 创建1数组 22 | // 作用:放置已经排序好的丑数 23 | // 长度:丑数的数量。如,为了得到第1500个丑数,则需要长度 = 1500的数组来记录已经计算出来的丑数 24 | int[] uglyNumber = new int[num]; 25 | 26 | uglyNumber[0] = 1; // 第1个丑数 = 1 27 | 28 | // 2. 定义1个指针指向第1个丑数1,3个指针 分别指向 前1个丑数乘2、乘3、乘5后的丑数 29 | int uglyIndex=0, multiply2=0, multiply3=0, multiply5=0; 30 | 31 | while (uglyIndex+1 头 复制 字符串 & 替换空格 36 | // 通过2个指针辅助:从后->前移动 37 | int indexOfOriginal = originLength-1; // P1指向旧字符串末尾 38 | int indexOfNew = newLength-1; // P2指向替换后的字符串末尾 39 | 40 | // 6. 当2指针不相等时,移动指针进行字符串复制 & 替换 41 | // 相等时就跳出循环 42 | while(indexOfOriginal >= 0 && indexOfOriginal != indexOfNew){ 43 | 44 | // P1指向空格后: 45 | if(str.charAt(indexOfOriginal)==' '){ 46 | 47 | // 在P2前插入“%20”,并将其向前移动3格 48 | str.setCharAt(indexOfNew--, '0'); 49 | str.setCharAt(indexOfNew--, '2'); 50 | str.setCharAt(indexOfNew--, '%'); 51 | indexOfOriginal--; // P1向前移动1格 52 | 53 | }else { 54 | // 向前移动指针P1,逐步将它指向的字符复制到P2指针,直到P1指向空格为止 55 | // 先复制,再移动 56 | 57 | str.setCharAt(indexOfNew--, str.charAt(indexOfOriginal--)); 58 | 59 | } 60 | } 61 | 62 | 63 | // 8. 返回结果 64 | return str.toString(); 65 | 66 | } 67 | 68 | /** 69 | * 测试用例 70 | */ 71 | public static void main(String[] args) { 72 | 73 | // 功能测试1:字符串中含空格 74 | StringBuffer string1 =new StringBuffer("We are happy"); 75 | System.out.println(answer(string1)); 76 | 77 | // 功能测试2:字符串中不含空格 78 | StringBuffer string2 =new StringBuffer("Wearehappy"); 79 | System.out.println(answer(string2)); 80 | 81 | // 特殊输入测试:为空指针、为空字符串、只有1个空格字符 82 | System.out.println(answer(null)); 83 | 84 | StringBuffer string3 =new StringBuffer(""); 85 | System.out.println(answer(string3)); 86 | 87 | StringBuffer string4 =new StringBuffer(" "); 88 | System.out.println(answer(string4)); 89 | 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_50.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/16. 5 | */ 6 | 7 | public class Exam_50 { 8 | 9 | /** 10 | * 解题算法 11 | */ 12 | public static char FirstNotRepeatingChar(String str) { 13 | 14 | // 1. 检查输入数据的合法性 15 | if(str == null || str.length() == 0) 16 | return '0'; 17 | 18 | // 2. 本题 实现1简易的哈希表 = 1维数组: 19 | // a. 长度 = 256:所有字符都可用ASCII表示 = 0-255 = 256位 20 | // b. 数组下标 = 字符的ASCII码 21 | // c. 数组的值 = 每个字符出现的次数 22 | int[] ch = new int[256]; 23 | 24 | 25 | // 3. 从头开始扫描字符串2次 26 | // 第1次:每扫描到1个字符,记录下字符出现的次数(即数组的值+1) 27 | for(int i = 0; i < str.length(); ++i) { 28 | ++ch[str.charAt(i)]; 29 | } 30 | 31 | // 第2次:每扫描到1个字符,从数组中获取字符出现的次数,第1个只出现1次的字符即为所求 32 | for(int i = 0; i < str.length(); ++i) { 33 | if(ch[str.charAt(i)] == 1) { 34 | return str.charAt(i); 35 | } 36 | } 37 | return '0'; 38 | } 39 | 40 | /** 41 | * 测试用例 42 | */ 43 | public static void main(String[] args){ 44 | 45 | // 功能测试1:字符串存在只出现1次的字符 46 | System.out.println(FirstNotRepeatingChar("abaccdeff")); 47 | 48 | // 功能测试1:字符串中所有字符都知出现1次 49 | System.out.println(FirstNotRepeatingChar("abcdefg")); 50 | 51 | // 特殊输入:无效输入 52 | System.out.println(FirstNotRepeatingChar(null)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_50_2.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/16. 5 | */ 6 | 7 | public class Exam_50_2 { 8 | 9 | 10 | /** 11 | * 解题算法 12 | */ 13 | public static class CharStatistics{ 14 | 15 | // 1. 本题 实现1简易的哈希表 = 1维数组: 16 | // a. 长度 = 256:所有字符都可用ASCII表示 = 0-255 = 256位 17 | // b. 数组下标 = 字符的ASCII码 18 | // c. 数组的值 = 每个字符在字符流的位置,初始化为 -1 19 | // 数值表示:-1 = 未出现,≥ 0 = 出现的位置且仅出现一次,-2 = 出现两次 or 以上 20 | private int[] times; // 哈希表 21 | private int index; // 字符在字符流的下标 22 | 23 | public CharStatistics(){ 24 | index = 0; 25 | times = new int[256]; 26 | for(int i=0;i<256;i++) 27 | times[i] = -1; 28 | } 29 | 30 | // 2. 读取字符时,存储 字符 在字符流中的位置 31 | // a. 当字符 第1次 从字符流读取时,在数组中对应的值 = 其在字符流的位置 32 | // b. 当字符 第(1+n)次 从字符流读取时,在数组中对应的值 从位置值 更新为:1特殊值,如负数 -2 33 | public void insert(char ch){ 34 | 35 | if(times[ch] == -1) 36 | times[ch] = index; 37 | else 38 | times[ch] = -2; 39 | 40 | index++; 41 | } 42 | 43 | // 3. 获取 字符串中第1个只出现1次(不重复)的字符 44 | // 扫描数组,从中找出最小的、值≥0 对应的字符 即为所求 45 | public char find(){ 46 | 47 | int minIndex = 256; 48 | 49 | char ret = '#'; // 若没有只出现一次的字符,显示# 50 | 51 | for(int i=0;i<256;i++){ 52 | 53 | if( times[i]>=0 && times[i]>1; 45 | 46 | // 2. 把数组逐步分割成 长度= 1 的子数组(通过 递归 实现) 47 | int left = inversePairsCore(copy, array, start, mid); // 左1半 48 | int right = inversePairsCore(copy, array, mid + 1, end); // 右1半 49 | 50 | // 3. 定义2指针,分别指向2个子数组的末尾 51 | 52 | int i = mid;// a. 前半段的最后1个数字的下标 53 | int j = end; // b. 后半段最后一个数字的下标 54 | 55 | int indexCopy = end; // 拷贝到辅助数组的位置从末尾开始 56 | int count = 0; // 逆序对的数量 57 | 58 | // 4. 对逆序对的数目的统计 59 | while (i >= start && j > mid) { 60 | 61 | // 若p1 > p2,则构成逆序对,数目 = 第2个子数组中 指针指向数字前面的数字个数、把较大数字放入到1辅助数组中、把指向较大数字的指针往前移1位 62 | // 若p1 ≤ p2,则不构成逆序对、把较大数字放入到1辅助数组中、把指向较大数字的指针往前移1位 63 | if (array[i] > array[j]) { 64 | count += j - mid; // 对应的逆序数 65 | copy[indexCopy--] = array[i--]; 66 | 67 | 68 | } else { 69 | copy[indexCopy--] = array[j--]; 70 | 71 | } 72 | } 73 | 74 | // 将剩余元素复制到辅助数组 75 | for (; i >= start;) { 76 | copy[indexCopy--] = array[i--]; 77 | } 78 | 79 | for (; j > mid;) { 80 | copy[indexCopy--] = array[j--]; 81 | 82 | } 83 | 84 | return count + left + right; 85 | } 86 | 87 | /** 88 | * 测试用例 89 | */ 90 | 91 | public static void main(String[] args){ 92 | // 功能测试 93 | System.out.println(inversePairs(new int[]{7,5,6,4})); // 未排序数组 94 | System.out.println(inversePairs(new int[]{4,5,6,7})); // 递增数组 95 | System.out.println(inversePairs(new int[]{7,6,5,4})); // 递减数组 96 | System.out.println(inversePairs(new int[]{7,5,4,4})); // 重复数组 97 | 98 | // 边界测试 99 | System.out.println(inversePairs(new int[]{7})); // 只有1个数字 100 | System.out.println(inversePairs(new int[]{7,4})); // 有2个数字 101 | 102 | // 特殊输入测试 103 | System.out.println(inversePairs(null)); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_52.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/17. 5 | */ 6 | 7 | 8 | public class Exam_52 { 9 | 10 | /** 11 | * 链表节点结构 12 | */ 13 | public static class ListNode { 14 | public int val; 15 | public ListNode next = null; 16 | ListNode(int val) { 17 | this.val = val; 18 | } 19 | } 20 | 21 | /** 22 | * 解题算法 23 | */ 24 | public static ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) { 25 | // 判断输入数据的合法性 26 | if(pHead1 == null || pHead2 == null) 27 | return null; 28 | int len1 = 0; 29 | int len2 = 0; 30 | ListNode cur1 = pHead1; 31 | ListNode cur2 = pHead2; 32 | 33 | // 1. 第1次遍历:遍历2个链表,获得2个链表的长度(即,m & n,设 m > n) 34 | for(ListNode cur = pHead1; cur != null; cur = cur.next) 35 | ++len1; 36 | for(ListNode cur = pHead2; cur != null; cur = cur.next) 37 | ++len2; 38 | 39 | // 2. 第2次遍历:在较长的链表先走(m-n)步,再同时在2个链表上遍历,找到的第1个相同节点即为2个链表的公共节点 40 | // 2.1 在较长的链表先走(m-n)步 41 | if(len1 > len2) { 42 | for(int i = 0; i < len1 - len2; ++i) 43 | cur1 = cur1.next; 44 | } 45 | else { 46 | for(int i = 0; i < len2 - len1; ++i) 47 | cur2 = cur2.next; 48 | } 49 | // 2.2 再同时在2个链表上遍历,找到的第1个相同节点即为2个链表的公共节点 50 | while(cur1 != null) { 51 | if(cur1 == cur2) 52 | return cur1; 53 | cur1 = cur1.next; 54 | cur2 = cur2.next; 55 | } 56 | return null; 57 | } 58 | 59 | /** 60 | * 测试用例 61 | */ 62 | 63 | public static void main(String[] args){ 64 | // 功能测试:二链表定义如下 65 | // 1->2->3->6->7 66 | // 4->5↗ 67 | ListNode node1 = new ListNode(1); 68 | ListNode node2 = new ListNode(2); 69 | ListNode node3 = new ListNode(3); 70 | ListNode node4 = new ListNode(4); 71 | ListNode node5 = new ListNode(5); 72 | ListNode node6 = new ListNode(6); 73 | ListNode node7 = new ListNode(7); 74 | node1.next = node2; 75 | node2.next = node3; 76 | node3.next = node6; 77 | node4.next = node5; 78 | node5.next = node6; 79 | node6.next = node7; 80 | 81 | ListNode commonNode = FindFirstCommonNode(node1,node4); 82 | System.out.println(commonNode.val); 83 | 84 | // 特殊输入测试:链表头节点 为空 85 | System.out.println(FindFirstCommonNode(null,null)); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_53_1.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/20. 5 | */ 6 | 7 | public class Exam_53_1 { 8 | 9 | /** 10 | * 解题算法 11 | */ 12 | public static int getMissingNumber(int[] data){ 13 | 14 | // 检查输入数据的合法性 15 | if(data == null || data.length == 0) 16 | return -1; 17 | 18 | int left = 0; 19 | int right = data.length-1; 20 | int mid; 21 | 22 | // 采用二分法 23 | while (left<=right){ 24 | 25 | // 求中间元素 26 | mid = left+((right-left)>>1); 27 | // 注: 28 | // a. 用位运算替换原来的除法:mid=left+(right-left)/2 29 | // b. 加减法 优先级 > 位运算 30 | 31 | 32 | // 1.若中间元素值 ≠ 下标 33 | 34 | if(data[mid]!=mid) { 35 | // a. 前1元素 = 其下标,中间元素 = 第1个值与下标不相等的元素,即中间元素下标 = 数组中不存在的数字 36 | if (mid ==0 || data[mid-1]== mid-1) 37 | return mid; 38 | // b. 前1元素 ≠ 其下标,那么下一轮 只需在左半边查找 39 | right = mid-1; 40 | 41 | } 42 | // 2. 若中间元素值 = 下标,那么下一轮 只需在右半边查找 43 | else 44 | left = mid + 1; 45 | 46 | } 47 | 48 | if (left == data.length) 49 | 50 | return left; 51 | 52 | // 无效输入时返回-1,即数组不按要求排序 / 有数字不在0—~n-1的范围内 53 | return -1; 54 | } 55 | 56 | /** 57 | * 测试用例 58 | */ 59 | public static void main(String[] args){ 60 | // 功能测试:缺失的数字在开始、中间 & 结尾 61 | int[] data1 = new int[]{1,2,3,4,5,6}; 62 | int[] data2 = new int[]{0,1,3,4,5}; 63 | int[] data3 = new int[]{0,1,2,3,4,5}; 64 | System.out.println(getMissingNumber(data1)); 65 | System.out.println(getMissingNumber(data2)); 66 | System.out.println(getMissingNumber(data3)); 67 | 68 | // 边界值测试:数组中只有1个数字 = 0 69 | int[] data4 = new int[]{0}; 70 | System.out.println(getMissingNumber(data4)); 71 | 72 | // 特殊输入测试:数组为空 73 | System.out.println(getMissingNumber(null)); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_53_2.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/20. 5 | */ 6 | 7 | public class Exam_53_2 { 8 | 9 | /** 10 | * 解题算法 11 | */ 12 | public static int getMissingNumber(int[] data){ 13 | 14 | // 检查输入数据的合法性 15 | if(data == null || data.length == 0) 16 | return -1; 17 | 18 | int left = 0; 19 | int right = data.length-1; 20 | int mid; 21 | 22 | // 采用二分法 23 | while (left<=right){ 24 | 25 | // 求中间元素 26 | mid = left+((right-left)>>1); 27 | // 注: 28 | // a. 用位运算替换原来的除法:mid=left+(right-left)/2 29 | // b. 加减法 优先级 > 位运算 30 | 31 | // 设中间元素的值 = m、下标 = i 32 | // a. 若m = i,该数字 即为所求 33 | if(data[mid] == mid) 34 | return mid; 35 | 36 | // b. 若m > i,下一轮直接查找左边的数组( 数组下标 mid) 38 | right = mid -1; 39 | // c. 若m < i,下一轮直接查找右边的数组( 数组下标>i ) 40 | else 41 | left = mid +1; 42 | } 43 | // 无效输入时返回-1 44 | return -1; 45 | } 46 | 47 | /** 48 | * 测试用例 49 | */ 50 | public static void main(String[] args){ 51 | // 功能测试:数组中含数组 & 下标相等的数字、不含数组 & 下标相等的数字 52 | int[] data1 = new int[]{-1, 0, 1, 3, 10}; 53 | int[] data2 = new int[]{-1, 0, 1, 2, 10}; 54 | System.out.println("功能测试"); 55 | System.out.println(getMissingNumber(data1)); 56 | System.out.println(getMissingNumber(data2)); 57 | 58 | // 边界值测试:数组中仅有1个数字、数组与下标相等的元素位于数组的开头 or 结尾 59 | int[] data3 = new int[]{3}; 60 | int[] data4 = new int[]{0, 3, 4, 5, 10}; 61 | int[] data5 = new int[]{-1, 0, 1, 2, 4}; 62 | System.out.println("边界值测试"); 63 | System.out.println(getMissingNumber(data3)); 64 | System.out.println(getMissingNumber(data4)); 65 | System.out.println(getMissingNumber(data5)); 66 | 67 | // 特殊输入测试:数组为空 68 | System.out.println("特殊输入测试"); 69 | System.out.println(getMissingNumber(null)); 70 | 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_54.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * Created by Carson_Ho on 17/11/20. 7 | */ 8 | 9 | public class Exam_54 { 10 | 11 | /** 12 | * 设置结点结构 13 | */ 14 | public static class TreeNode { 15 | int val; // 二叉树的结点数据 16 | TreeNode left; // 二叉树的左子树(左孩子) 17 | TreeNode right; // 二叉树的右子树(右孩子) 18 | 19 | public TreeNode(int data) { 20 | this.val = data; 21 | this.left = null; 22 | this.right = null; 23 | } 24 | } 25 | 26 | 27 | /** 28 | * 解题算法:中序遍历 29 | * 实现方式:非递归(栈实现) 30 | */ 31 | public static TreeNode InOrder_stack(TreeNode root, int k) { 32 | 33 | // 检查输入节点的合法性 34 | if (root == null || k < 0) 35 | return null; 36 | 37 | // 创建1个栈用于实现中序遍历 38 | Stack stack = new Stack(); 39 | TreeNode cur = root; 40 | int count = 0; 41 | 42 | // 步骤1:直到当前结点为空 & 栈空时,循环结束 43 | while (root != null || stack.size() > 0) { 44 | 45 | // 步骤2:判断当前结点是否为空 46 | // a. 若不为空,执行3、4 47 | // b. 若为空,执行5、6 48 | if (root != null) { 49 | 50 | // 步骤3:入栈当前结点 51 | stack.push(root); 52 | 53 | // 步骤4:置当前结点的左孩子为当前节点 54 | // 返回步骤1 55 | root = root.left; 56 | 57 | } else { 58 | 59 | // 步骤5:出栈栈顶结点 60 | root = stack.pop(); 61 | 62 | // 步骤6:判断出栈顺序是否等于所要求顺序,若是则输出 63 | // 即,不需将整个中序序列求出来,只需求到所需位置即可 64 | count++; 65 | if (count == k) 66 | return root; 67 | 68 | // 步骤7:置当前结点的右孩子为当前节点 69 | root = root.right; 70 | // 返回步骤1 71 | } 72 | } 73 | return null; 74 | } 75 | 76 | /** 77 | * 测试用例 78 | */ 79 | public static void main(String[] args){ 80 | 81 | // 构造二叉树结构: 82 | // 5 83 | // / \ 84 | // 3 7 85 | // / \ / \ 86 | // 2 4 6 8 87 | TreeNode root = new TreeNode (5); 88 | root.left = new TreeNode (3); 89 | root.left.left = new TreeNode (2); 90 | root.left.right = new TreeNode (4); 91 | root.right = new TreeNode (7); 92 | root.right.left = new TreeNode (6); 93 | root.right.right = new TreeNode (8); 94 | // 中序遍历序列为:{ 2,3,4,5,6,7,8 } 95 | System.out.println(InOrder_stack(root,3).val); // 求第3个 96 | System.out.println(InOrder_stack(root,6).val); // 求第6个 97 | 98 | // 特殊输入测试 99 | System.out.println(InOrder_stack(null,8)); //null 100 | } 101 | } 102 | 103 | 104 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_55.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | 6 | /** 7 | * Created by Carson_Ho on 17/11/21. 8 | */ 9 | 10 | public class Exam_55 { 11 | 12 | /** 13 | * 设置结点结构 14 | */ 15 | public static class TreeNode { 16 | int val; // 二叉树的结点数据 17 | TreeNode left; // 二叉树的左子树(左孩子) 18 | TreeNode right; // 二叉树的右子树(右孩子) 19 | 20 | public TreeNode(int data) { 21 | this.val = data; 22 | this.left = null; 23 | this.right = null; 24 | } 25 | } 26 | 27 | /** 28 | * 解题思路1 29 | * 原理 = 根据二叉树的形状判断 30 | * 实现方式 = 递归 31 | */ 32 | public static int treeDepth(TreeNode root){ 33 | 34 | // 检查输入数据的合法性 35 | if(root == null) 36 | return 0; 37 | 38 | // 采用递归获取左、右子树的深度 39 | int left = treeDepth(root.left); 40 | int right = treeDepth(root.right); 41 | 42 | return left > right? (left+1) : (right+1); 43 | } 44 | 45 | /** 46 | * 解题思路2 47 | * 原理 = 层序遍历 48 | * 实现方式 = 队列 49 | */ 50 | public static int treeDepth2(TreeNode root){ 51 | 52 | // 判断输入数据的合法性 53 | if(root==null) 54 | return 0; 55 | 56 | Queue q=new LinkedList<>();// 创建队列 57 | int depth = 0; // 用于记录深度 58 | 59 | // 步骤2:入队当前结点 60 | q.add(root); 61 | 62 | // 步骤3:判断当前队列是否为空,若为空则跳出循环 63 | while(!q.isEmpty()){ 64 | 65 | int size = q.size(); 66 | 67 | for(int i=0;i> 1; 55 | index++; 56 | } 57 | return index; 58 | } 59 | 60 | /** 61 | * 辅助算法:判断该数字的第index位是否为1 62 | * @param number 输入的数 63 | * @param index 位置 64 | * @return 是否为1 65 | */ 66 | private static boolean isBit1(int number, int index){ 67 | number = number >> index; 68 | return (number & 1) == 1; 69 | } 70 | 71 | /** 72 | * 测试用例 73 | */ 74 | public static void main(String[] args){ 75 | int[] array1 = {2, 4, 3, 6, 3, 2, 5, 5}; 76 | int[] num1 = new int[1]; 77 | int[] num2 = new int[1]; 78 | findTwoOnceNumber(array1,num1,num2); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_56_1.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/23. 5 | */ 6 | 7 | public class Exam_56_1 { 8 | 9 | /** 10 | * 解题算法 11 | */ 12 | public static int findOnceNumber(int[] intArray) throws Exception { 13 | 14 | // 判断输入数据的合法性 15 | if (intArray == null || intArray.length < 1) { 16 | System.out.println("数组为空或者长度不足"); 17 | return 0; 18 | } 19 | 20 | // 将和的结果存储到1个大小 = 32的数组中 21 | int[] bitSum = new int[32]; 22 | 23 | // 1. 将数组所有数字的二进制表示的每1位都加起来 24 | // 将和的结果都放到数组中 25 | for (int i : intArray) { 26 | 27 | int bitMask = 1; 28 | 29 | for (int j = 31; j >= 0; --j){ 30 | int bit = i & bitMask; 31 | if (bit != 0){ 32 | bitSum[j] += 1; 33 | } 34 | bitMask = bitMask << 1; 35 | } 36 | } 37 | 38 | //2. 对每1位的和 都对3取余 39 | int result = 0; 40 | for (int i = 0; i < 32; i++){ 41 | result = result << 1; 42 | // 若能被3整除(余数 = 0),则说明包含该位数的数字出现了3次,即 只出现1次的数字二进制表示中对应那位 = 0 43 | // 若不能被3整除(余数 = 1),则说明包含该位数的数字出现了1次,即 只出现1次的数字二进制表示中对应那位 = 1 44 | result += bitSum[i] % 3; 45 | } 46 | return result; 47 | } 48 | 49 | /** 50 | * 测试用例 51 | */ 52 | public static void main(String[] args) throws Exception { 53 | int[] array1 = {2,4,2,5,2,5,5}; 54 | System.out.println("数组中唯一出现一次数字是:" + findOnceNumber(array1)); 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_57.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Created by Carson_Ho on 17/11/24. 7 | */ 8 | 9 | public class Exam_57 { 10 | 11 | /** 12 | * 解题算法 13 | */ 14 | private static ArrayList findTwoNumberWithSum(int[] array, int sum){ 15 | 16 | ArrayList result = new ArrayList(); // 定义1个链表存储结果 17 | 18 | // 判断输入数据的合法性 19 | if (array == null || array.length < 2){ 20 | System.out.println("输入的数据不合法"); 21 | return result; 22 | } 23 | 24 | // 1. 定义2个指针 25 | // p1 = 指向数组的头数据元素 26 | // p2 = 指向数组的尾数据元素 27 | int start = 0; 28 | int end = array.length - 1; 29 | 30 | // 2. 计算2个指针指向元素的和(m) 31 | // 循环条件 32 | while (start < end){ 33 | 34 | int curSum = array[start] + array[end]; 35 | 36 | // 若m = 题目输入的s ,即2个指针指向的数据元素即为所求 37 | if (curSum == sum){ 38 | result.add(array[start]); 39 | result.add(array[end]); 40 | return result; 41 | 42 | // 若m > 题目输入的s,指针p2前移1位 43 | } else if (curSum < sum) { 44 | start++; 45 | // 若m < 题目输入的s,指针p1后移1位 46 | }else { 47 | end--; 48 | } 49 | } 50 | return result; 51 | } 52 | 53 | /** 54 | * 测试用例 55 | */ 56 | public static void main(String[] args) throws Exception { 57 | // 功能测试1:存在和为s的2个数 58 | int[] array1 = {1, 2, 4, 7, 11, 15}; 59 | System.out.println(findTwoNumberWithSum(array1,15)); 60 | 61 | // 功能测试2:不存在和为s的2个数 62 | int[] array2 = {1, 2, 3, 7, 11, 15}; 63 | System.out.println(findTwoNumberWithSum(array2,15)); 64 | 65 | // 特殊输入测试:数组为空 66 | System.out.println(findTwoNumberWithSum(null,15)); 67 | 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_57_1.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Created by Carson_Ho on 17/11/24. 7 | */ 8 | 9 | public class Exam_57_1 { 10 | 11 | /** 12 | * 解题算法 13 | */ 14 | private static ArrayList> findContinuousSequence(int sum) { 15 | 16 | ArrayList> result =new ArrayList>(); // 用于存储结果 17 | 18 | // 判断输入数据的合法性 19 | if (sum < 3){ 20 | System.out.println("输入的数据不合法"); 21 | return result; 22 | } 23 | 24 | // 1. 用2个指针分别指向当前序列的最小值 & 最大值 25 | // 指针1(small ): 初始化指向1 26 | // 指针2(big ): 初始化指向2 27 | int start = 1; 28 | int end = 2; 29 | int curSum = start + end; 30 | // 注:对于连续序列求和,考虑到每次操作前后的序列大部分数字相同,只是增加 / 减少1个数字 31 | // 故此处不采用循环求和,而是采用在前1个序列的基础上进行操作,从而减少不必要的运算,提高效率 32 | int mid = (sum + 1) / 2; 33 | 34 | // 2. 计算 指针1 ~ 指针2 之间数字的和(m) 35 | while (start < mid) { 36 | 37 | // 2.1 若m = 题目输入的s,即 指针1 ~ 指针2 之间数字 即为所求 = 1组解 38 | // 则:输出指针1 ~ 指针2 之间的数字序列、指针2 往后移1位、重复步骤2,继续求解 39 | if (curSum == sum) { 40 | 41 | // 求和 42 | ArrayList list = new ArrayList<>(); 43 | for (int i = start; i <= end; i++) { 44 | list.add(i); 45 | } 46 | result.add(list); 47 | 48 | } 49 | 50 | // 2.2 若m > 题目输入的s 51 | // 则:指针p1后移1位、重复步骤2,直到指针1(small) > (s+1)/2 为止 52 | while (curSum > sum && start < mid) { 53 | curSum -= start; 54 | start++; 55 | 56 | if (curSum == sum) { 57 | // 求和 58 | ArrayList list = new ArrayList<>(); 59 | for (int i = start; i <= end; i++) { 60 | list.add(i); 61 | } 62 | result.add(list); 63 | } 64 | } 65 | // 2.3 若m < 题目输入的s 66 | // 则,指针p2后移1位、指针1 ~ 指针2 之间数字多1个使得 m 变大 靠近 s,重复步骤2 67 | end++; 68 | curSum += end; 69 | } 70 | return result; 71 | } 72 | 73 | /** 74 | * 测试用例 75 | */ 76 | public static void main(String[] args){ 77 | // 功能测试 78 | System.out.println("功能测试"); 79 | System.out.println(findContinuousSequence(15)); 80 | 81 | // 边界值输入测试:连续序列最小和 = 3 82 | System.out.println("边界值测试"); 83 | System.out.println(findContinuousSequence(3)); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_58.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/24. 5 | */ 6 | 7 | public class Exam_58 { 8 | 9 | /** 10 | * 解题算法 11 | */ 12 | public static String reverseSentence(String str){ 13 | 14 | // 判断输入数据的合法性 15 | if (str == null || str.length() < 2){ 16 | System.out.println("输入的数据不合法"); 17 | return str; 18 | } 19 | 20 | // 转换成字符串数组便于处理 21 | char[] chars = str.toCharArray(); 22 | 23 | // 1. 翻转句子中所有字符顺序 24 | reverse(chars,0,chars.length - 1); 25 | 26 | // 2. 翻转每个单词中的字符顺序 27 | // 通过扫描空格来确定每个单词的起始 & 终止位置 28 | int start = 0; 29 | int end = 0; 30 | 31 | while (start < chars.length) { 32 | // 2.1 起始位置 = 空格时,继续往下扫描 33 | if (chars[start] == ' ') { 34 | start++; 35 | end++; 36 | 37 | // 2.2 终止位置 = 空格时,即可以开始反转单词 38 | } else if (end == chars.length || chars[end] == ' ') { 39 | reverse(chars, start, end - 1); 40 | end++; 41 | start = end; 42 | } else { 43 | end++; 44 | } 45 | } 46 | 47 | return String.valueOf(chars); 48 | 49 | } 50 | 51 | /** 52 | * 辅助算法:翻转句子 53 | */ 54 | private static char[] reverse(char[] chars, int start, int end){ 55 | if (chars == null || chars.length < 2){ 56 | return chars; 57 | } 58 | while (start < end){ 59 | char temp = chars[start]; 60 | chars[start] = chars[end]; 61 | chars[end] = temp; 62 | start++; 63 | end--; 64 | } 65 | return chars; 66 | } 67 | 68 | /** 69 | * 测试用例 70 | */ 71 | public static void main(String[] args) { 72 | 73 | // 功能测试:句子中多个单词 74 | String str1 = "I am a student."; 75 | System.out.println(reverseSentence(str1)); 76 | 77 | // 特殊输入测试:字符串指针为空 78 | System.out.println(reverseSentence(null)); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_58_1.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/24. 5 | */ 6 | 7 | public class Exam_58_1 { 8 | 9 | /** 10 | * 解题算法 11 | */ 12 | 13 | private static String leftRotate(String str, int index){ 14 | 15 | // 判断输入数据的合法性 16 | // a. 输入空指针时,会造成程序崩溃 17 | // b. 内存访问越界问题,即n < 0 ,那么指针指向的元素即不属于字符串 18 | if (str == null || str.length() < 2 || index < 0 ){ 19 | return ""; 20 | } 21 | 22 | // 转换成字符串数组便于处理 23 | char[] chars = str.toCharArray(); 24 | 25 | // 1. 翻转字符串中 需转移的字符 26 | reverse(chars,0,index - 1); 27 | 28 | // 2. 翻转字符串中 剩余的字符 29 | reverse(chars,index,chars.length - 1); 30 | 31 | // 3. 翻转整个字符串 32 | reverse(chars,0,chars.length - 1); 33 | 34 | // 返回翻转后的字符串 35 | return String.valueOf(chars); 36 | } 37 | 38 | /** 39 | * 辅助算法:翻转句子 40 | */ 41 | private static char[] reverse(char[] chars, int start, int end){ 42 | if (chars == null || chars.length < 2){ 43 | return chars; 44 | } 45 | while (start < end){ 46 | char temp = chars[start]; 47 | chars[start] = chars[end]; 48 | chars[end] = temp; 49 | start++; 50 | end--; 51 | } 52 | return chars; 53 | } 54 | 55 | /** 56 | * 测试用例 57 | */ 58 | public static void main(String[] args) throws Exception { 59 | // 功能测试 60 | String str1 = "abcdefg"; 61 | System.out.println(leftRotate(str1,2)); 62 | 63 | // 特殊输入测试:空指针 64 | System.out.println(leftRotate(null,2)); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_59.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Deque; 5 | import java.util.LinkedList; 6 | 7 | /** 8 | * Created by Carson_Ho on 17/11/24. 9 | */ 10 | 11 | public class Exam_59 { 12 | 13 | /** 14 | * 解题算法 15 | */ 16 | public static ArrayList findMaxInWindows(int[] num, int size){ 17 | 18 | // 使用一个list存储每个滑动窗口的结果 19 | ArrayList results = new ArrayList<>(); 20 | 21 | // 判断输入数据的合法性 22 | if (num == null || size < 1 || num.length < size){ 23 | System.out.println("输入数据不合法"); 24 | return results; 25 | } 26 | 27 | // 创建1个双端队列,队列存储顺序:大 -> 小,即: 28 | // 队头 存储 较大的元素 29 | // 队尾 存储 较小的元素 30 | Deque deque = new LinkedList<>(); 31 | 32 | // 1. 当滑动窗口还没完全滑进数组时 33 | for (int i = 0; i < size; i++){ 34 | // 若遍历的数m ≥ 队列队尾元素时 35 | // a. 不断出队 队尾元素、直到新的队尾元素 > m 或 队列为空 36 | // b. 将 m 入队 到队尾 37 | while (!deque.isEmpty() && num[i] >= num[deque.peekLast()]){ 38 | deque.pollLast(); 39 | } 40 | deque.addLast(i); 41 | } 42 | 43 | 44 | // 2. 当滑动窗口完全滑进数组时 45 | for (int i = size; i < num.length; i++){ 46 | 47 | // 把队头元素做为结果 48 | results.add(num[deque.peekFirst()]); 49 | // a. 若遍历的数m ≥ 队列队尾元素时,不断出队 队尾元素、直到新的队尾元素 > m 或 队列为空 50 | while (!deque.isEmpty() && num[i] >= num[deque.peekLast()]){ 51 | deque.pollLast(); 52 | } 53 | // b. 必须 判断队头元素 是否存在于 滑动窗口中 54 | // 若1个数字的下标 与 当前处理数字的下标之差 ≥ 滑动窗口的大小 55 | // 即代表该数字不在滑动窗口内 56 | // 即可从队列中删除该数据元素 57 | while (!deque.isEmpty() && deque.peekFirst() <= i - size){ 58 | deque.pollFirst(); 59 | } 60 | // 3. 将 m 入队 到队尾 61 | deque.addLast(i); 62 | } 63 | // 把最后的滑动窗口元素队头元素输出为结果 64 | results.add(num[deque.peekFirst()]); 65 | return results; 66 | } 67 | 68 | /** 69 | * 测试用例 70 | */ 71 | public static void main(String[] args) throws Exception { 72 | // 功能测试 73 | int[] array1 = {2, 3, 4, 2, 6, 2, 5, 1}; 74 | int size1 = 3; 75 | System.out.println(findMaxInWindows(array1,size1)); 76 | 77 | // 特殊输入测试 78 | System.out.println(findMaxInWindows(null,size1)); 79 | 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_6/Exam_6.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm.Exam_6; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Stack; 5 | 6 | /** 7 | * Created by Carson_Ho on 17/10/20. 8 | */ 9 | 10 | public class Exam_6 { 11 | 12 | /** 13 | * 结点结构 14 | */ 15 | static class ListNode { 16 | int val; 17 | ListNode next = null; 18 | 19 | ListNode(int val) { 20 | this.val = val; 21 | } 22 | 23 | } 24 | 25 | /** 26 | * 思路1:栈结构 27 | * @param head = 头结点 28 | */ 29 | private static ArrayList printListFromTailToHead1(ListNode head){ 30 | 31 | // 定义1链表结构用于存储结果 32 | ArrayList arrayList = new ArrayList<>(); 33 | 34 | // 1. 判断头结点是否为空 35 | if(head == null) 36 | return arrayList; 37 | 38 | ListNode cur = head; 39 | 40 | // 2. 声明用于存放 & 输出的栈 41 | Stack stack = new Stack<>(); 42 | 43 | // 3. 遍历链表(所有结点),每经过1个结点,就将该结点放入到栈中并指向下1个结点 44 | while(cur!=null){ 45 | stack.push(cur); 46 | cur = cur.next; 47 | } 48 | 49 | // 4. 遍历链表完毕后,从栈顶开始输出结点的值(放入到1个链表当中) 50 | while(!stack.isEmpty()){ 51 | arrayList.add(stack.pop().val); 52 | 53 | } 54 | 55 | return arrayList; 56 | 57 | } 58 | 59 | 60 | /** 61 | * 思路2:递归 62 | * @param listNode = 头结点 63 | */ 64 | 65 | // 定义1链表结构用于存储结果(注:为全局变量) 66 | ArrayList arrayList = new ArrayList<>(); 67 | 68 | // 解题算法 69 | public ArrayList printListFromTailToHead2(ListNode listNode) { 70 | 71 | if(listNode != null){ 72 | // 遍历链表:每访问1个结点,先递归输出它后面的结点,再输出该结点本身 73 | printListFromTailToHead2(listNode.next); 74 | arrayList.add(listNode.val); 75 | } 76 | return arrayList; 77 | } 78 | 79 | /** 80 | * 测试用例 81 | */ 82 | public static void main(String[] args) { 83 | 84 | // 功能测试 85 | ListNode head = new ListNode(67); 86 | ListNode node2 = new ListNode(0); 87 | ListNode node3 = new ListNode(24); 88 | ListNode node4 = new ListNode(58); 89 | head.next = node2; 90 | node2.next = node3; 91 | node3.next = node4; 92 | 93 | System.out.println(printListFromTailToHead1((head))); 94 | } 95 | 96 | 97 | } 98 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_61.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * Created by Carson_Ho on 17/11/29. 7 | */ 8 | 9 | public class Exam_61 { 10 | 11 | /** 12 | * 解题算法 13 | */ 14 | public static boolean isContinuous(int[] numbers) { 15 | 16 | // 判断输入数据的合法性 17 | if (numbers == null || numbers.length != 5) { 18 | return false; 19 | } 20 | 21 | // 1. 对元素进行排序 22 | Arrays.sort(numbers); 23 | 24 | int numberOfZero = 0; // 代表数组中0的个数 25 | int numberOfGap = 0;// 代表数组中数字间的空缺个数 26 | 27 | // 2. 统计数组中0的个数 28 | for (int i = 0; i < numbers.length && numbers[i] == 0; i++) { 29 | numberOfZero++; 30 | } 31 | 32 | // 3. 统计数组中空缺总数 33 | int small = numberOfZero; 34 | int big = small + 1; 35 | 36 | while (big < numbers.length) { 37 | // 若2个数相同,即存在对子,则不可能是顺子 38 | if (numbers[small] == numbers[big] && numbers[small] != 0 ) { 39 | return false; 40 | } 41 | 42 | // 计算空缺总数 43 | numberOfGap += (numbers[big] - numbers[small] - 1); 44 | small = big; 45 | big++; 46 | } 47 | 48 | // 比较 数组中空缺总数 与 0的个数 49 | // 若 空缺总数 > 0的个数,则该5个数字不连续 50 | // 若 空缺总数 < 0的个数,则该5个数字连续 51 | return numberOfGap <= numberOfZero; 52 | } 53 | 54 | /** 55 | * 测试用例 56 | */ 57 | public static void main(String[] args) { 58 | 59 | // 功能测试1:连续、不存在大、小王 60 | System.out.println("功能测试"); 61 | int[] numbers1 = {1, 3, 2, 5, 4}; 62 | System.out.println(isContinuous(numbers1)); 63 | 64 | // 功能测试2:不连续、不存在大、小王 65 | int[] numbers2 = {1, 3, 2, 6, 4}; 66 | System.out.println(isContinuous(numbers2)); 67 | 68 | // 功能测试2:存在大、小王 69 | int[] numbers3 = {1, 0, 2, 6, 4}; 70 | System.out.println(isContinuous(numbers3)); 71 | 72 | // 功能测试3:存在对子 73 | int[] numbers4 = {1, 3, 2, 2, 4}; 74 | System.out.println(isContinuous(numbers4)); 75 | 76 | // 特殊输入测试:输入为空指针 77 | System.out.println("特殊输入测试"); 78 | System.out.println(isContinuous(null)); 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_62.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by Carson_Ho on 17/11/29. 8 | */ 9 | 10 | public class Exam_62 { 11 | 12 | /** 13 | * 解题算法1:环形链表 14 | */ 15 | public static int lastRemaining(int n, int m) { 16 | 17 | // 判断输入数据的合法性 18 | if (n < 1 || m < 1) { 19 | return -1; 20 | } 21 | 22 | // 为链表添加数据 23 | List list = new LinkedList<>(); 24 | for (int i = 0; i < n; i++) { 25 | list.add(i); 26 | } 27 | 28 | int index = 0; // 要删除元素的位置 29 | int start = 0; // 开始计数的位置 30 | 31 | while (list.size() > 1) { 32 | 33 | // 只要移动(m-1)次就可移动到下1个要删除的元素上 34 | for (int i = 1; i < m; i++) { 35 | index = (index + 1) % list.size(); 36 | } 37 | 38 | list.remove(index); 39 | } 40 | 41 | return list.get(0); 42 | } 43 | 44 | /** 45 | * 解题算法2:数学分析法 46 | * 递归公式f(n,m) = [ f ( n-1,m ) + m ] % n 47 | * 可使用递归 & 循环实现,为了提高算法效率,采用 循环 实现 48 | */ 49 | 50 | public static int lastRemaining2(int n, int m) { 51 | 52 | // 检查输入数据的合法性 53 | if (n < 1 || m < 1) { 54 | return -1; 55 | } 56 | 57 | // 根据规律进行计算:f(n,m) = [ f ( n-1,m ) + m ] % n 58 | int last = 0; 59 | for (int i = 2; i <=n ; i++) { 60 | last = (last + m)%i; 61 | } 62 | 63 | return last; 64 | } 65 | 66 | /** 67 | * 测试用例 68 | */ 69 | 70 | public static void main(String[] args) { 71 | 72 | System.out.println(lastRemaining(5, 3)); 73 | System.out.println(lastRemaining2(5, 3)); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_63.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/29. 5 | */ 6 | 7 | public class Exam_63 { 8 | 9 | /** 10 | * 解题算法 11 | */ 12 | 13 | public static int maxDiff(int[] data){ 14 | 15 | // 判断输入数据的合法性 16 | if(data==null||data.length<2) 17 | return 0; 18 | 19 | int min = data[0]; // 存储遍历数组过程中,遍历过数字的最小值min 20 | int maxDiff = data[1] - min; // diff(i) = 当卖出价为 数组中第i个数字时 可能获得的最大利润 21 | 22 | if(data[1] maxDiff) 31 | maxDiff = data[i]-min; 32 | 33 | if( data[i] 0) && ((sum += Sum_Solution(n-1))>0)); 18 | return sum; 19 | } 20 | 21 | /** 22 | * 测试用例 23 | */ 24 | public static void main(String[] args){ 25 | // 功能测试:1+2+....+5、1+2+....+10 26 | System.out.println(Sum_Solution(5)); 27 | System.out.println(Sum_Solution(10)); 28 | 29 | // 特殊输入测试:0,1 30 | System.out.println(Sum_Solution(0)); 31 | System.out.println(Sum_Solution(1)); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_65.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/30. 5 | */ 6 | 7 | public class Exam_65 { 8 | 9 | /** 10 | * 解题算法 11 | */ 12 | public static int add(int x, int y) { 13 | int sum; // 记录二进制的每1位相加的和(不考虑进位) 14 | int carry;// 记录进位数的和 15 | 16 | do { 17 | // 1. 对2个数字的二进制的每1位相加 18 | // 实现方式 = 采用位运算的 异或 实现 19 | sum = x ^ y; 20 | 21 | // 2. 计算进位数 22 | // 实现方式 = 2个数先做位与运算,然后再向左移1位 23 | // 注:x & y 的某一位是1说明,它是它的前一位的进位,所以向左移动一位 24 | carry = (x & y) << 1; 25 | 26 | x = sum; 27 | y = carry; 28 | } while (y != 0); 29 | // 3. 将步骤2、3的结果相加,原理 同步骤2、3,直到不产生进位为止 30 | 31 | return x; 32 | } 33 | 34 | /** 35 | * 测试用例 36 | */ 37 | public static void main(String[] args){ 38 | // 功能测试:5+17 = 22 39 | System.out.println(add(5,17)); 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Algorithm/Exam_66.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Algorithm; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/30. 5 | */ 6 | 7 | public class Exam_66 { 8 | 9 | /** 10 | * 解题算法 11 | */ 12 | public static int[] multiply(int[] data) { 13 | 14 | // 判断输入数据的合法性 15 | if (data == null || data.length < 2) { 16 | return null; 17 | } 18 | 19 | int[] result = new int[data.length];// 定义数组用于存放结果,该数组即为B数组 20 | result[0] = 1;// B[0]初始化为1 21 | 22 | // 步骤1:求得数组C,存于result数组中 23 | // 即,C[ i ] = A[ 0 ] * A[ 1 ] ... A[ i-1 ] = C[ i-1 ] * A[ i-1 ] = 自上而下计算 24 | for (int i = 1; i < data.length; i++) { 25 | result[i] = result[i -1] * data[i - 1]; 26 | } 27 | 28 | // 步骤2:求得数组D,存于result数组中 29 | // 即,D[ i ] = A[ i+1 ] * ... A[ n-2 ] * A[ n-1 ] = D[ i+1 ] * A[ i+1 ] = 自下而上计算 30 | int tmp = 1; 31 | // 由于result[n-1]已计算,所以从data.length-2开始操作 32 | for (int i = data.length - 2; i >= 0; i--) { 33 | //计算数组D中的元素值 = D[ i+1 ] * A[ i+1 ] 34 | tmp *= data[i + 1]; 35 | // 步骤3:最终计算B[i] = C[i]*D[i] 36 | result[i] *= tmp; 37 | } 38 | 39 | return result; 40 | } 41 | 42 | /** 43 | * 测试用例 44 | */ 45 | public static void main(String[] args){ 46 | // 功能测试: 47 | int[] data = new int[]{1,2,3,4,5}; 48 | int[] result = multiply(data); 49 | for( int i=0;i { 54 | // T val; // 二叉树的结点数据 55 | // TreeNode leftNode; // 二叉树的左子树(左孩子) 56 | // TreeNode rightNode; // 二叉树的右子树(右孩子) 57 | // 58 | // public TreeNode(T data,TreeNode left,TreeNode right) { 59 | // this.val = data; 60 | // this.leftNode = left; 61 | // this.rightNode = right; 62 | // } 63 | // 64 | // 65 | // // 获得 & 设置二叉树的结点数据 66 | // public T getData(){ 67 | // return val; 68 | // } 69 | // 70 | // public void setData(T data){ 71 | // this.val = data; 72 | // } 73 | // 74 | // // 获得 & 设置二叉树的左子树(左孩子) 75 | // public TreeNode getLeftNode(){ 76 | // return leftNode; 77 | // } 78 | // 79 | // public void setLeftNode(TreeNode leftNode){ 80 | // this.leftNode = leftNode; 81 | // } 82 | // 83 | // // 获得 & 设置二叉树的右子树(右孩子) 84 | // public TreeNode getRightNode(){ 85 | // return rightNode; 86 | // } 87 | // public void setRightNode(TreeNode rightNode){ 88 | // this.rightNode = rightNode; 89 | // } 90 | //} 91 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Graph/GraphEdge.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Graph; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/9/30. 5 | * 作用:图的边 6 | */ 7 | 8 | public class GraphEdge { 9 | 10 | // 边的左端位置(下面会添加顶点) 11 | private GraphNode nodeLeft; 12 | 13 | // 边的右端位置(下面会添加顶点) 14 | private GraphNode nodeRight; 15 | 16 | 17 | // 设置边的左、右端顶点 18 | public GraphEdge(GraphNode nodeLeft, GraphNode nodeRight) { 19 | this.nodeLeft = nodeLeft; 20 | this.nodeRight = nodeRight; 21 | } 22 | 23 | // 获得顶点的左、右边 24 | public GraphNode getNodeLeft() { 25 | return nodeLeft; 26 | } 27 | public GraphNode getNodeRight() { 28 | return nodeRight; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Graph/GraphNode.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Graph; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by Carson_Ho on 17/9/30. 8 | * 作用:图的顶点 9 | */ 10 | 11 | public class GraphNode { 12 | 13 | public List edgeList ; 14 | 15 | private String label = ""; 16 | 17 | public GraphNode(String label) { 18 | this.label = label; 19 | edgeList = new ArrayList(); 20 | 21 | } 22 | 23 | // 作用:给当前顶点添加1条边(参数 = edge = 添加的边) 24 | public void addEdgeList(GraphEdge edge) { 25 | edgeList.add(edge); 26 | } 27 | 28 | public String getLabel() { 29 | return label; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/MainActivity.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | 6 | import scut.carson_ho.algorithmlearning.BinaryTree.BinaryTree_Recursion; 7 | import scut.carson_ho.algorithmlearning.BinaryTree.Node; 8 | import scut.carson_ho.algorithmlearning.Graph.MyGraph; 9 | 10 | public class MainActivity extends AppCompatActivity { 11 | 12 | private static final String TAG = "图的遍历算法:"; 13 | private static final String TAG1 = "图的遍历算法:"; 14 | private static final String TAG2 = "图的遍历算法:"; 15 | 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_main); 21 | 22 | BinaryTree_Recursion tree = new BinaryTree_Recursion(); 23 | Node root = tree.init(); 24 | 25 | /** 26 | * 前序遍历 27 | */ 28 | // Log.d(TAG, "前序遍历(递归)如下:"); 29 | // tree.preOrder(root); 30 | // Log.d(TAG, "前序遍历(非递归)如下:"); 31 | // tree.preOrder_stack(root); 32 | 33 | /** 34 | * 中序遍历 35 | */ 36 | // Log.d(TAG, "中序遍历(非递归)"); 37 | // tree.InOrder(root); 38 | // Log.d(TAG, "中序遍历(递归)"); 39 | // tree.InOrder_stack(root); 40 | 41 | /** 42 | * 后序遍历 43 | */ 44 | // Log.d(TAG, "后序遍历(递归)"); 45 | // tree.PostOrder(root); 46 | // Log.d(TAG, "后序遍历(非递归)"); 47 | // tree.PostOrder_stack(root); 48 | 49 | /** 50 | * 层序遍历 51 | */ 52 | // Log.d(TAG, "层序遍历(递归)"); 53 | // tree.levelTravel(root); 54 | 55 | /** 56 | * 执行深度优先遍历(DFS) - 递归 57 | */ 58 | 59 | // 步骤1: 初始化图的结构(顶点数量 = 9 60 | MyGraph g = new MyGraph(9); 61 | // 步骤2: 设置顶点数据 62 | char[] vertices = {'A','B','C','D','E','F','G','H','I'}; 63 | g.setVertices(vertices); 64 | 65 | // 步骤3: 设置边 66 | g.addEdge(0, 1); 67 | g.addEdge(0, 5); 68 | g.addEdge(1, 0); 69 | g.addEdge(1, 2); 70 | g.addEdge(1, 6); 71 | g.addEdge(1, 8); 72 | g.addEdge(2, 1); 73 | g.addEdge(2, 3); 74 | g.addEdge(2, 8); 75 | g.addEdge(3, 2); 76 | g.addEdge(3, 4); 77 | g.addEdge(3, 6); 78 | g.addEdge(3, 7); 79 | g.addEdge(3, 8); 80 | g.addEdge(4, 3); 81 | g.addEdge(4, 5); 82 | g.addEdge(4, 7); 83 | g.addEdge(5, 0); 84 | g.addEdge(5, 4); 85 | g.addEdge(5, 6); 86 | g.addEdge(6, 1); 87 | g.addEdge(6, 3); 88 | g.addEdge(6, 5); 89 | g.addEdge(6, 7); 90 | g.addEdge(7, 3); 91 | g.addEdge(7, 4); 92 | g.addEdge(7, 6); 93 | g.addEdge(8, 1); 94 | g.addEdge(8, 2); 95 | g.addEdge(8, 3); 96 | 97 | // // 步骤4: 执行 图的深度优先遍历(递归) 98 | // Log.d(TAG, "深度优先遍历(递归)"); 99 | // g.DFSTraverse(); 100 | 101 | // 步骤4: 执行 图的深度优先遍历(递归) 102 | System.out.print("广度优先遍历(非递归)"); 103 | System.out.print("广度优先遍历(非递归)"); 104 | // g.BFS(); 105 | 106 | 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/algorithmlearning/Search/BinarySearch.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning.Search; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/10/7. 5 | */ 6 | 7 | public class BinarySearch { 8 | 9 | /** 10 | * 二分查找方法 11 | * @param srcArray:有序数组 12 | * @param des:需要查找的元素 13 | */ 14 | public static int binarySearch(int[] srcArray, int des){ 15 | 16 | int low = 0; // 比较区间第1位 17 | int high = srcArray.length-1; // 比较区间最后1位 18 | int middle ; // 区间的中间位置 19 | 20 | 21 | while(low <= high) { 22 | 23 | // 1. 通过折半,求出区间的中间位置 24 | middle = (low + high)/2; 25 | 26 | // 2. 比较给定值和中间值 27 | // 2.1 若给定值 = 中间记录,则查找成功,返回该位置 28 | if(des == srcArray[middle]) { 29 | return middle; 30 | 31 | // 2.2 若给定值 < 中间记录,则 在中间记录的左半区 继续查找 32 | // 即 将比较区间的最后1位 设置为 原中间位置的前1位 33 | }else if(des 2 | 8 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/AlgorithmLearning/ebd613a6d98809f49fee998a2565696b2f30bf54/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/AlgorithmLearning/ebd613a6d98809f49fee998a2565696b2f30bf54/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/AlgorithmLearning/ebd613a6d98809f49fee998a2565696b2f30bf54/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/AlgorithmLearning/ebd613a6d98809f49fee998a2565696b2f30bf54/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/AlgorithmLearning/ebd613a6d98809f49fee998a2565696b2f30bf54/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/AlgorithmLearning/ebd613a6d98809f49fee998a2565696b2f30bf54/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/AlgorithmLearning/ebd613a6d98809f49fee998a2565696b2f30bf54/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/AlgorithmLearning/ebd613a6d98809f49fee998a2565696b2f30bf54/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/AlgorithmLearning/ebd613a6d98809f49fee998a2565696b2f30bf54/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/AlgorithmLearning/ebd613a6d98809f49fee998a2565696b2f30bf54/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Algorithm Learning 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/scut/carson_ho/algorithmlearning/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.algorithmlearning; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.3.1' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/AlgorithmLearning/ebd613a6d98809f49fee998a2565696b2f30bf54/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Sep 25 19:28:25 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /具体讲解/0~n-1中缺失的数字(53-2).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | 1个长度 = n-1 的递增排序数组中,所有数字都在范围 `0 ~ n-1` 内 & 唯一。在范围 `0 ~ n-1` 内的n个数字有且只有1个数字不在该数组中,请找出该数字。 3 | 4 | *** 5 | # 2. 考察点 6 | - 二分查找算法 7 | 8 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-de858f199f19afc6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 9 | 10 | - 知识迁移能力 11 | >根据实际问题特点,联想到已有解决方案 12 | 13 | *** 14 | # 3. 解题思路 15 | - 在 排序序列中 进行查找,**二分查找** 是最常用的解法 16 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-956e4f46554a938e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 17 | 18 | - 由于第2种解法效率更优,故下面主要介绍第2种解法 19 | 20 | 21 | 22 | *** 23 | # 4. 具体算法原理 & 步骤 24 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-a0904d9b86867a40.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 25 | 26 | 27 | 28 | 29 | *** 30 | # 5. 算法实现 31 | - 具体请看注释 32 | 33 | ``` 34 | public class Exam_53_1 { 35 | 36 | /** 37 | * 测试用例 38 | */ 39 | public static void main(String[] args){ 40 | // 功能测试:缺失的数字在开始、中间 & 结尾 41 | int[] data1 = new int[]{1,2,3,4,5,6}; 42 | int[] data2 = new int[]{0,1,3,4,5}; 43 | int[] data3 = new int[]{0,1,2,3,4,5}; 44 | System.out.println(getMissingNumber(data1)); 45 | System.out.println(getMissingNumber(data2)); 46 | System.out.println(getMissingNumber(data3)); 47 | 48 | // 边界值测试:数组中只有1个数字 = 0 49 | int[] data4 = new int[]{0}; 50 | System.out.println(getMissingNumber(data4)); 51 | 52 | // 特殊输入测试:数组为空 53 | System.out.println(getMissingNumber(null)); 54 | } 55 | 56 | 57 | /** 58 | * 解题算法 59 | */ 60 | public static int getMissingNumber(int[] data){ 61 | 62 | // 检查输入数据的合法性 63 | if(data == null || data.length == 0) 64 | return -1; 65 | 66 | int left = 0; 67 | int right = data.length-1; 68 | int mid; 69 | 70 | while (left<=right){ 71 | 72 | // 求中间元素 73 | mid = left+((right-left)>>1); 74 | // 注: 75 | // a. 用位运算替换原来的除法:mid=left+(right-left)/2 76 | // b. 加减法 优先级 > 位运算 77 | 78 | 79 | // 1.若中间元素值 ≠ 下标 80 | 81 | if(data[mid]!=mid) { 82 | // a. 前1元素 = 其下标,中间元素 = 第1个值与下标不相等的元素,即中间元素下标 = 数组中不存在的数字 83 | if (mid ==0 || data[mid-1]== mid-1) 84 | return mid; 85 | // b. 前1元素 ≠ 其下标,那么下一轮 只需在左半边查找 86 | right = mid-1; 87 | 88 | } 89 | // 2. 若中间元素值 = 下标,那么下一轮 只需在右半边查找 90 | else 91 | left = mid + 1; 92 | 93 | } 94 | 95 | if (left == data.length) 96 | return left; 97 | 98 | // 无效输入时返回-1,即数组不按要求排序 / 有数字不在0—~n-1的范围内 99 | return -1; 100 | } 101 | 102 | } 103 | ``` 104 | 105 | - 测试结果 106 | 107 | ``` 108 | 0 109 | 2 110 | 6 111 | 1 112 | -1 113 | ``` 114 | 115 | - Demo地址 116 | [Carson_Ho的Github地址:面试53.2 - 0~n-1中缺失的数字](https://github.com/Carson-Ho/AlgorithmLearning) 117 | -------------------------------------------------------------------------------- /具体讲解/1~n 整数中1出现的次数(43).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | 输入1个整数n,求 1至 n 这n个整数的十进制表示中出现1的次数 3 | >如:输入12 4 | >a. 1 至12 这12个整数中包含1的数字有1、10、11和12 5 | >b. 1一共出现了5次 6 | 7 | *** 8 | # 2. 考察点 9 | - 时间复杂度分析 10 | - 严密的数学思维能力 11 | >此题 = 数学规律题,通过分析具体例子找出通用规律 12 | 13 | *** 14 | # 3. 解题思路 15 | 16 | - 本题的思路有2种,具体介绍如下 17 | 18 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-b89c3645d85d3de7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 19 | 20 | - 由于第2种解题思路效率更优,故下面主要讲解该思路 21 | 22 | *** 23 | # 4. 算法示意图 24 | 举例说明:计算 百位上的1 25 | >个位、千位、万位 同理 26 | 27 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-20ae9038418fabad.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 28 | 29 | 30 | *** 31 | # 5. 算法实现 32 | 33 | - 具体请看注释 34 | 35 | ``` 36 | public class Exam_43 { 37 | 38 | /** 39 | * 解题算法:找规律 40 | */ 41 | public static int NumberOf1Between1AndN_Solution(int n){ 42 | 43 | // 1. 判断 输入数据的合法性 44 | if(n<=0) { 45 | System.out.println("输入的数据不合法"); 46 | return 0; 47 | } 48 | 49 | int count = 0 ;// 记录出现1的次数 50 | 51 | // 2. 通过遍历 进行记录 52 | for(int i = 1;i<=n;i*=10){ 53 | 54 | // 表示当前分析的是哪个数位(个、十、百...),分割该数位 55 | int a = n/i; // 高位 56 | int b = n%i; // 低位 57 | 58 | // 2.1 当 i 对应的数为1时 59 | if(a%10==1){ 60 | count = count+ (a+8)/10*i + (b+1); 61 | }else{ 62 | // 2.2 当i位对应的数为 ≥2 或 为0时 63 | count = count + (a+8)/10*i; 64 | } 65 | } 66 | return count; 67 | 68 | } 69 | 70 | /** 71 | * 测试用例 72 | */ 73 | public static void main(String[] args) { 74 | // 功能测试 75 | System.out.println(NumberOf1Between1AndN_Solution(12)); 76 | 77 | // 性能测试:数字很大 78 | System.out.println(NumberOf1Between1AndN_Solution(12222)); 79 | 80 | // 特殊输入测试 81 | System.out.println(NumberOf1Between1AndN_Solution(0)); 82 | } 83 | 84 | } 85 | ``` 86 | 87 | - 测试结果 88 | 89 | ``` 90 | 5 91 | 7976 92 | 输入的数据不合法 93 | 0 94 | ``` 95 | 96 | *** 97 | # 6. Demo地址 98 | [Carson_Ho的Github地址:面试43 - 1~n 整数中1出现的次数](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/不用加减乘除 实现 加法(65).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | 实现1个函数,功能 = 求2个整数的和 3 | >要求:函数体内不得使用`+`、`-`、`×`、`÷` 四则运算符号 4 | 5 | *** 6 | # 2. 考察点 7 | - 发散思维能力 8 | >在不使用四则运算的情况下解决加法问题 9 | - 二进制 & 位运算 10 | 11 | *** 12 | # 3. 解题思路 13 | ![解题思路](http://upload-images.jianshu.io/upload_images/944365-0347ea1343fa3ba4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 14 | 15 | 16 | 17 | *** 18 | # 4. 算法示意图 19 | - 如,求 5 加7 的和,即 5 + 17 = ? 20 | - 具体算法流程如下 21 | 22 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-d9925ca823f2f195.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 23 | 24 | 25 | *** 26 | # 5. 算法实现 27 | - 具体请看注释 28 | 29 | ``` 30 | public class Exam_65 { 31 | 32 | /** 33 | * 解题算法 34 | */ 35 | public static int add(int x, int y) { 36 | int sum; // 记录二进制的每1位相加的和(不考虑进位) 37 | int carry;// 记录进位数的和 38 | 39 | do { 40 | // 1. 对2个数字的二进制的每1位相加 41 | // 实现方式 = 采用位运算的 异或 实现 42 | sum = x ^ y; 43 | 44 | // 2. 计算进位数 45 | // 实现方式 = 2个数先做位与运算,然后再向左移1位 46 | // 注:x & y 的某一位是1说明,它是它的前一位的进位,所以向左移动一位 47 | carry = (x & y) << 1; 48 | 49 | x = sum; 50 | y = carry; 51 | } while (y != 0); 52 | // 3. 将步骤2、3的结果相加,原理 同步骤2、3,直到不产生进位为止 53 | 54 | return x; 55 | } 56 | 57 | /** 58 | * 测试用例 59 | */ 60 | public static void main(String[] args){ 61 | // 功能测试:5+17 = 22 62 | System.out.println(add(5,17)); 63 | 64 | } 65 | } 66 | ``` 67 | 68 | - 测试结果 69 | 70 | ``` 71 | 22 72 | ``` 73 | 74 | *** 75 | # 6. Demo地址 76 | [Carson_Ho的Github地址:面试65 - 不用加减乘除做加法](https://github.com/Carson-Ho/AlgorithmLearning) 77 | 78 | *** 79 | # 7. 相关题目 80 | - 问题描述 81 | 不使用新变量,交换2个变量的值。 82 | >如,交换 2个变量a、b 里的值 83 | 84 | - 解题思路 85 | 该解题思路有2种,具体如下: 86 | 87 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-22ea95542cec0b45.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 88 | 89 | -------------------------------------------------------------------------------- /具体讲解/丑数(49).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | 求按照从小到大的顺序第1500个丑数 3 | >1. 丑数的定义:只包含因子2、3、5的数 4 | >2. 第一个丑数 = 1 5 | *** 6 | 7 | # 2. 考察点 8 | - 时间复杂度的分析 9 | - 学习 & 沟通能力 10 | >对 丑数 的理解、询问面试官相关事宜 11 | 12 | *** 13 | # 3. 解题思路 14 | - 本题有2种解题思路,具体如下 15 | 16 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-ed07d455fc16bf5a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 17 | 18 | 19 | 20 | 21 | - 由于第2种解题思路更优,故下面主要讲解第2种解题思路 22 | 23 | *** 24 | # 4. 算法实现 25 | - 具体请看注释 26 | 27 | ``` 28 | public class Exam_49 { 29 | 30 | /** 31 | * 解题算法 32 | */ 33 | public static int getUglyNumber(int num){ 34 | // 检查数据的合法性 35 | if(num <= 0 ) 36 | return 0; 37 | 38 | // 因1-6都是丑数,所以当丑数数量<7个时直接返回数量 39 | if (num < 7) 40 | return num; 41 | 42 | // 1. 创建1数组 43 | // 作用:放置已经排序好的丑数 44 | // 长度:丑数的数量。如,为了得到第1500个丑数,则需要长度 = 1500的数组来记录已经计算出来的丑数 45 | int[] uglyNumber = new int[num]; 46 | 47 | uglyNumber[0] = 1; // 第1个丑数 = 1 48 | 49 | // 2. 定义1个指针指向第1个丑数1,3个指针 分别指向 前1个丑数乘2、乘3、乘5后的丑数 50 | int uglyIndex=0, multiply2=0, multiply3=0, multiply5=0; 51 | 52 | while (uglyIndex+1 n) 49 | for(ListNode cur = pHead1; cur != null; cur = cur.next) 50 | ++len1; 51 | for(ListNode cur = pHead2; cur != null; cur = cur.next) 52 | ++len2; 53 | 54 | // 2. 第2次遍历:在较长的链表先走(m-n)步,再同时在2个链表上遍历,找到的第1个相同节点即为2个链表的公共节点 55 | // 2.1 在较长的链表先走(m-n)步 56 | if(len1 > len2) { 57 | for(int i = 0; i < len1 - len2; ++i) 58 | cur1 = cur1.next; 59 | } 60 | else { 61 | for(int i = 0; i < len2 - len1; ++i) 62 | cur2 = cur2.next; 63 | } 64 | // 2.2 再同时在2个链表上遍历,找到的第1个相同节点即为2个链表的公共节点 65 | while(cur1 != null) { 66 | if(cur1 == cur2) 67 | return cur1; 68 | cur1 = cur1.next; 69 | cur2 = cur2.next; 70 | } 71 | return null; 72 | } 73 | 74 | /** 75 | * 测试用例 76 | */ 77 | 78 | public static void main(String[] args){ 79 | // 功能测试:二链表定义如下 80 | // 1->2->3->6->7 81 | // 4->5↗ 82 | ListNode node1 = new ListNode(1); 83 | ListNode node2 = new ListNode(2); 84 | ListNode node3 = new ListNode(3); 85 | ListNode node4 = new ListNode(4); 86 | ListNode node5 = new ListNode(5); 87 | ListNode node6 = new ListNode(6); 88 | ListNode node7 = new ListNode(7); 89 | node1.next = node2; 90 | node2.next = node3; 91 | node3.next = node6; 92 | node4.next = node5; 93 | node5.next = node6; 94 | node6.next = node7; 95 | 96 | ListNode commonNode = FindFirstCommonNode(node1,node4); 97 | System.out.println(commonNode.val); 98 | 99 | // 特殊输入测试:链表头节点 为空 100 | System.out.println(FindFirstCommonNode(null,null)); 101 | } 102 | } 103 | ``` 104 | 105 | - 测试结果 106 | 107 | ``` 108 | 6 109 | null 110 | ``` 111 | 112 | *** 113 | # 5. Demo地址 114 | [Carson_Ho的Github地址:面试52 - 两个链表的第一个公共节点](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/二叉搜索树最接近值查找.md: -------------------------------------------------------------------------------- 1 | # 题目 2 | 二叉搜索树最接近值查找 3 | 4 | # 思路 5 | 递归查找: 6 | 若当前节点值 < 目标值,则结果只可能是当前节点值 / 右子树中的值 7 | 若当前节点值 > 目标值,则结果只可能是当前节点值或者左子树中的值 8 | 9 | 10 | # 具体实现 11 | 12 | ``` 13 | public class Solution { 14 | 15 | public int closestValue(TreeNode root, double target) { 16 | if (root.val == target) return root.val; 17 | // 去右边查找 18 | if (root.val < target) { 19 | if (root.right == null) return root.val; 20 | int right = closestValue(root.right, target); 21 | if (Math.abs(root.val-target) <= Math.abs(right-target)) return root.val; 22 | return right; 23 | } else { 24 | // 去左边查找 25 | if (root.left == null) return root.val; 26 | int left = closestValue(root.left, target); 27 | if (Math.abs(root.val-target) <= Math.abs(left-target)) return root.val; 28 | return left; 29 | } 30 | } 31 | } 32 | ``` -------------------------------------------------------------------------------- /具体讲解/从尾到头打印链表(6).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | 输入 1个链表的头结点,从尾到头 反过来 打印出每个结点的值 3 | 4 | *** 5 | # 2. 考察点 6 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-2304cd7919879e47.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 7 | 8 | *** 9 | 10 | # 2. 解题思路 11 | - 该题的解题思路有3种: 12 | 13 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-4627deac92275680.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 14 | 15 | 16 | - 下面,将逐个讲解各个方法 17 | 18 | 19 | *** 20 | # 3. 算法实现 21 | - 具体请看注释 22 | 23 | ``` 24 | import java.util.ArrayList; 25 | import java.util.Stack; 26 | 27 | /** 28 | * Created by Carson_Ho on 17/10/20. 29 | */ 30 | 31 | public class Exam_6 { 32 | 33 | /** 34 | * 结点结构 35 | */ 36 | static class ListNode { 37 | int val; 38 | ListNode next = null; 39 | 40 | ListNode(int val) { 41 | this.val = val; 42 | } 43 | 44 | } 45 | 46 | /** 47 | * 思路1:栈结构 48 | * @param head = 头结点 49 | */ 50 | private static ArrayList printListFromTailToHead1(ListNode head){ 51 | 52 | // 定义1链表结构用于存储结果 53 | ArrayList arrayList = new ArrayList<>(); 54 | 55 | // 1. 判断头结点是否为空 56 | if(head == null) 57 | return arrayList; 58 | 59 | ListNode cur = head; 60 | 61 | // 2. 声明用于存放 & 输出的栈 62 | Stack stack = new Stack<>(); 63 | 64 | // 3. 遍历链表(所有结点),每经过1个结点,就将该结点放入到栈中并指向下1个结点 65 | while(cur!=null){ 66 | stack.push(cur); 67 | cur = cur.next; 68 | } 69 | 70 | // 4. 遍历链表完毕后,从栈顶开始输出结点的值(放入到1个链表当中) 71 | while(!stack.isEmpty()){ 72 | arrayList.add(stack.pop().val); 73 | 74 | } 75 | 76 | return arrayList; 77 | 78 | } 79 | 80 | 81 | /** 82 | * 思路2:递归 83 | * @param listNode = 头结点 84 | */ 85 | 86 | // 定义1链表结构用于存储结果(注:为全局变量) 87 | ArrayList arrayList = new ArrayList<>(); 88 | 89 | // 解题算法 90 | public ArrayList printListFromTailToHead2(ListNode listNode) { 91 | 92 | if(listNode != null){ 93 | // 遍历链表:每访问1个结点,先递归输出它后面的结点,再输出该结点本身 94 | printListFromTailToHead2(listNode.next); 95 | arrayList.add(listNode.val); 96 | } 97 | return arrayList; 98 | } 99 | 100 | /** 101 | * 测试用例 102 | */ 103 | public static void main(String[] args) { 104 | 105 | // 功能测试 106 | ListNode head = new ListNode(67); 107 | ListNode node2 = new ListNode(0); 108 | ListNode node3 = new ListNode(24); 109 | ListNode node4 = new ListNode(58); 110 | head.next = node2; 111 | node2.next = node3; 112 | node3.next = node4; 113 | 114 | System.out.println(printListFromTailToHead1((head))); 115 | } 116 | } 117 | ``` 118 | 119 | - 测试结果 120 | 121 | ``` 122 | [58, 24, 0, 67] 123 | ``` 124 | 125 | 126 | *** 127 | # 4. Demo地址 128 | [Carson_Ho的Github地址:面试6 - 从尾到头打印链表 ](https://github.com/Carson-Ho/AlgorithmLearning) 129 | 130 | 参考文章:http://blog.csdn.net/qq_25827845/article/details/71598318 -------------------------------------------------------------------------------- /具体讲解/判断2个二叉树是否一样.md: -------------------------------------------------------------------------------- 1 | ``` 2 | public boolean isSameTree(TreeNode p,TreeNode q){ 3 | if(p==null&&q==null){ 4 | return true; 5 | } 6 | if(p!=null&&q==null){ 7 | return false; 8 | } 9 | if(p==null&&q!=null){ 10 | return false; 11 | } 12 | if(p.val!=q.val){ 13 | return false; 14 | } 15 | return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right); 16 | } 17 | 18 | ``` 19 | 20 | 2)非递归方式 21 | 借助队列实现 22 | 23 | 实现算法: 24 | 25 | 首先将给定根节点pRoot1和pRoot2都入队 26 | 27 | 第一步:当两个队列未空时,分别获取两个树的当前层次中节点总数(即当前队列中节点个数),先比较节点个数是否相同,如果不相同,则两个树自然不同;如果节点个数相同,需要出队进行比较。如果有一个队列未空,则退出比较。 28 | 29 | 第二步:如果有一个队列未空,则清空队列并返回不同。 -------------------------------------------------------------------------------- /具体讲解/和为 s 的数字(57).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | - 输入1个递增排序的数组 & 1个数字s 3 | - 在数组中查找2个数,使它们的和为s 4 | >注:如果有多对和为s,输入任意一对即可 5 | 6 | - 如,输入的数组 = { 1,2,4,7,11,15 } 和数字15,则输出4、11 7 | 8 | *** 9 | # 2. 考察点 10 | 根据具体例子寻找规律 11 | 12 | *** 13 | # 3. 解题思路 14 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-2a9e02d30279cbc4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 15 | 16 | 注:还有1种解题思路,但效率不及上述思路高 17 | >1. 固定数组中1个数字,依次求出其余的 (n-1)个数与其的和,并进行判断 18 | >2. 时间效率 = O(n²) 19 | 20 | 21 | 22 | *** 23 | # 4. 算法示意图 24 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-b582c9a9ab58edf3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 25 | 26 | 27 | *** 28 | # 5. 算法实现 29 | - 具体请看注释 30 | 31 | ``` 32 | import java.util.ArrayList; 33 | 34 | public class Exam_57 { 35 | 36 | /** 37 | * 解题算法 38 | */ 39 | private static ArrayList findTwoNumberWithSum(int[] array, int sum){ 40 | 41 | ArrayList result = new ArrayList(); // 定义1个链表存储结果 42 | 43 | // 判断输入数据的合法性 44 | if (array == null || array.length < 2){ 45 | System.out.println("输入的数据不合法"); 46 | return result; 47 | } 48 | 49 | // 1. 定义2个指针 50 | // p1 = 指向数组的头数据元素 51 | // p2 = 指向数组的尾数据元素 52 | int start = 0; 53 | int end = array.length - 1; 54 | 55 | // 2. 计算2个指针指向元素的和(m) 56 | // 循环条件 57 | while (start < end){ 58 | 59 | int curSum = array[start] + array[end]; 60 | 61 | // 若m = 题目输入的s ,即2个指针指向的数据元素即为所求 62 | if (curSum == sum){ 63 | result.add(array[start]); 64 | result.add(array[end]); 65 | return result; 66 | 67 | // 若m > 题目输入的s,指针p2前移1位 68 | } else if (curSum < sum) { 69 | start++; 70 | // 若m < 题目输入的s,指针p1后移1位 71 | }else { 72 | end--; 73 | } 74 | } 75 | return result; 76 | } 77 | 78 | /** 79 | * 测试用例 80 | */ 81 | public static void main(String[] args) throws Exception { 82 | // 功能测试1:存在和为s的2个数 83 | int[] array1 = {1, 2, 4, 7, 11, 15}; 84 | System.out.println(findTwoNumberWithSum(array1,15)); 85 | 86 | // 功能测试2:不存在和为s的2个数 87 | int[] array2 = {1, 2, 3, 7, 11, 15}; 88 | System.out.println(findTwoNumberWithSum(array2,15)); 89 | 90 | // 特殊输入测试:数组为空 91 | System.out.println(findTwoNumberWithSum(null,15)); 92 | 93 | } 94 | 95 | } 96 | ``` 97 | 98 | - 测试结果 99 | 100 | ``` 101 | [4, 11] 102 | [] 103 | 输入的数据不合法 104 | [] 105 | 106 | ``` 107 | 108 | *** 109 | # 6. Demo地址 110 | [Carson_Ho的Github地址:面试57 - 和为 s 的数字](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/图:最小生成树算法-克鲁斯卡尔算法(Kruskal).md: -------------------------------------------------------------------------------- 1 | #算法概述 2 | 3 | 4 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-8eb6a97b8baad594.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 5 | 6 | 7 | 8 | # 算法对比 9 | 10 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-9260083dd53646ac.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -------------------------------------------------------------------------------- /具体讲解/图:最小生成树算法-普利姆算法(Prim).md: -------------------------------------------------------------------------------- 1 | # 算法概述 2 | 3 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-4d0553d96c30f5c6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 4 | 5 | # 算法原理流程示意图 6 | 7 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-f3e711d3517e0046.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 8 | 9 | # 举例说明 10 | 11 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-ed64bb93fc88b71d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -------------------------------------------------------------------------------- /具体讲解/图:最短路径算法.md: -------------------------------------------------------------------------------- 1 | #最短路径定义 2 | - 对于非网图(无权值),最短路径 = 两顶点间经过的边数最少的路径 3 | - 对于网图(有权值):最短路径 = 两顶点间经过的边上权值和最少的路径 4 | >第1个顶点 = 源点、第2个顶点 = 终点 5 | 6 | # 需解决的问题 7 | 从源点 -> 其余各顶点的最短路径 8 | 9 | # 寻找最短路径算法 10 | - 主要包括:迪杰斯特拉算法`(Dijkstra)`、弗洛伊德算法`(Floyd)` 11 | 12 | - 具体介绍如下 13 | 14 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-6fef2f991765a7e9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -------------------------------------------------------------------------------- /具体讲解/字符串的组合 : 子集(38-2).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | 输入1个字符串,打印出该字符串中字符的所有组合。 3 | >a. 假设字符串中的字符都不重复 4 | >b. 如,输入字符串 `abc`,则打印`a、b、c、ab、ac、bc、abc` 5 | *** 6 | # 2. 考察点 7 | - 分治法 8 | 即,将复制问题 **拆解成小问题 & 逐个解决的问题** 9 | - 对递归的理解 & 编程 10 | 11 | *** 12 | # 3. 解题思路 13 | - 与[算法题:字符串的排列](https://github.com/Carson-Ho/AlgorithmLearning/blob/master/%E5%85%B7%E4%BD%93%E8%AE%B2%E8%A7%A3/%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E6%8E%92%E5%88%97%EF%BC%8838%EF%BC%89.md)对比,排列的关键 = 次序,而组合的关键 = 状态,即该字符是否被选中进入组合中 14 | 15 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-8f2a037f9fdbae10.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 16 | 17 | *** 18 | 19 | # 4. 算法实现 20 | - 具体请看注释 21 | 22 | ``` 23 | import java.util.ArrayList; 24 | import java.util.LinkedList; 25 | import java.util.List; 26 | 27 | /** 28 | * Created by Carson_Ho on 17/11/9. 29 | */ 30 | 31 | public class Exam_38_2 { 32 | 33 | /** 34 | * 测试用例 35 | */ 36 | public static void main(String[] args) { 37 | 38 | // 功能测试:无重复字符 字符串 39 | System.out.println("功能测试1:无重复字符 字符串"); 40 | System.out.println(stringCombination("abc")); 41 | 42 | // 特殊输入测试:输入字符为空 43 | System.out.println("特殊输入测试:输入字符为空"); 44 | System.out.println(stringCombination(null)); 45 | 46 | } 47 | /** 48 | * 解题算法 49 | */ 50 | public static List stringCombination(String str) { 51 | // 1. 判断数据的合法性 52 | if(str == null || str.trim().length() == 0) { 53 | System.out.println("输入的字符串为空"); 54 | return new ArrayList<>(); 55 | } 56 | char chars[] = str.toCharArray();// 将字符串转换成数组便于处理 57 | StringBuilder sb = new StringBuilder(); 58 | int index = 0; 59 | 60 | // 2. 创建1链表用于存储排列 61 | List result = new LinkedList<>(); 62 | 63 | // 3. 通过 循环 求出字符串的组合 64 | for(int i = 1; i <= str.length(); ++i) { 65 | stringCombination(chars, i,result,sb,index); 66 | } 67 | 68 | return result; 69 | } 70 | 71 | private static void stringCombination(char[] chars, int length,List result,StringBuilder sb,int index) { 72 | if(length == 0) { 73 | result.add(sb.toString()); 74 | return; 75 | } 76 | if(chars.length - index < length) { 77 | return; 78 | } 79 | // 选择第一个,从剩下的中选择length-1个 80 | sb.append(chars[index]); 81 | ++index; 82 | stringCombination(chars, length - 1,result,sb,index); 83 | sb.deleteCharAt(sb.length() - 1); 84 | 85 | // 不选择第一个,从剩下的中选择length个 86 | stringCombination(chars, length,result,sb,index); 87 | --index; 88 | } 89 | } 90 | ``` 91 | 92 | - 测试结果 93 | 94 | ``` 95 | 功能测试:无重复字符 字符串 96 | [a, b, c, ab, ac, bc, abc] 97 | 98 | 特殊输入测试:输入字符为空 99 | 输入的字符串为空 100 | ``` 101 | 102 | *** 103 | # 5. Demo地址 104 | [Carson_Ho的Github地址:面试38-2 - 字符串的组合](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/实现单例模式(2).md: -------------------------------------------------------------------------------- 1 | 具体请看文章:[单例模式(Singleton)- 最易懂的设计模式解析](http://www.jianshu.com/p/b8c578b07fbc) 2 | 3 | # 1. 问题描述 4 | 设计1个类,只能生成该类的1个实例 5 | 6 | *** 7 | # 2. 考察点 8 | - 单例模式 9 | - `Java` 中的 多线程、静态构造函数 10 | 11 | # 3. 解题原理 12 | - 实现 单例模式有多种方式,根据需求场景,可分为2大类、6种实现方式。具体如下: 13 | 14 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-c1c4c00f4f9aa94d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 15 | 16 | 17 | >每种实现方式,具体请看文章[单例模式(Singleton)- 最易懂的设计模式解析](http://www.jianshu.com/p/b8c578b07fbc) 18 | 19 | 20 | - 在2种需求场景中,**枚举类型 & 静态内部类** 是最全面实现单例模式的实现方式 21 | >即 同时实现了:单例功能、线程安全、效率最优、实现简单 22 | 23 | - 下面将直接讲解该2种解题方式 24 | >在面试时,需提前问清楚面试官单例模式的需求场景,即问清楚 **单例创建时机是否需要控制、初始化速度 & 内存占用大小** 25 | 26 | *** 27 | # 4. 算法实现 28 | ### 4.1 需求场景 = 初始化单例类时即创建单例、初始化速度要求快 & 内存占存小 29 | 在该需求场景下,**枚举类实现** = 最好的单例实现方式 30 | 31 | - 原理 32 | 根据枚举类型的下述特点,满足单例模式所需的 **创建单例、线程安全、实现简洁的需求** 33 | 34 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-bdccdb7827be2eb8.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 35 | 36 | - 实现方式 37 | 38 | ``` 39 | public enum Singleton{ 40 | 41 | //定义1个枚举的元素,即为单例类的1个实例 42 | INSTANCE; 43 | 44 | // 隐藏了1个空的、私有的 构造方法 45 | // private Singleton () {} 46 | 47 | } 48 | 49 | // 获取单例的方式: 50 | Singleton singleton = Singleton.INSTANCE; 51 | ``` 52 | 53 | - 注:这是 **最简洁、易用** 的单例实现方式,借用`《Effective Java》 `的话: 54 | 55 | >单元素的枚举类型已经成为实现 `Singleton `的最佳方法 56 | 57 | 58 | 59 | ### 4.2 需求场景 = 按需、延迟创建单例、应用启动速度快 & 内存占存小 60 | 在该需求场景下,**静态内部类实现** = 最好的单例实现方式 61 | 62 | - 原理 63 | 根据 **静态内部类** 的特性,同时解决了按需加载、线程安全的问题,同时实现简洁 64 | >1. 在静态内部类里创建单例,在装载该内部类时才会去创建单例 65 | >2. 线程安全:类是由 `JVM`加载,而`JVM`只会加载1遍,保证只有1个单例 66 | 67 | - 具体实现 68 | 69 | ``` 70 | class Singleton { 71 | 72 | // 1. 创建静态内部类 73 | private static class Singleton2 { 74 | // 在静态内部类里创建单例 75 | private static Singleton ourInstance = new Singleton(); 76 | } 77 | 78 | // 私有构造函数 79 | private Singleton() { 80 | } 81 | 82 | // 延迟加载、按需创建 83 | public static Singleton newInstance() { 84 | return Singleton2.ourInstance; 85 | } 86 | 87 | } 88 | 89 | // 调用过程说明: 90 | // 1. 外部调用类的newInstance() 91 | // 2. 自动调用Singleton2.ourInstance 92 | // 2.1 此时单例类Singleton2得到初始化 93 | // 2.2 而该类在装载 & 被初始化时,会初始化它的静态域,从而创建单例; 94 | // 2.3 由于是静态域,因此只会JVM只会加载1遍,Java虚拟机保证了线程安全性 95 | // 3. 最终只创建1个单例 96 | ``` 97 | *** 98 | 99 | # 5. Demo地址 100 | [Carson_Ho的Github地址:面试2 - 实现单例模式](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/对称的二叉树(28).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | 判断1棵二叉树是否对称 3 | > 二叉树是否对称的判断规则:**若 二叉树与其镜像相同,即认为该二叉树对称** 4 | 5 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-94d846454b9cfc10.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 6 | 7 | *** 8 | # 2. 考察点 9 | - 学习、思维能力 10 | >即,对新概念(树的对称)的理解 & 解题思路的发现 11 | 12 | - 二叉树的遍历算法 13 | 14 | *** 15 | # 3. 解题思路 16 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-3c1da08dc83bfa3e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 17 | 18 | *** 19 | # 4. 算法实现 20 | 21 | ``` 22 | public class Exam_28 { 23 | 24 | /** 25 | * 设置结点结构 26 | */ 27 | 28 | public static class TreeNode { 29 | int val = 0; // 二叉树的结点数据 30 | TreeNode left = null; // 二叉树的左子树(左孩子) 31 | TreeNode right = null; // 二叉树的右子树(右孩子) 32 | 33 | public TreeNode(int val) { 34 | this.val = val; 35 | 36 | } 37 | 38 | } 39 | 40 | /** 41 | * 解题思路:比较 :左子树的左子树 = 右子树的右子树、左子树的右子树 = 右子树的左子树 42 | * 具体实现:递归 43 | * 注:非递归实现 = 队列 / 栈 44 | */ 45 | public static boolean isSymmetrical(TreeNode pRoot) { 46 | 47 | return compare(pRoot, pRoot); 48 | 49 | } 50 | 51 | public static boolean compare(TreeNode pRoot1, TreeNode pRoot2) { 52 | 53 | // 判断输入节点的合法性 54 | if (pRoot1 == null && pRoot2 == null) 55 | return true; 56 | 57 | if (pRoot1 == null || pRoot2 == null) { 58 | return false; 59 | } 60 | if (pRoot1.val != pRoot2.val) 61 | return false; 62 | 63 | // 通过递归 比较 :左子树的左子树 = 右子树的右子树、左子树的右子树 = 右子树的左子树 64 | return compare(pRoot1.left, pRoot2.right) && compare(pRoot1.right, pRoot2.left); 65 | } 66 | 67 | /** 68 | * 测试用例 69 | */ 70 | public static void main(String[] args){ 71 | // 构造测试二叉树1 72 | // 8 73 | // 6 6 74 | // 5 7 7 5 75 | TreeNode root1 = new TreeNode(8); 76 | root1.left = new TreeNode(6); 77 | root1.right = new TreeNode(6); 78 | root1.left.left = new TreeNode(5); 79 | root1.left.right = new TreeNode(7); 80 | root1.right.left = new TreeNode(7); 81 | root1.right.right = new TreeNode(5); 82 | System.out.println(isSymmetrical(root1)); 83 | 84 | // 构造测试二叉树2 85 | // 8 86 | // 6 9 87 | // 5 7 7 5 88 | TreeNode root2 = new TreeNode(8); 89 | root2.left = new TreeNode(6); 90 | root2.right = new TreeNode(9); 91 | root2.left.left = new TreeNode(5); 92 | root2.left.right = new TreeNode(7); 93 | root2.right.left = new TreeNode(7); 94 | root2.right.right = new TreeNode(5); 95 | 96 | System.out.println(isSymmetrical(root2)); 97 | 98 | } 99 | } 100 | ``` 101 | 102 | - 测试结果 103 | 104 | ``` 105 | true 106 | false 107 | ``` 108 | 109 | *** 110 | # 5. Demo地址 111 | [Carson_Ho的Github地址:面试28 - 对称的二叉树](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/扑克牌的顺子(61).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | 从 扑克牌中 随机抽5张牌,判断是不是1个顺子 3 | >a. 顺子的定义:5张牌连续 4 | >b. 注:2~10为数字本身;A = 1、 J = 11、Q = 12、 K = 13。小王可以看成任意数字。 5 | 6 | 7 | *** 8 | # 2. 考察点 9 | 抽象建模能力,即 **将现实中的扑克牌问题抽象成数学建模问题** 10 | 11 | *** 12 | # 3. 解题思路 13 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-e0a338e05fc2eed0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 14 | 15 | 16 | *** 17 | # 4. 算法实现 18 | - 具体请看注释 19 | 20 | ``` 21 | import java.util.Arrays; 22 | 23 | public class Exam_61 { 24 | 25 | /** 26 | * 解题算法 27 | */ 28 | public static boolean isContinuous(int[] numbers) { 29 | 30 | // 判断输入数据的合法性 31 | if (numbers == null || numbers.length != 5) { 32 | return false; 33 | } 34 | 35 | // 1. 对元素进行排序 36 | Arrays.sort(numbers); 37 | 38 | int numberOfZero = 0; // 代表数组中0的个数 39 | int numberOfGap = 0;// 代表数组中数字间的空缺个数 40 | 41 | // 2. 统计数组中0的个数 42 | for (int i = 0; i < numbers.length && numbers[i] == 0; i++) { 43 | numberOfZero++; 44 | } 45 | 46 | // 3. 统计数组中空缺总数 47 | int small = numberOfZero; 48 | int big = small + 1; 49 | 50 | while (big < numbers.length) { 51 | // 若2个数相同,即存在对子,则不可能是顺子 52 | if (numbers[small] == numbers[big] && numbers[small] != 0 ) { 53 | return false; 54 | } 55 | 56 | // 计算空缺总数 57 | numberOfGap += (numbers[big] - numbers[small] - 1); 58 | small = big; 59 | big++; 60 | } 61 | 62 | // 比较 数组中空缺总数 与 0的个数 63 | // 若 空缺总数 > 0的个数,则该5个数字不连续 64 | // 若 空缺总数 < 0的个数,则该5个数字连续 65 | return numberOfGap <= numberOfZero; 66 | } 67 | 68 | /** 69 | * 测试用例 70 | */ 71 | public static void main(String[] args) { 72 | 73 | // 功能测试1:连续、不存在大、小王 74 | System.out.println("功能测试"); 75 | int[] numbers1 = {1, 3, 2, 5, 4}; 76 | System.out.println(isContinuous(numbers1)); 77 | 78 | // 功能测试2:不连续、不存在大、小王 79 | int[] numbers2 = {1, 3, 2, 6, 4}; 80 | System.out.println(isContinuous(numbers2)); 81 | 82 | // 功能测试2:存在大、小王 83 | int[] numbers3 = {1, 0, 2, 6, 4}; 84 | System.out.println(isContinuous(numbers3)); 85 | 86 | // 功能测试3:存在对子 87 | int[] numbers4 = {1, 3, 2, 2, 4}; 88 | System.out.println(isContinuous(numbers4)); 89 | 90 | // 特殊输入测试:输入为空指针 91 | System.out.println("特殊输入测试"); 92 | System.out.println(isContinuous(null)); 93 | 94 | } 95 | } 96 | ``` 97 | 98 | - 测试结果 99 | 100 | ``` 101 | 功能测试 102 | true 103 | false 104 | false 105 | false 106 | 特殊输入测试 107 | false 108 | ``` 109 | 110 | *** 111 | # 5. Demo地址 112 | [Carson_Ho的Github地址:面试61 - 扑克牌的顺子](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/把数组的数字排成最小数(45).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | 输入1个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。 3 | >如,输入数组{3,32,321},则打印出这三个数字能排成的最小数字 = 321323 4 | 5 | *** 6 | # 2. 考察点 7 | - 思考出新的比较规则进行排序 8 | - 考虑大数问题 9 | >2个`int`型 10 | 11 | *** 12 | # 3. 解题思路 13 | 14 | ![示意图](https://upload-images.jianshu.io/upload_images/944365-2530075b49b99fed.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 15 | 16 | 17 | *** 18 | # 4. 算法流程 19 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-a103ad6e17213a63.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 20 | 21 | 22 | 23 | *** 24 | 25 | # 5. 算法实现 26 | - 具体请看注释 27 | 28 | ``` 29 | import java.util.Arrays; 30 | import java.util.Comparator; 31 | 32 | public class Exam_45 { 33 | 34 | /** 35 | * 解题算法 36 | */ 37 | public static String PrintMinNumber(int [] numbers) { 38 | 39 | // 检查输入数据的合法性 40 | if (numbers == null|| numbers.length < 0){ 41 | return "输入的数据不合法"; 42 | } 43 | 44 | // 1. 将整型数组 转换成 字符串数组,从而解决大数问题 45 | String[] str = new String[numbers.length]; 46 | 47 | for(int i = 0; i < numbers.length; i++){ 48 | str[i] = String.valueOf(numbers[i]); 49 | } 50 | 51 | // 2. 自定义比较规则 & 排序数组 52 | // 实现方式:通过Arrays.sort(String int[], Comparator)来定义 53 | // 原理:根据传入的 Comparator(可通过重写自定义)自定义排序规则,排序 数组元素 54 | // 注: 55 | // a. Comparator = 比较器 = 1个接口,通过实现这个接口重写compare(),可使用compareTo()比较两个对象的大小 56 | // b. 因为String类内部实现了该方法,故可直接用compareTo进行比较 57 | // c. 返回正值 = 大于 、返回0 = 等于、返回负值 = 小于。这样就可自定义排序规则 58 | // d. 系统函数默认 = 递增排序 59 | // e. jdk7中,集合通过Collections.sort()实现自定义比较器排序、数组通过Arrays.sort()实现自定义比较器排序 60 | Arrays.sort(str,new Comparator(){ 61 | @Override 62 | public int compare(String s1, String s2) { 63 | String c1 = s1 + s2; 64 | String c2 = s2 + s1; 65 | return c1.compareTo(c2); 66 | } 67 | }); 68 | 69 | 70 | // 3. 通过遍历字符串数组,拼接数组元素,从而成为最终最小的1个数字 71 | StringBuilder sb = new StringBuilder(); // 用于存储拼接后的数字 72 | 73 | for(int i = 0; i < numbers.length; i++){ 74 | sb.append(str[i]); 75 | } 76 | 77 | // 最终反馈 78 | return sb.toString(); 79 | } 80 | 81 | /** 82 | * 测试用例 83 | */ 84 | public static void main(String[] args) { 85 | int[] data = {3,32,321}; 86 | 87 | // 功能测试 88 | System.out.println(PrintMinNumber(data)); 89 | 90 | // 特殊输入测试 91 | System.out.println(PrintMinNumber(null)); 92 | } 93 | } 94 | ``` 95 | 96 | - 测试结果 97 | 98 | ``` 99 | 321323 100 | 输入的数据不合法 101 | ``` 102 | 103 | *** 104 | # 6. Demo地址 105 | [Carson_Ho的Github地址:面试45 - 把数组排成最小的数](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/排序算法:希尔排序.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | *** 4 | # 目录 5 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-5a6e20b0f3dd56cb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 6 | 7 | 8 | *** 9 | 10 | # 1. 简介 11 | - **也称:缩小增量 排序**,属于 内排序算法中 的 **插入排序类别** 12 | - 是对 **直接插入排序算法** 的优化和升级 13 | 14 | *** 15 | 16 | # 2. 算法原理 17 | 18 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-5639bc34294a4402.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 19 | 20 | 21 | 22 | 23 | *** 24 | 25 | # 3. 算法示意图 26 | 27 | 步骤1:初始状态 28 | ![](https://upload-images.jianshu.io/upload_images/944365-a4c771628cf53466.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 29 | 30 | 31 | 步骤2:跳跃分割 & 排序 32 | ![](https://upload-images.jianshu.io/upload_images/944365-10661f39e95b17db.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 33 | 34 | 35 | *** 36 | # 4. 算法实现 37 | 38 | - 具体请看注释 39 | 40 | ``` 41 | public class ShellSort { 42 | /** 43 | * 希尔排序 44 | */ 45 | public static void shellSort(int[] srcArray) { 46 | 47 | int j = 0; 48 | int temp = 0; 49 | 50 | // 增量序列值 计算公式 = 前1个增量序列值 / 2,直到增量序列值 = 1为止 51 | // 第1个值 = 初始值 = 序列长度 / 2 52 | for (int increment = srcArray.length / 2; increment > 0; increment /= 2) { 53 | 54 | // 根据增量值选取子序列 55 | for (int i = increment; i < srcArray.length; i++) { 56 | 57 | temp = srcArray[i]; 58 | 59 | // 对子序列执行直接插入排序,即 循环两两比较子序列的值 60 | for (j = i - increment; j >= 0; j -= increment) { 61 | 62 | if (temp < srcArray[j]) { 63 | 64 | // 将小的元素放到前面、大的元素放到后面 65 | srcArray[j + increment] = srcArray[j]; 66 | } else { 67 | break; 68 | } 69 | } 70 | srcArray[j + increment] = temp; 71 | } 72 | 73 | 74 | // 输出 根据增量值排序后的序列 75 | System.out.println("增量值为:" + increment + ",排序结果如下:"); 76 | for (int a = 0; a < srcArray.length; a++) { 77 | System.out.println(srcArray[a]); 78 | } 79 | } 80 | } 81 | 82 | /** 83 | * 执行 希尔排序 84 | */ 85 | public static void main(String[] args) { 86 | 87 | // 定义待排序数列 88 | int[] src = new int[]{ 4, 3, 6, 2, 7, 1, 5, 8 }; 89 | 90 | // 输出结果 91 | shellSort(src); 92 | 93 | } 94 | 95 | } 96 | ``` 97 | 98 | - 测试结果 99 | 100 | ``` 101 | 增量值为:4,排序结果如下: 102 | 4 103 | 1 104 | 5 105 | 2 106 | 7 107 | 3 108 | 6 109 | 8 110 | 增量值为:2,排序结果如下: 111 | 4 112 | 1 113 | 5 114 | 2 115 | 6 116 | 3 117 | 7 118 | 8 119 | 增量值为:1,排序结果如下: 120 | 1 121 | 2 122 | 3 123 | 4 124 | 5 125 | 6 126 | 7 127 | 8 128 | ``` 129 | 130 | - `Demo`地址 131 | 132 | [Carson_Ho的Github地址:希尔排序](https://github.com/Carson-Ho/AlgorithmLearning) 133 | 134 | *** 135 | # 5. 性能分析 136 | 以下将分析算法的性能:时间复杂度、空间复杂度、稳定性 137 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-af421d7dd6af730b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 138 | -------------------------------------------------------------------------------- /具体讲解/排序算法:直接插入排序.md: -------------------------------------------------------------------------------- 1 | #前言 2 | 本文主要讲解排序算法中的直接插入算法,希望你们会喜欢。 3 | 4 | *** 5 | # 目录 6 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-e60dd03fc4bd6ea2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 7 | 8 | 9 | *** 10 | 11 | # 1. 简介 12 | 属于 内排序算法中 的 **插入排序类别** 13 | 14 | *** 15 | # 2. 算法原理 16 | 1. 将 1个待排序的数据 按顺序大小 插入到 1个已排序的序列中 17 | 2. 重复上述步骤,直到全部插入 & 排序完为止 18 | 19 | *** 20 | # 3. 算法示意图 21 | 22 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-e473c0475f7b157b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 23 | 24 | 25 | *** 26 | # 4. 算法实现 27 | 28 | - 具体请看注释 29 | 30 | ``` 31 | public class InsertSort { 32 | /** 33 | * 简单选择排序 34 | */ 35 | public static void insertSort(int[] srcArray) { 36 | 37 | int i; // 用于存放当前插入数据记录的数组下标 38 | int j; // 用于存放需要比较记录的下标 39 | int temp; // 用于交换数据 40 | 41 | // 1. 从第1个数据记录 开始,该元素可以认为已经被排序 42 | for(i = 0 ; i < srcArray.length ; i++) 43 | { 44 | temp = srcArray[i]; 45 | 46 | // 2. 取出下一个数据记录,在已经排序的序列中从后向前扫描 47 | // 3. 将 当前数据记录 与 前面排序好的值进行比较 48 | for(j = i ; j > 0 && temp < srcArray[j-1] ; j --) 49 | { 50 | // 4. 按照顺序小 -> 大 将 当前需要插入的数据记录插入到合适位置 = 后移已排序好的元素 + 插入新的数据记录 51 | // a. 后移已排序好的元素 52 | srcArray[j] = srcArray[j-1]; 53 | } 54 | 55 | // b. 插入新的数据记录 56 | srcArray[j] = temp; 57 | } 58 | 59 | // 5. 输出排序后的序列 60 | for(int a =0;a`n` = 表长,`i` = 当前位置 12 | 13 | 1. 比较 第 `i` 个记录 & 剩余的 (`n-i`)个记录 14 | 2. 在`(n - i +1)`个记录中,选择最小的记录 15 | 3. 将最小的记录 与 第 `i` 个记录进行交换 16 | 17 | 重复上述过程,直到 `i` 到序列最后1个元素比较后 结束。 18 | 19 | 20 | 21 | *** 22 | 23 | # 3. 算法示意图 24 | 25 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-dc78e66930c78eb7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 26 | 27 | *** 28 | 29 | # 4. 算法实现 30 | - 具体请看注释 31 | 32 | ``` 33 | public class ChooseSort { 34 | 35 | /** 36 | * 简单选择排序 37 | */ 38 | public static void chooseSort(int[] srcArray) { 39 | 40 | int i; // 用于存放当前数组下标 41 | int j; // 用于存放需要比较记录的下标 42 | 43 | for(i=0;i srcArray[j]){ 50 | int temp=srcArray [i]; 51 | srcArray[i] = srcArray[j]; 52 | srcArray[j] = temp; 53 | } 54 | } 55 | } 56 | 57 | 58 | // 输出排序后的序列 59 | for(int a =0;a如,第5位 = 5,第13位 = 1,第19位 = 4 5 | 6 | *** 7 | # 2. 考察点 8 | - 时间复杂度分析 9 | - 严密的数学思维能力 10 | >此题 = 数学规律题,通过分析具体例子找出通用规律 11 | 12 | *** 13 | # 3. 解题思路 14 | 15 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-4e53abfae130ecd6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 16 | 17 | 18 | *** 19 | # 4. 算法示意图 20 | 21 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-c2703dfb62284458.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 22 | 23 | 24 | 25 | *** 26 | # 5. 算法实现 27 | - 具体请看注释 28 | 29 | ``` 30 | public class Exam_44 { 31 | 32 | /** 33 | * 解题算法 34 | */ 35 | 36 | public static int digitAtIndex(int index){ 37 | // 判断输入数据的合法性 38 | if(index<0) { 39 | System.out.println("输入的数据不合法"); 40 | return -1; 41 | } 42 | 43 | /** 44 | * 1. 确定该数字 属于几位数 45 | * 若 n < (10 + 2*9*(10^i) ),那么该数字 属于 i 位数 46 | */ 47 | // 个位数 48 | if(index<10) 49 | return index; 50 | // 2位以上 51 | int curIndex = 10; 52 | int length = 2; // 表示多少位数 53 | int boundNum = 10; 54 | 55 | while (curIndex + lengthSum(length)1. 额外要求:时间复杂度 = O(n)、空间复杂度 = O(1) 5 | >2. 如,输入数组 = { 2,4,3,6,3,2,5,5 },则输出4、6 6 | 7 | *** 8 | # 2. 考察点 9 | 二进制 & 位运算 10 | 11 | *** 12 | # 3. 解题思路 13 | 14 | 15 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-98d6ba57068fcc18.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 16 | 17 | 18 | *** 19 | # 4. 算法示意图 20 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-22cd34918e25c494.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 21 | 22 | 23 | *** 24 | 25 | # 5. 算法实现 26 | - 具体请看注释 27 | 28 | ``` 29 | public class Exam_56 { 30 | 31 | /** 32 | * 解题算法 33 | * @param array 题目数组 34 | * @param num1 存储第1个子数组的异或结果,即其中1个出现了1次的数字 35 | * @param num2 存储第2个子数组的异或结果,即其中1个出现了1次的数字 36 | */ 37 | public static void findTwoOnceNumber(int[] array,int num1[] , int num2[]){ 38 | 39 | // 判断输入数据的合法性 40 | if (array == null || array.length < 2){ 41 | System.out.println("数组为空或者长度不足"); 42 | return; 43 | } 44 | 45 | // 1. 将原数组分成2个子数组 46 | // 1.1 从头到尾 依次异或 数组中的每个数字 47 | int orResult = 0; 48 | for (int i : array){ 49 | orResult ^= i; 50 | } 51 | // 1.2 在上述结果找出第1个= 1的位置,记为 第 n 位 52 | int indexOf1 = findFirstBitIs1(orResult); 53 | 54 | // 1.3 以二进制的第n位是否为1,将原数组的数字分为2个子数组 55 | 56 | for (int i : array){ 57 | if (isBit1(i,indexOf1)){ 58 | // 2. 分别从头到尾 依次异或 2个子数组中的每个数字 59 | num1[0] ^= i; 60 | }else { 61 | num2[0] ^= i; 62 | } 63 | } 64 | System.out.println("只出现一次的数字是:" + num1[0] + "和" + num2[0]); 65 | 66 | } 67 | 68 | /** 69 | * 辅助算法:寻找输入数字在二进制表示中,从右到左第1位是1的位置 70 | * @param i 输入的数 71 | * @return 第一个位是1的位置 72 | */ 73 | private static int findFirstBitIs1(int i){ 74 | int index = 0; 75 | while (((i & 1) == 0) && index < 32){ 76 | i = i >> 1; 77 | index++; 78 | } 79 | return index; 80 | } 81 | 82 | /** 83 | * 辅助算法:判断该数字的第index位是否为1 84 | * @param number 输入的数 85 | * @param index 位置 86 | * @return 是否为1 87 | */ 88 | private static boolean isBit1(int number, int index){ 89 | number = number >> index; 90 | return (number & 1) == 1; 91 | } 92 | 93 | /** 94 | * 测试用例 95 | */ 96 | public static void main(String[] args){ 97 | int[] array1 = {2, 4, 3, 6, 3, 2, 5, 5}; 98 | int[] num1 = new int[1]; 99 | int[] num2 = new int[1]; 100 | findTwoOnceNumber(array1,num1,num2); 101 | } 102 | } 103 | ``` 104 | 105 | - 测试结果 106 | 107 | ``` 108 | 只出现一次的数字是:6和4 109 | ``` 110 | 111 | *** 112 | # 6. Demo地址 113 | [Carson_Ho的Github地址:面试56 - 数组中只出现一次的两个数字](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/数组中唯一出现一次的数字(56-1).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | - 1个整数数组中,有1个数字出现1次,其他数字都出现3次 3 | - 请找出这个出现1次的数字 4 | >1. 额外要求:时间复杂度 = O(n)、空间复杂度 = O(1) 5 | >2. 如,输入数组 = { 2,4,2,5,2,5,5 },则输出4 6 | 7 | *** 8 | # 2. 考察点 9 | - 二进制 位运算 10 | - 知识迁移能力 11 | ># 即,是否能从上题获得提示 12 | 13 | *** 14 | # 3. 解题思路 15 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-afd8216bf5ea088d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 16 | 17 | 18 | - 此外还有2种解题思路,但性能却不如上述思路。 19 | a. 从排序数组中寻找,但排序需时间效率 = `O(nlogn)` 20 | b. 用哈希表记录数组中数字出现的次数,但需空间 = 哈希表 = `O(n)` 21 | 22 | 23 | *** 24 | 25 | # 4. 算法实现 26 | - 具体请看注释 27 | 28 | ``` 29 | public class Exam_56_1 { 30 | 31 | /** 32 | * 解题算法 33 | */ 34 | public static int findOnceNumber(int[] intArray) throws Exception { 35 | 36 | // 判断输入数据的合法性 37 | if (intArray == null || intArray.length < 1) { 38 | System.out.println("数组为空或者长度不足"); 39 | return 0; 40 | } 41 | 42 | // 将和的结果存储到1个大小 = 32的数组中 43 | int[] bitSum = new int[32]; 44 | 45 | // 1. 将数组所有数字的二进制表示的每1位都加起来 46 | // 将和的结果都放到数组中 47 | for (int i : intArray) { 48 | 49 | int bitMask = 1; 50 | 51 | for (int j = 31; j >= 0; --j){ 52 | int bit = i & bitMask; 53 | if (bit != 0){ 54 | bitSum[j] += 1; 55 | } 56 | bitMask = bitMask << 1; 57 | } 58 | } 59 | 60 | //2. 对每1位的和 都对3取余 61 | int result = 0; 62 | for (int i = 0; i < 32; i++){ 63 | result = result << 1; 64 | // 若能被3整除(余数 = 0),则说明包含该位数的数字出现了3次,即 只出现1次的数字二进制表示中对应那位 = 0 65 | // 若不能被3整除(余数 = 1),则说明包含该位数的数字出现了1次,即 只出现1次的数字二进制表示中对应那位 = 1 66 | result += bitSum[i] % 3; 67 | } 68 | return result; 69 | } 70 | 71 | /** 72 | * 测试用例 73 | */ 74 | public static void main(String[] args) throws Exception { 75 | int[] array1 = {2,4,2,5,2,5,5}; 76 | System.out.println("数组中唯一出现一次数字是:" + findOnceNumber(array1)); 77 | 78 | } 79 | } 80 | ``` 81 | 82 | - 测试结果 83 | 84 | ``` 85 | 数组中唯一出现一次数字是:4 86 | ``` 87 | 88 | *** 89 | # 5. Demo地址 90 | [Carson_Ho的Github地址:面试56_1 - 数组中唯一出现一次的数字](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/机器人的运动范围(13).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-6fbd5d5cc1590fd0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 3 | 4 | *** 5 | # 2. 考察点 6 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-f2ffeeebe6493927.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 7 | 8 | *** 9 | # 3. 解题思路 10 | - 本题目主要采用 **回溯法**,具体介绍如下 11 | 12 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-49f16a3ce02c2e02.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 13 | 14 | - 将回溯法应用于本题中,思路如下: 15 | 16 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-2f6e8c4da3f66e0a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 17 | 18 | # 本题实现思路非常类似 :面试题12:矩阵中的最短路径 19 | 20 | *** 21 | 22 | # 4. 算法实现 23 | - 具体请看注释 24 | 25 | ``` 26 | public class Exam_13 { 27 | 28 | /** 29 | * 解题算法 30 | * 实现原理 = 递归 31 | * @param threshold = 临界值K 32 | * @param rows = 矩阵行数 33 | * @param cols = 矩阵列数 34 | * @return boolean = 矩阵中是否存在要寻找的字符串路径 35 | */ 36 | public static int movingCount(int threshold, int rows, int cols) { 37 | // 定义1个矩阵,用于标识路径是否已进入每个格子 38 | // 0表示未访问、1表示访问过 39 | int flag[][] = new int[rows][cols]; 40 | //通过计算到达格子次数 41 | return helper(0, 0, rows, cols, flag, threshold); 42 | } 43 | 44 | /** 45 | * 辅助算法:计算到达格子次数 实现 46 | * 实现原理 = 递归 47 | */ 48 | private static int helper(int i, int j, int rows, int cols, int[][] flag, int threshold) { 49 | // 1. 需判断机器人是否可以进入当前坐标 50 | // 判断条件 = 横纵坐标数位之和是否>k,小于则可以进入 51 | if (i < 0 || i >= rows || j < 0 || j >= cols || numSum(i) + numSum(j) > threshold || flag[i][j] == 1) return 0; 52 | // 若可进入,则标记该位置已被访问 53 | flag[i][j] = 1; 54 | // 通过递归,继续向4个方向走 55 | return (1+helper(i - 1, j, rows, cols, flag, threshold) 56 | + helper(i + 1, j, rows, cols, flag, threshold) 57 | + helper(i, j - 1, rows, cols, flag, threshold) 58 | + helper(i, j + 1, rows, cols, flag, threshold) 59 | ); 60 | } 61 | 62 | /** 63 | * 辅助算法:计算横、纵坐标数位用于相加的值 64 | */ 65 | public static int numSum(int number){ 66 | int sum=0; 67 | while (number>0){ 68 | sum += number%10; 69 | number/=10; 70 | } 71 | return sum; 72 | } 73 | 74 | /** 75 | * 测试用例 76 | */ 77 | 78 | public static void main(String[] args){ 79 | // 测试1:3行4列、k = 0 80 | System.out.println(movingCount(0,3,4)); // 1 81 | 82 | // 测试2:3行4列、k = 1 83 | System.out.println(movingCount(1,3,4)); // 3 84 | 85 | // 测试3:2行20列、k = 9 86 | System.out.println(movingCount(9,2,20)); // 36 87 | } 88 | } 89 | ``` 90 | 91 | - 测试结果 92 | 93 | ``` 94 | 1 95 | 3 96 | 36 97 | ``` 98 | 99 | *** 100 | # 5. Demo地址 101 | [Carson_Ho的Github地址:面试13 - 机器人的运动范围](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/构建乘积数组(66).md: -------------------------------------------------------------------------------- 1 | 2 | # 1. 问题描述 3 | 给定1个数组A = [ 0,1...n-1 ],求另1个数组B = [ 0,1...n-1 ] 4 | >a. 定义:B[ i ] = A[0] * A[1] ... A[i-1] * A[i+1] ... A[n-1],即B[ i ] = A数组中除了 A[ i ] 以外的所有数乘积 5 | >b. 要求:不能使用除法 6 | 7 | *** 8 | # 2. 考察点 9 | - 发散思维能力 10 | - 对数组的理解 & 编程 11 | 12 | 13 | *** 14 | # 3. 解题思路 15 | - 本题的解题思路有3种,具体介绍如下 16 | 17 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-a9ca802bc3545ed8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 18 | 19 | - 由于第3种思路效率最优,故下面主要介绍第3种解题思路 20 | 21 | *** 22 | # 4. 算法原理 23 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-22a5f8f32e8104f6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 24 | 25 | 26 | 27 | 28 | *** 29 | # 5. 算法实现 30 | - 具体请看注释 31 | 32 | ``` 33 | public class Exam_66 { 34 | 35 | /** 36 | * 解题算法 37 | */ 38 | public static int[] multiply(int[] data) { 39 | 40 | // 判断输入数据的合法性 41 | if (data == null || data.length < 2) { 42 | return null; 43 | } 44 | 45 | int[] result = new int[data.length];// 定义数组用于存放结果,该数组即为B数组 46 | result[0] = 1;// B[0]初始化为1 47 | 48 | // 步骤1:求得数组C,存于result数组中 49 | // 即,C[ i ] = A[ 0 ] * A[ 1 ] ... A[ i-1 ] = C[ i-1 ] * A[ i-1 ] = 自上而下计算 50 | for (int i = 1; i < data.length; i++) { 51 | result[i] = result[i -1] * data[i - 1]; 52 | } 53 | 54 | // 步骤2:求得数组D,存于result数组中 55 | // 即,D[ i ] = A[ i+1 ] * ... A[ n-2 ] * A[ n-1 ] = D[ i+1 ] * A[ i+1 ] = 自下而上计算 56 | int tmp = 1; 57 | // 由于result[n-1]已计算,所以从data.length-2开始操作 58 | for (int i = data.length - 2; i >= 0; i--) { 59 | //计算数组D中的元素值 = D[ i+1 ] * A[ i+1 ] 60 | tmp *= data[i + 1]; 61 | // 步骤3:最终计算B[i] = C[i]*D[i] 62 | result[i] *= tmp; 63 | } 64 | 65 | return result; 66 | } 67 | 68 | /** 69 | * 测试用例 70 | */ 71 | public static void main(String[] args){ 72 | // 功能测试: 73 | int[] data = new int[]{1,2,3,4,5}; 74 | int[] result = multiply(data); 75 | for( int i=0;i1. 假设压入栈的所有数字均不相等。 4 | >2. 如,压入序列 = {1,2,3,4,5},序列{ 4,5,3,2,1 } = 其弹出序列,而{ 4,3,5,1,2 }则不是。 5 | *** 6 | # 2. 考察点 7 | - 栈的理解 & 编程 8 | - 分析复杂问题的能力:通过 **举例** 寻找规律 9 | >由于本文 = 讲解类型文章,故将直接抛出 结论规律 10 | 11 | *** 12 | # 3. 解题思路 13 | - 对于1个给定的压入序列,由于弹出时机不同,会出现多种弹出序列 14 | - 若需找到之间的规律,只需 **复现栈的压入、弹出过程即可** 15 | - 下面,将直接给出 判断规则: 16 | 1. 根据 压入序列、弹出序列,入栈一定元素到辅助栈 17 | 2. 通过 判断栈顶元素是否与 弹出序列 相同等规则进行判断 弹出序列是否 是该栈的弹出序列 ,具体规则如下: 18 | 19 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-ebdb616a78d5a32a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 20 | 21 | 22 | 23 | 24 | 25 | *** 26 | # 4. 算法示意图 27 | 下面,将通过例子演示该算法 28 | >设压入序列 = {1,2,3,4,5},请判断 序列{ 4,5,3,2,1 } 和 { 4,3,5,1,2 } 是否是其压出序列 29 | 30 | - 实例1:请判断 序列{ 4,5,3,2,1 } 是否是其压出序列 31 | 32 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-3e3f87e8945a1ccd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 33 | 34 | - 实例2:请判断 序列{ 4,3,5,1,2 }是否是其压出序列 35 | 36 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-961e0573a94dd3e5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 37 | 38 | 39 | 40 | 41 | 42 | *** 43 | 44 | # 5. 算法实现 45 | - 具体请看注释 46 | 47 | ``` 48 | import java.util.Stack; 49 | 50 | public class Exam_31 { 51 | 52 | /** 53 | * 解题算法 54 | */ 55 | public static boolean isPopOrder(int[] pushSeq,int[] popSeq){ 56 | 57 | // 判断输入数据的合法性:传入参数是否为null,压入序列与弹出序列长度是否一致 58 | if(pushSeq == null || popSeq == null|| pushSeq.length != popSeq.length) 59 | return false; 60 | 61 | // 设置2指针分别指向压入序列、弹出序列 62 | int pushSeqIndex=0,popSeqIndex=0; 63 | 64 | // 创建1个栈 用于辅助判断 65 | Stack stack = new Stack<>(); 66 | 67 | // 若指向弹出序列的指针已到序列结尾,则代表被测序列 = 弹出序列 68 | // 即,循环跳出 69 | while (popSeqIndex < popSeq.length){ 70 | 71 | // 若栈顶元素 ≠ 弹出序列指针指向元素时,入栈 压栈序列指针指向的元素,指针后移1位 72 | if(stack.isEmpty()||stack.peek()!=popSeq[popSeqIndex]) { 73 | if(pushSeqIndex < pushSeq.length ) { 74 | stack.push(pushSeq[pushSeqIndex]); 75 | pushSeqIndex++; 76 | 77 | }else 78 | // 若指向压栈序列的指针p1已到序列结尾,则代表被测序列 ≠ 弹出序列 79 | return false; 80 | } 81 | // 否则,出栈 栈顶元素,弹出序列指针向后移1位 82 | else{ 83 | stack.pop(); 84 | popSeqIndex++; 85 | } 86 | } 87 | return true; 88 | } 89 | 90 | /** 91 | * 测试用例 92 | */ 93 | public static void main(String[] args){ 94 | // 压入序列 95 | int[] push = {1,2,3,4,5}; 96 | 97 | // 判断弹出序列 98 | int[] pop1 = {4,5,3,2,1}; 99 | int[] pop2 = {4,3,5,1,2}; 100 | System.out.println(isPopOrder(push,pop1)); 101 | System.out.println(isPopOrder(push,pop2)); 102 | // 特殊输入测试 103 | System.out.println(isPopOrder(null,null)); 104 | } 105 | 106 | 107 | } 108 | ``` 109 | 110 | - 测试结果 111 | 112 | ``` 113 | true 114 | false 115 | false 116 | ``` 117 | 118 | *** 119 | # 6. Demo地址 120 | [Carson_Ho的Github地址:面试31 - 栈的压入、弹出序列](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/求1+2+....+n(64).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | 求 `1+2+...+n` 的和 3 | >要求:不能使用 4 | >- 乘除法 5 | >- 关键词:for、while、if、else、switch、case等 6 | >- 条件判断语句: A?B:C 7 | 8 | *** 9 | # 2. 考察点 10 | 思维方式 11 | 12 | *** 13 | # 3. 解题思路 14 | 15 | - 使用 **递归求和** 16 | - 具体实现方式 17 | 18 | ``` 19 | // 核心 = 使用短路与 && 20 | boolean a = (递归判断条件b)&& ((递归执行语句c ) > 0) 21 | 22 | // 解释: 23 | // 布尔类型变量 a:无实际的用途,仅为使表达式完成 24 | // 递归判断条件b:当b不满足时,递归结束(原理:后面采用了短路与&&,即只有前1个条件满足时才可以去判断执行第2个条件) 25 | // 递归执行语句c:递归时需执行的语句,即累加 26 | ``` 27 | 28 | >注: 限制了关键词( `for`、`while`)的使用 = 限制了 循环 的使用 29 | 30 | *** 31 | # 4. 算法实现 32 | - 具体请看注释 33 | 34 | ``` 35 | public class Exam_64 { 36 | 37 | /** 38 | * 测试用例 39 | */ 40 | public static void main(String[] args){ 41 | // 功能测试:1+2+....+5、1+2+....+10 42 | System.out.println(Sum_Solution(5)); 43 | System.out.println(Sum_Solution(10)); 44 | 45 | // 特殊输入测试:0,1 46 | System.out.println(Sum_Solution(0)); 47 | System.out.println(Sum_Solution(1)); 48 | } 49 | 50 | 51 | /** 52 | * 解题算法 53 | */ 54 | public static int Sum_Solution(int n) 55 | { 56 | int sum = n; 57 | // 递归的结束条件:n=0 58 | // 递归执行语句:累加 59 | boolean s = ((n > 0) && ((sum += Sum_Solution(n-1))>0)); 60 | return sum; 61 | } 62 | 63 | } 64 | ``` 65 | 66 | - 测试结果 67 | 68 | ``` 69 | 15 70 | 55 71 | 0 72 | 1 73 | ``` 74 | 75 | *** 76 | # 5. Demo地址 77 | [Carson_Ho的Github地址:面试64 - 求1+2+....+n](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/翻转字符串 之 左旋转字符串(58-1).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | 实现1个函数,功能 = 左旋转 字符串 3 | >a. 左旋转:把字符串前面的n个字符转移到字符串的尾部 4 | >b. 如,输入 `abcdefg` 和 数字2,即把字符串前面的2个字符(`ab`)转移到字符串的尾部,即输出 = `cdefgab` 5 | 6 | *** 7 | # 2. 考察点 8 | - 字符串编程 9 | - 知识迁移能力 10 | ># 即根据 翻转字符串的思路 本题的解决思路 11 | 12 | 13 | 14 | *** 15 | # 3. 解题思路 16 | 以 把字符串(`abcdefg`)前面的2个字符(`ab`)转移到字符串的尾部 为例 17 | 18 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-640c91c1b5d86a42.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 19 | 20 | 21 | *** 22 | # 4. 算法实现 23 | - 具体请看注释 24 | 25 | ``` 26 | public class Exam_58_1 { 27 | 28 | /** 29 | * 解题算法 30 | */ 31 | 32 | private static String leftRotate(String str, int index){ 33 | 34 | // 判断输入数据的合法性 35 | // a. 输入空指针时,会造成程序崩溃 36 | // b. 内存访问越界问题,即n < 0 ,那么指针指向的元素即不属于字符串 37 | if (str == null || str.length() < 2 || index < 0 ){ 38 | return ""; 39 | } 40 | 41 | // 转换成字符串数组便于处理 42 | char[] chars = str.toCharArray(); 43 | 44 | // 1. 翻转字符串中 需转移的字符 45 | reverse(chars,0,index - 1); 46 | 47 | // 2. 翻转字符串中 剩余的字符 48 | reverse(chars,index,chars.length - 1); 49 | 50 | // 3. 翻转整个字符串 51 | reverse(chars,0,chars.length - 1); 52 | 53 | // 返回翻转后的字符串 54 | return String.valueOf(chars); 55 | } 56 | 57 | /** 58 | * 辅助算法:翻转句子 59 | */ 60 | private static char[] reverse(char[] chars, int start, int end){ 61 | if (chars == null || chars.length < 2){ 62 | return chars; 63 | } 64 | while (start < end){ 65 | char temp = chars[start]; 66 | chars[start] = chars[end]; 67 | chars[end] = temp; 68 | start++; 69 | end--; 70 | } 71 | return chars; 72 | } 73 | 74 | /** 75 | * 测试用例 76 | */ 77 | public static void main(String[] args) throws Exception { 78 | // 功能测试 79 | String str1 = "abcdefg"; 80 | System.out.println(leftRotate(str1,2)); 81 | 82 | // 特殊输入测试:空指针 83 | System.out.println(leftRotate(null,2)); 84 | } 85 | } 86 | ``` 87 | 88 | - 测试结果 89 | 90 | ``` 91 | cdefgab 92 | "" 93 | ``` 94 | 95 | *** 96 | # 5. Demo地址 97 | [Carson_Ho的Github地址:面试58_1 - 左旋转字符串](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/翻转字符串 之 翻转单词顺序(58).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | 输入1个英文句子,翻转单词顺序、单词内字符不翻转,标点符号 & 3 | 普通字母一样处理 4 | >如,输入 英文句子 = “I am a student.”,则输出“student. a am I” 5 | 6 | *** 7 | # 2. 考察点 8 | 字符串编程 9 | 10 | *** 11 | # 3. 解题思路 12 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-8715979838ef90d2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 13 | 14 | *** 15 | # 4. 算法实现 16 | - 具体请看注释 17 | 18 | ``` 19 | public class Exam_58 { 20 | 21 | /** 22 | * 解题算法 23 | */ 24 | public static String reverseSentence(String str){ 25 | 26 | // 判断输入数据的合法性 27 | if (str == null || str.length() < 2){ 28 | System.out.println("输入的数据不合法"); 29 | return str; 30 | } 31 | 32 | // 转换成字符串数组便于处理 33 | char[] chars = str.toCharArray(); 34 | 35 | // 1. 翻转句子中所有字符顺序 36 | reverse(chars,0,chars.length - 1); 37 | 38 | // 2. 翻转每个单词中的字符顺序 39 | // 通过扫描空格来确定每个单词的起始 & 终止位置 40 | int start = 0; 41 | int end = 0; 42 | 43 | while (start < chars.length) { 44 | // 2.1 起始位置 = 空格时,继续往下扫描 45 | if (chars[start] == ' ') { 46 | start++; 47 | end++; 48 | 49 | // 2.2 终止位置 = 空格时,即可以开始反转单词 50 | } else if (end == chars.length || chars[end] == ' ') { 51 | reverse(chars, start, end - 1); 52 | end++; 53 | start = end; 54 | } else { 55 | end++; 56 | } 57 | } 58 | 59 | return String.valueOf(chars); 60 | 61 | } 62 | 63 | /** 64 | * 辅助算法:翻转句子 65 | */ 66 | private static char[] reverse(char[] chars, int start, int end){ 67 | if (chars == null || chars.length < 2){ 68 | return chars; 69 | } 70 | while (start < end){ 71 | char temp = chars[start]; 72 | chars[start] = chars[end]; 73 | chars[end] = temp; 74 | start++; 75 | end--; 76 | } 77 | return chars; 78 | } 79 | 80 | /** 81 | * 测试用例 82 | */ 83 | public static void main(String[] args) { 84 | 85 | // 功能测试:句子中多个单词 86 | String str1 = "I am a student."; 87 | System.out.println(reverseSentence(str1)); 88 | 89 | // 特殊输入测试:字符串指针为空 90 | System.out.println(reverseSentence(null)); 91 | } 92 | 93 | } 94 | ``` 95 | 96 | - 测试结果 97 | 98 | ``` 99 | student. a am I 100 | 输入的数据不合法 101 | null 102 | ``` 103 | 104 | *** 105 | # 5. Demo地址 106 | [Carson_Ho的Github地址:面试58 - 翻转单词顺序](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/股票的最大利润(63).md: -------------------------------------------------------------------------------- 1 | # 1. 问题描述 2 | 把某股票的价格按照 时间先后顺序 存储在数组中,求买卖股票一次能获得的最大利润 3 | >如,输入数组 = {9,11,8,5,7,12,16,14};买入价 = 5,卖出价 = 16,则能获得最大利润 = 11 4 | 5 | *** 6 | # 2. 考察点 7 | - 数组的编程 8 | - 抽象建模能力 9 | >将股票买卖的实际问题 抽象 成建模能力 10 | 11 | *** 12 | # 3. 解题思路 13 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-5a7c3bc27f83814e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 14 | 15 | 16 | 17 | *** 18 | # 4. 算法实现 19 | - 具体请看注释 20 | 21 | ``` 22 | public class Exam_63 { 23 | 24 | /** 25 | * 解题算法 26 | */ 27 | 28 | public static int maxDiff(int[] data){ 29 | 30 | // 判断输入数据的合法性 31 | if(data==null||data.length<2) 32 | return 0; 33 | 34 | int min = data[0]; // 存储遍历数组过程中,遍历过数字的最小值min 35 | int maxDiff = data[1] - min; // diff(i) = 当卖出价为 数组中第i个数字时 可能获得的最大利润 36 | 37 | if(data[1] maxDiff) 46 | maxDiff = data[i]-min; 47 | 48 | if( data[i]即,考虑无效输入、合适处理 12 | 13 | *** 14 | # 3. 解题思路 15 | - 本题目解题思路有2种 16 | 17 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-b100a0ea1b9c2b01.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 18 | 19 | 20 | - 由于第2种效率较高,故下面主要讲解第2种 21 | 22 | *** 23 | 24 | # 4. 算法实现 25 | - 具体请看注释 26 | 27 | ``` 28 | public class Exam_42 { 29 | 30 | /** 31 | * 解题算法:动态规划 32 | */ 33 | public static int findGreatestSumOfSumArrays(int[] array){ 34 | // 1. 判断输入数据的合法性 35 | if(array==null || array.length==0){ 36 | System.out.println("输入的数组为空"); 37 | return 0; 38 | } 39 | 40 | // 2. 定义所需变量 41 | int dp = array[0]; // dp[i]:以array[i]为结尾元素的连续数组的最大和(初始化为第1个元素) 42 | int maxdp = dp; // maxdp:当前dp的最大值 43 | 44 | // 3. 通过遍历数组进行动态规划 45 | for(int i=1;i < array.length;i++){ 46 | 47 | // 3.1 当以 第( i - 1) 个数字结尾的子数组中所有数字的和 > 0时,最大值 = 等于 二者的和 48 | if( dp>0 ) 49 | dp += array[i]; 50 | else 51 | // 3.2 当以 第( i - 1) 个数字结尾的子数组中所有数字的和 < 0时,最大值 = 其本身 52 | dp = array[i]; 53 | // 3.3 当前dp的最大值 54 | if(dp > maxdp) 55 | maxdp = dp; 56 | } 57 | return maxdp; 58 | } 59 | 60 | /** 61 | * 测试用例 62 | */ 63 | public static void main(String[] args){ 64 | 65 | // 功能测试:数组中无相同数字 66 | int[] array = {1,-2,3,10,-4,7,2,-5}; 67 | System.out.println(findGreatestSumOfSumArrays(array)); 68 | 69 | // 特殊输入测试:数组为空 70 | int[] array2 = null; 71 | System.out.println(findGreatestSumOfSumArrays(array2)); 72 | 73 | 74 | } 75 | 76 | } 77 | 78 | ``` 79 | 80 | - 测试结果 81 | 82 | ``` 83 | 18 84 | 输入的数组为空 85 | ``` 86 | 87 | *** 88 | # 5. Demo地址 89 | [Carson_Ho的Github地址:面试42 - 连续子数组的最大和](https://github.com/Carson-Ho/AlgorithmLearning) -------------------------------------------------------------------------------- /具体讲解/面试技巧.md: -------------------------------------------------------------------------------- 1 | # 1. 总体思想 2 | 面试考察内容,会根据面试者的工作能力而有所不同 3 | 4 | | 能力 | 考察内容 | 5 | | ------------- |:-------------:| 6 | | 初级 | 偏**基础** = 编程语言 + 数据结构 + 算法 | 7 | | 高级 | 偏**专业深度** = 专业技能 + 项目 | 8 | 9 | *** 10 | # 2. 面试的形式 11 | - 面试的形式有3种:电话面试、共享桌面远程面试 & 现场面试 12 | - 具体如下 13 | 14 | ![示意图](http://upload-images.jianshu.io/upload_images/944365-c33e8fb57a026eb7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 15 | 16 | *** 17 | # 3. 面试的环节 18 | - 面试的环节有3种:行为面试、技术面试 & 应聘者提问 19 | - 具体如下 20 | 21 | 22 | ![面试环节](http://upload-images.jianshu.io/upload_images/944365-34623a65eb7f2c2f.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 23 | 24 | # 遇到不懂的问题时 25 | - 把劣势变成优势,让面试官看到你能力闪亮的一面 26 | >1. “这个问题我之前没有接触过,但是类似这样的一个问题,我的解题思路是……我的整体思考过程是……” 27 | >2. 换个角度解答:“我对这个问题不是很熟悉,但我对相应另外一个技术我很了解。” 28 | 29 | - 体现你的好学、谦虚品质 30 | >1. “其实我也不懂,您能不能给个解题思路,教教我,可以吗?”这时候本官一般都会给一些tips 31 | >2. 千万不要回答“哦这样啊”就完了,要沿着面试官的tips继续说下去,当然,最好能够举一反三 --------------------------------------------------------------------------------