├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.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 │ │ │ └── layout │ │ │ │ └── activity_main.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── scut │ │ │ └── carson_ho │ │ │ └── shootatoffer │ │ │ ├── Exam_64.java │ │ │ ├── Exam_41Test.java │ │ │ ├── MainActivity.java │ │ │ ├── Exam_65.java │ │ │ ├── Exam_2.java │ │ │ ├── Exam_43.java │ │ │ ├── Exam_50.java │ │ │ ├── Exam_63.java │ │ │ ├── Exam_42.java │ │ │ ├── Exam_41Solution.java │ │ │ ├── Exam_66.java │ │ │ ├── Exam_56_1.java │ │ │ ├── Exam_15.java │ │ │ ├── Exam_58_1.java │ │ │ ├── Exam_4.java │ │ │ ├── Exam_46.java │ │ │ ├── Exam_62.java │ │ │ ├── Exam_1.java │ │ │ ├── Exam_31.java │ │ │ ├── Exam_10.java │ │ │ ├── Exam_44.java │ │ │ ├── Exam_57.java │ │ │ ├── Exam_53_1.java │ │ │ ├── Exam_13.java │ │ │ ├── Exam_49.java │ │ │ ├── Exam_38_2.java │ │ │ ├── Exam_58.java │ │ │ ├── Exam_45.java │ │ │ ├── Exam_50_2.java │ │ │ ├── Exam_53_2.java │ │ │ ├── Exam_56.java │ │ │ ├── Exam_61.java │ │ │ ├── Exam_11.java │ │ │ ├── Exam_6 │ │ │ └── Exam_6.java │ │ │ ├── Exam_47.java │ │ │ ├── Exam_59.java │ │ │ ├── Exam_22.java │ │ │ ├── Exam_52.java │ │ │ ├── Exam_57_1.java │ │ │ ├── Exam_30.java │ │ │ ├── Exam_5.java │ │ │ ├── Exam_24.java │ │ │ ├── Exam_55.java │ │ │ ├── Exam_33.java │ │ │ ├── Exam_54.java │ │ │ ├── Exam_16.java │ │ │ ├── Exam_17.java │ │ │ ├── Exam_12.java │ │ │ ├── Exam_27.java │ │ │ ├── Exam_51.java │ │ │ ├── Exam_14.java │ │ │ ├── Exam_25.java │ │ │ ├── Exam_21.java │ │ │ ├── Exam_8.java │ │ │ ├── Exam_28.java │ │ │ ├── Exam_7.java │ │ │ ├── Exam_34.java │ │ │ ├── Exam_53.java │ │ │ ├── Exam_26.java │ │ │ ├── Exam_9.java │ │ │ ├── Exam_40.java │ │ │ ├── Exam_48.java │ │ │ ├── Exam_37.java │ │ │ ├── Exam_23.java │ │ │ ├── Exam_38.java │ │ │ ├── Exam_55_1.java │ │ │ ├── Exam_60.java │ │ │ ├── Exam_29.java │ │ │ ├── Exam_35.java │ │ │ ├── Exam_59_1.java │ │ │ ├── Exam_67.java │ │ │ ├── Exam_68.java │ │ │ ├── Exam_19.java │ │ │ └── Exam_36.java │ ├── test │ │ └── java │ │ │ └── scut │ │ │ └── carson_ho │ │ │ └── shootatoffer │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── scut │ │ └── carson_ho │ │ └── shootatoffer │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── .idea ├── copyright │ └── profiles_settings.xml ├── vcs.xml ├── modules.xml ├── runConfigurations.xml ├── gradle.xml ├── compiler.xml └── misc.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties └── gradlew.bat /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Shoot At Offer 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/ShootAtOffer/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/ShootAtOffer/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/ShootAtOffer/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/ShootAtOffer/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/ShootAtOffer/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/ShootAtOffer/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/ShootAtOffer/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/ShootAtOffer/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/ShootAtOffer/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/ShootAtOffer/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Carson-Ho/ShootAtOffer/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /.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/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 18 20:21:29 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 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/scut/carson_ho/shootatoffer/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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 | } -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/shootatoffer/Exam_64.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/30. 5 | */ 6 | 7 | public class Exam_64 { 8 | 9 | /** 10 | * 解题算法 11 | */ 12 | public static int Sum_Solution(int n) 13 | { 14 | int sum = n; 15 | // 递归的结束条件:n=0 16 | // 递归执行语句:累加 17 | boolean s = ((n > 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/shootatoffer/Exam_41Test.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/androidTest/java/scut/carson_ho/shootatoffer/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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.shootatoffer", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/MainActivity.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | 6 | public class MainActivity extends AppCompatActivity { 7 | 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | setContentView(R.layout.activity_main); 12 | 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 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_65.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/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/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.shootatoffer" 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/src/main/java/scut/carson_ho/shootatoffer/Exam_2.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/12/4. 5 | */ 6 | 7 | public class Exam_2 { 8 | 9 | // /** 10 | // * 枚举类型 11 | // */ 12 | // public enum Singleton{ 13 | // 14 | // //定义1个枚举的元素,即为单例类的1个实例 15 | // INSTANCE; 16 | // 17 | // // 隐藏了1个空的、私有的 构造方法 18 | // // private Singleton () {} 19 | // 20 | // } 21 | // 22 | // // 获取单例的方式: 23 | // Singleton singleton = Singleton.INSTANCE; 24 | // 25 | // /** 26 | // * 静态内部类 27 | // */ 28 | // class Singleton { 29 | // 30 | // // 1. 创建静态内部类 31 | // private static class Singleton2 { 32 | // // 在静态内部类里创建单例 33 | // private static Singleton ourInstance = new Singleton(); 34 | // } 35 | // 36 | // // 私有构造函数 37 | // private Singleton() { 38 | // } 39 | // 40 | // // 延迟加载、按需创建 41 | // public static Singleton newInstance() { 42 | // return Singleton2.ourInstance; 43 | // } 44 | // 45 | // } 46 | 47 | // 调用过程说明: 48 | // 1. 外部调用类的newInstance() 49 | // 2. 自动调用Singleton2.ourInstance 50 | // 2.1 此时单例类Singleton2得到初始化 51 | // 2.2 而该类在装载 & 被初始化时,会初始化它的静态域,从而创建单例; 52 | // 2.3 由于是静态域,因此只会JVM只会加载1遍,Java虚拟机保证了线程安全性 53 | // 3. 最终只创建1个单例 54 | 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_43.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_50.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_63.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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时,最大值 = 等于 二者的和 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/shootatoffer/Exam_41Solution.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | import java.util.Collections; 4 | import java.util.TreeSet; 5 | 6 | /** 7 | * Created by Carson_Ho on 17/11/13. 8 | */ 9 | 10 | /** 11 | * 解题算法:使用堆 12 | */ 13 | public class Exam_41Solution { 14 | 15 | // 通过 TreeSet 类实现最大堆、最小堆 16 | TreeSet 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/shootatoffer/Exam_66.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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= 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/shootatoffer/Exam_15.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_58_1.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_4.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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 | */ 53 | public static void main(String[] args) { 54 | 55 | // 功能测试 56 | int[][] src = new int[][]{{1,2,8,9},{2,4,9,12},{4,7,10,13},{6,8,11,15}}; 57 | System.out.println(answer(src,7)); 58 | 59 | // 特殊输入测试 60 | System.out.println(answer(null,7)); 61 | 62 | } 63 | 64 | } 65 | 66 | 67 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_46.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_62.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_1.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_31.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_10.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_44.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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) 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/shootatoffer/Exam_53_1.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_13.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_49.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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 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/shootatoffer/Exam_58.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_45.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | import java.util.Arrays; 4 | import java.util.Comparator; 5 | 6 | /** 7 | * Created by Carson_Ho on 17/11/14. 8 | */ 9 | 10 | public class Exam_45 { 11 | 12 | /** 13 | * 解题算法 14 | */ 15 | public static String PrintMinNumber(int [] numbers) { 16 | 17 | // 检查输入数据的合法性 18 | if (numbers == null|| numbers.length < 0){ 19 | return "输入的数据不合法"; 20 | } 21 | 22 | // 1. 将整型数组 转换成 字符串数组,从而解决大数问题 23 | String[] str = new String[numbers.length]; 24 | 25 | for(int i = 0; i < numbers.length; i++){ 26 | str[i] = String.valueOf(numbers[i]); 27 | } 28 | 29 | // 2. 自定义比较规则 & 排序数组 30 | // 实现方式:通过Arrays.sort(String int[], Comparator)来定义 31 | // 原理:根据传入的 Comparator(可通过重写自定义)自定义排序规则,排序 数组元素 32 | // 注: 33 | // a. Comparator = 比较器 = 1个接口,通过实现这个接口重写compare(),可使用compareTo()比较两个对象的大小 34 | // b. 因为String类内部实现了该方法,故可直接用compareTo进行比较 35 | // c. 返回正值 = 大于 、返回0 = 等于、返回负值 = 小于。这样就可自定义排序规则 36 | // d. 系统函数默认 = 递增排序 37 | // e. jdk7中,集合通过Collections.sort()实现自定义比较器排序、数组通过Arrays.sort()实现自定义比较器排序 38 | Arrays.sort(str,new Comparator(){ 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/shootatoffer/Exam_50_2.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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; 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/shootatoffer/Exam_61.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_11.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_6/Exam_6.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer.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/shootatoffer/Exam_47.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_59.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_22.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/10/31. 5 | */ 6 | 7 | public class Exam_22 { 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 | * 解题思路:采用2指针,仅用1次遍历找到倒数第k个节点 25 | * 注意 代码的鲁棒性,即要考虑:输入头节点是否为空、链表长度是否< k、k是否< 0 26 | * @param head 链表头节点 27 | * @param k 倒数第k个节点 28 | */ 29 | public static ListNode findKthToTail(ListNode head,int k){ 30 | 31 | // 异常情况1、3:输入头节点是否为空、k是否< 0 32 | // 均返回 null 33 | if( head == null || k <= 0) { 34 | System.out.println("出现异常情况输入头节点为空 / k是< 0"); 35 | return null; 36 | } 37 | 38 | // 1. 定义2个指针,均初始化为第1个指针 39 | ListNode slow=head; 40 | ListNode fast=head; 41 | 42 | // 2. p1 向前移动 (k-1)步,p2保持不动 43 | for(int i=0;i < k-1;i++){ 44 | // 异常情况2:链表长度是否< k 45 | if(fast.next!=null) 46 | fast = fast.next; 47 | else { 48 | System.out.println("链表长度< k"); 49 | return null; 50 | } 51 | } 52 | 53 | // 3. 从 第k步 开始,2指针同时往前移动(二指针始终保持距离 = k-1) 54 | // 直到p1移动到链表尾节点,p2刚好移动到倒数第k个节点位置 55 | while(fast.next!=null){ 56 | slow = slow.next; 57 | fast = fast.next; 58 | } 59 | // 最终返回的第2个指针指向的元素 = 倒数第k个元素 60 | return slow; 61 | } 62 | 63 | /** 64 | * 测试用例 65 | */ 66 | public static void main(String[] args){ 67 | // 创建链表:1->2->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/shootatoffer/Exam_52.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_57_1.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_30.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_5.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/10/20. 5 | */ 6 | 7 | public class Exam_5 { 8 | 9 | /** 10 | * 替换空格 11 | * @param str = 需替换的字符串 12 | * @return 返回替换后的新序列 13 | */ 14 | 15 | private static String answer(StringBuffer str) { 16 | 17 | // 1. 检查输入的合法性 18 | if(str == null) 19 | return null; 20 | 21 | // 2. 求出当前字符串的长度 22 | int originLength = str.length(); 23 | 24 | // 3. 通过遍历字符串,统计出字符串中的空格个数 25 | int numOfBlank = 0; // 记录空格数 26 | for (int i = 0; i < originLength; i++) { 27 | if( str.charAt(i) ==' ') 28 | numOfBlank++; 29 | } 30 | 31 | // 4. 计算替换后的字符串总长度,增加所需内存空间 32 | int newLength = originLength + numOfBlank*2; // 替换后的字符串长度 33 | str.setLength(newLength); // 增加所需内存空间 34 | 35 | // 5. 从尾 -> 头 复制 字符串 & 替换空格 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/shootatoffer/Exam_24.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_55.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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;iend)就返回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/shootatoffer/Exam_54.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_16.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_17.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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; 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/shootatoffer/Exam_27.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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/shootatoffer/Exam_51.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/16. 5 | */ 6 | 7 | public class Exam_51 { 8 | 9 | /** 10 | * 解题算法 11 | */ 12 | public static int inversePairs(int[] array) { 13 | 14 | // 1. 检查输入数据的合法性 15 | if (array == null || array.length < 1) { 16 | System.out.print("输入的数据不合法"); 17 | return 0; 18 | } 19 | 20 | // 2. 创建辅助数组 21 | int[] copy = new int[array.length]; 22 | 23 | for(int i=0;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/shootatoffer/Exam_14.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 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 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Android 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 62 | 63 | 64 | 65 | 66 | 1.8 67 | 68 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_25.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/2. 5 | */ 6 | 7 | public class Exam_25 { 8 | 9 | /** 10 | * 设置结点结构 11 | */ 12 | public static class ListNode { 13 | int val; 14 | ListNode next = null; 15 | 16 | ListNode(int val) { 17 | this.val = val; 18 | } 19 | } 20 | 21 | /** 22 | * 解题算法:合并链表 23 | * 解题思路:比较指针结点大小 24 | * 注:鲁棒性 25 | */ 26 | public static ListNode Merge(ListNode head1,ListNode head2){ 27 | 28 | // 鲁棒性判断: 29 | // a. 二链表中,随意1个链表的头节点为空,合并结果 = 另外1个链表 30 | // b. 二链表的头节点同时为空,合并结果 = 1空链表 31 | if(head1 == null) { 32 | return head2; 33 | } 34 | 35 | if(head2 == null) { 36 | return head1; 37 | } 38 | 39 | // 1. 定义2指针,初始分别指向2链表的头节点 40 | // 第3个指针指向合并的新链表 41 | ListNode index1 = head1; 42 | ListNode index2 = head2; 43 | ListNode indexMerge = null; 44 | 45 | // 2. 比较2指针上的节点值,将小的值合并到新链表的尾节点后 46 | if(index1.val3->5->7 66 | ListNode head1 = new ListNode(1); 67 | ListNode node3 = new ListNode(3); 68 | ListNode node5 = new ListNode(5); 69 | ListNode node7 = new ListNode(7); 70 | head1.next = node3; 71 | node3.next = node5; 72 | node5.next = node7; 73 | 74 | // 链表2 = 2->4->6->8 75 | ListNode head2 = new ListNode(2); 76 | ListNode node4 = new ListNode(4); 77 | ListNode node6 = new ListNode(6); 78 | ListNode node8 = new ListNode(8); 79 | 80 | head2.next = node4; 81 | node4.next = node6; 82 | node6.next = node8; 83 | 84 | ListNode newHead1 = Merge(head1,head2); 85 | // 输出链表 86 | while (newHead1 != null) { 87 | System.out.print(newHead1.val); 88 | newHead1 = newHead1.next; 89 | } 90 | 91 | // 异常情况测试1:二链表中,随意1个链表的头节点为空,合并结果 = 另外1个链表 92 | System.out.println("异常情况测试1:二链表中,随意1个链表的头节点为空,合并结果 = 另外1个链表"); 93 | ListNode newHead2 = Merge(head1,null); 94 | while (newHead2 != null) { 95 | System.out.print(newHead2.val); 96 | newHead2 = newHead2.next; 97 | } 98 | 99 | System.out.println("异常情况测试1:二链表中,随意1个链表的头节点为空,合并结果 = 另外1个链表"); 100 | ListNode newHead3 = Merge(null,head2); 101 | while (newHead3 != null) { 102 | System.out.print(newHead3.val); 103 | newHead3 = newHead3.next; 104 | } 105 | 106 | // 异常情况测试2:二链表的头节点同时为空,合并结果 = 1空链表 107 | System.out.println("异常情况测试2:二链表的头节点同时为空,合并结果 = 1空链表"); 108 | Merge(null,null); 109 | 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_21.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/10/31. 5 | */ 6 | 7 | public class Exam_21 { 8 | 9 | /** 10 | * 优化解题算法 11 | * 解题思路:通过2指针判断 & 交换位置 12 | */ 13 | public static void reorder(int[] array){ 14 | 15 | // 1. 判断输入数据的合法性 16 | if(array==null||array.length<2) 17 | return; 18 | 19 | // 2. 初始化2指针 20 | // p1 = 初始化指向数组第1个元素, 21 | // p2 = 初始化指向数组最后1个元素 22 | int left = 0; 23 | int right = array.length-1; 24 | 25 | // 3. 跳出循环条件 = p2在p1的前1个位置 26 | while(left endPre || startIn>endIn ) 41 | return null; 42 | 43 | // 1. 重建二叉树的根节点 = 前序遍历序列的第1个结点 44 | TreeNode root=new TreeNode(pre[startPre]); 45 | 46 | // 2. 通过数组遍历,在中序遍历中找出根节点 47 | for(int i= startIn; i<=endIn ;i++) 48 | 49 | // 3. 根据在上1步找出的根节点,找出前序遍历 & 中序遍历中的左、右子树,并 作为递归输入,重复上述步骤进行递归 50 | // 最终找出重建二叉树的左、右子树 51 | if(in[i]==pre[startPre]){ 52 | 53 | // a. 前序遍历的左子树 = 第1位(根节点)的下1位 ~ x ,其中,x = 根节点 + 中序遍历中左子树的数量、中序遍历左子树的数量 = 中序遍历根节点位置的前部分 54 | // b. 中序遍历的左子树 = 第1位~ 根节点前1位 55 | root.left=reConstructBinaryTree(pre,startPre+1,startPre+ (i-startIn), in,startIn,i-1); 56 | 57 | // a. 前序遍历的右子树 = 左子树最后1位的后1位 ~ 最后1位 58 | // b. 中序遍历的右子树 = 根节点前1位 ~ 最后1位 59 | root.right=reConstructBinaryTree(pre, (startPre+1) + i-startIn,endPre,in,i+1,endIn); 60 | } 61 | 62 | return root; 63 | } 64 | 65 | /** 66 | * 辅助类:二叉树节点结构类 67 | */ 68 | 69 | static class TreeNode { 70 | int val; 71 | TreeNode left; 72 | TreeNode right; 73 | 74 | public TreeNode(int val) { 75 | this.val = val; 76 | } 77 | 78 | 79 | } 80 | 81 | /** 82 | * 测试用例 83 | */ 84 | 85 | // 1 86 | // / \ 87 | // 2 3 88 | // / \ / \ 89 | // 4 5 7 6 90 | public static void main(String[] args) { 91 | //前序遍历{1,2,4,7,3,5,6,8} & 中序遍历序列{4,7,2,1,5,3,8,6} 92 | int[] pre = {1,2,4,7,3,5,6,8}; 93 | int[] in = {4,7,2,1,5,3,8,6}; 94 | // 重建二叉树后,使用宽度遍历进行输出测试 95 | levelTraversal(reConstructBinaryTree(pre,in)); 96 | 97 | 98 | } 99 | 100 | /** 101 | * 测试辅助算法:分层遍历二叉树,使用一个队列,也就是宽度优先遍历 102 | * @param root 103 | */ 104 | public static void levelTraversal(TreeNode root){ 105 | if(root==null) 106 | return ; 107 | 108 | LinkedList queue = new LinkedList(); 109 | queue.add(root); 110 | while(!queue.isEmpty()){ 111 | TreeNode cur = queue.remove(); 112 | System.out.print(cur.val+" "); 113 | if(cur.left!=null) 114 | queue.add(cur.left); 115 | if(cur.right!=null) 116 | queue.add(cur.right); 117 | 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_34.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Created by Carson_Ho on 17/11/7. 7 | */ 8 | 9 | public class Exam_34 { 10 | 11 | /** 12 | * 结点结构 13 | */ 14 | public static class TreeNode { 15 | int val = 0; 16 | TreeNode left = null; 17 | TreeNode right = null; 18 | 19 | public TreeNode(int val) { 20 | this.val = val; 21 | 22 | } 23 | 24 | } 25 | 26 | /** 27 | * 解题算法 28 | * 29 | * @param root 树的根结点 30 | * @param exceptedSum 要求的路径和 31 | */ 32 | public static ArrayList> findPath(TreeNode root,int exceptedSum){ 33 | 34 | // 1. 创建1链表 & 链表集合 35 | 36 | ArrayList> result = new ArrayList>(); // 链表集合:存储所有符合条件的路径 37 | ArrayList path = new ArrayList(); // 链表:用于 存储路径节点,即存放根结点到当前处理结点的所经过的结点 38 | 39 | // 2. 判断输入数据的合法性:若二叉树的根结点为空,则结束判断 40 | if(root == null) { 41 | System.out.println("输入的二叉树节点为空"); 42 | return result; 43 | } 44 | 45 | // 3. 调用辅助方法通过前序遍历二叉树 & 记录下路径 46 | findPath(root,path,result,exceptedSum,0); 47 | 48 | return result; 49 | } 50 | 51 | /** 52 | * 辅助方法 53 | * 54 | * @param curNode 当前要处理的结点(未加入到路径中) 55 | * @param path 路径节点,即存放根结点到当前处理结点的所经过的结点(未包括当前结点) 56 | * @param result 存储所有符合条件的路径 57 | * @param exceptedSum 要求的路径和 58 | * @param currentSum 当前记录的和(未加上当前结点的值) 59 | */ 60 | 61 | public static void findPath(TreeNode curNode,ArrayList path,ArrayList> result,int exceptedSum,int currentSum){ 62 | 63 | // 1. 记录下当前节点值到路径 64 | path.add(curNode.val); 65 | 66 | // 2. 计算路径节点值的和 67 | currentSum += curNode.val; 68 | 69 | // 3. 若当前节点 ≠ 叶子结点,则通过 递归 继续遍历其子节点 70 | if(curNode.left!=null) 71 | findPath(curNode.left,path,result,exceptedSum,currentSum); 72 | 73 | if(curNode.right!=null) { 74 | findPath(curNode.right, path, result, exceptedSum, currentSum); 75 | } 76 | 77 | // 4. 若当前节点 = 叶节点 & 路径节点值的和 = 期望值 78 | // 则认为该路径符合要求,将其添加到链表集合当中 79 | if(curNode.left == null && curNode.right == null && currentSum == exceptedSum) { 80 | System.out.println(path); 81 | result.add(new ArrayList(path)); 82 | System.out.println(result); 83 | } 84 | 85 | // 5. 若遍历的结点 = 叶结点 & 路径中节点值的和 ≠ 输入的整数 86 | // 自动回到其父结点 & 继续遍历其他子节点 87 | // 注:函数退出前,要在路径上删除当前结点 & 减去当前结点的值,以确保返回父结点时,路径 = 从根结点 -> 父结点的路径 88 | path.remove(path.size()-1) ; 89 | 90 | } 91 | 92 | 93 | /** 94 | * 测试用例 95 | */ 96 | public static void main(String[] args) { 97 | 98 | // 功能测试 99 | // 10 100 | // / \ 101 | // 5 12 102 | // / \ 103 | // 4 7 104 | TreeNode root = new TreeNode(10); 105 | root.left = new TreeNode(5); 106 | root.right = new TreeNode(12); 107 | root.left.left = new TreeNode(4); 108 | root.left.right = new TreeNode(7); 109 | System.out.println(findPath(root,22)); 110 | System.out.println(findPath(root,32)); 111 | 112 | // 特殊输入测试 113 | findPath(null,12); 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_53.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/20. 5 | */ 6 | 7 | public class Exam_53 { 8 | 9 | /** 10 | * 解题算法 11 | * @ param array 排序数组 12 | * @ param k 需统计的数字 13 | */ 14 | public static int getNumberOfK(int [] array , int k) { 15 | 16 | // 检查输入数据的合法性 17 | if(array == null || array.length == 0) 18 | return 0; 19 | 20 | // 1. 使用 二分法 查找数组中的 第1个 m 21 | int indexOfFirstK = getFirstK(array, 0, array.length - 1, k); 22 | 23 | // 2. 使用 二分法 查找数组中的 最后1个 m 24 | int indexOfLastK = getLastK(array, 0, array.length - 1, k); 25 | 26 | if(indexOfFirstK == -1) 27 | return 0; 28 | 29 | // 3. 返回需统计数字的次数 = 最后1个的下标 — 前1个下标 30 | return indexOfLastK - indexOfFirstK + 1; 31 | } 32 | 33 | /** 34 | * 辅助算法:使用 二分法 查找数组中的 第1个 m 35 | */ 36 | private static int getFirstK(int[] array, int low, int high, int k) { 37 | 38 | if(low > high) 39 | return -1; 40 | 41 | // 1. 找出中间元素 42 | int mid = low + (high - low) / 2; 43 | // 此处需特别注意以下二者区别: 44 | // a. mid = (low + high) / 2 45 | // b. mid = low + (high - low ) / 2 46 | // 当low、high都是比较大的数时,完成同样的功能,(a)可能造成上溢,但(b)不会。 47 | 48 | // 通过 递归 方式找出元素 49 | // a. 若给定值 < 中间记录,则 在中间记录的左半区(低半区) 继续查找 50 | if(k < array[mid] ) 51 | return getFirstK(array, low, mid - 1, k); 52 | 53 | // b. 若给定值 > 中间记录,则 在中间记录的右半区 继续查找 54 | else if( k > array[mid] ) 55 | return getFirstK(array, mid + 1, high, k); 56 | 57 | // c. 若给定值 = 中间记录,需判断该k是否是数组中的第一个k 58 | // 若中间记录 前1个数字 ≠ k,那么 中间记录 = 第1个k 59 | // 若中间记录 前1个数字 = k,那么第1个k 在数组的左半段,下轮继续在数组的左半段查找 60 | else if(mid > 0 && array[mid - 1] == k) 61 | return getFirstK(array, low, mid - 1, k); 62 | else 63 | return mid; 64 | 65 | } 66 | 67 | /** 68 | * 辅助算法:使用 二分法 查找数组中的 最后1个 m 69 | */ 70 | private static int getLastK(int[] array, int low, int high, int k) { 71 | 72 | // 1. 找出中间元素 73 | if(low > high) 74 | return -1; 75 | 76 | // 2. 找出中间元素 77 | int mid = low + (high - low) / 2; 78 | 79 | // a. 若给定值 < 中间记录,则 在中间记录的左半区 继续查找 80 | if( k < array[mid] ) 81 | return getLastK(array, low, mid - 1, k); 82 | 83 | else if( k < array[mid] ) 84 | // b. 若给定值 > 中间记录,则 在中间记录的右半区 继续查找 85 | return getLastK(array, mid + 1, high, k); 86 | 87 | // c. 若给定值 = 中间记录,需判断该k是否是数组中的最后一个k 88 | // 若中间记录 后1个数字 ≠ k,那么 中间记录 = 最后1个k 89 | // 若中间记录 后1个数字 = k,那么第1个k 在数组的右半段,下轮继续在数组的右半段查找 90 | else if(mid < high && array[mid + 1] == k) 91 | return getLastK(array, mid + 1, high, k); 92 | else 93 | return mid; 94 | } 95 | 96 | 97 | /** 98 | * 测试用例 99 | */ 100 | public static void main(String[] args){ 101 | // 功能测试:含查找的数字,出现1次 / 多次、无查找的数字 102 | int[] data1 = new int[]{1,2,3,4,5,6,7,8}; 103 | int[] data2 = new int[]{1,2,3,3,3,3,5,6}; 104 | System.out.println(getNumberOfK(data1,4)); 105 | System.out.println(getNumberOfK(data2,3)); 106 | 107 | // 特殊输入测试:null、输入的数组只有1个数字 108 | System.out.println(getNumberOfK(null,1)); 109 | int[] data3 = new int[]{1}; 110 | System.out.println(getNumberOfK(data3,1)); 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_26.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/2. 5 | */ 6 | 7 | public class Exam_26 { 8 | 9 | /** 10 | * 设置结点结构 11 | */ 12 | public static class TreeNode { 13 | double val; 14 | TreeNode left; 15 | TreeNode right; 16 | 17 | public TreeNode(double data) { 18 | this.val = data; 19 | this.left = null; 20 | this.right = null; 21 | } 22 | } 23 | 24 | 25 | 26 | /** 27 | * 解题算法 28 | * 主要步骤: 29 | * 1. 在树A中找到与树B的根节点值相同的节点R 30 | * 2. 判断 树A中以R为根节点的子树 是否 包含和树B一样的结构 31 | */ 32 | public static boolean HasSubtree(TreeNode root1, TreeNode root2){ 33 | 34 | // 输入的树A、树B空指针的判断 35 | if(root1 == null || root2 == null) 36 | return false; 37 | 38 | // 1. 在树A中找到与树B的根节点值相同的节点R 39 | // 通过新定义的判断函数,判断2个小数(doubl)是否相等 40 | if(equal(root1.val,root2.val)){ 41 | // 若相等,则继续判断 树A中以R为根节点的子树 是否 包含和树B一样的结构 42 | if(tree1HasTree2FromRoot(root1,root2)) 43 | return true; 44 | } 45 | 46 | // 通过递归继续寻找 树A中与树B的根节点值相同的节点R 47 | return HasSubtree(root1.left,root2) || HasSubtree(root1.right,root2); 48 | } 49 | 50 | /** 51 | * 辅助算法:判断 树A中以R为根节点的子树 是否 包含和树B一样的结构 52 | */ 53 | 54 | public static boolean tree1HasTree2FromRoot(TreeNode root1, TreeNode root2){ 55 | // 注意输入的子树节点的空指针判断 56 | if(root2 == null) 57 | return true; 58 | 59 | if(root1 == null) 60 | return false; 61 | 62 | // 若子树节点相等,则继续往下判断 63 | if(equal(root1.val,root2.val) && tree1HasTree2FromRoot(root1.left,root2.left) && tree1HasTree2FromRoot(root1.right,root2.right)) 64 | // 若根节点的左、右子树均相等,则说明 B是A的子结构 65 | 66 | return true; 67 | else 68 | return false; 69 | 70 | } 71 | 72 | /** 73 | * 辅助算法:判断 double值相等函数 74 | * 原理:通过判断二者之间的差的绝对值是否在一个很小的范围(-0.00000001~0.00000001),若 是,则认为二者相等。反之亦然 75 | */ 76 | public static boolean equal(double root1, double root2){ 77 | if((root1 - root2 > -0.0000001) && (root1 - root2 < 0.0000001)) 78 | return true; 79 | else 80 | return false; 81 | 82 | } 83 | 84 | /** 85 | * 测试用例 86 | */ 87 | 88 | public static void main(String[] args){ 89 | // 树结构定义 90 | // 树A 91 | TreeNode root1 = new TreeNode (8); 92 | root1.left = new TreeNode(8); 93 | root1.right = new TreeNode(7); 94 | root1.left.left = new TreeNode(9); 95 | root1.left.right = new TreeNode(2); 96 | root1.left.right.left = new TreeNode(4); 97 | root1.left.right.right = new TreeNode(7); 98 | 99 | // 树B 100 | TreeNode root2 = new TreeNode(8); 101 | root2.left = new TreeNode(9); 102 | root2.right = new TreeNode(2); 103 | 104 | // 树C 105 | TreeNode root3 = new TreeNode(2); 106 | root3.left = new TreeNode(4); 107 | root3.right = new TreeNode(3); 108 | 109 | // 功能测试:树B是树A的子结构、树C是树A的子结构 110 | System.out.println(HasSubtree(root1,root2)); 111 | System.out.println(HasSubtree(root1,root3)); 112 | 113 | // 异常测试:树A、树B 任一为空 / 均为空时 114 | System.out.println(HasSubtree(null,root3)); 115 | System.out.println(HasSubtree(root1,null)); 116 | System.out.println(HasSubtree(null,null)); 117 | } 118 | 119 | 120 | } 121 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_9.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | import java.util.Stack; 6 | 7 | /** 8 | * Created by Carson_Ho on 17/10/24. 9 | */ 10 | 11 | public class Exam_9 { 12 | 13 | /** 14 | * 测试用例 15 | */ 16 | 17 | public static void main(String[] args) { 18 | MyQueue queue = new MyQueue<>(); 19 | 20 | // 插入元素 21 | queue.push(1); 22 | queue.push(2); 23 | queue.push(3); 24 | 25 | // 删除元素 26 | System.out.println(queue.pop()); 27 | System.out.println(queue.pop()); 28 | System.out.println(queue.pop()); 29 | 30 | 31 | MyStack stack = new MyStack<>(); 32 | // 入栈 33 | stack.push("a"); 34 | stack.push("b"); 35 | stack.push("c"); 36 | 37 | // 出栈 38 | System.out.println(stack.pop()); 39 | System.out.println(stack.pop()); 40 | System.out.println(stack.pop()); 41 | 42 | 43 | 44 | 45 | } 46 | } 47 | 48 | /** 49 | * 用2个栈实现一个队列 50 | */ 51 | 52 | class MyQueue{ 53 | 54 | // 1. 建立2个堆栈 55 | Stack stack1 = new Stack(); 56 | Stack stack2 = new Stack(); 57 | 58 | /** 59 | * 插入元素结点 60 | * 在栈1中进行 61 | */ 62 | 63 | public void push(int node){ 64 | // 直接将元素结点插入到栈1 65 | stack1.push(node); 66 | } 67 | 68 | /** 69 | * 删除元素结点 70 | * 在栈2中进行 71 | * @return 72 | */ 73 | public int pop(){ 74 | 75 | // 判断栈2是否为空(是否有结点) 76 | 77 | // a. 若栈2、栈1均为空 78 | // 则抛出异常 79 | if(stack2.isEmpty() && stack1.isEmpty()){ 80 | 81 | try { 82 | throw new Exception("队列中没元素啦"); 83 | } catch (Exception e) { 84 | e.printStackTrace(); 85 | } 86 | 87 | } 88 | 89 | // b. 若栈2为空、栈1不为空 90 | // 则将栈1中的结点依次弹出 & 压入到栈2,最终直接弹出栈2元素 91 | if(stack2.isEmpty()){ 92 | 93 | while(!stack1.isEmpty()){ 94 | stack2.push(stack1.pop()); 95 | 96 | } 97 | } 98 | 99 | // c. 若栈2不为空,则直接弹出 100 | return stack2.pop(); 101 | } 102 | 103 | } 104 | 105 | /** 106 | * 用两个队列实现一个栈 107 | */ 108 | 109 | class MyStack{ 110 | 111 | // 1. 定义2个队列 112 | Queue queue1 = new LinkedList<>(); 113 | Queue queue2 = new LinkedList<>(); 114 | 115 | 116 | // 2. 入栈 117 | // 使用队列1 118 | public void push(E n){ 119 | // 直接插入到队列1 120 | queue1.add(n); 121 | } 122 | 123 | // 3. 出栈 124 | // 使用队列2 125 | public E pop(){ 126 | 127 | // a. 若队列1 & 2均为空,则抛出异常 128 | if(queue1.isEmpty() && queue2.isEmpty()) 129 | try { 130 | throw new Exception("栈中没元素啦"); 131 | } catch (Exception e) { 132 | e.printStackTrace(); 133 | } 134 | 135 | // b. 若队列1不为空 136 | // 将队列1的元素(除最后1个)依次出列 & 插入到队列2 137 | if(!queue1.isEmpty()){ 138 | 139 | while(queue1.size()>1){ 140 | queue2.add(queue1.remove()); 141 | 142 | } 143 | // 返回队列1中的最后1个元素,即需删除的元素 144 | return queue1.remove(); 145 | } 146 | 147 | // c. 若队列1为空时 148 | // 将队列2的元素(除最后1个)依次出列 & 插入到队列1 149 | while(queue2.size()>1){ 150 | queue1.add(queue2.remove()); 151 | } 152 | // 返回队列2中的最后1个元素,即需删除的元素 153 | return queue2.remove(); 154 | 155 | } 156 | } 157 | 158 | 159 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_40.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | import java.util.ArrayList; 4 | import java.util.TreeSet; 5 | 6 | /** 7 | * Created by Carson_Ho on 17/11/11. 8 | */ 9 | 10 | public class Exam_40 { 11 | 12 | /** 13 | * 测试用例 14 | */ 15 | public static void main(String[] args){ 16 | 17 | // 解题算法1测试用例 18 | // 功能测试1:数组中无相同数字 19 | int[] data = {4,5,1,6,2,7,3,8}; 20 | System.out.println(GetLeastNumbers_Solution(data,4)); 21 | 22 | // 功能测试2:数组中有相同数字 23 | int[] data1 = {4,5,1,6,3,7,3,8}; 24 | System.out.println(GetLeastNumbers_Solution(data1,4)); 25 | 26 | // 特殊输入测试:数组为空 27 | int[] data2 = null; 28 | System.out.println(GetLeastNumbers_Solution(data2,4)); 29 | 30 | } 31 | 32 | /** 33 | * 解题算法1:使用Partition算法实现 34 | */ 35 | // public static ArrayList GetLeastNumbers_Solution(int [] input, int k) { 36 | // 37 | // // 1. 判断输入数据的合法性 38 | // if(input == null || input.length == 0 || k <= 0 || k > input.length) { 39 | // System.out.println("输入的数据不合法"); 40 | // return new ArrayList<>(); 41 | // } 42 | // 43 | // 44 | // ArrayList result = new ArrayList<>(); // 该链表用于存储结果 45 | // int start = 0; 46 | // int end = input.length - 1; 47 | // 48 | // // 2. 通过 partition函数 选择第k个数字 来调整数组中数字的顺序 49 | // // 使得 比第k个数字小 的数字都排在它左边、比 第k个数字大 的数字都排在它右边 50 | // int p = partition(input, start, end); 51 | // 52 | // while(p != k - 1) { 53 | // // 2.1 若选中数字的下标 > k-1,那么下面接着在它的左边部分的数组中查找 54 | // if(p > k - 1) 55 | // end = p - 1; 56 | // // 2.2 若选中数字的下标 < k-1,那么下面接着在它的右边部分的数组中查找 57 | // if(p < k - 1) 58 | // start = p + 1; 59 | // p = partition(input, start, end); 60 | // } 61 | // 62 | // // 3. 直接选取数组的前k个数 63 | // for(int i = 0; i < k; ++i) 64 | // result.add(input[i]); 65 | // return result; 66 | // } 67 | // 68 | // /** 69 | // * 辅助算法:Partition算法 70 | // */ 71 | // private static int partition(int[] array, int low, int high) { 72 | // if(low >= high) return low; 73 | // int val = array[0]; 74 | // int i = low; 75 | // int j = high + 1; 76 | // while(true) { 77 | // while(array[++i] < val) if(i == high) break; 78 | // while(array[--j] > val) if(j == low) break; 79 | // if(i >= j) break; 80 | // swap(array, i, j); 81 | // } 82 | // swap(array, low, j); 83 | // return j; 84 | // } 85 | // 86 | // /** 87 | // * 辅助算法:交换位置 88 | // */ 89 | // private static void swap(int[] array, int indexA, int indexB) { 90 | // int t = array[indexA]; 91 | // array[indexA] = array[indexB]; 92 | // array[indexB] = t; 93 | // } 94 | 95 | 96 | 97 | /** 98 | * 解题算法2:使用红黑树作为数据容器 99 | */ 100 | 101 | public static ArrayList GetLeastNumbers_Solution(int [] input, int k) { 102 | 103 | // 1. 判断输入数据的合法性 104 | if(input == null || input.length == 0 || k <= 0 || k > input.length) { 105 | System.out.println("输入的数据不合法"); 106 | return new ArrayList<>(); 107 | } 108 | 109 | // 2. 创建1树结构作为数据容器 110 | TreeSet set = new TreeSet<>(); 111 | for(int i : input) { 112 | 113 | // 2.1 从n个整数中选取前k个数字放入到容器里 114 | if(set.size() != k) { 115 | set.add(i); 116 | 117 | } else if(i < set.last() ) { 118 | // 2. 若下1个读取的数字 < 数据容器中的最大值,则替换 119 | set.pollLast(); 120 | set.add(i); 121 | } 122 | } 123 | return new ArrayList<>(set); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_48.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * Created by Carson_Ho on 17/12/14. 8 | */ 9 | 10 | public class Exam_48 { 11 | 12 | /** 13 | * 解题思路1:直接法 14 | */ 15 | 16 | // public static int lengthOfLongestSubstring(String s){ 17 | // 18 | // int n = s.length(); 19 | // int ans = 0; 20 | // // 1. 遍历给定字符串s的所有可能的子字符串 21 | // // 通过枚举它们的开始和结束索引 22 | // // 设开始 & 结束索引 = i和j 23 | // // 那么我们有0 <= i < j <=n(这里的结束索引j按照惯例排除) 24 | // // 因此 使用2个循环嵌套枚举S的所有子串(i:从0到n-1、j:从i + 1到n) 25 | // for(int i = 0; i set = new HashSet <>(); 45 | // 46 | // for(int i = start; i set = new HashSet<>(); // 使用HashSet实现滑动窗口 71 | // 72 | // int ans = 0, i = 0, j = 0; 73 | // 74 | // // 通过移动滑动窗口,找出最长不含重复字符的 75 | // while (i < n && j < n) { 76 | // // a. 若S[j]不在HashSet中,则将滑动窗口的索引j向右滑动,包含S[j],即添加该S[j]到HashSet中 77 | // if (!set.contains(s.charAt(j))){ 78 | // set.add(s.charAt(j++)); 79 | // ans = Math.max(ans, j - i); 80 | // } 81 | // // b. 若S[j]在HashSet中,则将滑动窗口的索引i向右滑动 & 删除 82 | // else { 83 | // set.remove(s.charAt(i++)); 84 | // } 85 | // } 86 | // return ans; 87 | // } 88 | // 89 | // /** 90 | // * 测试用例 91 | // */ 92 | // public static void main(String[] args){ 93 | // 94 | // // 功能测试 95 | // System.out.println(lengthOfLongestSubstring("arabcacfr")); 96 | // 97 | // } 98 | 99 | 100 | 101 | /** 102 | * 解题思路3:优化使用滑动窗口 103 | */ 104 | public static int lengthOfLongestSubstring(String s) { 105 | 106 | int n = s.length(), ans = 0; 107 | 108 | // 1. 使用 HashMap 将字符存储在当前窗口[i,j](初始化:j = i) 109 | Map map = new HashMap<>(); // 滑动窗口使用HashMap实现 110 | 111 | // 2. 往右移动滑动窗口 112 | for (int j = 0, i = 0; j < n; j++) { 113 | // a. 若 S[j] 在HashMap中(即为S [j']) 则将滑动窗口的索引i 直接移动到S[j]的后1个位置 即 i=j'+ 1 114 | if (map.containsKey(s.charAt(j))) { 115 | i = Math.max(map.get(s.charAt(j)), i); 116 | } 117 | // b. 若 S[j] 不在HashMap中 则将滑动窗口的索引 j 向右滑动 包含S[j] 即添加该S[j]到HashMap中 118 | ans = Math.max(ans, j - i + 1); 119 | map.put(s.charAt(j), j + 1); 120 | } 121 | return ans; 122 | } 123 | 124 | /** 125 | * 测试用例 126 | */ 127 | public static void main(String[] args){ 128 | 129 | // 功能测试 130 | System.out.println(lengthOfLongestSubstring("arabcacfr")); 131 | 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_37.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/8. 5 | */ 6 | 7 | public class Exam_37 { 8 | 9 | /** 10 | * 节点结构 11 | */ 12 | public static class TreeNode { 13 | int val; 14 | TreeNode left = null; 15 | TreeNode right = null; 16 | 17 | public TreeNode(int val) { 18 | this.val = val; 19 | } 20 | } 21 | 22 | /** 23 | * 序列化:二叉树转换成字符串 24 | * 核心思想:采用 递归 实现前序遍历 25 | */ 26 | 27 | public static String Serialize(TreeNode root) { 28 | 29 | StringBuilder sb = new StringBuilder(); 30 | 31 | // 检查输入数据的合法性 32 | if(root == null){ 33 | sb.append("#,"); // 遍历时当遇到空指针时,则采用 特殊字符(如$)代替 34 | return sb.toString(); 35 | 36 | } 37 | 38 | // 根据 前序遍历的顺序 序列化二叉树 39 | sb.append(root.val + ",");// 1. 序列化根节点 40 | sb.append(Serialize(root.left));// 2. 序列化左子树 41 | sb.append(Serialize(root.right)); // 3. 序列化右子树 42 | return sb.toString(); 43 | 44 | } 45 | 46 | /** 47 | * 反序列化 48 | * 核心思想:拆分分3步反序列化:反序列化根节点、左子树、右子树 49 | */ 50 | 51 | static int index = -1; // 当前反序列化节点的下标 52 | private static TreeNode Deserialize(String str) { 53 | 54 | index++; // 每次递归时记录下当前反序列化节点的下标 55 | 56 | String[] strr = str.split(","); 57 | TreeNode node = null; 58 | 59 | if(!strr[index].equals("#")){ 60 | node = new TreeNode(Integer.valueOf(strr[index])); // 1. 反序列化根节点,即读取到的第1个数字 61 | node.left = Deserialize(str); // 2. 反序列化左子树 62 | node.right = Deserialize(str);// 3. 反序列化右子树 63 | } 64 | 65 | return node; 66 | 67 | } 68 | 69 | /** 70 | * 测试用例 71 | */ 72 | public static void main(String[] args) { 73 | // 功能测试 74 | // 1 75 | // / \ 76 | // 2 3 77 | // / / \ 78 | // 4 5 6 79 | TreeNode root = new TreeNode(1); 80 | 81 | root.left = new TreeNode(2); 82 | root.right = new TreeNode(3); 83 | 84 | root.left.left = new TreeNode(4); 85 | root.right.left = new TreeNode(5); 86 | root.right.right = new TreeNode(6); 87 | 88 | // 序列化二叉树 89 | System.out.print("序列化后的二叉树:"); 90 | System.out.println(Serialize(root)); 91 | 92 | // 反序列化二叉树 93 | System.out.print("反序列化后的二叉树:"); 94 | printTree(Deserialize(Serialize(root))); 95 | 96 | } 97 | 98 | /** 99 | * 仅用于测试时 打印二叉树(中序遍历) 100 | */ 101 | 102 | private static void printTree(TreeNode root) { 103 | if (root != null) { 104 | printTree(root.left); 105 | System.out.print(root.val + " "); 106 | printTree(root.right); 107 | } 108 | } 109 | 110 | } 111 | 112 | 113 | /** 114 | * 序列化:二叉树转换成字符串 115 | * 核心思想:采用 递归 实现前序遍历 116 | */ 117 | // public static void serialize(TreeNode root, List result) { 118 | // 119 | // // 1. 判断数据的合法性 120 | // if (root == null){ 121 | // result.add(null); 122 | // return; 123 | // } 124 | // // 2. 通过递归实现前序遍历序列化二叉树 125 | // result.add(root.val); 126 | // serialize(root.left, result); 127 | // serialize(root.right, result); 128 | // } 129 | 130 | 131 | /** 132 | * 反序列化 133 | * 核心思想:拆分分3步反序列化:反序列化根节点、左子树、右子树 134 | */ 135 | 136 | // int index = -1; // 当前反序列化节点的下标 137 | // private static TreeNode deserialize(TreeNode root, List result) { 138 | // 139 | // if (result.get(0) == null) { 140 | // result.remove(0); 141 | // return null; 142 | // } 143 | // // 1. 反序列化根节点,即读取到的第1个数字 144 | // root = new TreeNode( result.remove(0)); 145 | // // 2. 反序列化左子树 146 | // root.left = deserialize(root.left, result); 147 | // // 3. 反序列化右子树 148 | // root.right = deserialize(root.right, result); 149 | // return root; 150 | // } 151 | 152 | 153 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_23.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/1. 5 | */ 6 | 7 | public class Exam_23 { 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 | * 分为3个步骤:1.判断链表中是否有环;2.获得环中节点;3.找到环的入口节点 26 | */ 27 | public static ListNode meetingNode(ListNode pHead) { 28 | 29 | // 判断输入头节点的合法性 30 | if(pHead == null) { 31 | System.out.println("头节点为空"); 32 | return null; 33 | } 34 | 35 | /** 36 | * 步骤1:判断链表中是否有环 37 | */ 38 | 39 | // 定义2个指针,同时从链表头节点出发 40 | ListNode fast = pHead; 41 | ListNode slow = pHead; 42 | 43 | // p1移动1步/次,p2移动2步/次 44 | while (fast != null && fast.next != null) { 45 | fast = fast.next.next; 46 | slow = slow.next; 47 | // 若2者相遇,则代表有环,跳出循环 48 | if (fast == slow) { 49 | System.out.println("链表中有环"); 50 | break; 51 | } 52 | 53 | } 54 | 55 | // 若p2走到了链表的尾端依然未遇到p1,则代表链表中无环,返回空 56 | if (fast == null || fast.next == null) { 57 | System.out.println("链表中无环"); 58 | return null; 59 | } 60 | 61 | /** 62 | * 步骤2:获得环中节点 63 | * a. 从上述2指针相遇的节点出发(该节点在环内) 64 | * b. 一直往前移动 & 计数 65 | * c. 当指针再次回到该相遇节点时,计数器即为环的数量 66 | */ 67 | 68 | int nodesLoop =1 ; // 用于记录环节点的数量 69 | while(fast.next!= slow){ 70 | fast = fast.next; 71 | ++ nodesLoop; 72 | } 73 | 74 | 75 | /** 76 | * 步骤3:找到环的入口节点 77 | * a. 定义2个指针p1、p2 78 | * b. 同时从链表头节点出发:p1先移动n步,p2不动(n= 环的节点数量) 79 | * c. 2指针同时往前移动,当2指针相遇时,相遇节点 = 环的入口节点 80 | * 此时,p2到达环的入口节点、p1也已经围绕环走了1圈到达环的入口节点 81 | */ 82 | // 将2个指针p1、p2重新回到链表头节点出发 83 | fast = pHead; 84 | slow = pHead; 85 | 86 | // p1先移动n步(n= 环的节点数量,此处n=4),p2不动 87 | for (int i = 0;i < nodesLoop;++i){ 88 | fast = fast.next; 89 | } 90 | 91 | // 2指针同时往前移动 92 | // 当2指针相遇时,相遇节点 = 环的入口节点 93 | while (fast != slow) { 94 | fast = fast.next; 95 | slow = slow.next; 96 | } 97 | return fast; 98 | } 99 | 100 | 101 | /** 102 | * 测试用例 103 | */ 104 | public static void main(String[] args){ 105 | // 创建链表: 106 | // 1->2->3->4->5->6 107 | // ^ | 108 | // | | 109 | // +--------+ 110 | ListNode pHead = new ListNode(1); 111 | ListNode node2 = new ListNode(2); 112 | ListNode node3 = new ListNode(3); 113 | ListNode node4 = new ListNode(4); 114 | ListNode node5 = new ListNode(5); 115 | ListNode node6 = new ListNode(6); 116 | pHead.next = node2; 117 | node2.next = node3; 118 | node3.next = node4; 119 | node4.next = node5; 120 | node5.next = node6; 121 | node6.next = node3; 122 | 123 | // 功能测试:倒数第1个节点(尾结点)、倒数第6个节点(头节点)、中间节点 124 | System.out.println(meetingNode(pHead).val); 125 | 126 | // 异常测试:链表中没环 127 | // 创建链表: 128 | // 1->2->3->4->5->6 129 | // ListNode pHead = new ListNode(1); 130 | // ListNode node2 = new ListNode(2); 131 | // ListNode node3 = new ListNode(3); 132 | // ListNode node4 = new ListNode(4); 133 | // ListNode node5 = new ListNode(5); 134 | // ListNode node6 = new ListNode(6); 135 | // pHead.next = node2; 136 | // node2.next = node3; 137 | // node3.next = node4; 138 | // node4.next = node5; 139 | // node5.next = node6; 140 | // 141 | // System.out.println(meetingNode(pHead).val); 142 | 143 | 144 | // 异常测试:头节点为空 145 | // System.out.println(meetingNode(null).val); 146 | 147 | 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_38.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | 6 | /** 7 | * Created by Carson_Ho on 17/11/8. 8 | */ 9 | 10 | public class Exam_38 { 11 | 12 | /** 13 | * 解题算法 14 | * @param strs 传入的字符串 15 | */ 16 | public static ArrayList permutation(String strs) { 17 | 18 | // 1. 创建1链表用于存储排列 19 | ArrayList ret = new ArrayList<>(); 20 | 21 | // 2. 判断数据的合法性 22 | if (strs == null || strs.length() == 0) { 23 | System.out.println("输入的头节点为空"); 24 | return ret; 25 | } 26 | 27 | // 3. 将字符串转换成数组便于处理 28 | char[] chars = strs.toCharArray(); 29 | 30 | // 4. 求出字符串的排列 31 | permutationCore(chars, ret, 0); 32 | 33 | // 5. 调用Collections工具类的sort()对结果进行排序 34 | Collections.sort(ret); 35 | return ret; 36 | } 37 | 38 | /** 39 | * 辅助算法1:求字符串的排列 40 | */ 41 | public static void permutationCore(char[] strs, ArrayList ret, int k) { 42 | // 下标 = k的字符 = 当前字符串的第1个字符 43 | 44 | // 若下标 = 最后1个字符,则输出所有结果 45 | if (k == strs.length) { 46 | ret.add(String.valueOf(strs)); 47 | return; 48 | } 49 | 50 | // 下标=i的字符 = 当前字符串第1个字符的后面所有字符(i的范围 = [k,length) ) 51 | // 下标=k的字符 依次 与下标为i的字符 交换 52 | // 若相同,则不交换,直到最后一个元素为止 53 | for (int i = k; i < strs.length; i++) { 54 | 55 | // 注:此处需考虑字符重复的情况:若第1个字符与后面字符存在重复情况,则不交换(因为交换后的排列相同) 56 | if (i == k || strs[k] != strs[i]) { 57 | swap(strs, i, k); 58 | permutationCore(strs, ret, k + 1); 59 | swap(strs, i, k); 60 | 61 | } 62 | } 63 | } 64 | 65 | /** 66 | * 辅助算法2:交换字符位置 67 | */ 68 | public static void swap(char[] strs, int x, int y) { 69 | char temp = strs[x]; 70 | strs[x] = strs[y]; 71 | strs[y] = temp; 72 | 73 | } 74 | 75 | 76 | /** 77 | * 测试用例 78 | */ 79 | public static void main(String[] args) { 80 | 81 | // 功能测试1:无重复字符 字符串 82 | System.out.println("功能测试1:无重复字符 字符串"); 83 | System.out.println(permutation("abc")); 84 | 85 | 86 | // 功能测试2:重复字符 字符串 87 | System.out.println("功能测试2:重复字符 字符串"); 88 | System.out.println(permutation("aac")); 89 | 90 | // 特殊输入测试:输入字符为空 91 | System.out.println("特殊输入测试:输入字符为空"); 92 | System.out.println(permutation(null)); 93 | 94 | } 95 | 96 | } 97 | 98 | 99 | /** 100 | * 解题算法 101 | * @param strs 传入的字符串 102 | */ 103 | // public static List permutation(String strs) { 104 | // 105 | // // 1. 判断数据的合法性 106 | // if (strs == null || strs.length() == 0) { 107 | // System.out.println("输入的头节点为空"); 108 | // return null; 109 | // } 110 | // 111 | // char[] chars = strs.toCharArray();// 将字符串转换成数组便于处理 112 | // 113 | // // 2. 创建1链表用于存储排列 114 | // List ret = new LinkedList<>(); 115 | // // 3. 求出字符串的排列 116 | // permutationCore(chars, ret, 0); 117 | // return ret; 118 | // } 119 | // 120 | // /** 121 | // * 求字符串的排列 122 | // */ 123 | // public static void permutationCore(char[] strs, List ret, int k) { 124 | // // 下标 = k的字符 = 当前字符串的第1个字符 125 | // if (k == strs.length) 126 | // ret.add(Arrays.copyOf(strs, strs.length)); 127 | // 128 | // // 下标 = i的字符 = 当前字符串第1个字符的后面所有字符(i的范围 = [k,length) ) 129 | // // 下标 = k的字符依次与下标为i的字符交换 130 | // // 若相同,则不交换,直到最后一个元素为止 131 | // for (int i = k; i < strs.length; i++) { 132 | // if (i == k || strs[k] != strs[i]) { 133 | // swap(strs, k, i); 134 | // permutationCore(strs, ret, k + 1); 135 | // swap(strs, k, i); 136 | // } 137 | // } 138 | // } 139 | // 140 | // /** 141 | // * 交换字符位置 142 | // */ 143 | // public static void swap(char[] strs, int x, int y) { 144 | // char temp = strs[x]; 145 | // strs[x] = strs[y]; 146 | // strs[y] = temp; 147 | // } 148 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_55_1.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/22. 5 | */ 6 | 7 | public class Exam_55_1 { 8 | 9 | /** 10 | * 设置结点结构 11 | */ 12 | public static class TreeNode { 13 | int val; // 二叉树的结点数据 14 | TreeNode left; // 二叉树的左子树(左孩子) 15 | TreeNode right; // 二叉树的右子树(右孩子) 16 | 17 | public TreeNode(int data) { 18 | this.val = data; 19 | this.left = null; 20 | this.right = null; 21 | } 22 | } 23 | 24 | /** 25 | * 解题算法 26 | * 原理 = 后续遍历每个节点时,每遍历到1个节点,其左右子树已经遍历 依次自底向上判断即可,每个节点只需要遍历一次 27 | */ 28 | 29 | private static boolean isBalanced = true; // 用于存储是否是平衡二叉树 30 | 31 | public static boolean isBalanced(TreeNode root) { 32 | 33 | // 1. 判断输入数据的合法性 34 | // 注:空树也算 平衡二叉树 35 | if (root == null) 36 | return true; 37 | 38 | // 2. 通过后序遍历进行遍历 & 判断 39 | getDepth(root); 40 | 41 | return isBalanced; 42 | } 43 | 44 | /** 45 | * 辅助算法 46 | */ 47 | 48 | public static int getDepth(TreeNode root) { 49 | 50 | if (root == null) 51 | return 0; 52 | 53 | int left = getDepth(root.left); // 通过递归获取左子树的深度 54 | int right = getDepth(root.right); // 通过递归获取右子树的深度 55 | 56 | // 判断是否符合平衡二叉树的要求:即任意节点的左右子树深度之差不超过1 57 | // 边遍历、边判断 58 | if (Math.abs(left - right) > 1) { 59 | isBalanced = false; 60 | } 61 | 62 | return right > left ? right + 1 : left + 1; 63 | 64 | } 65 | 66 | 67 | 68 | 69 | /** 70 | * 测试用例 71 | */ 72 | public static void main(String[] args){ 73 | 74 | // 功能测试: 75 | // a. 是平衡二叉树 76 | TreeNode root = new TreeNode(1); 77 | root.left = new TreeNode(2); 78 | root.left.left = new TreeNode(4); 79 | root.left.right = new TreeNode(5); 80 | root.left.right.left = new TreeNode(7); 81 | root.right = new TreeNode(3); 82 | root.right.right = new TreeNode(6); 83 | System.out.println(isBalanced(root)); 84 | 85 | // b. 不是平衡二叉树 86 | TreeNode root2 = new TreeNode(1); 87 | root2.left = new TreeNode(2); 88 | root2.left.left = new TreeNode(4); 89 | root2.left.right = new TreeNode(5); 90 | root2.left.right.left = new TreeNode(7); 91 | root2.right = new TreeNode(3); 92 | System.out.println(isBalanced(root2)); 93 | 94 | // 特殊输入测试 95 | System.out.println(isBalanced(null)); 96 | 97 | } 98 | 99 | } 100 | 101 | // /** 102 | // * 解题算法 103 | // * 原理 = 后序遍历,记录每个节点的深度,从而可以通过一次遍历完成整棵树的判断 104 | // * 返回值 : true = 是平衡二叉树,false = 不是平衡二叉树 105 | // */ 106 | // public static boolean isBalanced(TreeNode node){ 107 | // 108 | // // 1. 判断输入数据的合法性 109 | // // 注:空树也算 平衡二叉树 110 | // if(node == null) 111 | // return true; 112 | // 113 | // // 2. 传入1个表示节点深度的整型变量 114 | // // 注:采用数组传递的原因:在Java中,基本数据类型无法使用引用传递 115 | // int[] depth = new int[1]; 116 | // return isBalancedCore(node,depth); 117 | // } 118 | // 119 | // /** 120 | // * 辅助算法 121 | // * 原理 = 对于某1子树,需给出它的是否是平衡性的判断 & 深度 122 | // * 返回值 = 此处将平衡性的判断作为返回值,深度通过长度为1的数组传递 123 | // * 注:采用数组传递的原因:在Java中,基本数据类型无法使用引用传递 124 | // */ 125 | // 126 | // public static boolean isBalancedCore(TreeNode node,int[] depth){ 127 | // // 1. 判断输入数据的合法性 128 | // if(node == null){ 129 | // depth[0] = 0; 130 | // return true; 131 | // } 132 | // 133 | // // 2. 定义2数组变量,用于保存节点的左、右子树深度 134 | // // 注:采用数组传递的原因:在Java中,基本数据类型无法使用引用传递 135 | // int[] left = new int[1]; 136 | // int[] right = new int[1]; 137 | // 138 | // // 3. 使用后序遍历遍历二叉树的每个节点 139 | // if(isBalancedCore(node.left,left) && isBalancedCore(node.right,right)){ 140 | // 141 | // // 每遍历1个节点,即记录下节点的深度 142 | // int diff = left[0] - right[0]; 143 | // 144 | // // 判断二叉树是不是平衡的 145 | // if(diff <=1 && diff >= -1){ 146 | // depth[0] = 1 + (left[0] > right[0] ? left[0] : right[0]); 147 | // return true; 148 | // } 149 | // } 150 | // return false; 151 | // } 152 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_60.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/29. 5 | */ 6 | 7 | public class Exam_60 { 8 | 9 | /** 10 | * 测试用例 11 | */ 12 | public static void main(String[] args) throws Exception { 13 | 14 | printProbability(2, 4); 15 | printProbability2(2, 4); 16 | } 17 | 18 | 19 | 20 | /** 21 | * 基于递归的 求解方式 22 | * 23 | * @param number 骰子个数 24 | * @param max 骰子的最大值 25 | */ 26 | public static void printProbability(int number, int max) { 27 | // 判断输入数据的合法性 28 | if (number < 1 || max < 1) { 29 | return; 30 | } 31 | 32 | // 定义1个数组 33 | // 长度 = 6n-n+1、第s-n 个元素存储和为s 的点数出现的次数 34 | int maxSum = number * max; 35 | int[] probabilities = new int[maxSum - number + 1]; 36 | probability(number, probabilities, max); 37 | 38 | double total = 1; 39 | for (int i = 0; i < number; i++) { 40 | total *= max; 41 | } 42 | 43 | for (int i = number; i <= maxSum; i++) { 44 | double ratio = probabilities[i - number] / total; 45 | System.out.printf("%-8.4f", ratio); 46 | } 47 | 48 | System.out.println(); 49 | 50 | } 51 | 52 | /** 53 | * @param number 骰子个数 54 | * @param probabilities 不同骰子数出现次数的计数数组 55 | * @param max 骰子的最大值 56 | */ 57 | private static void probability(int number, int[] probabilities, int max) { 58 | for (int i = 1; i <= max; i++) { 59 | probability(number, number, i, probabilities, max); 60 | } 61 | } 62 | 63 | /** 64 | * @param original 总的骰子数 65 | * @param current 剩余要处理的骰子数 66 | * @param sum 已经前面的骰子数和 67 | * @param probabilities 不同骰子数出现次数的计数数组 68 | * @param max 骰子的最大值 69 | */ 70 | private static void probability(int original, int current, int sum, int[] probabilities, int max) { 71 | if (current == 1) { 72 | probabilities[sum - original]++; 73 | } else { 74 | for (int i = 1; i <= max; i++) { 75 | probability(original, current - 1, i + sum, probabilities, max); 76 | } 77 | } 78 | } 79 | 80 | /*************************/ 81 | 82 | /** 83 | * 基于循环求解: f(n)=f(n-1)+f(n-2)+f(n-3)+f(n-4)+f(n-5)+f(n-6) 84 | * @param number 骰子个数 85 | * @param max 骰子的最大值 86 | */ 87 | public static void printProbability2(int number, int max) { 88 | // 检查输入数据的合法性 89 | if (number < 1 || max < 1) { 90 | return; 91 | } 92 | // 使用1个二维数组代替2个数组 93 | // 作用 = 存储骰子点数的每个总数出现的次数 94 | int[][] probabilities = new int[2][max * number + 1]; 95 | 96 | // 数据初始化 97 | for (int i = 0; i < max * number + 1; i++) { 98 | probabilities[0][i] = 0; 99 | probabilities[1][i] = 0; 100 | } 101 | 102 | // 标记当前要使用的是第0个数组还是第1个数组 103 | int flag = 0; 104 | 105 | // 抛出一个骰子时出现的各种情况 106 | for (int i = 1; i <= max; i++) { 107 | probabilities[flag][i] = 1; 108 | } 109 | 110 | // 抛出其它骰子 111 | for (int k = 2; k <= number; k++) { 112 | // 如果抛出了k个骰子,那么和为[0, k-1]的出现次数为0 113 | for (int i = 0; i < k; i++) { 114 | probabilities[1 - flag][i] = 0; 115 | } 116 | 117 | // 抛出k个骰子,所有和的可能 118 | for (int i = k; i <= max * k; i++) { 119 | probabilities[1 - flag][i] = 0; 120 | 121 | // 每个骰子的出现的所有可能的点数 122 | for (int j = 1; j <= i && j <= max; j++) { 123 | // 统计出和为i的点数出现的次数 124 | probabilities[1 - flag][i] += probabilities[flag][i - j]; 125 | } 126 | } 127 | 128 | flag = 1 - flag; 129 | } 130 | 131 | 132 | double total = 1; 133 | for (int i = 0; i < number; i++) { 134 | total *= max; 135 | } 136 | 137 | int maxSum = number * max; 138 | for (int i = number; i <= maxSum; i++) { 139 | double ratio = probabilities[flag][i] / total; 140 | System.out.printf("%-8.4f", ratio); 141 | } 142 | 143 | System.out.println(); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_29.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Created by Carson_Ho on 17/11/4. 7 | */ 8 | 9 | public class Exam_29 { 10 | 11 | /** 12 | * 解题算法:按照从外向里以顺时针的顺序依次打印每一个数字 13 | * 核心思路:通过1个循环打印矩阵,每次打印矩阵中的1个圈 14 | * @param matrix 输入的矩阵 (使用二维数组表示) 15 | */ 16 | 17 | static ArrayList result = new ArrayList (); // 用于存放结果 18 | 19 | public static ArrayList printMatrixClockWisely(int[][] matrix) { 20 | 21 | // 1. 判断输入的矩阵的异常情况:为空、仅有1行、仅有1列 22 | if (matrix == null || matrix.length <= 0 || matrix[0].length <= 0) { 23 | System.out.println("出现异常情况:输入的矩阵为空 / 仅有1行 / 仅有1列"); 24 | return null; 25 | } 26 | 27 | // 记录第1个圈开始位置的行 & 横坐标 28 | int x = 0; 29 | // 记录一圈开始位置的列 & 纵坐标 30 | int y = 0; 31 | 32 | // 解决子问题1:循环结束条件:行数 > 矩阵最后1圈左上角横坐标的2倍、列数 > 矩阵最后1圈左上角纵坐标的2倍 33 | // 矩阵的列数 = matrix.length、列号最大 = (matrix.length-1)/2 34 | // 矩阵的行数 = matrix[0].length、行号最大 = (matrix[0].length-1)/2 35 | while (matrix.length > (x * 2) && matrix[0].length > (y * 2) ) { 36 | 37 | // 解决子问题2:按规律 打印矩阵的每个圈(分为4步,但不是每步都必须) 38 | printMatrixInCircle(matrix, x, y); 39 | 40 | // 指向下一个要处理的圈的左上角位置 41 | x++; 42 | y++; 43 | } 44 | return result; 45 | 46 | } 47 | 48 | /** 49 | * 辅助算法:解决子问题2,即 按规律 打印矩阵的每个圈(分为4步,但不是每步都必须) 50 | */ 51 | 52 | public static void printMatrixInCircle(int[][] matrix, int x, int y) { 53 | 54 | int rows = matrix.length; // 数组的行数 = 矩阵行数 55 | int cols = matrix[0].length; // 数组的列数 = 矩阵列数 56 | 57 | int endX = cols - y - 1;// 终止列号 58 | int endY = rows - x - 1;// 终止行号 59 | 60 | 61 | // 第1步:从左 -> 右 打印1行 62 | // 前置条件:必需 63 | for (int i = y; i <= endX; i++) { 64 | System.out.print(matrix[x][i] + " "); 65 | result.add(matrix[x][i]); 66 | } 67 | 68 | // 第2步:2. 从上 -> 下 打印1列 69 | // 前置条件:终止行号 > 起始行号 70 | if (endY > x) { 71 | // 因为右边那一列的最上面那一个已经被输出了,所以行从起始行的第2行(x+1)开始, 72 | // 输出包括右边那列的最下面那个 73 | for (int i = x + 1; i <= endY; i++) { 74 | System.out.print(matrix[i][endX] + " "); 75 | result.add(matrix[i][endX]); 76 | } 77 | } 78 | 79 | // 第3步:从右 -> 左 打印1行 80 | // 前置条件:至少2行2列、即:终止行号 > 起始行号、终止列号 > 起始列号 81 | // cols-1-y:表示的是环最右那一列的列号 82 | if (endY > x && endX > y) { 83 | // 因为环的左下角的位置已经输出了,所以列号从倒数第2列(endX-1)开始 84 | for (int i = endX-1; i >= y; i--) { 85 | System.out.print(matrix[endY][i] + " "); 86 | result.add(matrix[endY][i]); 87 | } 88 | } 89 | 90 | // 第4步:从下 -> 上 打印1列 91 | // 前置条件:至少3行2列,即:终止行号 比 起始行号 大2、终止列号 > 起始列号 92 | // rows-x-1:终止行 = endY 93 | if (endX > y && endY > x + 1) { 94 | // 因为最左边那一列的第一个和最后一个已经被输出了 95 | // 所以从第2个开始、倒数第2个结束 96 | for (int i = endY - 1; i >= x + 1; i--) { 97 | System.out.print(matrix[i][y] + " "); 98 | result.add(matrix[i][y]); 99 | } 100 | } 101 | } 102 | 103 | 104 | /** 105 | * 测试用例 106 | */ 107 | public static void main(String[] args){ 108 | // 功能测试:多行多列、1行1列 109 | int[][] data1={ 110 | {1,2,3}, 111 | {8,9,4}, 112 | {7,6,5}, 113 | }; 114 | 115 | int[][] data2={ 116 | {1,2,3,4}, 117 | {10,11,12,5}, 118 | {9,8,7,6}, 119 | }; 120 | 121 | int[][] data3={ 122 | {1,2,3}, 123 | {8}, 124 | {7}, 125 | }; 126 | 127 | printMatrixClockWisely(data1); 128 | printMatrixClockWisely(data2); 129 | printMatrixClockWisely(data3); 130 | 131 | // 异常测试:仅有1行、仅有1列、空 132 | int[][] data4={ 133 | {1,2,3}, 134 | }; 135 | int[][] data5={ 136 | {1}, 137 | {8}, 138 | {7}, 139 | }; 140 | int[][] data6= null ; 141 | 142 | printMatrixClockWisely(data4); 143 | printMatrixClockWisely(data5); 144 | printMatrixClockWisely(data6); 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_35.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/7. 5 | */ 6 | 7 | public class Exam_35 { 8 | 9 | /** 10 | * 结点结构 11 | */ 12 | public static class RandomListNode { 13 | int label; 14 | RandomListNode next; 15 | RandomListNode random; 16 | 17 | public RandomListNode(int label) { 18 | this.label = label; 19 | } 20 | 21 | } 22 | 23 | /** 24 | * 解题思路3 25 | * 核心思想:分为3步 26 | * 1. 复制原始链表的每个节点,将复制节点(N`)放在对应的原始节点(N)后1个位置,即 N->N` 27 | * 2. 设置复制节点(N`)的m_pSibling指针,即 S -> S` 28 | * 3. 将该复制的长链表拆分成2个链表 29 | */ 30 | public static RandomListNode clone3(RandomListNode head) { 31 | 32 | // 判断输入节点是否为空 33 | if(head == null) { 34 | System.out.println("输入的头节点为空"); 35 | return null; 36 | } 37 | 38 | // 将3步分别封装成3个函数逐步实现 39 | 40 | // 1.复制原始链表的每个节点,将复制节点(N`)放在对应的原始节点(N)后1个位置 41 | cloneNodes(head); 42 | 43 | // 2.设置复制节点(N`)的m_pSibling指针 44 | connectRandomNodes(head); 45 | 46 | // 3.将该复制的长链表拆分成2个链表 47 | return reconnectNodes(head); 48 | } 49 | 50 | /** 51 | * 步骤1:复制原始链表的每个节点,将复制节点(N`)放在对应的原始节点(N)后1个位置 52 | * 核心思想:即添加节点到链表 53 | */ 54 | public static void cloneNodes(RandomListNode head){ 55 | RandomListNode cur = head; 56 | RandomListNode temp = null; 57 | while (cur!=null){ 58 | // 创建复制结点 & 插入 59 | temp = new RandomListNode(cur.label); 60 | temp.next = cur.next; 61 | cur.next = temp; 62 | 63 | cur = cur.next.next; 64 | } 65 | } 66 | 67 | /** 68 | * 步骤2:设置复制节点(N`)的m_pSibling指针 69 | * 核心思想:该指针指向的S`节点 = 原始节点(N) m_pSibling指针 指向的节点S的m_pNext指针指向的节点,即 S` = S的下1个结点:S -> S` 70 | */ 71 | public static void connectRandomNodes(RandomListNode head){ 72 | RandomListNode cur = head; 73 | RandomListNode curNext = head.next; 74 | 75 | while (true){ 76 | if(cur.random!=null) 77 | curNext.random = cur.random.next; 78 | 79 | cur = cur.next.next; 80 | if(cur == null) 81 | break; 82 | curNext = curNext.next.next; 83 | } 84 | } 85 | 86 | /** 87 | * 步骤3:将该复制的长链表拆分成2个链表: 88 | * 核心思想: 89 | * a. 原始链表:将奇数位置的节点用 m_pNext指针 连接起来 90 | * b. 复制链表 = 将数偶位置的节点用 m_pNext指针 连接起来 91 | */ 92 | public static RandomListNode reconnectNodes(RandomListNode head){ 93 | 94 | RandomListNode cur = head; // 原始链表的头节点 95 | RandomListNode newHead = head.next;// 复制链表的头节点 96 | RandomListNode newCur = newHead; 97 | 98 | while (true){ 99 | // 创建原始链表:将奇数位置的节点用m_pNext指针连接起来 100 | cur.next = cur.next.next; 101 | cur = cur.next; 102 | if(cur==null){ 103 | newCur.next = null; 104 | break; 105 | } 106 | 107 | // 复制链表 = 将数偶位置的节点用 m_pNext指针 连接起来 108 | newCur.next = newCur.next.next; 109 | newCur = newCur.next; 110 | } 111 | return newHead; 112 | } 113 | 114 | /** 115 | * 测试用例 116 | */ 117 | public static void main(String[] args){ 118 | // 功能测试 119 | RandomListNode head = new RandomListNode(1); 120 | RandomListNode c2 = new RandomListNode(2); 121 | RandomListNode c3 = new RandomListNode(3); 122 | RandomListNode c4 = new RandomListNode(4); 123 | RandomListNode c5 = new RandomListNode(5); 124 | head.next = c2; 125 | head.random = c3; 126 | head.next.next = c3; 127 | head.next.random = c5; 128 | head.next.next.next = c4; 129 | head.next.next.next.next = c5; 130 | head.next.next.next.random = c2; 131 | 132 | System.out.print("原始链表如下"); 133 | // 输出链表 134 | RandomListNode head1 = head; 135 | while (head1 != null) { 136 | System.out.print(head1.label); 137 | head1 = head1.next; 138 | } 139 | 140 | System.out.println(" "); 141 | System.out.print("复制链表如下"); 142 | RandomListNode head2 = clone3(head); 143 | // 输出链表 144 | while (head2 != null) { 145 | System.out.print(head2.label); 146 | head2 = head2.next; 147 | } 148 | 149 | // 特殊输入测试 150 | System.out.println(" "); 151 | System.out.println("复制链表 :"+'\t'+clone3(null)); 152 | } 153 | 154 | 155 | } 156 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_59_1.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | import java.util.Deque; 4 | import java.util.LinkedList; 5 | 6 | /** 7 | * Created by Carson_Ho on 17/11/25. 8 | */ 9 | 10 | public class Exam_59_1 { 11 | 12 | /** 13 | * 测试用例 14 | */ 15 | 16 | public static void main(String[] args) throws Exception { 17 | // 构造1队列 = { 1,5,4,6,1,2 } 18 | QueueWithMax queueWithMax1 = new QueueWithMax(); 19 | queueWithMax1.pushBack(1); 20 | System.out.println("queueWithMax1最大值:" + queueWithMax1.getMax()); 21 | queueWithMax1.pushBack(5); 22 | System.out.println("queueWithMax1最大值:" + queueWithMax1.getMax()); 23 | queueWithMax1.pushBack(4); 24 | System.out.println("queueWithMax1最大值:" + queueWithMax1.getMax()); 25 | queueWithMax1.pushBack(6); 26 | System.out.println("queueWithMax1最大值:" + queueWithMax1.getMax()); 27 | queueWithMax1.pushBack(1); 28 | System.out.println("queueWithMax1最大值:" + queueWithMax1.getMax()); 29 | queueWithMax1.pushBack(2); 30 | System.out.println("queueWithMax1最大值:" + queueWithMax1.getMax()); 31 | queueWithMax1.popFront(); 32 | queueWithMax1.popFront(); 33 | queueWithMax1.popFront(); 34 | System.out.println("queueWithMax1最大值:" + queueWithMax1.getMax()); 35 | queueWithMax1.popFront(); 36 | System.out.println("queueWithMax1最大值:" + queueWithMax1.getMax()); 37 | queueWithMax1.popFront(); 38 | System.out.println("queueWithMax1最大值:" + queueWithMax1.getMax()); 39 | 40 | } 41 | 42 | /** 43 | * 解题类 44 | */ 45 | static class QueueWithMax { 46 | 47 | private int currentIndex = 0; 48 | 49 | // 创建2个辅助队列 50 | private Deque dateDeque = new LinkedList<>(); // 队列1:存放原始数据的原始队列 51 | private Deque maxDeque = new LinkedList<>(); // 队列2:双端开口的队列,存储 可能成为 队列最大值 的数值 52 | 53 | /** 54 | * 入队,将数字加到队尾 55 | * @param number 入队数字 56 | */ 57 | public void pushBack(int number) { 58 | 59 | 60 | // 队列2 61 | // a. 若入队的数m ≥ 队列队尾元素时:不断出队 队尾元素、直到新的队尾元素 > m 或 队列为空、将 m 入队 到队尾 62 | // b. 若入队的数m < 队列队尾元素时:则直接将m入队到队尾 63 | while (!maxDeque.isEmpty() && number >= maxDeque.peekLast().number) { 64 | maxDeque.pollLast(); 65 | } 66 | 67 | // 注:将入队数m入队到队尾时,此处需用到1个辅助类,因题目要求 = 队列,不是数组,所以需创建1个有数值下标(index)的Data类 68 | InterData newData = new InterData(number, currentIndex); 69 | maxDeque.addLast(newData); 70 | 71 | // 队列1正常入队 72 | dateDeque.addLast(newData); 73 | 74 | ++ currentIndex; 75 | } 76 | 77 | /** 78 | * 出队,移出队头数字 79 | * 80 | * @return 出队数字 81 | * @throws EmptyQueueException 队列是空的时候会抛出这个异常 82 | */ 83 | public int popFront() throws EmptyQueueException { 84 | 85 | if (maxDeque.isEmpty()) { 86 | throw new EmptyQueueException(); 87 | } 88 | // 队列2 89 | // 若在队列1(原始队列)需出队的是最大元素,则队列2也需出队 队头元素(最大元素) 90 | if (maxDeque.getFirst().index == dateDeque.getFirst().index) { 91 | maxDeque.removeFirst(); 92 | } 93 | // 队列1正常出队 94 | return dateDeque.removeFirst().number; 95 | } 96 | 97 | /** 98 | * 获取队列里面元素的最大值 99 | * 原理:根据队列2的特点,直接获取 存放队列2的队头元素 即可 100 | * @return 最大值 101 | * @throws EmptyQueueException 队列是空的时候会抛出这个异常 102 | */ 103 | public int getMax() throws EmptyQueueException { 104 | if (maxDeque.isEmpty()) { 105 | throw new EmptyQueueException(); 106 | } 107 | return maxDeque.peekFirst().number; 108 | } 109 | 110 | /** 111 | * 辅助类1:数据结构类 112 | * 因题目要求 = 队列,不是数组,所以需创建1个有数值下标(index)的Data类 113 | */ 114 | private class InterData { 115 | public InterData(int number, int index) { 116 | this.number = number; 117 | this.index = index; 118 | } 119 | 120 | int number; 121 | int index; 122 | } 123 | /** 124 | * 辅助类2:抛出异常类 125 | * 因题目要求 = 队列,不是数组,所以需创建1个有数值下标(index)的Data类 126 | */ 127 | private class EmptyQueueException extends Exception { 128 | public EmptyQueueException() { 129 | super("Queue is empty!"); 130 | } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_67.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/30. 5 | */ 6 | 7 | public class Exam_67 { 8 | 9 | // 设置一个全局变量,用于判断:当输出为0时,是字符串输入异常还是输入字符串为'0' 10 | // true = 输入异常、false = 输入字符串为'0' 11 | public static boolean flag; 12 | 13 | /** 14 | * 解题算法 15 | */ 16 | 17 | public static int StrToInt(String str) { 18 | 19 | flag = false; 20 | 21 | // 非法输入1:空指针 & 空字符串 “ ” 22 | if (str == null || str.length() < 1 || str.trim().equals("")) { 23 | flag = true; 24 | System.out.println("非法输入1:空指针 & 空字符串 “ ”"); 25 | return 0; 26 | } 27 | 28 | 29 | // 字符串开头 = “+”号 / “-”号的处理 30 | char first = str.charAt(0); 31 | if (first == '-') { 32 | // 将该开头-符号传入 到 转换字符串操作 中 33 | return parseString(str, 1, false); 34 | 35 | } else if (first == '+') { 36 | // 将该开头+符号传入到转换字符串中 37 | return parseString(str, 1, true); 38 | 39 | 40 | } else if (first <= '9' && first >= '0') { 41 | // 或开头没有+、-符号,那数字必须是 ‘0’~‘9’ 42 | return parseString(str, 0, true); 43 | 44 | } else { 45 | // 非法输入2:第1位含 ‘0’~‘9’、‘+’、‘ - ’ 以外的字符 46 | flag = true; 47 | System.out.println("非法输入2:第1位含 ‘0’~‘9’、‘+’、‘ - ’ 以外的字符"); 48 | return 0; 49 | } 50 | } 51 | 52 | /** 53 | * 辅助算法1:解析字符串 54 | * 55 | * @param str 数字串 56 | * @param index 开始解析的索引 57 | * @param positive 是正数还是负数 58 | * @return 返回结果 59 | */ 60 | private static int parseString(String str, int index, boolean positive) { 61 | // 非法输入3:当字符串仅有 "+" 或 "-"号时,也算非法输入 62 | if (index >= str.length()) { 63 | flag = true; 64 | System.out.println("非法输入3:字符串仅有 \"+\" 或 \"-\"号"); 65 | return 0; 66 | } 67 | 68 | int result; 69 | long tmp = 0; 70 | 71 | // 非法输入4:其余位含 ‘0’~‘9’以外的字符 72 | while (index < str.length() && isDigit(str.charAt(index))) { 73 | // 若字符串中除第1位外,其余位 属于 ‘0’~‘9’,则转换字符串为整数 74 | // 转换原理:每扫描字符串中的1个字符,将之前得到的结果 乘以10 再加上 当前扫描字符表示的数字 75 | tmp = tmp * 10 + str.charAt(index) - '0'; 76 | 77 | // 非法输入5:溢出 78 | // 保证求得的值不溢出,即,整数不能上下溢出 最大值不可大于Integer.MAX_VALUE (2^31-1) ,最小值不可小于Integer.MIN_VALUE(-2^31) 79 | // 根据字符串的第1位正 / 负,判断求得的值是否溢出 80 | if ((positive && tmp >Integer.MAX_VALUE) || (!positive && tmp < Integer.MIN_VALUE)) { 81 | 82 | flag = true; 83 | System.out.println("溢出了"); 84 | return 0; 85 | } 86 | 87 | index++; 88 | } 89 | 90 | // 其余位不含 ‘0’~‘9’以外的字符时,输出转换后的整数 91 | if (index == str.length()) { 92 | if (positive) { 93 | result = (int) tmp; 94 | return result; 95 | } else { 96 | result = (int) -tmp; 97 | return result; 98 | } 99 | } else { 100 | flag = true; 101 | System.out.println("其余位含 ‘0’~‘9’以外的字符"); 102 | return 0; 103 | } 104 | 105 | 106 | } 107 | 108 | /** 109 | * 辅助算法2:判断字符是否是数字 110 | * 111 | * @param c 字符 112 | * @return true是,false否 113 | */ 114 | private static boolean isDigit(char c) { 115 | return c >= '0' && c <= '9'; 116 | } 117 | 118 | 119 | /** 120 | * 测试用例 121 | */ 122 | public static void main(String[] args) { 123 | // 功能测试 124 | System.out.println(StrToInt("+345")); // 正数 125 | System.out.println(""); 126 | System.out.println(StrToInt("-345")); // 负数 127 | System.out.println(""); 128 | System.out.println(StrToInt("0")); // 0 129 | System.out.println(""); 130 | 131 | // 异常输入测试:空指针、空字符串、、其余位 含 ‘0’~‘9’以外的字符、字符串 仅有"+" 或 "-"号 132 | System.out.println(StrToInt(null)); // 空指针 133 | System.out.println(flag); 134 | System.out.println(""); 135 | 136 | System.out.println(StrToInt("")); // 空字符串 137 | System.out.println(flag); 138 | System.out.println(""); 139 | 140 | System.out.println(StrToInt("a345")); // 第1位含 ‘0’~‘9’、‘+’、‘ - ’ 以外的字符 141 | System.out.println(flag); 142 | System.out.println(""); 143 | 144 | System.out.println(StrToInt("345a")); // 其余位 含 ‘0’~‘9’以外的字符 145 | System.out.println(flag); 146 | System.out.println(""); 147 | 148 | System.out.println(StrToInt("+")); // 字符串 仅有"+" 或 "-"号 149 | System.out.println(flag); 150 | System.out.println(""); 151 | } 152 | } -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_68.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | import java.util.Iterator; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | /** 8 | * Created by Carson_Ho on 17/12/1. 9 | */ 10 | 11 | public class Exam_68 { 12 | 13 | /** 14 | * 辅助类结构:树的节点结构 15 | */ 16 | public static class TreeNode { 17 | int val; // 二叉树的结点数据 18 | List children = new LinkedList<>(); // 保存路径 19 | 20 | public TreeNode(int data) { 21 | this.val = data; 22 | } 23 | } 24 | 25 | /** 26 | * 解题算法 27 | * @param root 树的根结点 28 | * @param p1 结点1 29 | * @param p2 结点2 30 | * @return 公共结点,没有返回null 31 | */ 32 | public static TreeNode getLastCommonParent(TreeNode root, TreeNode p1, TreeNode p2) { 33 | // 判断输入节点的合法性 34 | if (root == null || p1 == null || p2 == null) { 35 | System.out.println("输入的节点存在空节点"); 36 | return null; 37 | } 38 | 39 | // 1. 创建2个链表,用于保存 从根节点到输入2个节点的路径 40 | List path1 = new LinkedList<>(); 41 | List path2 = new LinkedList<>(); 42 | 43 | // 2. 求出从根节点到输入2个节点的路径 & 保存到链表中 44 | getNodePath(root, p1, path1); 45 | getNodePath(root, p2, path2); 46 | 47 | // 3. 寻找2个链表中的最后1个公共节点 48 | return getLastCommonNode(path1, path2); 49 | } 50 | 51 | /** 52 | * 辅助方法1:求出 从根节点到输入2个节点的路径 53 | * 54 | * @param root 根结点 55 | * @param target 目标结点 56 | * @param path 从根结点到目标结点的路径(保存位置) 57 | */ 58 | public static void getNodePath(TreeNode root, TreeNode target, List path) { 59 | if (root == null) { 60 | return; 61 | } 62 | 63 | // 添加当前结点 64 | path.add(root); 65 | 66 | List children = root.children; 67 | 68 | // 处理子结点 69 | for (TreeNode node : children) { 70 | 71 | if (node == target) { 72 | path.add(node); 73 | return; 74 | } else { 75 | getNodePath(node, target, path); 76 | } 77 | } 78 | // 返回父节点前,在路径上删除当前节点 79 | // 因为此节点已经走不下去到达目标节点,所以需要继续找下1个路径 80 | path.remove(path.size() - 1); 81 | } 82 | 83 | 84 | /** 85 | * 辅助算法2:寻找2个链表中的最后1个公共节点 86 | * 原理:同时遍历 & 比较 2个链表的节点 87 | * @param p1 路径1 88 | * @param p2 路径2 89 | * @return 共同的结点,没有返回null 90 | */ 91 | 92 | public static TreeNode getLastCommonNode(List p1, List p2) { 93 | Iterator ite1 = p1.iterator(); 94 | Iterator ite2 = p2.iterator(); 95 | TreeNode last = null; 96 | 97 | while (ite1.hasNext() && ite2.hasNext()) { 98 | TreeNode tmp = ite1.next(); 99 | if (tmp == ite2.next()) { 100 | last = tmp; 101 | } 102 | } 103 | 104 | return last; 105 | 106 | } 107 | 108 | /** 109 | * 测试用例 110 | */ 111 | public static void main(String[] args){ 112 | // 功能测试1:形状普通的树 113 | System.out.print("功能测试1:形状普通的树:"); 114 | 115 | // 1 116 | // / \ 117 | // 2 3 118 | // / \ 119 | // 4 5 120 | // / \ / | \ 121 | // 6 7 8 9 10 122 | TreeNode n1 = new TreeNode(1); 123 | TreeNode n2 = new TreeNode(2); 124 | TreeNode n3 = new TreeNode(3); 125 | TreeNode n4 = new TreeNode(4); 126 | TreeNode n5 = new TreeNode(5); 127 | TreeNode n6 = new TreeNode(6); 128 | TreeNode n7 = new TreeNode(7); 129 | TreeNode n8 = new TreeNode(8); 130 | TreeNode n9 = new TreeNode(9); 131 | TreeNode n10 = new TreeNode(10); 132 | 133 | n1.children.add(n2); 134 | n1.children.add(n3); 135 | 136 | n2.children.add(n4); 137 | n2.children.add(n5); 138 | 139 | n4.children.add(n6); 140 | n4.children.add(n7); 141 | 142 | n5.children.add(n8); 143 | n5.children.add(n9); 144 | n5.children.add(n10); 145 | 146 | // 调用解题算法,获得2个链表中的公共节点 147 | TreeNode common = getLastCommonParent(n1, n6, n8); 148 | System.out.println(common.val); 149 | 150 | // 功能测试2:树退化成链状的树 151 | System.out.print("功能测试2:树退化成链状的树:"); 152 | 153 | // 1 154 | // / 155 | // 2 156 | // / 157 | // 3 158 | // / 159 | // 4 160 | // / 161 | // 5 162 | 163 | TreeNode m1 = new TreeNode(1); 164 | TreeNode m2 = new TreeNode(2); 165 | TreeNode m3 = new TreeNode(3); 166 | TreeNode m4 = new TreeNode(4); 167 | TreeNode m5 = new TreeNode(5); 168 | TreeNode m6 = new TreeNode(6); 169 | 170 | m1.children.add(m2); 171 | m2.children.add(m3); 172 | m3.children.add(m4); 173 | m4.children.add(m5); 174 | // 调用解题算法,获得2个链表中的公共节点 175 | TreeNode common2 = getLastCommonParent(n1, n4, n5); 176 | System.out.println(common2.val); 177 | 178 | // 特殊输入测试:树的指针 = 空指针null 179 | System.out.print("特殊输入测试:树的指针 = 空指针null:"); 180 | System.out.println(getLastCommonParent(null, n4, n5)); 181 | 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_19.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/10/31. 5 | */ 6 | 7 | public class Exam_19 { 8 | 9 | /** 10 | * 解题算法:模式匹配算法 11 | * @param str 目标字符串 12 | * @param pattern 模式字符串 13 | */ 14 | 15 | public static boolean match(char[] str, char[] pattern) { 16 | // 1. 检查数据的合法性 17 | if (str == null || pattern == null) { 18 | return false; 19 | } 20 | 21 | int strIndex = 0; // 当前匹配的目标字符串的字符下标 22 | int patternIndex = 0; // 当前匹配的模式字符串的字符下标 23 | 24 | // 2. 进行模式匹配 25 | // 返回true = 匹配成功 26 | // 返回false = 匹配失败 27 | return matchCore(str, strIndex, pattern, patternIndex); 28 | } 29 | 30 | /** 31 | * 辅助算法:详细的模式匹配 32 | * @param str 目标字符串 33 | * @param strIndex 当前匹配的目标字符串的字符下标 34 | * @param pattern 模式字符串 35 | * @param patternIndex 当前匹配的模式字符串的字符下标 36 | */ 37 | 38 | public static boolean matchCore(char[] str, int strIndex, char[] pattern, int patternIndex) { 39 | // 有效性检验:若目标字符串 & 模式字符串的下标同时到尾,则匹配成功 40 | if (strIndex == str.length && patternIndex == pattern.length) { 41 | return true; 42 | } 43 | // 若模式字符串的下标先到尾,匹配失败 44 | if (strIndex != str.length && patternIndex == pattern.length) { 45 | return false; 46 | } 47 | 48 | // 1. 若 当前匹配的模式字符串的下1个字符 = * & 目标字符串第当前字符跟模式字符串当前字符匹配 49 | // 分3种匹配模式; 50 | // 1. 视为匹配了0个字符,即 在模式字符串上直接后移2位(模式字符串中的"x*"被忽略) 51 | // 2. 视为匹配了1个字符,即 字符串后移1字符,模式后移2字符; 52 | // 3. 视为*匹配多为,即 字符串后移1字符,模式不变,即继续匹配字符下一位 53 | if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') { 54 | 55 | if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) { 56 | return matchCore(str, strIndex, pattern, patternIndex + 2) 57 | || matchCore(str, strIndex + 1, pattern, patternIndex + 2) 58 | || matchCore(str, strIndex + 1, pattern, patternIndex); 59 | } else { 60 | 61 | // 2. 若 当前匹配的模式字符串的下1个字符 = * & 目标字符串当前字符 跟 模式字符串当前字符不匹配 62 | // 则模式后移2个字符,继续匹配 63 | return matchCore(str, strIndex, pattern, patternIndex + 2); 64 | } 65 | } 66 | // 3. 若当前匹配的模式字符串的下1个字符不 = * 且 目标字符串当前字符 跟 模式字符串当前字符匹配 67 | // 则视为匹配成功,目标字符串 & 模式字符串都后移1位,继续匹配剩余的 68 | // 视为匹配不成功,直接返回false 69 | if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) { 70 | return matchCore(str, strIndex + 1, pattern, patternIndex + 1); 71 | } 72 | return false; 73 | } 74 | 75 | /** 76 | * 测试模式匹配算法 77 | */ 78 | public static void main(String[] args){ 79 | // 功能测试用例:模式字符串含普通字符、'.'、'*' 80 | char[] a = {'a','a','a'}; 81 | char[] b = {'a','.','a'}; 82 | char[] c = {'a','b','*','a','c','*','a'}; 83 | char[] d = {'a','a','.'}; 84 | char[] e = {'a','a','*','a'}; 85 | System.out.println(match(a,b));//true 86 | System.out.println(match(a,c));//true 87 | System.out.println(match(a,d));//true 88 | System.out.println(match(a,e));//true 89 | 90 | // 异常测试用例:为空 、空格字符串 91 | char[] f = {' '}; 92 | System.out.println(match(null,null));//false 93 | System.out.println(match(f, f));//true 94 | } 95 | } 96 | 97 | // 旧的 98 | 99 | // public static boolean match(String str,String pattern){ 100 | // 101 | // // 1. 检查数据的合法性 102 | // if(str == null || pattern == null) 103 | // return false; 104 | // 105 | // // 2. 进行模式匹配 106 | // // 返回true = 匹配成功 107 | // // 返回false = 匹配失败 108 | // return matchCore(new StringBuilder(str),0,new StringBuilder(pattern),0); 109 | // } 110 | // 111 | // /** 112 | // * 辅助算法:详细的模式匹配 113 | // * @param str 目标字符串 114 | // * @param strIndex 当前匹配的目标字符串的字符下标 115 | // * @param pattern 模式字符串 116 | // * @param patternIndex 当前匹配的模式字符串的字符下标 117 | // */ 118 | // 119 | // public static boolean matchCore(StringBuilder str,Integer strIndex,StringBuilder pattern, Integer patternIndex){ 120 | // 121 | // // 若下标 = 字符串长度,则代表匹配结束 122 | // if(strIndex == str.length() && patternIndex==pattern.length()) 123 | // return true; 124 | // // 若仅有其中1个匹配结束,则代表不匹配 125 | // if(strIndex == str.length() || patternIndex == pattern.length()) 126 | // return false; 127 | // // 若当前匹配的模式字符串的第2个字符不是* Or 只剩1个字符 128 | // if(patternIndex==pattern.length()-1|| pattern.charAt(patternIndex+1)!='*'){ 129 | // if(pattern.charAt(patternIndex)=='.' || pattern.charAt(patternIndex)==str.charAt(strIndex)) 130 | // return matchCore(str,strIndex+1,pattern,patternIndex+1); 131 | // else 132 | // return false; 133 | // } 134 | // // 否则,当前匹配的模式字符串的第2个字符不是 = "*",则直接跳过 135 | // else{ 136 | // if(pattern.charAt(patternIndex)==str.charAt(strIndex)) 137 | // return matchCore(str,strIndex+1,pattern,patternIndex) ||matchCore(str,strIndex+1,pattern,patternIndex+2); 138 | // else 139 | // return matchCore(str,strIndex,pattern,patternIndex+2); 140 | // } 141 | // } 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /app/src/main/java/scut/carson_ho/shootatoffer/Exam_36.java: -------------------------------------------------------------------------------- 1 | package scut.carson_ho.shootatoffer; 2 | 3 | /** 4 | * Created by Carson_Ho on 17/11/8. 5 | */ 6 | 7 | public class Exam_36 { 8 | 9 | /** 10 | * 节点结构 11 | */ 12 | public static class TreeNode { 13 | int val; 14 | TreeNode left; 15 | TreeNode right; 16 | 17 | public TreeNode(int val) { 18 | this.val = val; 19 | } 20 | 21 | } 22 | 23 | /** 24 | * 解题算法 25 | * 原理:中序遍历 26 | * 27 | * @param root 二叉树的根结点 28 | * @return 双向链表的头结点 29 | */ 30 | 31 | static TreeNode realHead = null;// 排序 / 拼接后(即根节点连接上左、右子树后),链表的第1个结点 32 | static TreeNode tail = null; // 每次排序 / 拼接后(即根节点连接上左、右子树后),链表的最后1个结点 33 | 34 | public static TreeNode Convert(TreeNode pRootOfTree) { 35 | 36 | if(pRootOfTree == null) { 37 | System.out.print(" 输入的根节点为空"); 38 | return null; 39 | } 40 | 41 | // 执行辅助算法 42 | ConvertSub(pRootOfTree); 43 | return realHead; 44 | } 45 | 46 | /** 47 | * 辅助算法 48 | * 原理:中序遍历 49 | */ 50 | private static void ConvertSub(TreeNode pRootOfTree) { 51 | 52 | if(pRootOfTree == null) { 53 | return; 54 | } 55 | 56 | // 1. 遍历左子树,即 转换左子树为链表 57 | ConvertSub(pRootOfTree.left); 58 | 59 | // 2. 遍历到根节点时,连接 根节点 与 当前转换的链表 60 | // 即,连接根节点 与 当前双向链表的尾节点 :设置尾结点的后继 = 根节点、根节点的前驱元素 = 尾结点 61 | // 当前双向链表 = 已排序的左子树、当前双向链表的尾节点 = 左子树最大值 62 | if (tail == null) { 63 | tail = pRootOfTree; 64 | realHead = pRootOfTree; 65 | } else { 66 | tail.right = pRootOfTree; // a. 设置尾结点的后继 = 根节点 67 | pRootOfTree.left = tail; // b. 设置根节点的前驱元素 = 尾结点 68 | tail = pRootOfTree; 69 | } 70 | // 3. 遍历右子树,即 转换右子树为链表 71 | ConvertSub(pRootOfTree.right); 72 | 73 | } 74 | 75 | 76 | 77 | 78 | 79 | /** 80 | * 与解题算法无关,仅用于测试时打印二叉树 81 | */ 82 | 83 | private static void printTree(TreeNode root) { 84 | if (root != null) { 85 | printTree(root.left); 86 | System.out.print(root.val + " "); 87 | printTree(root.right); 88 | } 89 | } 90 | 91 | /** 92 | * 与解题算法无关,仅用于测试时打印链表 93 | */ 94 | private static void printList(TreeNode head) { 95 | while (head != null) { 96 | System.out.print(head.val + " "); 97 | head = head.right; 98 | } 99 | } 100 | 101 | /** 102 | * 测试用例 103 | */ 104 | 105 | public static void main(String[] args) { 106 | // 功能测试 107 | // 10 108 | // / \ 109 | // 6 14 110 | // / \ / \ 111 | // 4 8 12 16 112 | TreeNode root = new TreeNode(10); 113 | root.left = new TreeNode(6); 114 | root.right = new TreeNode(14); 115 | root.left.left = new TreeNode(4); 116 | root.left.right = new TreeNode(8); 117 | root.right.left = new TreeNode(12); 118 | root.right.right = new TreeNode(16); 119 | 120 | 121 | System.out.print("原始的二叉树:"); 122 | // 输出二叉树 123 | printTree(root); 124 | 125 | System.out.println(" "); 126 | System.out.print("转换后的链表:"); 127 | // 输出链表 128 | printList(Convert(root)); 129 | 130 | // 特殊输入测试 131 | System.out.println(" "); 132 | System.out.print("转换后的链表:" ); 133 | printList(Convert(null)); 134 | } 135 | } 136 | 137 | 138 | /** 139 | * 解题算法(剑指的) 140 | * 141 | * @param root 二叉树的根结点 142 | * @return 双向链表的头结点 143 | */ 144 | // public static TreeNode convert(TreeNode root) { 145 | // 146 | // // 1. 双向链表的尾结点 147 | // // 使用一个长度为1的数组存储,类似C++中的二级指针 148 | // TreeNode[] lastNode = new TreeNode[1]; 149 | // 150 | // // 2. 链表转换过程如下函数 151 | // convertNode(root, lastNode); 152 | // 153 | // // 3. 通过链表的尾节点的前驱指针,找到双向链表的头结点 154 | // TreeNode head = lastNode[0]; 155 | // while (head != null && head.left != null) { 156 | // head = head.left; 157 | // } 158 | // return head; 159 | // } 160 | // 161 | // 162 | // /** 163 | // * 链表转换 164 | // * 165 | // * @param node 当前的根结点 166 | // * @param lastNode 当前双向链表的尾结点 167 | // */ 168 | // public static void convertNode(TreeNode node, TreeNode[] lastNode) { 169 | // // 1. 检查输入的合法性 170 | // if (node == null) { 171 | // System.out.print(" 输入的根节点为空"); 172 | // return; 173 | // } 174 | // 175 | // // 1. 如果有左子树,就先转换左子树 176 | // if (node.left != null) { 177 | // convertNode(node.left, lastNode); 178 | // } 179 | // 180 | // // 2. 连接 根节点 与 当前转换的链表 181 | // // 即,连接根节点 与 当前双向链表的尾节点 :设置根节点的前驱元素 = 尾结点、尾结点的后继 = 根节点 182 | // // 当前双向链表 = 已排序的左子树、当前双向链表的尾节点 = 左子树最大值 183 | // 184 | // node.left = lastNode[0]; // a. 设置根节点的前驱元素 = 尾结点 185 | // 186 | // if (lastNode[0] != null) { 187 | // lastNode[0].right = node; 188 | // } // b. 设置尾结点的后继 = 根节点 189 | // 190 | // 191 | // // 3. 此时,当前链表的尾节点 = 根节点 192 | // // 进行当前链表尾节点更新 193 | // lastNode[0] = node; 194 | // 195 | // // 4. 通过 转换右子树 & 连接右子树的最小值 与根节点 196 | // if (node.right != null) { 197 | // convertNode(node.right, lastNode); 198 | // } 199 | // 200 | // 201 | // } 202 | --------------------------------------------------------------------------------