├── .gitignore ├── Java 基础提高 ├── Java 基础提高.iml ├── media │ └── 搞懂基本排序算法.md ├── src │ ├── Annotation.java │ ├── ClassAnnotation │ │ ├── JsonAnnotation.java │ │ └── JsonProcessor.java │ ├── DesignMode │ │ ├── BasicSingleTon.java │ │ ├── EnumSingleTon.java │ │ ├── LazyBasicSingleTon.java │ │ ├── SingleTon.java │ │ ├── StaticInnerSingleTon.java │ │ ├── SyncSingleTon.java │ │ └── VolatileSingleTon.java │ ├── Reflection │ │ ├── ReflectionTest.java │ │ ├── Student.java │ │ └── TopStudent.java │ ├── proxy │ │ ├── Client.java │ │ ├── DynamicProxy.java │ │ ├── HouseOwner.java │ │ └── RentHouse.java │ └── sync │ │ ├── JoinTest.java │ │ ├── SyncObjectTest.java │ │ ├── TestDoubleSyncMethod.java │ │ ├── TestMethodRunnable.java │ │ ├── TestOtherMethodRunnable.java │ │ └── TestStaticMethodRunnable.java ├── 二叉树遍历.md ├── 夯实 Java 基础 - 反射.md ├── 搞懂 JAVA 内部类.md ├── 算法总结 │ ├── 搞懂单链表常见面试题.md │ └── 数据结构-二叉树基本知识.md └── 集合框架源码 │ ├── ArrayList 源码分析.md │ ├── HashMap 源码分析.md │ ├── LinkedList 源码分析.md │ ├── 关于 Java List 容器的源码分析的补充.md │ ├── 搞懂 HashSet & LinkedHashSet 源码 以及集合常见面试题目.md │ ├── 搞懂 Java equals 和 hashCode 方法.md │ └── 搞懂 LinkedHashMap 源码.md ├── README.md ├── src ├── ArrayAMartix │ ├── ArrOrMatrixMaxSum.java │ ├── ArrayLIstSourceCodeLearn.java │ ├── FindHalfMajor.java │ ├── FindKLagestNum.java │ ├── FindLILArray.java │ ├── FindLastK.java │ ├── FindLocalMinimum.java │ ├── FindMedianSortedArrays.java │ ├── FindSumKMaxLength.java │ ├── GetMinLengthNeedSort.java │ ├── MatrixContainK.java │ ├── MatrixPrint.java │ ├── MaxArea.java │ ├── Merge2SortedArr.java │ ├── MinSubArrayLen.java │ ├── ModifyArr.java │ ├── NaNTest.java │ ├── PrintUniquePairArr.java │ ├── SortArrAsNature.java │ └── SortColors.java ├── BinaryTree │ ├── BinaryOrder.java │ ├── BinaryTreeUtils.java │ └── TreeNode.java ├── NodePractice │ ├── NodePractice.java │ └── 搞懂单链表常见面试题.md ├── Set │ ├── ThreeSum.java │ ├── isAnagram.java │ └── isHappyNums.java ├── Sorting_Basic │ ├── Insertion_Sort │ │ ├── InsertIonSort.java │ │ └── InsertionSortAdvance.java │ ├── Selection_Sort │ │ ├── SelectionSort.java │ │ ├── SortTestHelper.java │ │ └── Student.java │ ├── merge_sort │ │ └── MergerSort.java │ └── quick_sort │ │ ├── QuickSort.java │ │ ├── QuickSort2Ways.java │ │ └── QuickSort3ways.java ├── String │ └── Palindrome.java ├── TestUItls │ ├── DoubleNode.java │ ├── Node.java │ └── TestUtils.java ├── day1 │ ├── LeetCode461.java │ ├── LeetCode657.java │ └── leetCode760.java ├── day10 │ ├── zcy2_10.java │ ├── zcy2_11.java │ └── zcy2_9.java ├── day11 │ ├── zcy2_12.java │ ├── zcy2_13.java │ ├── zcy2_14.java │ └── zcy2_15.java ├── day12 │ └── PrintMatrix.java ├── day2 │ ├── leetCode561.java │ ├── leetCode617.java │ └── leetCode728.java ├── day3 │ ├── leetCode339.java │ ├── leetCode359.java │ └── leetCode476.java ├── day4 │ ├── zcy1.java │ └── zcy2.java ├── day5 │ ├── zcy3.java │ ├── zcy4.java │ └── zcy5.java ├── day6 │ ├── zcy6.java │ └── zcy7.java ├── day7 │ ├── zcy2_1.java │ ├── zcy2_2.java │ └── zcy2_3.java ├── day8 │ ├── zcy2_4.java │ └── zcy2_5.java └── day9 │ ├── zcy2_6.java │ ├── zcy2_7.java │ └── zcy2_8.java └── 剑指offer ├── src ├── FindTheDupliNum.java ├── PrintLinkListReverse.java ├── RebuildBinaryTree.java ├── ReplaceBlank.java └── SimpleStack.java └── 剑指offer.iml /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff: 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/dictionaries 10 | 11 | # Sensitive or high-churn files: 12 | .idea/**/dataSources/ 13 | .idea/**/dataSources.ids 14 | .idea/**/dataSources.xml 15 | .idea/**/dataSources.local.xml 16 | .idea/**/sqlDataSources.xml 17 | .idea/**/dynamic.xml 18 | .idea/**/uiDesigner.xml 19 | 20 | # Gradle: 21 | .idea/**/gradle.xml 22 | .idea/**/libraries 23 | 24 | # CMake 25 | cmake-build-debug/ 26 | 27 | # Mongo Explorer plugin: 28 | .idea/**/mongoSettings.xml 29 | 30 | ## File-based project format: 31 | *.iws 32 | 33 | ## Plugin-specific files: 34 | 35 | # IntelliJ 36 | out/ 37 | 38 | # mpeltonen/sbt-idea plugin 39 | .idea_modules/ 40 | 41 | # JIRA plugin 42 | atlassian-ide-plugin.xml 43 | 44 | # Cursive Clojure plugin 45 | .idea/replstate.xml 46 | 47 | # Crashlytics plugin (for Android Studio and IntelliJ) 48 | com_crashlytics_export_strings.xml 49 | crashlytics.properties 50 | crashlytics-build.properties 51 | fabric.properties 52 | ### Java template 53 | # Compiled class file 54 | *.class 55 | 56 | # Log file 57 | *.log 58 | 59 | # BlueJ files 60 | *.ctxt 61 | 62 | # Mobile Tools for Java (J2ME) 63 | .mtj.tmp/ 64 | 65 | # Package Files # 66 | *.jar 67 | *.war 68 | *.ear 69 | *.zip 70 | *.tar.gz 71 | *.rar 72 | 73 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 74 | hs_err_pid* 75 | 76 | LeetCode.iml 77 | .idea/ 78 | -------------------------------------------------------------------------------- /Java 基础提高/Java 基础提高.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Java 基础提高/src/ClassAnnotation/JsonAnnotation.java: -------------------------------------------------------------------------------- 1 | package ClassAnnotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.FIELD) 9 | @Retention(RetentionPolicy.CLASS) 10 | public @interface JsonAnnotation { 11 | } 12 | -------------------------------------------------------------------------------- /Java 基础提高/src/ClassAnnotation/JsonProcessor.java: -------------------------------------------------------------------------------- 1 | package ClassAnnotation; 2 | 3 | import javax.annotation.processing.AbstractProcessor; 4 | import javax.annotation.processing.RoundEnvironment; 5 | import javax.annotation.processing.SupportedOptions; 6 | import javax.annotation.processing.SupportedSourceVersion; 7 | import javax.lang.model.SourceVersion; 8 | import javax.lang.model.element.TypeElement; 9 | import java.util.HashSet; 10 | import java.util.Set; 11 | 12 | @SupportedOptions("ClassAnnotation.JsonAnnotation") 13 | @SupportedSourceVersion(SourceVersion.RELEASE_6) 14 | public class JsonProcessor extends AbstractProcessor { 15 | 16 | @Override 17 | public SourceVersion getSupportedSourceVersion() { 18 | return SourceVersion.latestSupported(); 19 | } 20 | 21 | @Override 22 | public Set getSupportedAnnotationTypes() { 23 | HashSet set = new HashSet<>(); 24 | set.add(JsonAnnotation.class.getCanonicalName()); 25 | return set; 26 | } 27 | 28 | 29 | @Override 30 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 31 | return false; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Java 基础提高/src/DesignMode/BasicSingleTon.java: -------------------------------------------------------------------------------- 1 | package DesignMode; 2 | 3 | public class BasicSingleTon { 4 | 5 | //创建唯一实例 6 | private static BasicSingleTon instance = new BasicSingleTon(); 7 | 8 | //第二部暴露静态方法返回唯一实例 9 | public static BasicSingleTon getInstance() { 10 | return instance; 11 | } 12 | 13 | //第一步构造方法私有 14 | private BasicSingleTon() { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Java 基础提高/src/DesignMode/EnumSingleTon.java: -------------------------------------------------------------------------------- 1 | package DesignMode; 2 | 3 | public enum EnumSingleTon { 4 | INSTANCE 5 | } 6 | 7 | 8 | -------------------------------------------------------------------------------- /Java 基础提高/src/DesignMode/LazyBasicSingleTon.java: -------------------------------------------------------------------------------- 1 | package DesignMode; 2 | 3 | public class LazyBasicSingleTon { 4 | 5 | private static LazyBasicSingleTon singleTon = null; 6 | 7 | public static LazyBasicSingleTon getInstance() { 8 | //延迟初始化 9 | if (singleTon == null) { 10 | singleTon = new LazyBasicSingleTon(); 11 | } 12 | 13 | return singleTon; 14 | } 15 | 16 | private LazyBasicSingleTon() { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Java 基础提高/src/DesignMode/SingleTon.java: -------------------------------------------------------------------------------- 1 | package DesignMode; 2 | 3 | public class SingleTon { 4 | public static void main(String[] args) { 5 | EnumSingleTon instance = EnumSingleTon.INSTANCE; 6 | EnumSingleTon instance1 = EnumSingleTon.INSTANCE; 7 | 8 | System.out.println("instance1 == instance = " + (instance1 == instance)); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Java 基础提高/src/DesignMode/StaticInnerSingleTon.java: -------------------------------------------------------------------------------- 1 | package DesignMode; 2 | 3 | public class StaticInnerSingleTon { 4 | 5 | private static class InnerStaticClass{ 6 | private static StaticInnerSingleTon singleTon = new StaticInnerSingleTon(); 7 | } 8 | 9 | public StaticInnerSingleTon getInstance(){ 10 | return InnerStaticClass.singleTon; //引用一个类的静态成员,将会触发该类的初始化 11 | } 12 | 13 | private StaticInnerSingleTon() { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Java 基础提高/src/DesignMode/SyncSingleTon.java: -------------------------------------------------------------------------------- 1 | package DesignMode; 2 | 3 | public class SyncSingleTon { 4 | 5 | private static SyncSingleTon singleTon = null; 6 | 7 | public static SyncSingleTon getInstance() { 8 | 9 | //这次判空是避免了,保证的多线程只有第一次调用getInstance 的时候才会加锁初始化 10 | if (singleTon == null) { 11 | synchronized (SyncSingleTon.class) { 12 | if (singleTon == null) { 13 | singleTon = new SyncSingleTon(); 14 | } 15 | } 16 | } 17 | return singleTon; 18 | } 19 | 20 | private SyncSingleTon() { 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Java 基础提高/src/DesignMode/VolatileSingleTon.java: -------------------------------------------------------------------------------- 1 | package DesignMode; 2 | 3 | public class VolatileSingleTon { 4 | 5 | //使用 Volatile 保证了指令重排序在这个对象创建的时候不可用 6 | private volatile static VolatileSingleTon singleTon = null; 7 | 8 | public static VolatileSingleTon getInstance() { 9 | 10 | 11 | if (singleTon == null) { 12 | synchronized (VolatileSingleTon.class) { 13 | if (singleTon == null) { 14 | singleTon = new VolatileSingleTon(); 15 | } 16 | } 17 | } 18 | return singleTon; 19 | } 20 | 21 | private VolatileSingleTon() { 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Java 基础提高/src/Reflection/Student.java: -------------------------------------------------------------------------------- 1 | package Reflection; 2 | 3 | public class Student { 4 | public Student() { 5 | } 6 | 7 | public Student(Integer code, String name, double scores) { 8 | this.code = code; 9 | this.name = name; 10 | this.scores = scores; 11 | } 12 | 13 | public final int id = 30; 14 | public static final int ID = 1000; 15 | 16 | public Integer code = 90; 17 | private String name; 18 | private double scores; 19 | 20 | 21 | 22 | public String getName() { 23 | System.out.println("我是 Student 的 public 方法"); 24 | return name; 25 | } 26 | 27 | private void testPrivate(){ 28 | System.out.println("我是 Student 的 private 方法"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Java 基础提高/src/Reflection/TopStudent.java: -------------------------------------------------------------------------------- 1 | package Reflection; 2 | 3 | public class TopStudent extends Student { 4 | private boolean isReal; 5 | public int grade; 6 | 7 | // public TopStudent() { 8 | // 9 | // } 10 | 11 | public TopStudent(boolean isReal) { 12 | 13 | this.isReal = isReal; 14 | } 15 | 16 | private TopStudent(int grade){ 17 | this.grade = grade; 18 | } 19 | 20 | public TopStudent(boolean isReal, int grade) { 21 | this.isReal = isReal; 22 | this.grade = grade; 23 | } 24 | 25 | public boolean isReal() { 26 | System.out.println("我是 TopStudent 的 public 方法"); 27 | return isReal; 28 | } 29 | 30 | private void testSelfPrivate() { 31 | System.out.println("我是 TopStudent 的 private 方法"); 32 | } 33 | 34 | public void testParams(String p1, int p2) { 35 | System.out.println("我是 TopStudent 的 testParams(String p1,int p2) 方法," + " p1 = " + p1 + " p2 = " + p2); 36 | } 37 | 38 | public void testParams(double p) { 39 | System.out.println("我是 TopStudent 的 testParams(double p) 方法 ," + " p = " + p); 40 | } 41 | 42 | public String testParams(int p) { 43 | System.out.println("我是 TopStudent 的 testParams(int p) 方法 ," + " p = " + p); 44 | return String.valueOf(p * 100); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Java 基础提高/src/proxy/Client.java: -------------------------------------------------------------------------------- 1 | package proxy; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | import java.util.Stack; 6 | 7 | /** 8 | * 如果使用静态代理,首先被代理这要实现接口,然后代理这要实现这个接口,这个代理接口是需求者需要执行的操作, 9 | * 最后想需求者,通过调用已经实现了这些操作接口的代理类的方法,来实现需求。 10 | *

11 | * 而动态代理: 12 | *

13 | * 首先有一个委托类,同样实现了已经规定好的与外界沟通的操作方法的接口。而代理类则不用实现"之前所谓的代理接口", 14 | * 而是转而实现 JDK 的 InvocationHandler,最后需求者通过调用 Proxy.newProxyInstance() 生成的"代理接口的实现类的方法来实现需求。 15 | *

16 | * 可以看出差异在与没有手动创建一个代理类,而是通过 JDK 的 Proxy.newProxyInstance() 来进行创建的。 这又是 JDK 为我做的事情。实际上可以看出 17 | * 真正的方法调用是通过 Invoke 方法去调用的,也就是在 InvocationHandler 的 invoke 里边我们可以区分代理接口的方法来做一些前置和后置操作。 18 | *

19 | * 那现在还有一点不明白的是为什么 Proxy.newProxyInstance() 生成的对象就是对应的代理接口实现类。 20 | *

21 | * newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h) 22 | * 第一传入 classLoader,第二个传入对应的代理接口,第三个传入 InvocationHandler 想想一下流程 23 | *

24 | * 通过这三个参数,去生成对应的代理接口的实现类,然后调用改接口内的方法的时候将会调用 InvocationHandler 的 invoke 方法 25 | *

26 | * 想要跟被代理类打交道的对象,只能通过代理的类去打交道 27 | */ 28 | public class Client { 29 | 30 | 31 | public static void main(String[] args) { 32 | //被代理的类不能为抽象类 33 | HouseOwner houseOwner = new HouseOwner(); 34 | 35 | //这里要想强转就得实现知道 HouseOwer 实现的是哪个代理接口 36 | RentHouse rentHouse = (RentHouse) new DynamicProxy().bind(houseOwner); 37 | 38 | System.out.println(rentHouse.getClass().getName()); 39 | 40 | rentHouse.rent(); 41 | 42 | rentHouse.charge("10000"); 43 | Solution solution = new Solution(); 44 | int[] pushA = new int[]{1, 2, 3, 4, 5}; 45 | int[] popA1 = new int[]{4, 3, 5, 1, 2}; 46 | int[] popA2 = new int[]{4, 5, 3, 2, 1}; 47 | 48 | System.out.println("popA1 是否是出栈队列 " + solution.IsPopOrder(pushA, popA1)); 49 | System.out.println("popA2 是否是出栈队列 " + solution.IsPopOrder(pushA, popA2)); 50 | // TwoQueueStack queueStack = new TwoQueueStack<>(); 51 | // queueStack.push(1); 52 | // queueStack.push(2); 53 | // queueStack.push(3); 54 | // queueStack.push(4); 55 | // queueStack.pop(); 56 | // queueStack.pop(); 57 | // queueStack.push(5); 58 | // queueStack.pop(); 59 | 60 | System.out.println("log2 = " + log2(3)); 61 | System.out.println("isodd = " + isOdd2(1)); 62 | System.out.println("11111 = " + count1(-1)); 63 | int[] arr = {2,1,3,3,2,1,4,5}; 64 | printOddTimesNum(arr); 65 | 66 | } 67 | 68 | public static int count1(int n){ 69 | int res = 0; 70 | while(n != 0){ 71 | n &= (n - 1); 72 | res++; 73 | } 74 | return res; 75 | } 76 | 77 | 78 | public static boolean log2(int num) { 79 | return (num & (num - 1)) == 0; 80 | } 81 | 82 | public static boolean isOdd2(int num) { 83 | return (num & 1) != 0; 84 | } 85 | 86 | public static int OddTimesNum(int[] arr) { 87 | int eO = 0; 88 | for (int cur : arr) { 89 | eO = eO ^ cur; 90 | } 91 | 92 | return eO; 93 | } 94 | 95 | 96 | public static void printOddTimesNum(int[] arr) { 97 | 98 | int eO = 0; 99 | int eOhasOne = 0; 100 | 101 | for (int cur : arr) { 102 | eO = eO ^ cur; 103 | } 104 | 105 | int rightOne = eO & (~eO + 1); 106 | for (int cur : arr) { 107 | if ((rightOne & cur) != 0) { 108 | eOhasOne = eOhasOne ^ cur; 109 | } 110 | } 111 | 112 | System.out.println("eOhasOne = " + eOhasOne + " " + (eOhasOne ^ eO)); 113 | } 114 | 115 | public static class TwoStackQueue { 116 | private Stack stackA; 117 | private Stack stackB; 118 | 119 | public TwoStackQueue() { 120 | stackA = new Stack<>(); 121 | stackB = new Stack<>(); 122 | } 123 | 124 | /** 125 | * 添加元素逻辑 126 | * 127 | * @param e 要添加的元素 128 | * @return 这里只是遵循 Queue 的习惯,这里简单处理返回 true 即可 129 | */ 130 | public boolean add(E e) { 131 | stackA.push(e); 132 | return true; 133 | } 134 | 135 | /** 136 | * 去除元素的时候需要判断两个地方,StackA & StackB 是否都为空 137 | * StackB 为空的时候讲StackA中的元素全部依次压入 StackB 138 | * 139 | * @return 返回队列中的元素 如果队列为空返回 null 140 | */ 141 | public E poll() { 142 | //如果队列中没有元素则直接返回空,也可以选择抛出异常 143 | if (stackB.isEmpty() && stackA.isEmpty()) { 144 | return null; 145 | } 146 | 147 | if (stackB.isEmpty()) { 148 | while (!stackA.isEmpty()) { 149 | stackB.add(stackA.pop()); 150 | } 151 | } 152 | 153 | return stackB.pop(); 154 | } 155 | 156 | /** 157 | * peek 操作不取出元素,只返回队列头部的元素值 158 | * 159 | * @return 队列头部的元素值 160 | */ 161 | public E peek() { 162 | //如果队列中没有元素则直接返回空,也可以选择抛出异常 163 | if (stackB.isEmpty() && stackA.isEmpty()) { 164 | return null; 165 | } 166 | 167 | if (stackB.isEmpty()) { 168 | while (!stackA.isEmpty()) { 169 | stackB.add(stackA.pop()); 170 | } 171 | } 172 | 173 | return stackB.peek(); 174 | } 175 | } 176 | 177 | public static class Solution { 178 | 179 | public boolean IsPopOrder(int[] pushA, int[] popA) { 180 | int len = pushA.length; 181 | 182 | Stack stack = new Stack<>(); 183 | for (int pushIndex = 0, popIndex = 0; pushIndex < len; pushIndex++) { 184 | stack.push(pushA[pushIndex]); 185 | while (popIndex < len && popA[popIndex] == stack.peek()) { 186 | stack.pop(); 187 | popIndex++; 188 | } 189 | } 190 | return stack.isEmpty(); 191 | } 192 | } 193 | 194 | 195 | public static class TwoQueueStack { 196 | private Queue queueA; 197 | private Queue queueB; 198 | 199 | public TwoQueueStack() { 200 | queueA = new LinkedList<>(); 201 | queueB = new LinkedList<>(); 202 | } 203 | 204 | /** 205 | * 选一个非空的队列入队 206 | * 207 | * @param e 208 | * @return 209 | */ 210 | public E push(E e) { 211 | if (queueA.size() != 0) { 212 | System.out.println("从 queueA 入队 " + e); 213 | queueA.add(e); 214 | } else if (queueB.size() != 0) { 215 | System.out.println("从 queueB 入队 " + e); 216 | queueB.add(e); 217 | } else { 218 | System.out.println("从 queueA 入队 " + e); 219 | queueA.add(e); 220 | } 221 | return e; 222 | } 223 | 224 | public E pop() { 225 | if (queueA.size() == 0 && queueB.size() == 0) { 226 | return null; 227 | } 228 | 229 | E result = null; 230 | if (queueA.size() != 0) { 231 | while (queueA.size() > 0) { 232 | result = queueA.poll(); 233 | if (queueA.size() != 0) { 234 | System.out.println("从 queueA 出队 并 queueB 入队 " + result); 235 | queueB.add(result); 236 | } 237 | } 238 | System.out.println("从 queueA 出队 " + result); 239 | 240 | } else { 241 | while (queueB.size() > 0) { 242 | result = queueB.poll(); 243 | if (queueB.size() != 0) { 244 | System.out.println("从 queueB 出队 并 queueA 入队 " + result); 245 | queueA.add(result); 246 | } 247 | } 248 | System.out.println("从 queueB 出队" + result); 249 | } 250 | return result; 251 | } 252 | } 253 | } 254 | 255 | 256 | 257 | -------------------------------------------------------------------------------- /Java 基础提高/src/proxy/DynamicProxy.java: -------------------------------------------------------------------------------- 1 | package proxy; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.Method; 5 | import java.lang.reflect.Proxy; 6 | 7 | /** 8 | * 代理的类 需要给他要代理的对象 9 | */ 10 | public class DynamicProxy implements InvocationHandler { 11 | 12 | 13 | // 这个就是我们要代理的真实对象,即房东 14 | private Object subject; 15 | 16 | // 构造方法,给我们要代理的真实对象赋初值 17 | public DynamicProxy(Object subject) { 18 | this.subject = subject; 19 | } 20 | 21 | public DynamicProxy(){ 22 | 23 | } 24 | 25 | public Object bind(Object subject){ 26 | this.subject = subject; 27 | return Proxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass().getInterfaces(),this); 28 | } 29 | 30 | @Override 31 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 32 | 33 | //在代理真实对象前我们可以添加一些自己的操作,中介收取中介费 34 | System.out.println("before "+method.getName()+" house"); 35 | 36 | System.out.println("Method:" + method.getName()); 37 | 38 | 39 | //如果方法是 charge 则中介收取100元中介费 40 | if (method.getName().equals("charge")) { 41 | 42 | method.invoke(subject, args); 43 | System.out.println("I will get 100 RMB ProxyCharge."); 44 | 45 | } else { 46 | // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用 47 | method.invoke(subject, args); 48 | } 49 | 50 | //  在代理真实对象后我们也可以添加一些自己的操作 51 | System.out.println("after "+method.getName()+" house"); 52 | 53 | 54 | return null; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Java 基础提高/src/proxy/HouseOwner.java: -------------------------------------------------------------------------------- 1 | package proxy; 2 | 3 | /** 4 | * 被代理的类,必须实现代理接口中的方法 5 | */ 6 | public class HouseOwner implements RentHouse { 7 | 8 | @Override 9 | public void rent() { 10 | System.out.println("我要出租房子"); 11 | } 12 | 13 | @Override 14 | public void charge(String str) { 15 | System.out.println("你需要支付 " + str + " RMB 费用"); 16 | } 17 | 18 | public void abstractMethod(){ 19 | System.out.println("自己的方法"); 20 | 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Java 基础提高/src/proxy/RentHouse.java: -------------------------------------------------------------------------------- 1 | package proxy; 2 | 3 | /** 4 | * 代理接口 5 | */ 6 | public interface RentHouse { 7 | void rent(); 8 | void charge(String str); 9 | } 10 | -------------------------------------------------------------------------------- /Java 基础提高/src/sync/JoinTest.java: -------------------------------------------------------------------------------- 1 | package sync; 2 | 3 | public class JoinTest { 4 | 5 | 6 | public static void main(String[] args) { 7 | Thread t1 = new Thread(new Runnable() { 8 | int count = 10000; 9 | 10 | @Override 11 | public void run() { 12 | while (count-- != 0) 13 | System.out.println("t1 在执行"); 14 | } 15 | }); 16 | 17 | Thread t2 = new Thread(new Runnable() { 18 | int count = 10000; 19 | 20 | @Override 21 | public void run() { 22 | while (count-- != 0) 23 | System.out.println("t2 在执行"); 24 | } 25 | }); 26 | 27 | try { 28 | t1.start(); 29 | t2.start(); 30 | t2.join(); 31 | } catch (InterruptedException e) { 32 | e.printStackTrace(); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Java 基础提高/src/sync/SyncObjectTest.java: -------------------------------------------------------------------------------- 1 | package sync; 2 | 3 | public class SyncObjectTest { 4 | 5 | public synchronized void methodA() { 6 | System.out.println("A 获得执行"); 7 | while (true) { 8 | } 9 | } 10 | 11 | public synchronized void methodB() { 12 | System.out.println("B 获得执行"); 13 | while (true) { 14 | } 15 | } 16 | 17 | 18 | public synchronized static void methodStaticA() { 19 | System.out.println("A 获得执行"); 20 | while (true) { 21 | } 22 | } 23 | 24 | public static synchronized void methodStaticB() { 25 | System.out.println("B 获得执行"); 26 | while (true) { 27 | } 28 | } 29 | 30 | Object lock = new Object(); 31 | 32 | public void methodOtherA() { 33 | synchronized (lock) { 34 | System.out.println("A 获得执行"); 35 | while (true) { 36 | } 37 | } 38 | } 39 | 40 | public void methoOtherB() { 41 | synchronized (lock) { 42 | System.out.println("B 获得执行"); 43 | while (true) { 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Java 基础提高/src/sync/TestDoubleSyncMethod.java: -------------------------------------------------------------------------------- 1 | package sync; 2 | 3 | public class TestDoubleSyncMethod { 4 | 5 | 6 | public static void main(String[] args) { 7 | SyncObjectTest objectTest = new SyncObjectTest(); 8 | // Thread t1 = new Thread(new TestMethodRunnable(objectTest,false)); 9 | // Thread t2 = new Thread(new TestMethodRunnable(objectTest,true)); 10 | 11 | Thread t1 = new Thread(new TestOtherMethodRunnable(objectTest,false)); 12 | Thread t2 = new Thread(new TestOtherMethodRunnable(objectTest,true)); 13 | 14 | // Thread t1 = new Thread(new TestStaticMethodRunnable(false)); 15 | // Thread t2 = new Thread(new TestStaticMethodRunnable(true)); 16 | 17 | t1.start(); 18 | t2.start(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Java 基础提高/src/sync/TestMethodRunnable.java: -------------------------------------------------------------------------------- 1 | package sync; 2 | 3 | public class TestMethodRunnable implements Runnable { 4 | private SyncObjectTest objectTest; 5 | private boolean meathodA; 6 | 7 | public TestMethodRunnable(SyncObjectTest objectTest, boolean meathodA) { 8 | this.objectTest = objectTest; 9 | this.meathodA = meathodA; 10 | } 11 | 12 | @Override 13 | public void run() { 14 | if (meathodA) 15 | objectTest.methodA(); 16 | else 17 | objectTest.methodB(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Java 基础提高/src/sync/TestOtherMethodRunnable.java: -------------------------------------------------------------------------------- 1 | package sync; 2 | 3 | public class TestOtherMethodRunnable implements Runnable { 4 | private boolean meathodA; 5 | private SyncObjectTest objectTest; 6 | 7 | public TestOtherMethodRunnable(SyncObjectTest objectTest,boolean meathodA) { 8 | this.meathodA = meathodA; 9 | this.objectTest = objectTest; 10 | } 11 | 12 | @Override 13 | public void run() { 14 | if (meathodA) 15 | objectTest.methodOtherA(); 16 | else 17 | objectTest.methoOtherB(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Java 基础提高/src/sync/TestStaticMethodRunnable.java: -------------------------------------------------------------------------------- 1 | package sync; 2 | 3 | public class TestStaticMethodRunnable implements Runnable { 4 | private boolean meathodA; 5 | 6 | public TestStaticMethodRunnable(boolean meathodA) { 7 | this.meathodA = meathodA; 8 | } 9 | 10 | @Override 11 | public void run() { 12 | if (meathodA) 13 | SyncObjectTest.methodStaticA(); 14 | else 15 | SyncObjectTest.methodStaticB(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Java 基础提高/二叉树遍历.md: -------------------------------------------------------------------------------- 1 | # 二叉树的遍历 2 | 3 | > 上一节我们说过了树的相关定义,其实定义原本就是辅助我们理解和定义的,一种数据结构存在的原因是在于其应用价值,而并非定义本身。那么这一篇文章就来讲讲,大学的时候讲过的,书上讲过的,网上讲过的,面试题问过... 二叉树的四种遍历方式:前序遍历,中序遍历,后序遍历,层级遍历。跟网上一些文章不同的是,本文从层级遍历这种相对比较简单的遍历方法来写起,也算是辅助理解吧。并放上一段讲解非常好的视频连接,我在 B站 学编程之: [二叉树遍历](https://www.bilibili.com/video/av14272736/index_6.html#page=3) 4 | 5 | ## 构建一个二叉树 6 | 7 | 前文讲到表示一个数的结点的三种方法,「双亲表示法」,「孩子弟兄表示法」,「孩子表示法」。这里采用最常见用的孩子兄弟表示法来表示一个二叉树: 8 | 9 | ```java 10 | /** 11 | * 孩子兄弟法表示一个二叉树 12 | */ 13 | public class TreeNode { 14 | public T data; 15 | public TreeNode leftChild; 16 | public TreeNode rightChild; 17 | public TreeNode(T data) { 18 | this.data = data; 19 | this.leftChild = null; 20 | this.rightChild = null; 21 | } 22 | } 23 | ``` 24 | 然后我们可以创建一个二叉树: 25 | 26 | ```java 27 | public class BinaryTreeUtils { 28 | 29 | public TreeNode root = null; 30 | public BinaryTreeUtils() { 31 | root = new TreeNode( "A"); 32 | } 33 | /** 34 | * 二叉树的构建首先需要跟节点,一般在构造方法里面创建 35 | */ 36 | 37 | public TreeNode createBinaryTree() { 38 | TreeNode nodeB = new TreeNode<>("B"); 39 | TreeNode nodeC = new TreeNode<>("C"); 40 | TreeNode nodeD = new TreeNode<>("D"); 41 | TreeNode nodeE = new TreeNode<>("E"); 42 | TreeNode nodeF = new TreeNode<>("F"); 43 | TreeNode nodeG = new TreeNode<>("G"); 44 | root.leftChild = nodeB; 45 | root.rightChild = nodeC; 46 | nodeB.leftChild = nodeD; 47 | nodeB.rightChild = nodeE; 48 | nodeC.leftChild = nodeF; 49 | nodeC.rightChild = nodeG; 50 | 51 | return root; 52 | } 53 | } 54 | 55 | ``` 56 | 我们可以知道我们构造出的二叉树样子是长这个样的: 57 | > A 58 | > / \ 59 | > B C 60 | > / \ / \ 61 | > D E F G 62 | 63 | 接下来就可以写我们的层级遍历函数了。 64 | 65 | ------- 66 | 67 | ## 层级遍历(Traverse the level) 68 | 69 | > ***层级遍历是指以二叉树根节点为开始,一层一层从左到右一次访问各个节点的遍历方式。*** 70 | > 71 | > 层级遍历的思想是:利用队列的思想来遍历一个二叉树。 72 | > 遇到节点将其放入队列,然后出队的时候将分别将其左子树和右子树按顺序入队 再依次出队。循环结束的条件是 队列为空且要入队的节点为空。 73 | 74 | 如 A 的孩子为 B(left) C(right) , A 入队 A出队 B 入队 C 入队 B 出队 C 出队 循环结束。非常简单,那么看下代码实现: 75 | 76 | ```java 77 | // 这里假设二叉树元素类型为 String 78 | public static void levelTraverse(TreeNode root){ 79 | if (root == null){ 80 | System.out.print("二叉树为空二叉树"); 81 | return; 82 | } 83 | // 用于记录当前处理的结点 84 | TreeNode curNode = root; 85 | Queue queue = new LinkedList<>(); 86 | queue.add(curNode); 87 | while (curNode != null && !queue.isEmpty()){ 88 | System.out.print(queue.poll().data + " "); 89 | if (curNode.leftChild != null) { 90 | queue.add(curNode.leftChild); 91 | } 92 | if (curNode.rightChild != null) { 93 | queue.add(curNode.rightChild); 94 | } 95 | curNode = queue.peek(); 96 | } 97 | } 98 | ``` 99 | 那么我们看下结果: 100 | 101 | > A B C D E F G 102 | 103 | 层级遍历是不是很简单,通过一个 While 循环就可以实现,假设二叉树中节点的个数为 n 则时间复杂度为 O(1) ,额外的空间复杂对为 O(n) 104 | 105 | -------- 106 | 107 | ## 递归思想 108 | 109 | > 这里先放一个我对递归的启蒙视频 :[如何理解递归](https://zh.coursera.org/learn/c-chengxu-sheji/lecture/JIkJB/ru-he-li-jie-di-gui) 110 | 111 | 古人常云:递归便于理解! 我听见这句话简直就是心中冒出无数个 mmp 。老子为啥经常被递归搞得神魂颠倒,欲生欲死。其实递归如果写出来的确是便于理解,但是写递归和设计递归的过程其实是与人们常见的归纳思想不太一样的。比如说注明的递归问题 肥波纳妾兔子的问题(停!那叫斐波那契数列). 112 | 113 | > 3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 114 | 115 | 打一个正常人来讲,肯定是这样分析问题的: 116 | 117 | n = 1 兔子个数为: 1, n = 2 兔子个数为: 1, n = 3 兔子个数为: 3 ,n = 4 兔子个数为: 5 ,n = 6 兔子个数为: 7 ..... 118 | 119 | 然后就啪啪啪的找 天数 n 与兔子个数的关系去了。这就是我们常说的归纳演绎法。那么天生的数学家头脑告诉我们不应该这样干其实规律很简单 F(n) = F(n - 1) + F(n -2) ; (n>2)。其实这个表达式就充分表明了递归这个思想。 120 | 121 | 说了那么多到底怎么理解递归呢? 其实大多数教科书上还有网上的资料都对递归的定义是下边这样的: 122 | 123 | 124 | > 递归思想:函数通过直接或者间接方式调用函数本身的过程。 125 | 126 | 127 | 有人认为我要反驳了? Naive ! 我只是想说明,这个定义没问题。but 就是不太便于理解。那么怎么去理解自己调用自己的这个形式呢? 128 | 129 | 我们都知道,函数可以嵌套调用。拿一个常见的例子来说: 130 | 131 | ```java 132 | 133 | private int[] sortArr(int[] arr){ 134 | .... 135 | } 136 | 137 | int[] arr = {5,6,1,3,54,10}; 138 | public static void main(String[] args) { 139 | int[] arr = sortArr(arr); 140 | System.out.print(arr); 141 | } 142 | 143 | ``` 144 | 上述过程在 main 函数中调用了,sortArr 方法,然后执行输出语句。 ok 没问题,这么简单大家都明白。那么其实这就是一个函数嵌套调用过程。jvm 调用 main 函数 ,main 函数中又调用 sortArr 方法,然后又回到 main 函数执行输出语句。 145 | 146 | 聪明的小伙伴看到回到这个词其实就明白了。其实在嵌套调用的过程实际上是:虚拟机为 每个函数执行开辟一个新的内存空间,待函数执行完成后又 回到之前的内存空间去执行接下来的语句。如下图所示: 147 | 148 | ![](https://ws2.sinaimg.cn/large/006tNc79ly1fnz5dpifd0j30hs0fyn1a.jpg) 149 | 150 | 概念理解了,但是对于能够使用递归来说还是远远不够的,比如说我们现在考一个 [汉诺塔问题](https://zh.wikipedia.org/wiki/%E6%B1%89%E8%AF%BA%E5%A1%94),我相信没有几个能够写起来的。面对一个问题,设计一种递归方法这本身就并非简单的问题。 151 | 152 | 递归,其实是分而治之的一种思想表现,分析一些规律的问题的时候我们总是可以找到一个最小问题集,比如快速排序的算法其实就是运用分而治之的思想,把一个数组对半分,再把分完的对半分,直到一组就剩下两个元素的是我们比较大小然后排序。递归思想需要理解,更需要掌握用递归求解的思维途径。这个路还很长,我们一起走下去吧。 153 | 154 | 上边把递归解释完了。怎么样是不是恍然大悟,然后说:嗯~ 古人说递归便于理解有道理! 然后微笑着透着一丝 mmp。 155 | 156 | 其实递归的使用过程中,我们会造成很多额外的空间浪费。其实这就是递归的一缺点。 157 | 158 | ----- 159 | 160 | ## 递归方式完成 先 中 后 序遍历二叉树 161 | 162 | 学习二叉树必定要学习其遍历过程,上边我们说过了利用队列思想去层级遍历二叉树。把每个节点都看成根节点,每个节点出队列的时候要看看自己有没有左孩子和右孩子,如果有则依次进站,然后开始将队列的队头当做根节点出站...依次循环完成整个遍历过程。 163 | 164 | 165 | 其实对于递归来遍历来说,大部分人可能背都能背下来,但是为什么递归这样就能实现呢?我们下边来对前中后三种递归遍历二叉树的方法分析一下,废话不多说先上代码: 166 | 167 | ###前序遍历: 168 | 169 | 所谓的前序遍历就是:先访问根节点,然后在访问左,再访问右节点的过程。 170 | 171 | ``` 172 | /** 173 | * 递归先序遍历 根左右 174 | * 175 | * @param root 176 | */ 177 | public static void beforeTraverse(TreeNode root) { 178 | if (root == null) { 179 | return; 180 | } 181 | System.out.print(root.data + " "); 182 | beforeTraverse(root.leftChild); 183 | beforeTraverse(root.rightChild); 184 | } 185 | 186 | ``` 187 | 188 | 实话说我刚开始看到这段代码的感觉是,一个函数内部递归调用了自己两次,从表面上看是根左右的顺序。那么为啥这样就能成功呢? 我们来看一张图拿一个简单的3层二叉树来看: 189 | 190 | > 3 191 | > 2 1 192 | > 4 5 5 193 | 194 | 那么对于上边这个二叉树递归是怎么调用的呢?建议同学们自己去拿笔画一下,这样便于思考。下边用一个图来描述一下: 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /Java 基础提高/算法总结/数据结构-二叉树基本知识.md: -------------------------------------------------------------------------------- 1 | # 二叉树基本知识 2 | > 本文主要介绍二叉树的基本概念和分类。如有不正确之处请多指正。 3 | 4 | ## 树的相关定义 5 | 6 | ### 什么是树 7 | - 树是 N 个结点的有限集。 N = 0,表示空数。在任意一个非空树中: 8 | 1. **有且仅有一个**特定的称为根的节点。 9 | 2. 当 n > 1 时,其余节点可分为 m (m > 0) 个**互不相交**的有限集,T1,T2,T3...Tm,其中每个集合本身又是一棵树,并且称为当前根的子树。 10 | ---- 11 | ### 结点的定义及分类 12 | 13 | - 数的结点:是包含一个数据元素及若干指向其字数的分支。 14 | - 度(Degree): 结点拥有的子树称为**结点的度(Degree)**。 15 | - 叶子结点(Leaf): 度为 0 的结点称为叶子结点。 16 | - 分支结点:度不为 0 的结点。也称为非终端结点或内部结点(除根结点外) 17 | - 树的度: 树的度是树内各分支结点的度的最大值。 18 | 19 | ![图1-1结点(截图自大话数据结构)](https://ws2.sinaimg.cn/large/006tNc79ly1fnyzc3mt9tj30q20j0wh8.jpg =260x200) 20 | 21 | ----- 22 | ### 结点间的关系 23 | 24 | - 孩子&双亲: 结点的子树的根称为该节点孩子,相应的该结点称为孩子的双亲(Parent)。 25 | - 兄弟(Sibling):同一个双亲的孩子之间互相称为兄弟。 26 | - 祖先:结点的祖先是**从根到该节点**所经历分支上的所有结点,这里注意从跟到某个特定的结点有且只有一条路径(原因在于数的定义中不相交的特点)。如上图 J 的祖先只有 E C A 而没有 F。 27 | - 子孙:以某个结点为根的子树中的任意一个结点都成为改结点的子孙。如上图 B 的子孙有 D G H I。 28 | 29 | ----- 30 | ### 树的其他概念: 31 | - 结点的层级(Level):定义根为第一层,跟的孩子为第二层,孩子的孩子为第三层。一次类推。 32 | - 树的深度(Depth): 书中结点的最大层次为树的深度,也称为树的高度。注意区分**度**与**深度**。 33 | - 有序树 & 无序树: 如果树中结点的各个子树看成从左到右是有次序的,不能互换的则概述称为有序树,否则为无序树。即左右子树(这里假设只有左右两个孩子),不能调换位置,如果调换位置则不再是原来的树。 34 | - 森林: 若干个不相交的树的集合。 35 | ------- 36 | ### 树的三种表示方法: 37 | 1. 双亲表示法。 38 | 2. 孩子表示法。 39 | 3. 孩子兄弟表示法。 40 | #### 双亲表示法 41 | > 在每个结点中,设有一个指示器指向其双亲结点到链表中的位置。数据结构为: 42 | ```java 43 | public class MNode { 44 | private E data;//数据域 45 | private int parent;//双亲的位置 46 | } 47 | ``` 48 | 49 | 50 | 这种结构却可以轻松找出指定节点的父节点,但当要找出某节点的所有子节点需要遍历整个树,时间花费长。 51 | 52 | #### 孩子表示法 53 | > 每个结点有多个指针域,其中每个指针域指向该结点子树的根节点。 54 | 55 | 由于上述两种方式不常用就不过多介绍。想知道详细的双亲表示法,孩子表示法表示一个树的代码请自行网上搜索或者参考: [数据结构与算法Java版——树的两种表现方式](http://blog.csdn.net/xichang702/article/details/73662470) 。 56 | 57 | #### 孩子兄弟表示法: 58 | > 这种数据存储方式最为常用: 59 | > 60 | > ***任意一颗树,它的结点的第一个孩子如果存在就是唯一的,他的右兄弟也是唯一的。我们设置一个指针,分别指向该节点的左孩子和右孩子 则这种表示法方式为孩子兄弟表示方法*** 61 | 62 | 代码表示如下 63 | ```java 64 | class Node{ 65 | T data; 66 | Node leftChild; 67 | Node rightChild; 68 | } 69 | ``` 70 | 71 | ## 树的常见类型 72 | 73 | - 二叉树 (Binary Tree) :是 n(n≥0)个结点的有限集合,该集合或者为空集,或者由一个根结点和两个互不相交的,分别称为改根节点的左子树和右子树的子树组成。 74 | 75 | 76 | - 完全二叉树 (Complete Binary Tree) : 具有 n 个结点的二叉树按层序编号,如果编号为 i(1≤i≤n)的结点与同样深度的满二叉树中编号为 i 的结点在二叉树中的位置相同,则该二叉树称为完全二叉树。 定义很繁琐但是要知道如何判断一个数是否是完全二叉树: 77 | - 叶子结点只能出现在最下边两层(不信你自己画画)。 78 | - 最下层的叶子一定集中在左部连续位置。 79 | - 倒数第二层,若有叶子结点,则一定都在根节点右子树的的连续位置。 80 | - 如果结点的度为1,则该节点只有左孩子没有右孩子。即不存在只有右孩子的情况 81 | - 同样结点数的二叉树,完全二叉树深度最小。 82 | - 如果想判断一个数是不是完全二叉树,只需按照层级从左到右连续编号,如果编到最后一个节点前号码不连续了,则该树肯定不是完全二叉树。 83 | 84 | 85 | - 满二叉树 (Full Binary Tree) : 如果一个二叉树中所有分支的节点都存在左子树和右子树,且叶子都在同一层上,则改树为完全二叉树。满二叉树用数组表示方法最为合适,因为数组角标即为结点在二叉树中的位置。 86 | 87 | 88 | - 二叉搜索树 (Binary Search Tree): 二叉搜索树是一种特殊的二叉树,也可以称为二叉排序树,二叉查找树。除了具有二叉树的基本性质外,它还具备: 89 | 90 | - 树中每个节点最多有两个子树,通常称为左子树和右子树 91 | - 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值 92 | - 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值 93 | - 它的左右子树仍然是一棵二叉搜索树 (recursive) 94 | 95 | 如下图: 96 | ![搜索二叉树](https://ws1.sinaimg.cn/large/006tNc79ly1fnz17oofzqj30pa0eyt8z.jpg =300x200) 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 项目创建日志 2 | 为了进阶高级 Android,定下了刷 LeetCode 的计划,此项目是 leetCode 的解题过程 3 | 刷题顺序参照网站:https://leetcode.com/problemset/algorithms/ 按从简单到难的顺序排列 4 | 5 | 本项目创建初衷是因为发现了自己在算法方面的知识薄弱,从而立下了刷算法题的目标。前后刷了 LeetCode 上的题目,左程云写的《程序员代码面试指南》,《剑指 offer 第二版》上的题目。一边刷一边学习相关知识。 6 | 7 | 后续,在空闲时间总结了单链表的面试题目,排序算法等知识,目前在整理 Java 集合框架相关源码,相关文章在本人的掘金专栏和简书与CSDN博客都有发布。欢迎关注本项目的朋友去浏览。 8 | 9 | > -- 10 | 11 | - 2018年04月12日更新 Java 基础提高相关博文 12 | 13 | to be continue 14 | 15 | 16 | 17 | 18 | -- 19 | 我目前就职于[北京淘金者科技有限公司(牛股王)](https://www.niuguwang.com/index.html),自己也在工作和学习中不断学习进步。也希望能够通过自己的努力,让自己能够有质的飞跃。 20 | 21 | 本人的博客地址: 22 | 23 | [掘金专栏 - 像一只狗](https://juejin.im/user/56793b0860b2b7af14c637db/posts) 24 | [CSDN-王小六的博客](https://blog.csdn.net/learningcoding) 25 | [简书-醒着的码者](https://www.jianshu.com/u/970052f2d105) 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/ArrayAMartix/ArrOrMatrixMaxSum.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | 6 | public class ArrOrMatrixMaxSum { 7 | public static void main(String[] args) { 8 | ArrOrMatrixMaxSum matrixMaxSum = new ArrOrMatrixMaxSum(); 9 | int[] arr = {1, -2, 3, 5, 2, 6, -1}; 10 | int arrMaxSum = matrixMaxSum.getArrMaxSum(arr); 11 | System.out.println("arrMaxSum : "+arrMaxSum); 12 | 13 | int[][] matrix = {{-90, 48, 78}, {64, -40, 64}, {-81, -7, 66}}; 14 | int matrixMaxSum1 = matrixMaxSum.getMatrixMaxSum(matrix); 15 | System.out.println("matrixMaxSum1 : "+matrixMaxSum1); 16 | } 17 | // 18 | 19 | private int getArrMaxSum(int[] arr) { 20 | if (arr == null || arr.length < 1) { 21 | return -1; 22 | } 23 | 24 | int max = Integer.MIN_VALUE; 25 | int cur = 0; 26 | 27 | for (int i = 0; i < arr.length; i++) { 28 | cur += arr[i]; 29 | max = Math.max(cur, max); 30 | cur = cur < 0 ? 0 : cur; 31 | } 32 | 33 | return max; 34 | } 35 | 36 | /** 37 | * @param matrix 38 | * @return 39 | */ 40 | private int getMatrixMaxSum(int[][] matrix) { 41 | if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 42 | return 0; 43 | } 44 | 45 | int max = Integer.MIN_VALUE; 46 | int cur = 0; 47 | 48 | int[] s = null; 49 | 50 | for (int i = 0; i < matrix.length; i++) { 51 | s = new int[matrix[0].length]; 52 | for (int j = i; j < matrix.length; j++) { 53 | cur = 0; 54 | for (int k = 0; k < s.length; k++) { 55 | s[k] += matrix[j][k]; 56 | cur += s[k]; 57 | max = Math.max(cur, max); 58 | cur = cur < 0 ? 0 : cur; 59 | } 60 | } 61 | 62 | } 63 | 64 | return max; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/ArrayAMartix/FindHalfMajor.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | /*** 4 | * 找到数组中出现次数超过一半的那个数 5 | * 6 | * 遍历一遍数组,如果相邻的数是相同的则 time + 1 如果相邻的数不同的则 time-1;当 time 为 0的时候需要记录当前为数组中出现次数超过一半的那个数 7 | * 遍历一遍以后剩下个的那个数 cand 并不一定为数组中出现超过一半的数,比如数组中每个数都不通则最后 cand = arr[arr.length - 1 ] 8 | * 但是如果一个数出现次数超过长度一半那么最后剩下的数一定是它,因为 time--的操作 相当于删除已经遍历数组长度中的两个数, 9 | * 如果一个数出现次数大于数组一半最后 time >= 1 10 | */ 11 | public class FindHalfMajor { 12 | public static void main(String[] args) { 13 | int[] arr = {1, 2, 3, 2, 2, 2, 5,2, 2}; 14 | FindHalfMajor findHalfMajor = new FindHalfMajor(); 15 | findHalfMajor.printHalfMajor(arr); 16 | } 17 | 18 | private void printHalfMajor(int[] arr) { 19 | int cand = 0; 20 | int time = 0; 21 | 22 | for (int i = 0; i < arr.length; i++) { 23 | if (time == 0) { 24 | cand = arr[i]; 25 | time = 1; 26 | } else if (arr[i] == cand) { 27 | time++; 28 | } else { 29 | time--; 30 | } 31 | } 32 | 33 | time = 0; 34 | 35 | for (int i = 0; i < arr.length; i++) { 36 | if (arr[i] == cand) { 37 | time++; 38 | } 39 | } 40 | 41 | if (time > arr.length / 2) { 42 | System.out.println(cand); 43 | } else { 44 | System.out.println("没有一个数出现次数超过数组长度的一半"); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/ArrayAMartix/FindKLagestNum.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | public class FindKLagestNum { 4 | 5 | private static int quickSort(int[] nums, int l, int r, int k) { 6 | if( l == r ) 7 | return nums[l]; 8 | 9 | int index = nums.length - k; 10 | 11 | 12 | int p = partition(nums, l, r); 13 | 14 | if (index == p) { 15 | return nums[p]; 16 | } else if (index < p) {// 如果 k < p, 只需要在nums[l...p-1]中找第k小元素即可 17 | return quickSort(nums, l, p - 1, index); 18 | } else {//index>p 则在 [p+1,r] 中去排序,返回 p 由于我们传入的数组为nums,所以 index 和 p 始终代表原来角标 19 | return quickSort(nums, p + 1, r, index); 20 | } 21 | } 22 | 23 | /** 24 | * arr[l+1 ... i) <= v 25 | * arr(j....r] >= v 26 | */ 27 | private static int partition(int[] nums, int l, int r) { 28 | int v = nums[l]; 29 | int j = l; 30 | for (int i = l + 1; i <= r; i++) { 31 | if (nums[i] < v) { 32 | j++; 33 | swap(nums, i, j); 34 | } 35 | } 36 | swap(nums, l, j); 37 | return j; 38 | } 39 | 40 | private static void swap(int[] nums, int a, int b) { 41 | int temp = nums[a]; 42 | nums[a] = nums[b]; 43 | nums[b] = temp; 44 | } 45 | 46 | public static void main(String[] args) { 47 | // int[] arr = {3, 2, 1, 5, 6, 4}; 48 | int[] arr = {3,2,3,1,2,4,5,5,6}; 49 | System.out.println(arr[quickSort(arr, 0, arr.length - 1, 2)]); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/ArrayAMartix/FindLILArray.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | 6 | /*** 7 | * 最长的可整合子数组的长度 8 | * 9 | * 最长可整合子数组: 如果一个数组在排序之后,每个相邻的数之差的绝对值都为1 那么该数组为最常可整合数组 10 | */ 11 | public class FindLILArray { 12 | public static void main(String[] args) { 13 | int[] arr = {5, 5, 3, 2, 6, 4, 3}; 14 | FindLILArray findLILArray = new FindLILArray(); 15 | int len = findLILArray.getLIL(arr); 16 | System.out.println(" 最大可整合子数组长度为 " + len); 17 | int lilAdvance = findLILArray.getLILAdvance(arr); 18 | System.out.println(" 最大可整合子数组长度为 " + lilAdvance); 19 | } 20 | 21 | /** 22 | * 实现方法1 每一个子数组排序,然后判断是不是整合数组 23 | * 24 | * @param arr 25 | * @return 26 | */ 27 | private int getLIL(int[] arr) { 28 | if (arr == null || arr.length < 1) { 29 | return 0; 30 | } 31 | int len = 0; 32 | for (int i = 0; i < arr.length; i++) { 33 | for (int j = i; j < arr.length; j++) { 34 | if (isIntegrated(arr, i, j)) { 35 | len = Math.max(len, j - i + 1); 36 | } 37 | } 38 | } 39 | 40 | return len; 41 | } 42 | 43 | 44 | private boolean isIntegrated(int[] arr, int left, int right) { 45 | int[] newArr = Arrays.copyOfRange(arr, left, right + 1);//[i,j) 46 | 47 | sortArray(newArr); 48 | 49 | for (int i = 1; i < newArr.length; i++) { 50 | if (newArr[i - 1] != newArr[i] - 1) { 51 | return false; 52 | } 53 | } 54 | return true; 55 | } 56 | 57 | 58 | /** 59 | * 由于可整合数组 中必定不包含重复元素 又因为 如果一个数组中没有重复元素,其最大值减去最小值,再加上1结果就等于元素个数 len 60 | * 61 | * @param arr 62 | * @return 63 | */ 64 | private int getLILAdvance(int[] arr) { 65 | if (arr == null || arr.length < 1) { 66 | return 0; 67 | } 68 | int len = 0; 69 | int max = 0; 70 | int min = 0; 71 | 72 | HashSet set = new HashSet<>();//用于判断重复 73 | for (int i = 0; i < arr.length; i++) { 74 | max = Integer.MIN_VALUE; 75 | min = Integer.MAX_VALUE; 76 | 77 | for (int j = i; j < arr.length; j++) { 78 | if (set.contains(arr[j])) { 79 | break; 80 | } 81 | 82 | set.add(arr[j]); 83 | 84 | max = Math.max(max, arr[j]); 85 | min = Math.min(min, arr[j]); 86 | 87 | //max - min +1 等于 数组长度的时候表示该数组为可整合数组 更新长度 88 | if (max - min + 1 == j - i + 1) { 89 | len = Math.max(j - i + 1, len); 90 | } 91 | } 92 | //记得清空 set 93 | set.clear(); 94 | } 95 | 96 | 97 | return len; 98 | } 99 | 100 | 101 | private void sortArray(int[] newArr) { 102 | quickSort(newArr, 0, newArr.length - 1); 103 | } 104 | 105 | private void quickSort(int[] arr, int l, int r) { 106 | if (l >= r) { 107 | return; 108 | } 109 | 110 | int index = partition(arr, l, r); 111 | quickSort(arr, l, index - 1); 112 | quickSort(arr, index + 1, r); 113 | } 114 | 115 | private int partition(int[] arr, int l, int r) { 116 | int randomIndex = (int) (Math.random() * (r - l + 1) + l); 117 | swap(arr, randomIndex, l); 118 | int v = arr[l]; 119 | int i = l + 1; 120 | int j = r; 121 | 122 | while (true) { 123 | while (i <= r && arr[i] <= v) { 124 | i++; 125 | } 126 | 127 | while (j > l + 1 && arr[j] >= v) { 128 | j--; 129 | } 130 | 131 | if (i > j) { 132 | break; 133 | } 134 | 135 | swap(arr, i, j); 136 | i++; 137 | j--; 138 | } 139 | 140 | swap(arr, l, j); 141 | return j; 142 | } 143 | 144 | private void swap(int[] arr, int i, int j) { 145 | int temp = arr[i]; 146 | arr[i] = arr[j]; 147 | arr[j] = temp; 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/ArrayAMartix/FindLastK.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | import java.util.Arrays; 4 | import java.util.Random; 5 | 6 | public class FindLastK { 7 | public static void main(String[] args) { 8 | FindLastK findLastK = new FindLastK(); 9 | int[] arr = findLastK.buildArr(15); 10 | // int[] arr = {80, 66, 77, 39, 73, 6, 3, 29, 60, 54}; 11 | 12 | 13 | System.out.println("原始数组为 : " + Arrays.toString(arr)); 14 | 15 | int[] lastKNums = findLastK.getLastKNums(arr, 3); 16 | System.out.println("最小的k 个元素的数组为 : " + Arrays.toString(lastKNums)); 17 | } 18 | 19 | private int[] buildArr(int len) { 20 | int[] arr = new int[len]; 21 | Random random = new Random(); 22 | for (int i = 0; i < len; i++) { 23 | arr[i] = random.nextInt(100); 24 | } 25 | 26 | return arr; 27 | } 28 | 29 | 30 | /** 31 | * @param arr 32 | * @param k 33 | */ 34 | private int[] getLastKNums(int[] arr, int k) { 35 | if (arr == null || arr.length < k) { 36 | return arr; 37 | } 38 | 39 | int start = 0; 40 | int end = arr.length - 1; 41 | 42 | int index = partition(arr, start, end); 43 | 44 | //如果 index 比 k -1 要大,则说明第一次 partition 出来的左边区域比较大则下次要在左边区域去 partition 45 | //反之 在右边区域去寻找 46 | while (index != k - 1) { 47 | if (index > k - 1) { 48 | end = index - 1; 49 | } else if (index < k - 1) { 50 | start = index + 1; 51 | } 52 | 53 | index = partition(arr, start, end); 54 | } 55 | 56 | return Arrays.copyOf(arr, k); 57 | } 58 | 59 | /** 60 | * arr[start+1 ... i) <= v 61 | * arr(i....end] >= v 62 | */ 63 | private int partition(int[] arr, int start, int end) { 64 | int provit = arr[start]; 65 | 66 | int i = start + 1; 67 | int j = end; 68 | 69 | 70 | while (true) { 71 | while (i <= end && arr[i] <= provit) { 72 | i++; 73 | } 74 | while (j >= start + 1 && arr[j] >= provit) { 75 | j--; 76 | } 77 | 78 | if (i > j) break; 79 | 80 | swap(arr, i, j); 81 | i++; 82 | j--; 83 | 84 | } 85 | 86 | //j 最后角标停留在 i > j 即为 比 v 小的最后一个一元素位置 87 | swap(arr, start, j); 88 | return j; 89 | } 90 | 91 | private void swap(int[] arr, int i, int j) { 92 | int temp = arr[i]; 93 | arr[i] = arr[j]; 94 | arr[j] = temp; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/ArrayAMartix/FindLocalMinimum.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | public class FindLocalMinimum { 4 | 5 | public static void main(String[] args) { 6 | 7 | 8 | int[] arr = {2, 1, 3, 4, 5, 6, 11, 14, 8, 25}; 9 | 10 | FindLocalMinimum minimum = new FindLocalMinimum(); 11 | int index = minimum.localMinimum(arr); 12 | System.out.println("局部最小元素 角标为 " + index + "值为" + arr[index]); 13 | 14 | } 15 | 16 | private int localMinimum(int[] arr) { 17 | 18 | if (arr == null || arr.length == 0) { 19 | return -1; 20 | } 21 | if (arr.length == 1 || arr[0] < arr[1]) { 22 | return 0; 23 | } 24 | if (arr[arr.length - 1] < arr[arr.length - 2]) { 25 | return arr.length - 1; 26 | } 27 | 28 | int mid = 0; 29 | int left = 1; 30 | int right = arr.length - 2; 31 | //走到这里说明 arr[0] > arr[1] arr[n -2 ] < arr[n-1] 32 | while (left < right) { 33 | mid = (left + right) / 2; 34 | System.out.println( " mid " + arr[mid] ); 35 | //认为局部是上升结构 则局部最小值出现在左侧 且 因为 arr[0] > arr[1] 所以 肯定有一个 最低点 36 | if (arr[mid] > arr[mid - 1]) { 37 | right = mid - 1; 38 | } else if (arr[mid] > arr[mid + 1]) { 39 | left = mid + 1; 40 | } else { 41 | return mid; 42 | } 43 | } 44 | return left; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/ArrayAMartix/FindMedianSortedArrays.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | import java.util.Stack; 4 | 5 | public class FindMedianSortedArrays { 6 | 7 | 8 | public static void main(String[] args) { 9 | int[] nums1 = {1,2}; 10 | int[] nums2 = {3,4}; 11 | double medianSortedArrays = new FindMedianSortedArrays().findMedianSortedArrays(nums1, nums2); 12 | System.out.println("medianSortedArrays = " + medianSortedArrays); 13 | } 14 | public double findMedianSortedArrays(int[] nums1, int[] nums2) { 15 | 16 | int[] result; 17 | 18 | if(nums1.length == 0){ 19 | result = nums2; 20 | }else if(nums2.length == 0){ 21 | result = nums1; 22 | }else{ 23 | result = meger(nums1,nums2); 24 | } 25 | 26 | if(result.length % 2 == 1){ 27 | return result[result.length/2]; 28 | }else{ 29 | return (result[result.length /2 ] + result[result.length / 2 - 1 ]) / 2.0d; 30 | } 31 | } 32 | 33 | private int[] meger(int[] nums1, int[] nums2){ 34 | int[] result = new int[nums1.length + nums2.length]; 35 | int i = 0; 36 | int j = 0; 37 | int k = 0; 38 | while(i < nums1.length && j< nums2.length){ 39 | if(nums1[i] < nums2[j]){ 40 | result[k++] = nums1[i++]; 41 | }else{ 42 | result[k++] = nums2[j++]; 43 | } 44 | } 45 | 46 | while(i < nums1.length ){ 47 | result[k++] = nums1[i++]; 48 | } 49 | 50 | while(j < nums2.length ){ 51 | result[k++] = nums2[j++]; 52 | } 53 | 54 | return result; 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/ArrayAMartix/FindSumKMaxLength.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | /** 4 | * 求未排序正数数组红乐嘉何为给定值得最长连续子数组的长度 5 | * 6 | * 思路: 7 | * 1. 两个指针 left 和 right 分别指向数组的头部表示长度为1的滑动窗口 8 | * 2. sum 始终表示子数组 arr[left,right]的和 9 | * 3. len 始终表示 sum == k 的时候数组的最大长度 10 | * 3. 每一次指针变化都进行 sum 和 k 比较 11 | * 3.1 如果 sum == k 则表示 right 再向右移动 sum 就大于 K 了 此时需要left++ 改变滑动窗口的大小,并改变当前的 sum 值 12 | * 3.2 如果 sum < k 则移动 right 此时需要注意 right 向右移动以后 可能 right 已经等于数组长度了,但是此时 sum k 则表示窗口比大了,所以此时缩小窗口 left ++ 14 | */ 15 | public class FindSumKMaxLength { 16 | public static void main(String[] args) { 17 | int[] arr = {1, 2, 1, 1, 1}; 18 | FindSumKMaxLength findSumKMaxLength = new FindSumKMaxLength(); 19 | 20 | System.out.println("和为 K的最长子数组长度为 " + findSumKMaxLength.getMaxLength(arr, 4)); 21 | } 22 | 23 | private int getMaxLength(int[] arr, int k) { 24 | 25 | if (arr == null || arr.length == 0 || k <= 0) { 26 | return 0; 27 | } 28 | 29 | int left = 0; 30 | int right = 0; 31 | int sum = arr[0]; 32 | int len = 0; 33 | 34 | while (right < arr.length) { 35 | if (sum == k) { 36 | len = Math.max(len, right - left + 1); 37 | sum -= arr[left++]; 38 | } else if (sum < k) { 39 | right++; 40 | if (right == arr.length) { 41 | break; 42 | } 43 | sum += arr[right]; 44 | } else { 45 | sum -= arr[left]; 46 | left++; 47 | } 48 | 49 | } 50 | return len; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/ArrayAMartix/GetMinLengthNeedSort.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | import java.util.Arrays; 4 | import java.util.Random; 5 | 6 | public class GetMinLengthNeedSort { 7 | 8 | public static void main(String[] args) { 9 | GetMinLengthNeedSort getMinLengthNeedSort = new GetMinLengthNeedSort(); 10 | int[] arr = getMinLengthNeedSort.buildArr(10); 11 | System.out.println("原数组为 :" + Arrays.toString(arr)); 12 | int minLenNeedSort = getMinLengthNeedSort.findMinLenNeedSort(arr); 13 | System.out.println("最小应该排序的数数组长度为 :" + minLenNeedSort); 14 | } 15 | 16 | private int findMinLenNeedSort(int[] arr) { 17 | if (arr == null || arr.length < 2) { 18 | return 0; 19 | } 20 | 21 | int min = arr[arr.length - 1]; 22 | int noMinIndex = -1; 23 | 24 | //遍历后 min 为当前数组最小的元素, noMinIndex 为数组无序的开始 25 | for (int i = arr.length - 2; i >= 0; i--) { 26 | //如果 arr[i] > min 代表 arr[i]应该位于 min 的右边 则 i 是无序的开始 27 | if (arr[i] > min) { 28 | noMinIndex = i; 29 | } else { 30 | //如果当前值小于所设置的 min 的 则表示 有比 min 小的数,去更新 min 31 | min = Math.min(arr[i], min); 32 | } 33 | } 34 | 35 | //表示数组整体升序 36 | if (noMinIndex == -1) { 37 | return 0; 38 | } 39 | 40 | int max = arr[0]; 41 | int noMaxIndex = -1; 42 | 43 | for (int i = 1; i < arr.length; i++) { 44 | if (arr[i] < max) { 45 | noMaxIndex = i; 46 | } else { 47 | max = Math.max(arr[i], max); 48 | } 49 | } 50 | for (int i = noMinIndex; i <= noMaxIndex; i++) { 51 | System.out.print(arr[i] + " "); 52 | } 53 | return noMaxIndex - noMinIndex + 1; 54 | } 55 | 56 | 57 | private int[] buildArr(int len) { 58 | int[] arr = new int[len]; 59 | Random random = new Random(); 60 | for (int i = 0; i < len; i++) { 61 | arr[i] = random.nextInt(10); 62 | } 63 | 64 | return arr; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/ArrayAMartix/MatrixContainK.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | /*** 4 | * 一个纵向横向都递增的 N * M 矩阵中是否包含 k 5 | */ 6 | public class MatrixContainK { 7 | public static void main(String[] args) { 8 | MatrixContainK matrixContainK = new MatrixContainK(); 9 | int[][] matrix = matrixContainK.buildMatrix(); 10 | System.out.println("矩阵中是否包含 k = 7 的元素 : " + matrixContainK.containK(matrix, 7)); 11 | } 12 | 13 | private boolean containK(int[][] matrix, int k) { 14 | if (matrix == null) { 15 | return false; 16 | } 17 | //从矩阵右上角开始找 18 | int row = 0; 19 | int col = matrix[0].length - 1; 20 | while (row < matrix.length && col > -1) { 21 | if (matrix[row][col] == k) { 22 | return true; 23 | } else if (matrix[row][col] < k) { 24 | row++; 25 | } else { 26 | col--; 27 | } 28 | } 29 | 30 | return false; 31 | } 32 | 33 | private int[][] buildMatrix() { 34 | return new int[][]{{0, 1, 2, 5}, {2, 3, 4, 7}, {4, 4, 4, 8}, {5, 7, 7, 9}}; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/ArrayAMartix/MatrixPrint.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | public class MatrixPrint { 4 | public static void main(String[] args) { 5 | System.out.println(); 6 | System.out.println("------- 旋转打印矩阵 ------"); 7 | 8 | MatrixPrint matrixPrint = new MatrixPrint(); 9 | int[][] matrix = matrixPrint.buildMatrix(); 10 | matrixPrint.spiralOrderPrint(matrix); 11 | 12 | System.out.println(); 13 | System.out.println("------- 之字型打印矩阵 ------"); 14 | 15 | 16 | int[][] matrix2 = matrixPrint.buildMatrix2(); 17 | matrixPrint.printMatrixZigZag(matrix2); 18 | 19 | System.out.println(); 20 | System.out.println("------将矩阵旋转 90° -------"); 21 | 22 | int[][] matrix3 = matrixPrint.buildMatrix3(); 23 | matrixPrint.rotateMatrix(matrix3); 24 | matrixPrint.spiralOrderPrint(matrix3); 25 | 26 | } 27 | 28 | /*** 29 | * 旋转的打印矩阵 一个矩阵的一圈可以用左上角的角标 (tR,tC) 右下角的角标 (dR,dC)来表示 30 | * 旋转矩阵的打印临界值判断 就是每次打印完一圈以后左上角标就+1 和右下角标-1,知道右下角标的位置出现在左上角标的左上方,则打印结束 31 | * 打印的过程 就是打印最上边一行(tR 不变 ,tC++),打印最右边一列(tR++,tC = dC 不变), 32 | * 打印最下边一行(tR= dR 不变,tC--),打印最左边一列(tR--,tC =初始值不变), 33 | * @param matrix 34 | */ 35 | private void spiralOrderPrint(int[][] matrix) { 36 | int tR = 0; 37 | int tC = 0; 38 | int dR = matrix.length - 1; 39 | int dC = matrix[0].length - 1; 40 | 41 | while (tR <= dR && tC <= dC) { 42 | printEdge(matrix, tR++, tC++, dR--, dC--); 43 | } 44 | 45 | } 46 | 47 | /** 48 | * @param matrix 矩阵 49 | * @param tR 左上角的行数 50 | * @param tC 左上角的列数 51 | * @param dR 右上角的行数 52 | * @param dC 右下角的列数 53 | *

54 | * 行 r == row 列 c == column 55 | */ 56 | private void printEdge(int[][] matrix, int tR, int tC, int dR, int dC) { 57 | //行数相同,只有一行的情况 58 | if (tR == dR) { 59 | for (int i = tC; i <= dC; i++) { 60 | System.out.print(matrix[tR][i] + " "); 61 | } 62 | } else if (tC == dC) { 63 | for (int i = tR; i <= dR; i++) { 64 | System.out.print(matrix[i][tC] + " "); 65 | } 66 | } else { 67 | 68 | int curR = tR; 69 | int curC = tC; 70 | 71 | //打印上边一行 tR 行不包括最右一个元素 列数递增 72 | while (curC != dC) { 73 | System.out.print(matrix[tR][curC] + " "); 74 | curC++; 75 | } 76 | 77 | //打印最右一列 dC 不包括最下边一个元素 行数递增 78 | while (curR != dR) { 79 | System.out.print(matrix[curR][dC] + " "); 80 | curR++; 81 | } 82 | 83 | //打印最下边一行 dR 不包括最左边一个元素 列数递减 84 | while (curC != tC) { 85 | System.out.print(matrix[dR][curC] + " "); 86 | curC--; 87 | } 88 | //打印最左边一列 tC 不包括最上边一个元素 行数递减 89 | while (curR != tR) { 90 | System.out.print(matrix[curR][tC] + " "); 91 | curR--; 92 | } 93 | } 94 | } 95 | 96 | 97 | /*** 98 | * 之字形打印矩阵 之字型打印 每一斜行打印顺序相反 可以用 boolean 标志打印方向 99 | * 如果从上往下打印则现象是行数递增,列数递减 行数临界值为当前打印的最大行数 dR 100 | * 从下往上打印的现象是行数递减,列数递增 临界值为当前起始的行数 tR 101 | * @param matrix 矩阵 102 | */ 103 | private void printMatrixZigZag(int[][] matrix) { 104 | 105 | int tR = 0; 106 | int tC = 0; 107 | int dR = 0; 108 | int dC = 0; 109 | int endR = matrix.length - 1; 110 | int endC = matrix[0].length - 1; 111 | boolean fromUp = false; 112 | 113 | //tR 最大等于矩阵的行数 114 | while (tR != endR + 1) { 115 | printLevel(matrix, tR, tC, dR, dC, fromUp); 116 | //这里必须让 tR 先让 tR 判断 因为当列数没达到最大的时候,tR 应该是一直为 0 的 117 | tR = tC == endC ? tR + 1 : tR; 118 | tC = tC == endC ? tC : tC + 1; 119 | //这里必须让 dC 先让 dC 判断 因为当行数没达到最大的时候,dC 应该是一直为 0 的 120 | dC = dR == endR ? dC + 1 : dC; 121 | dR = dR == endR ? dR : dR + 1; 122 | fromUp = !fromUp; 123 | } 124 | 125 | } 126 | 127 | 128 | private void printLevel(int[][] matrix, int tR, int tC, int dR, int dC, boolean fromUp) { 129 | if (fromUp) { 130 | while (tR != dR + 1) {//从上往下列数减行数递增 131 | System.out.print(matrix[tR++][tC--] + " "); 132 | } 133 | } else { 134 | while (dR != tR - 1) {//从下往上行数递减,列数递增 135 | System.out.print(matrix[dR--][dC++] + " "); 136 | } 137 | } 138 | 139 | } 140 | 141 | /** 142 | * 将正方形顺时针旋转 90° 143 | * 144 | * @param matrix 矩阵 145 | */ 146 | private void rotateMatrix(int[][] matrix) { 147 | int tR = 0; 148 | int tC = 0; 149 | int dR = matrix.length - 1; 150 | int dC = matrix[0].length - 1; 151 | // M * M 的矩阵 只有 行数不同的时候才能旋转 若已有一行或者只有一列的时候无法旋转 152 | while (tR < dR) { 153 | rotateEdge(matrix, tR++, tC++, dR--, dC--); 154 | } 155 | } 156 | 157 | private void rotateEdge(int[][] matrix, int tR, int tC, int dR, int dC) { 158 | int time = dC - tC; 159 | int temp = 0; 160 | for (int i = 0; i < time; i++) { 161 | //记录第一行的值 从第一行来看 遍历翻转的时候只列在递增 162 | temp = matrix[tR][tC + i]; 163 | //最左边一列变成最上边一行 164 | matrix[tR][tC + i] = matrix[dR - i][tC]; 165 | //最下边一行变成最左边一列 166 | matrix[dR - i][tC] = matrix[dR][dC - i]; 167 | //最右边一列变成最下边一行 168 | matrix[dR][dC - i] = matrix[tR + i][dC]; 169 | //最上边一行变成最右边一列 170 | matrix[tR + i][dC] = temp; 171 | } 172 | } 173 | 174 | private int[][] buildMatrix() { 175 | return new int[][]{{1, 2, 3, 4}, {12, 13, 14, 5}, {11, 16, 15, 6}, {10, 9, 8, 7}}; 176 | } 177 | 178 | private int[][] buildMatrix2() { 179 | return new int[][]{{1, 2, 6, 7}, {3, 5, 8, 13}, {4, 9, 12, 14}, {10, 11, 15, 16}}; 180 | } 181 | 182 | private int[][] buildMatrix3() { 183 | return new int[][]{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/ArrayAMartix/MaxArea.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | /*** 4 | * 盛水最多的容器: 5 | * 给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 6 | * 说明:你不能倾斜容器,且 n 的值至少为 2。 7 | * 8 | * 即求 sum = (j - i) * min(i,j) 最大值 9 | * 此题需要一定的理论证明,即证明 只需要自动较小值的角标位置可以一定遍历到 sum 为最大的情况。 10 | * https://segmentfault.com/a/1190000008824222 11 | * 12 | */ 13 | public class MaxArea { 14 | public static void main(String[] args) { 15 | int[] arr = {1,8,6,2,5,4,8,3,7}; 16 | System.out.println(new MaxArea().maxArea(arr)); 17 | } 18 | 19 | public int maxArea(int[] arr) { 20 | int l = 0; 21 | int r = arr.length - 1; 22 | int max = 0; 23 | while (l < r) { 24 | int height = Math.min(arr[l], arr[r]); 25 | int width = r - l; 26 | 27 | max = Math.max(max, width * height); 28 | 29 | if (arr[l] < arr[r]) { 30 | l++; 31 | } else { 32 | r--; 33 | } 34 | 35 | } 36 | 37 | return max; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/ArrayAMartix/Merge2SortedArr.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | public class Merge2SortedArr { 4 | public static void main(String[] args) { 5 | int[] nums1 = {1,2,3,0,0,0}; 6 | int[] nums2 = {2,5,6}; 7 | int m = 3; int n = 3; 8 | //归并解法 9 | meger(nums1,m,nums2,n); 10 | //尾部插入解法 11 | mergeByInsertEnd(nums1,m,nums2,n); 12 | 13 | } 14 | 15 | /** 16 | * 归并排序的思路求解 17 | */ 18 | private static void meger(int[] nums1, int m, int[] nums2, int n) { 19 | if (n < 1) return; 20 | if (m < 1) System.arraycopy(nums2, 0, nums1, 0, n); 21 | 22 | 23 | int[] temp = new int[m + n]; 24 | int i = 0; 25 | int j = 0; 26 | int index = 0; 27 | while (i < m && j < n) { 28 | if (nums1[i] <= nums2[j]) { 29 | temp[index] = nums1[i]; 30 | i++; 31 | } else { 32 | temp[index] = nums2[j]; 33 | j++; 34 | } 35 | index++; 36 | } 37 | 38 | if (i < m) { 39 | for (int k = i; k < m; k++) { 40 | temp[index] = nums1[k]; 41 | index++; 42 | } 43 | } 44 | 45 | if (j < n) { 46 | for (int k = j; k < n; k++) { 47 | temp[index] = nums2[k]; 48 | index++; 49 | } 50 | } 51 | 52 | System.arraycopy(temp, 0, nums1, 0, temp.length); 53 | 54 | } 55 | 56 | private static void mergeByInsertEnd(int[] nums1, int m, int[] nums2, int n) { 57 | // 尾插法 不开拓新的数组节约空间复杂度 58 | if (n < 1) return; 59 | if (m < 1) System.arraycopy(nums2, 0, nums1, 0, n); 60 | 61 | int k = m + n - 1; //最 nums1 数组中元素的个数 62 | int i = m - 1; 63 | int j = n - 1; 64 | 65 | 66 | while (i >= 0 && j >= 0) { 67 | if (nums1[i] >= nums2[j]) { 68 | nums1[k] = nums1[i]; 69 | i--; 70 | k--; 71 | } else { 72 | nums1[k] = nums2[j]; 73 | j--; 74 | k--; 75 | } 76 | } 77 | 78 | while (j >= 0) { 79 | nums1[k] = nums2[j]; 80 | j--; 81 | k--; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/ArrayAMartix/MinSubArrayLen.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | import java.lang.reflect.Array; 4 | import java.util.*; 5 | import java.util.function.Consumer; 6 | 7 | public class MinSubArrayLen { 8 | 9 | public static void main(String[] args) { 10 | // int[] arr = {2, 3, 1, 2, 4, 3}; 11 | int[] arr = {1, 2, 3, 4, 5}; 12 | HashSet set = new HashSet<>(); 13 | int sum = 15; 14 | System.out.println(new MinSubArrayLen().minSubArrayLen(sum, arr)); 15 | 16 | } 17 | 18 | public int minSubArrayLen(int s, int[] nums) { 19 | 20 | for (int area = 1; area <= nums.length; area++) { 21 | int i = 0; 22 | int j = i + area - 1; 23 | 24 | while (j < nums.length) { 25 | if (sum(nums, i, j) >= s) { 26 | return j - i + 1; 27 | } 28 | j++; 29 | i++; 30 | } 31 | } 32 | return 0; 33 | } 34 | 35 | 36 | private int sum(int[] nums, int i, int j) { 37 | int sum = 0; 38 | System.out.println("i " + i + " j " + j); 39 | for (int n = i; n <= j; n++) { 40 | sum += nums[n]; 41 | } 42 | return sum; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/ArrayAMartix/ModifyArr.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | import java.util.Arrays; 4 | 5 | public class ModifyArr { 6 | 7 | public static void main(String[] args) { 8 | ModifyArr modifyArr = new ModifyArr(); 9 | int[] arr = {1, 8, 3, 2, 4, 6}; 10 | modifyArr.modify(arr); 11 | 12 | System.out.println(Arrays.toString(arr)); 13 | 14 | } 15 | 16 | private void modify(int[] arr) { 17 | if (arr == null || arr.length < 1) { 18 | return; 19 | } 20 | 21 | int even = 0; 22 | int odd = 1; 23 | int end = arr.length - 1; 24 | 25 | while (even <= end && odd <= end) { 26 | //偶数 27 | if ((arr[end] & 1) == 0) { 28 | swap(arr, end, even); 29 | even += 2; 30 | } else { 31 | swap(arr, end, odd); 32 | odd += 2; 33 | } 34 | } 35 | 36 | } 37 | 38 | private void swap(int[] arr, int index1, int index2) { 39 | int temp = arr[index1]; 40 | arr[index1] = arr[index2]; 41 | arr[index2] = temp; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ArrayAMartix/NaNTest.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | public class NaNTest { 4 | public static void main(String[] args) { 5 | Double aDouble = Double.valueOf(1.0 / 0.0); //浮点数相除 6 | System.out.println(aDouble);//1.8 之后Infinity Or 1.8 之前 NaN 7 | 8 | Double bDouble = Double.valueOf(1 / 0); //int 想÷ 9 | System.out.println(bDouble);// java.lang.ArithmeticException: / by zero 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/ArrayAMartix/PrintUniquePairArr.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | /*** 4 | * 不重复打印有序数组中相加和为给定值的所有二元数组和三元数组 5 | */ 6 | public class PrintUniquePairArr { 7 | public static void main(String[] args) { 8 | int[] arr = {-8, -4, -3, 0, 1, 2, 4, 5, 8, 9}; 9 | PrintUniquePairArr printUniquePairArr = new PrintUniquePairArr(); 10 | System.out.println("--------- 打印不重复二元数组 -------"); 11 | printUniquePairArr.printUniquePair(arr, 10); 12 | System.out.println("--------- 打印不重复三元数组 -------"); 13 | printUniquePairArr.printUniqueTriad(arr, 10); 14 | } 15 | 16 | /** 17 | * 打印不重复二元数组 18 | * 19 | * @param arr 20 | * @param k 21 | */ 22 | private void printUniquePair(int[] arr, int k) { 23 | if (arr == null || arr.length < 1) { 24 | return; 25 | } 26 | 27 | int left = 0; 28 | int right = arr.length - 1; 29 | 30 | while (left < right) { 31 | int sum = arr[left] + arr[right]; 32 | if (sum < k) { 33 | left++; 34 | } else if (sum > k) { 35 | right--; 36 | } else { 37 | // arr[left - 1] != arr[left] 保证了 不重复打印 38 | if (left == 0 || arr[left - 1] != arr[left]) { 39 | System.out.println(arr[left] + "," + arr[right]); 40 | //同时操作两个指针 41 | left++; 42 | right--; 43 | } 44 | } 45 | } 46 | } 47 | 48 | /** 49 | * 打印不重复三元数组 50 | * 与二元数组不同的是: 需要记录三元数组的起始值,再去寻找另外两个相加为 k - arr[i] 51 | * 52 | * @param arr 53 | * @param k 54 | */ 55 | private void printUniqueTriad(int[] arr, int k) { 56 | if (arr == null || arr.length < 1) { 57 | return; 58 | } 59 | 60 | for (int i = 0; i < arr.length - 2; i++) { 61 | //保证三元数组不重复 62 | if (i == 0 || arr[i] != arr[i - 1]) { 63 | printRest(arr, i, i + 1, arr.length - 1, k - arr[i]); 64 | } 65 | } 66 | } 67 | 68 | private void printRest(int[] arr, int f, int l, int r, int k) { 69 | while (l < r) { 70 | int sum = arr[l] + arr[r]; 71 | if (sum < k) { 72 | l++; 73 | } else if (sum > k) { 74 | r--; 75 | } else { 76 | if (l == f || arr[l - 1] != arr[l]) { 77 | System.out.println(arr[f] + "," + arr[l] + "," + arr[r]); 78 | //同时操作两个指针 79 | l++; 80 | r--; 81 | } 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/ArrayAMartix/SortArrAsNature.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | import java.util.Arrays; 4 | 5 | public class SortArrAsNature { 6 | public static void main(String[] args) { 7 | int[] arr = {1, 2, 5, 3, 4}; 8 | 9 | SortArrAsNature asNature = new SortArrAsNature(); 10 | int[] arr1 = Arrays.copyOf(arr, arr.length); 11 | asNature.sort2(arr1); 12 | int[] arr2 = Arrays.copyOf(arr, arr.length); 13 | asNature.sort(arr2); 14 | 15 | System.out.println(Arrays.toString(arr1)); 16 | System.out.println(Arrays.toString(arr2)); 17 | 18 | } 19 | 20 | private void sort(int[] arr) { 21 | 22 | int temp = 0; 23 | 24 | for (int i = 0; i < arr.length; i++) { 25 | while (arr[i] != i + 1) { 26 | temp = arr[arr[i] - 1]; 27 | arr[arr[i] - 1] = arr[i]; 28 | arr[i] = temp; 29 | } 30 | } 31 | } 32 | 33 | /** 34 | * @param arr 35 | */ 36 | private void sort2(int[] arr) { 37 | 38 | int temp = 0; 39 | int next = 0; 40 | 41 | for (int i = 0; i != arr.length; i++) { 42 | temp = arr[i]; 43 | 44 | while (arr[i] != i + 1) { 45 | next = arr[temp - 1]; 46 | arr[temp - 1] = temp; 47 | temp = next; 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/ArrayAMartix/SortColors.java: -------------------------------------------------------------------------------- 1 | package ArrayAMartix; 2 | 3 | import java.util.Arrays; 4 | 5 | /*** 6 | *给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 7 | *此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 8 | * 9 | * 示例: 10 | * 11 | * 输入: [2,0,2,1,1,0] 12 | * 输出: [0,0,1,1,2,2] 13 | * 14 | */ 15 | public class SortColors { 16 | 17 | public static void main(String[] args) { 18 | int[] nums = {2, 0, 2, 1, 1, 0}; 19 | // sortColors(nums); 20 | sortColorByQuickSort(nums); 21 | System.out.println(Arrays.toString(nums)); 22 | } 23 | 24 | // public static void sortColors(int[] nums) { 25 | // int lenCount = 3; 26 | // int[] countArr = new int[lenCount]; 27 | // //计数 28 | // for (int i = 0; i < nums.length; i++) { 29 | // countArr[nums[i]] += 1; 30 | // } 31 | // 32 | // //求和 33 | // for (int i = 1; i < countArr.length; i++) { 34 | // countArr[i] = countArr[i] + countArr[i - 1]; 35 | // } 36 | // 37 | // System.out.println(Arrays.toString(countArr)); 38 | // 39 | // //赋值 40 | // int[] result = new int[nums.length]; 41 | // 42 | // for (int i = nums.length - 1; i >= 0; i--) { 43 | // //这里减1是必须的? 44 | // result[countArr[nums[i]] - 1] = nums[i]; 45 | // countArr[nums[i]] -= 1; 46 | // } 47 | // 48 | // System.arraycopy(result, 0, nums, 0, nums.length); 49 | // } 50 | 51 | 52 | /** 53 | * 基于计数排序 54 | * 55 | * @param nums 待排序数组 56 | */ 57 | public static void sortColors(int[] nums) { 58 | int max = 0; 59 | int min = 0; 60 | //求数组中的最大最小值,得到计数集合的大小 61 | for (int i : nums) { 62 | if (i > max) { 63 | max = i; 64 | } 65 | 66 | if (i < min) { 67 | min = i; 68 | } 69 | } 70 | //计数 71 | 72 | //数组中最多可能有多少种元素 73 | int lenResult = max - min + 1; 74 | //该数组用来存放,对应数出现的次数 75 | int[] countArr = new int[lenResult]; 76 | 77 | for (int i = 0; i < nums.length; i++) { 78 | int num = nums[i]; 79 | countArr[num - min] += 1; 80 | } 81 | 82 | //求和 数组当前角标的值,等于前一个角标的值,与当前角标的值求和 这步的原理是什么? 83 | //此后每一位并不代表出现的次数了 84 | // countArr[num[i] - min] 代表小于该位置元素值的元素的个数,所以num[i] 应该处于countArr[num[i] - min] -1 位置 85 | 86 | for (int i = 1; i < countArr.length; i++) { 87 | countArr[i] = countArr[i] + countArr[i - 1]; 88 | } 89 | 90 | 91 | int[] result = new int[nums.length]; 92 | //倒着取元素 赋值 93 | for (int i = nums.length - 1; i >= 0; i--) { 94 | //数组中第i个元素的值 - 最小值 - 1 为所代表的数字在 countArr 中的角标 95 | //而这个i元素在结果数组中的位置正是 上一步得到的角标所在 countArr 的数值? 96 | int index = countArr[nums[i] - min] - 1; 97 | result[index] = nums[i]; 98 | //当前角标数减去 1 避免下次取得的相同的数放在同样的位置 99 | countArr[nums[i] - min] -= 1; 100 | } 101 | 102 | System.arraycopy(result, 0, nums, 0, nums.length); 103 | } 104 | 105 | 106 | private static void sortColorByQuickSort(int[] nums) { 107 | quickSort(nums, 0, nums.length - 1); 108 | } 109 | 110 | private static void quickSort(int[] nums, int l, int r) { 111 | 112 | _quickSort(nums, l, r); 113 | 114 | } 115 | 116 | private static void _quickSort(int[] nums, int l, int r) { 117 | if (l >= r) { 118 | return; 119 | } 120 | 121 | int v = nums[l]; 122 | int lt = l;// arr[l+1...lt] < v 123 | int gt = r + 1;// arr[gt...r] > v 124 | int i = l + 1;// arr[lt+1...i) == v 125 | 126 | while (i < gt) { 127 | int num = nums[i]; 128 | if (num < v) { 129 | swap(nums, i, lt + 1);//如果不交换 则会得到不正确的结果 130 | lt++; 131 | i++; 132 | } else if (num > v) { 133 | swap(nums, i, gt - 1); 134 | gt--; 135 | } else { 136 | i++; 137 | } 138 | } 139 | 140 | swap(nums, l, lt); 141 | 142 | _quickSort(nums, l, lt - 1); 143 | _quickSort(nums, gt, r); 144 | } 145 | 146 | private static void swap(int[] nums, int a, int b) { 147 | int temp = nums[a]; 148 | nums[a] = nums[b]; 149 | nums[b] = temp; 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /src/BinaryTree/BinaryOrder.java: -------------------------------------------------------------------------------- 1 | package BinaryTree; 2 | 3 | 4 | public class BinaryOrder { 5 | public static void main(String[] args) { 6 | BinaryTreeUtils tree = new BinaryTreeUtils(); 7 | TreeNode binaryTree = tree.createBinaryTree(); 8 | 9 | BinaryTreeUtils.levelTraverse(binaryTree); 10 | System.out.println(); 11 | 12 | TreeNode binaryTree2 = tree.createBinaryTree2(); 13 | //层级遍历结果 8 7 6 4 1 3 10 20 11 14 | BinaryTreeUtils.levelTraverse(binaryTree2); 15 | System.out.println(); 16 | 17 | //先序遍历 8 7 4 20 1 6 3 11 10 18 | BinaryTreeUtils.beforeTraverse(binaryTree2); 19 | System.out.println(); 20 | 21 | //先序遍历 8 7 4 20 1 6 3 11 10 22 | BinaryTreeUtils.beforeTraverseByStack(binaryTree2); 23 | System.out.println(); 24 | 25 | //中序遍历 20 4 7 1 8 3 11 6 10 26 | BinaryTreeUtils.middleTraverse(binaryTree2); 27 | System.out.println(); 28 | 29 | //中序遍历 20 4 7 1 8 3 11 6 10 30 | BinaryTreeUtils.middleTraverseByStack(binaryTree2); 31 | System.out.println(); 32 | 33 | //后续遍历 20 4 1 7 11 3 10 6 8 34 | BinaryTreeUtils.afterTraverse(binaryTree2); 35 | System.out.println(); 36 | 37 | //后续遍历 20 4 1 7 11 3 10 6 8 38 | BinaryTreeUtils.afterTraverseByStack1(binaryTree2); 39 | System.out.println(); 40 | 41 | //后续遍历 20 4 1 7 11 3 10 6 8 42 | BinaryTreeUtils.afterTraverseByStack2(binaryTree2); 43 | System.out.println(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/BinaryTree/TreeNode.java: -------------------------------------------------------------------------------- 1 | package BinaryTree; 2 | 3 | /** 4 | * 孩子兄弟法表示一个二叉树 5 | */ 6 | public class TreeNode { 7 | public T data; 8 | public TreeNode leftChild; 9 | public TreeNode rightChild; 10 | public TreeNode(T data) { 11 | this.data = data; 12 | this.leftChild = null; 13 | this.rightChild = null; 14 | } 15 | } -------------------------------------------------------------------------------- /src/Set/ThreeSum.java: -------------------------------------------------------------------------------- 1 | package Set; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | public class ThreeSum { 8 | 9 | public static void main(String[] args) { 10 | int[] nums = {-1, 0, 1, 2, -1, -4}; 11 | ThreeSum three = new ThreeSum(); 12 | List> threeSum = three.threeSum(nums); 13 | System.out.println("threeSum = " + threeSum); 14 | 15 | int[] fourTest = {5,0,2,-5,-5,4,-5,1,-1};// -5 -5 -5 -1 0 1 2 4 5 16 | 17 | List> fourSum = three.fourSum(fourTest,-5); 18 | 19 | System.out.println("fourSum = " + fourSum); 20 | 21 | 22 | } 23 | 24 | 25 | public List> threeSum(int[] nums) { 26 | if (nums == null || nums.length < 3) { 27 | return null; 28 | } 29 | 30 | ArrayList> result = new ArrayList<>(); 31 | 32 | Arrays.sort(nums); 33 | 34 | for (int k = 0; k < nums.length - 2; k++) { 35 | //我们要寻求的目标解的值 36 | int target = 0 - nums[k]; 37 | 38 | //忽略相同的目标值只比较同一个相同的值 39 | 40 | int i = k + 1; 41 | int j = nums.length - 1; 42 | 43 | //对撞指针求解有续数组两数只和为target的数 44 | while (i < j) { 45 | if (nums[i] + nums[j] < target) { 46 | i++; 47 | } else if (nums[i] + nums[j] > target) { 48 | j--; 49 | } else { 50 | ArrayList twoSumList = new ArrayList<>(); 51 | twoSumList.add(nums[k]); 52 | twoSumList.add(nums[i]); 53 | twoSumList.add(nums[j]); 54 | result.add(twoSumList); 55 | //滤除可能的重复解 56 | while (i < j && nums[i] == nums[i + 1]) i++; 57 | while (i < j && nums[j] == nums[j - 1]) j--; 58 | //上述遍历之后是最后一个相等的位置,所以还要++ 59 | i++; 60 | j--; 61 | } 62 | } 63 | 64 | while (k < nums.length -2 && nums[k] == nums[k + 1]) 65 | k++; 66 | 67 | } 68 | 69 | return result; 70 | } 71 | 72 | 73 | public List> fourSum(int[] nums,int target) { 74 | 75 | 76 | ArrayList> result = new ArrayList<>(); 77 | 78 | if (nums == null || nums.length < 4) { 79 | return result; 80 | } 81 | 82 | Arrays.sort(nums);// -5 -5 -5 -1 0 1 2 4 5 83 | 84 | 85 | for (int i = 0; i < nums.length - 3; i++) { 86 | 87 | for (int j = i + 1; j < nums.length - 2; j++) { 88 | 89 | //我们要寻求的目标解的值 90 | int sum = target - nums[i] - nums[j]; 91 | 92 | int a = j + 1; 93 | int b = nums.length - 1; 94 | 95 | //对撞指针求解有续数组两数只和为target的数 96 | while (a < b) { 97 | if (nums[a] + nums[b] < sum) { 98 | a++; 99 | } else if (nums[a] + nums[b] > sum) { 100 | b--; 101 | } else { 102 | ArrayList twoSumList = new ArrayList<>(); 103 | twoSumList.add(nums[i]); 104 | twoSumList.add(nums[j]); 105 | twoSumList.add(nums[a]); 106 | twoSumList.add(nums[b]); 107 | result.add(twoSumList); 108 | //滤除可能的重复解 109 | while (a < b && nums[a] == nums[a + 1]) a++; 110 | while (a < b && nums[b] == nums[b - 1]) b--; 111 | //上述遍历之后是最后一个相等的位置,所以还要++ 112 | a++; 113 | b--; 114 | } 115 | } 116 | 117 | //如果放到循环 ab 前边会忽略两个 0 的情况 118 | while (j < nums.length - 2 && nums[j] == nums[j + 1]) 119 | j++; 120 | 121 | } 122 | 123 | //忽略相同的目标值只比较同一个相同的值 124 | while ( i < nums.length -3 && nums[i + 1] == nums[i]) 125 | i++; 126 | } 127 | 128 | return result; 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/Set/isAnagram.java: -------------------------------------------------------------------------------- 1 | package Set; 2 | 3 | import java.util.HashMap; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | /*** 8 | * 字母异位词 两个字符串长度相同,包含的字符相同(包括同一个字符的个数)相同,(字符顺序可以相同也可以不同) 9 | * 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的一个字母异位词。 10 | * 11 | * 输入: s = "anagram", t = "nagaram" 12 | * 输出: true 13 | * 14 | * 输入: s = "rat", t = "car" 15 | * 输出: false 16 | * 17 | * 计数每个字符出现的次序,如果 18 | */ 19 | public class isAnagram { 20 | public static void main(String[] args) { 21 | String s = "anagram"; 22 | String t = "anagram"; 23 | 24 | isAnagram isAnagram = new isAnagram(); 25 | boolean anagram = isAnagram.isAnagram(s, t); 26 | System.out.println("anagram = " + anagram); 27 | 28 | // 用例 ab aa false // ab ca true 29 | boolean isomorphic = isAnagram.isIsomorphic("ab", "aa"); 30 | System.out.println("isomorphic = " + isomorphic); 31 | 32 | 33 | } 34 | 35 | public boolean isIsomorphic(String s, String t) { 36 | if (s == null || t == null) { 37 | return false; 38 | } 39 | 40 | if (s.length() != t.length()) { 41 | return false; 42 | } 43 | 44 | if (s.length() == 1) { 45 | return true; 46 | } 47 | 48 | 49 | char[] charsS = s.toCharArray(); 50 | char[] charsT = t.toCharArray(); 51 | 52 | HashMap map = new HashMap<>(); 53 | Set set = new HashSet<>(); 54 | 55 | for (int i = 0; i < s.length(); i++) { 56 | char sChar = charsS[i]; 57 | char tChar = charsT[i]; 58 | 59 | if(map.containsKey(sChar)){ 60 | if(map.get(sChar) != tChar){ 61 | return false; 62 | } 63 | }else { 64 | if(set.contains(tChar)){ 65 | return false; 66 | }else { 67 | map.put(sChar, tChar); 68 | set.add(tChar); 69 | } 70 | } 71 | } 72 | 73 | return true; 74 | } 75 | 76 | private boolean isAnagram(String s, String t) { 77 | if (s == null || t == null) { 78 | return false; 79 | } 80 | 81 | if (s.length() != t.length()) { 82 | return false; 83 | } 84 | 85 | char[] sArr = s.toCharArray(); 86 | char[] tArr = t.toCharArray(); 87 | 88 | //不考虑特殊情况时,因为共有26个字符 89 | int[] result = new int[26]; 90 | 91 | for (int i = 0; i < s.length(); i++) { 92 | result[sArr[i] - 'a']++; 93 | result[tArr[i] - 'a']--; 94 | } 95 | 96 | for (int res : result) { 97 | if (res != 0) { 98 | return false; 99 | } 100 | } 101 | 102 | return true; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Set/isHappyNums.java: -------------------------------------------------------------------------------- 1 | package Set; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | public class isHappyNums { 7 | 8 | public static void main(String[] args) { 9 | boolean happy = new isHappyNums().isHappy(19); 10 | System.out.println("happy = " + happy); 11 | } 12 | public boolean isHappy(int n) { 13 | HashSet set = new HashSet<>(); 14 | 15 | while( n != 1){ 16 | n = getNextHappyNum(n); 17 | 18 | System.out.println("next = " + n); 19 | 20 | if(set.contains(n)){ 21 | return false; 22 | }else{ 23 | set.add(n); 24 | } 25 | } 26 | return true; 27 | } 28 | 29 | private int getNextHappyNum(int num){ 30 | int result = 0; 31 | while (num > 0) { 32 | result += (num % 10) * (num % 10); 33 | num = num / 10; 34 | } 35 | return result; 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/Sorting_Basic/Insertion_Sort/InsertIonSort.java: -------------------------------------------------------------------------------- 1 | package Sorting_Basic.Insertion_Sort; 2 | 3 | import Sorting_Basic.Selection_Sort.SortTestHelper; 4 | 5 | import java.util.Arrays; 6 | 7 | /*** 8 | * 插入排序 9 | */ 10 | public class InsertIonSort { 11 | 12 | public static > void sort(T[] arr) { 13 | int n = arr.length; 14 | for (int i = 0; i < n; i++) { 15 | //内层循环比较 i 与前边所有元素值,如果 j 索引所指的值小于 j- 1 则交换两者的位置 16 | for (int j = i; j > 0 && arr[j].compareTo(arr[j - 1]) < 0; j--) { 17 | SortTestHelper.swap(arr, j, j - 1); 18 | } 19 | } 20 | } 21 | 22 | 23 | // 测试InsertionSort 24 | public static void main(String[] args) { 25 | 26 | int N = 20000; 27 | Integer[] arr1 = SortTestHelper.generateRandomArray(N, 0, N); 28 | Integer[] arr2 = Arrays.copyOf(arr1, arr1.length); 29 | 30 | // 比较SelectionSort和InsertionSort两种排序算法的性能效率 31 | // 此时,插入排序比选择排序性能略低 32 | SortTestHelper.testSort("Sorting_Basic.Selection_Sort.SelectionSort", arr2); 33 | SortTestHelper.testSort("Sorting_Basic.Insertion_Sort.InsertIonSort", arr1); 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/Sorting_Basic/Insertion_Sort/InsertionSortAdvance.java: -------------------------------------------------------------------------------- 1 | package Sorting_Basic.Insertion_Sort; 2 | 3 | import Sorting_Basic.Selection_Sort.SortTestHelper; 4 | 5 | import java.util.Arrays; 6 | 7 | /** 8 | * 优化后的 插入排序 插入排序本身是 O(n²) 的时间复杂度 但是之前内层循环在不停的交换 j j-1 (如果 j-1 大于 j 的情况下) 9 | * 交换需要进行三次赋值 导致效率很低下 因此有改善的版本原理就是 如果 (如果 j-1 大于 j 的情况下) 那么就让 j 等于 j -1 直到 10 | * 内存循环因为 j-1 小于 j 了 那么此时 j 应该就是 外层循环 i 对应的值所应该处于的角标位置 11 | */ 12 | public class InsertionSortAdvance { 13 | 14 | public static > void sort(T[] arr) { 15 | int length = arr.length; 16 | for (int i = 0; i < length; i++) { 17 | 18 | T e = arr[i]; 19 | int j = i; 20 | // 注意内层循环结束条件 21 | for (; j > 0 && arr[j - 1].compareTo(arr[j]) > 0; j--) { 22 | arr[j] = arr[j - 1]; 23 | } 24 | arr[j] = e; 25 | } 26 | } 27 | 28 | public static void main(String[] args) { 29 | int N = 20000; 30 | 31 | // 测试1 一般测试 32 | System.out.println("Test for random array, size = " + N + " , random range [0, " + N + "]"); 33 | 34 | Integer[] arr1 = SortTestHelper.generateRandomArray(N, 0, N); 35 | Integer[] arr2 = Arrays.copyOf(arr1, arr1.length); 36 | Integer[] arr3 = Arrays.copyOf(arr1, arr1.length); 37 | 38 | SortTestHelper.testSort("Sorting_Basic.Insertion_Sort.InsertionSortAdvance", arr1); 39 | SortTestHelper.testSort("Sorting_Basic.Insertion_Sort.InsertIonSort", arr3); 40 | SortTestHelper.testSort("Sorting_Basic.Selection_Sort.SelectionSort", arr2); 41 | 42 | System.out.println(); 43 | 44 | 45 | // 测试2 有序性更强的测试 46 | System.out.println("Test for more ordered random array, size = " + N + " , random range [0,3]"); 47 | 48 | arr1 = SortTestHelper.generateRandomArray(N, 0, 3); 49 | arr2 = Arrays.copyOf(arr1, arr1.length); 50 | arr3 = Arrays.copyOf(arr1, arr1.length); 51 | 52 | SortTestHelper.testSort("Sorting_Basic.Insertion_Sort.InsertionSortAdvance", arr1); 53 | SortTestHelper.testSort("Sorting_Basic.Insertion_Sort.InsertIonSort", arr3); 54 | SortTestHelper.testSort("Sorting_Basic.Selection_Sort.SelectionSort", arr2); 55 | 56 | System.out.println(); 57 | 58 | 59 | // 测试3 测试近乎有序的数组 60 | int swapTimes = 100; 61 | System.out.println("Test for nearly ordered array, size = " + N + " , swap time = " + swapTimes); 62 | 63 | arr1 = SortTestHelper.generateNearlyOrderedArray(N, swapTimes); 64 | arr2 = Arrays.copyOf(arr1, arr1.length); 65 | arr3 = Arrays.copyOf(arr1, arr1.length); 66 | 67 | SortTestHelper.testSort("Sorting_Basic.Insertion_Sort.InsertionSortAdvance", arr1); 68 | SortTestHelper.testSort("Sorting_Basic.Insertion_Sort.InsertIonSort", arr3); 69 | SortTestHelper.testSort("Sorting_Basic.Selection_Sort.SelectionSort", arr2); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Sorting_Basic/Selection_Sort/SelectionSort.java: -------------------------------------------------------------------------------- 1 | package Sorting_Basic.Selection_Sort; 2 | /** 3 | * 选择排序 4 | */ 5 | public class SelectionSort { 6 | public static > void sort(T[] arr) { 7 | int n = arr.length; 8 | for (int i = 0; i < n; i++) { 9 | int minIndex = i; 10 | // for 循环 i 之后所有的数字 找到剩余数组中最小值得索引 11 | for (int j = i + 1; j < n; j++) { 12 | // < 0 比较的对象 小于 被比较的对象 13 | if (arr[j].compareTo(arr[minIndex]) < 0) { 14 | minIndex = j; 15 | } 16 | } 17 | swap(arr, i, minIndex); 18 | } 19 | 20 | } 21 | 22 | /** 23 | * 角标的形式 交换元素 24 | */ 25 | private static void swap(Object[] arr, int i, int j) { 26 | Object temp = arr[i]; 27 | arr[i] = arr[j]; 28 | arr[j] = temp; 29 | } 30 | 31 | public static void main(String[] args) { 32 | // Integer[] arr = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; 33 | // SelectionSort.sort(arr); 34 | // System.out.println(Arrays.toString(arr)); 35 | // 36 | // // 测试Double 37 | // Double[] doubles = {4.4, 3.3, 2.2, 1.1}; 38 | // SelectionSort.sort(doubles); 39 | // System.out.println(Arrays.toString(doubles)); 40 | // 41 | // // 测试String 42 | // String[] strings = {"D", "C", "B", "A"}; 43 | // SelectionSort.sort(strings); 44 | // System.out.println(Arrays.toString(strings)); 45 | // 46 | // 47 | // // 测试自定义的类 Student 48 | // Student[] students = new Student[4]; 49 | // students[0] = new Student("D",90); 50 | // students[1] = new Student("C",100); 51 | // students[2] = new Student("B",95); 52 | // students[3] = new Student("A",95); 53 | // SelectionSort.sort(students); 54 | // 55 | // System.out.println(Arrays.toString(students)); 56 | 57 | 58 | // 测试排序算法辅助函数 59 | int N = 20000; 60 | Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000); 61 | SortTestHelper.printArray(arr); 62 | SortTestHelper.testSort("Sorting_Basic.Selection_Sort.SelectionSort", arr); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Sorting_Basic/Selection_Sort/SortTestHelper.java: -------------------------------------------------------------------------------- 1 | package Sorting_Basic.Selection_Sort; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | public class SortTestHelper { 6 | // SortTestHelper不允许产生任何实例 7 | private SortTestHelper(){} 8 | 9 | // 生成有n个元素的随机数组,每个元素的随机范围为[rangeL, rangeR] 10 | public static Integer[] generateRandomArray(int n, int rangeL, int rangeR) { 11 | 12 | assert rangeL <= rangeR; 13 | 14 | Integer[] arr = new Integer[n]; 15 | 16 | for (int i = 0; i < n; i++) 17 | arr[i] = (int) (Math.random() * (rangeR - rangeL + 1) + rangeL); 18 | return arr; 19 | } 20 | 21 | // 打印arr数组的所有内容 22 | public static void printArray(Object arr[]) { 23 | 24 | for (Object anArr : arr) { 25 | System.out.print(anArr); 26 | System.out.print(' '); 27 | } 28 | System.out.println(); 29 | } 30 | 31 | /** 32 | * 角标的形式 交换元素 33 | */ 34 | public static void swap(Object[] arr, int i, int j) { 35 | Object temp = arr[i]; 36 | arr[i] = arr[j]; 37 | arr[j] = temp; 38 | } 39 | 40 | // 判断arr数组是否有序 41 | public static boolean isSorted(Comparable[] arr){ 42 | 43 | for( int i = 0 ; i < arr.length - 1 ; i ++ ) 44 | if( arr[i].compareTo(arr[i+1]) > 0 ) 45 | return false; 46 | return true; 47 | } 48 | 49 | // 测试sortClassName所对应的排序算法排序arr数组所得到结果的正确性和算法运行时间 50 | public static void testSort(String sortClassName, Comparable[] arr){ 51 | 52 | // 通过Java的反射机制,通过排序的类名,运行排序函数 53 | // * 依然是,使用反射机制并不是这个课程的重点, 大家也完全可以使用自己的方式书写代码, 最终只要能够测试出自己书写的算法的效率即可 54 | // * 推荐大家阅读我在问答区向大家分享的一个学习心得: 【学习心得分享】请大家抓大放小,不要纠结于C++语言的语法细节 55 | // * 链接: http://coding.imooc.com/learn/questiondetail/4100.html 56 | try{ 57 | // 通过sortClassName获得排序函数的Class对象 58 | Class sortClass = Class.forName(sortClassName); 59 | // 通过排序函数的Class对象获得排序方法 60 | Method sortMethod = sortClass.getMethod("sort",new Class[]{Comparable[].class}); 61 | // 排序参数只有一个,是可比较数组arr 62 | Object[] params = new Object[]{arr}; 63 | 64 | long startTime = System.currentTimeMillis(); 65 | // 调用排序函数 66 | sortMethod.invoke(null,params); 67 | long endTime = System.currentTimeMillis(); 68 | 69 | assert isSorted( arr ); 70 | 71 | System.out.println( sortClass.getSimpleName()+ " : " + (endTime-startTime) + "ms" ); 72 | } 73 | catch(Exception e){ 74 | e.printStackTrace(); 75 | } 76 | } 77 | 78 | 79 | // 生成一个近乎有序的数组 80 | // 首先生成一个含有[0...n-1]的完全有序数组, 之后随机交换swapTimes对数据 81 | // swapTimes定义了数组的无序程度: 82 | // swapTimes == 0 时, 数组完全有序 83 | // swapTimes 越大, 数组越趋向于无序 84 | 85 | public static Integer[] generateNearlyOrderedArray(int n, int swapTimes){ 86 | 87 | Integer[] arr = new Integer[n]; 88 | for( int i = 0 ; i < n ; i ++ ) 89 | arr[i] = i; 90 | 91 | for( int i = 0 ; i < swapTimes ; i ++ ){ 92 | int a = (int)(Math.random() * n); 93 | int b = (int)(Math.random() * n); 94 | 95 | int t = arr[a]; 96 | arr[a] = arr[b]; 97 | arr[b] = t; 98 | } 99 | 100 | return arr; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Sorting_Basic/Selection_Sort/Student.java: -------------------------------------------------------------------------------- 1 | package Sorting_Basic.Selection_Sort; 2 | 3 | public class Student implements Comparable { 4 | 5 | private String name; 6 | private int score; 7 | 8 | public Student(String name, int score){ 9 | this.name = name; 10 | this.score = score; 11 | } 12 | 13 | // 定义Student的compareTo函数 14 | // 如果分数相等,则按照名字的字母序排序 15 | // 如果分数不等,则分数高的靠前 16 | @Override 17 | public int compareTo(Student that) { 18 | 19 | if( this.score == that.score ) 20 | return this.name.compareTo(that.name); 21 | 22 | if( this.score < that.score ) 23 | return 1; 24 | else if( this.score > that.score ) 25 | return -1; 26 | else // this.score == that.score 27 | return 0; 28 | } 29 | 30 | // 定义Student实例的打印输出方式 31 | @Override 32 | public String toString() { 33 | return "Student: " + this.name + " " + Integer.toString( this.score ); 34 | } 35 | } -------------------------------------------------------------------------------- /src/Sorting_Basic/merge_sort/MergerSort.java: -------------------------------------------------------------------------------- 1 | package Sorting_Basic.merge_sort; 2 | 3 | import Sorting_Basic.Selection_Sort.SortTestHelper; 4 | 5 | import java.util.Arrays; 6 | 7 | 8 | public class MergerSort { 9 | public static void main(String[] args) { 10 | int N = 200; 11 | Integer[] num = SortTestHelper.generateRandomArray(N, 0, N); 12 | mergeSort(num, num.length - 1); 13 | mergeSortBU(num, num.length); 14 | System.out.println(Arrays.toString(num)); 15 | 16 | int[] arr = {8, 3, 1, 6, 7, 4, 2, 19}; 17 | BubbleSort(arr, arr.length); 18 | System.out.println(Arrays.toString(arr)); 19 | } 20 | 21 | /** 22 | * @param arr 待排序数组 23 | * @param n 数组长度 24 | */ 25 | private static void BubbleSort(int[] arr, int n) { 26 | //n = 1 的时候不需要排序 27 | for (int i = 0; i < n - 1; i++) { 28 | for (int j = 1; j < n - i; j++) { 29 | if (arr[j - 1] > arr[j]) { 30 | //交换两个元素 31 | int temp = arr[j]; 32 | arr[j] = arr[j - 1]; 33 | arr[j - 1] = temp; 34 | } 35 | } 36 | } 37 | } 38 | 39 | private static void mergeSort(Integer[] num, int n) { 40 | __mergeSort(num, 0, n - 1); 41 | } 42 | 43 | /*** 44 | * 45 | * @param arr 待排序数组 46 | * @param l 其实元素角标 0 47 | * @param r 最后一个元素角标 n -1 48 | */ 49 | private static void __mergeSort(Integer[] arr, int l, int r) { 50 | if (l >= r) { 51 | return; 52 | } 53 | 54 | //开始归并排序 55 | int mid = (l + r) / 2; 56 | //递归划分数组 57 | __mergeSort(arr, l, mid); 58 | __mergeSort(arr, mid + 1, r); 59 | 60 | //检查是否上一步归并完的数组是否有序,如果有序则直接进行下一次归并 61 | if (arr[mid] <= arr[mid + 1]) { 62 | return; 63 | } 64 | //将两边的元素归并排序 65 | merge(arr, l, mid, r); 66 | } 67 | 68 | /** 69 | * 自低向上的归并排序 70 | * 71 | * @param n 为数组长度 72 | * @param arr 数组 73 | */ 74 | private static void mergeSortBU(Integer[] arr, int n) { 75 | //外层遍历从归并区间长度为1 开始 每次递增一倍的空间 1 2 4 8 sz 需要遍历到数组长度那么大 76 | //sz = 1 : [0] [1]... 77 | //sz = 2 : [0,1] [2.3] ... 78 | //sz = 4 : [0..3] [4...7] ... 79 | for (int sz = 1; sz <= n; sz += sz) { 80 | 81 | //内层遍历要比较 arr[i,i+sz-1] arr[i+sz,i+sz+sz-1] 两个区间的大小 也就是每次对 sz - 1 大小的数组空间进行归并 82 | // 注意每次 i 递增 两个 sz 的长度 ,因为每次 merge 的时候已经归并了两个 sz 长度 部分的数组 83 | for (int i = 0; i + sz < n; i += sz + sz) { 84 | merge(arr, i, i + sz - 1, Math.min(i + sz + sz - 1, n - 1)); 85 | } 86 | } 87 | } 88 | 89 | /** 90 | * arr[l,mid] 和 arr[mid+1,r] 两部分进行归并 91 | */ 92 | private static void merge(Integer[] arr, int l, int mid, int r) { 93 | 94 | // 复制数组 用来比较归并的数组两边的值 95 | int[] aux = new int[r - l + 1]; 96 | 97 | for (int i = l; i <= r; i++) { 98 | aux[i - l] = arr[i]; 99 | } 100 | 101 | int i = l; 102 | int j = mid + 1; 103 | 104 | for (int k = l; k <= r; k++) { 105 | if (i > mid) { 106 | //说明左边部分已经全都放入临时数组了 107 | arr[k] = aux[j - l]; 108 | j++; 109 | } else if (j > r) { 110 | arr[k] = aux[i - l]; 111 | i++; 112 | } else if (aux[i - l] < aux[j - l]) { 113 | arr[k] = aux[i - l]; 114 | i++; 115 | } else { 116 | arr[k] = aux[j - l]; 117 | j++; 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Sorting_Basic/quick_sort/QuickSort.java: -------------------------------------------------------------------------------- 1 | package Sorting_Basic.quick_sort; 2 | 3 | import Sorting_Basic.Selection_Sort.SortTestHelper; 4 | 5 | import java.lang.reflect.Array; 6 | import java.util.Arrays; 7 | 8 | /*** 9 | * 快速排序:将数组中的一个数,放在合适的位置: 10 | * 这个位置左边的数全部小于该数值,这个位置右边的数全部大于该数值。 11 | * 12 | * 单路快速排序: 13 | * 假设指定数值为数组第一个元素 int v = arr[l] 14 | * 假设 j 标记为比 v 小的最后一个元素, 即 arr[j+1] > v 15 | * 当前考察的元素为 i 16 | * 17 | * 则有; 18 | * arr[l + 1 ... j] < v 19 | * arr[j+1,i) >= v 20 | * 21 | * 初始化 22 | * 23 | * int v = arr[l]; 24 | * 25 | * int j = l; // 使 arr[l + 1 ... j] 最开始为空 26 | * int i = l + 1; 使 arr[j+1,i) 最开始为空 27 | * 28 | * 在循环考察的过程中 29 | * 30 | * 遇到 arr[i] >= v 的时候 i++ 31 | * 遇到 arr[i] < v 的时候 需要交换 arr[j+1] 与 arr[i] 的位置,交换完成后,j ++ 32 | * 33 | * 考察完成后 34 | * 35 | * arr[l+1...j] < v 36 | * arr[j+1...r] >= v 37 | * 38 | * 最后需要做的就是 将 arr[l] 与 arr[j] 交换位置 39 | * 40 | * 这样一顿操作后 就能保证 41 | * 42 | * arr[l ... j-1] < v 43 | * arr[j+1 ... r] >=v 44 | * 45 | * 随后递归的将左右两边进行同样的操作就可以了 46 | * 47 | * 48 | */ 49 | public class QuickSort { 50 | 51 | public static void main(String[] args) { 52 | int N = 200; 53 | Integer[] num = SortTestHelper.generateRandomArray(N, 0, N); 54 | quickSort(num, num.length); 55 | System.out.println(Arrays.toString(num)); 56 | } 57 | 58 | /** 59 | * 单路快速排序 60 | * 61 | * @param num 数组大小 62 | * @param n 数组长度 63 | */ 64 | private static void quickSort(Integer[] num, int n) { 65 | __quickSort(num, 0, n - 1); 66 | } 67 | 68 | private static void __quickSort(Integer[] arr, int l, int r) { 69 | 70 | if (l >= r) { 71 | return; 72 | } 73 | 74 | int p = partition(arr, l, r); 75 | 76 | __quickSort(arr, l, p - 1); 77 | __quickSort(arr, p + 1, r); 78 | } 79 | 80 | private static int partition(Integer[] arr, int l, int r) { 81 | 82 | // 为了提高效率,减少造成快速排序的递归树不均匀的概率, 83 | // 对于一个数组,每次随机选择的数为当前 partition 操作中最小最大元素的可能性 降低 1/n! 84 | 85 | int randomNum = (int) (Math.random() * (r - l + 1) + l); 86 | swap(arr, l, randomNum); 87 | 88 | int v = arr[l]; 89 | int j = l; 90 | 91 | for (int i = l + 1; i <= r; i++) { 92 | if (arr[i] < v) { 93 | swap(arr, j + 1, i); 94 | j++; 95 | } 96 | } 97 | swap(arr, l, j); 98 | return j; 99 | } 100 | 101 | private static void swap(Integer[] arr, int i, int j) { 102 | int temp = arr[i]; 103 | arr[i] = arr[j]; 104 | arr[j] = temp; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Sorting_Basic/quick_sort/QuickSort2Ways.java: -------------------------------------------------------------------------------- 1 | package Sorting_Basic.quick_sort; 2 | 3 | import Sorting_Basic.Selection_Sort.SortTestHelper; 4 | 5 | import java.util.Arrays; 6 | 7 | /*** 8 | * 快速排序优化版 双路快速排序 采用双指针的方式来遍历数组 9 | * 10 | * 跟单路一样,双路快速排序 11 | * 12 | * 同样选择数组的第一个元素当做标志位(经过随机选择后的) 13 | * 14 | * 双路快速排序要求有两个指针,指针i j 分别指向 l+1 和 r 的位置 15 | * 然后两者同时向数组中间遍历 在遍历过程中要保证 16 | * 17 | * arr[l+1 ... i) <= v 18 | * arr(j....r] >= v 19 | * 20 | * 因此我们可以初始化 i = l+1 以保证左侧区间初始为空 21 | * j = r 保证右侧空间为空 22 | * 23 | * 遍历过程中要 i <= r 且 arr[i] <= v 的时候 i ++ 就可以了 当 arr[i] > v 时表示遇到了 i 的值大于 v 数值 此刻能等待 j 角标的值 24 | * 25 | * 而 j 角标的移动过程为 j >= l + 1 为临界点 ,从右向左遍历数组 当 arr[i] < v 表示遇到了 j 的值小于 v 的元素,它不该在这个位置呆着 26 | * 27 | * 得到了 i j 的角标后 先要判断是否到了循环结束的时候了,即 i 是否已经 大于 j 了 28 | * 29 | * 否则 应该讲 i 位置的元素和 j 位置的元素交换位置,然后 i++ j-- 继续循环 30 | * 31 | * 32 | * 遍历结束的条件是 i>j 此时 arr[j]为最后一个小于 v 的元素 arr[i] 为第一个大于 v 的元素 33 | * 因此 j 这个位置 就应该是 v 所应该在数组中的位置 因此遍历结束后需要交换 arr[l] 与 arr[j] 34 | * 35 | */ 36 | public class QuickSort2Ways { 37 | 38 | public static void main(String[] args) { 39 | int N = 20; 40 | Integer[] num = SortTestHelper.generateRandomArray(N, 0, N); 41 | quickSort2(num, num.length); 42 | System.out.println(Arrays.toString(num)); 43 | } 44 | 45 | 46 | private static void quickSort2(Integer[] num, int length) { 47 | __quickSort(num, 0, length - 1); 48 | } 49 | 50 | private static void __quickSort(Integer[] arr, int l, int r) { 51 | 52 | if (l >= r) { 53 | return; 54 | } 55 | 56 | int p = partition(arr, l, r); 57 | 58 | __quickSort(arr, l, p - 1); 59 | __quickSort(arr, p + 1, r); 60 | } 61 | 62 | 63 | private static int partition(Integer[] arr, int l, int r) { 64 | // 为了提高效率,减少造成快速排序的递归树不均匀的概率, 65 | // 对于一个数组,每次随机选择的数为当前 partition 操作中最小最大元素的可能性 降低 1/n! 66 | 67 | int randomNum = (int) (Math.random() * (r - l + 1) + l); 68 | swap(arr, l, randomNum); 69 | 70 | int v = arr[l]; 71 | 72 | int i = l + 1; 73 | int j = r; 74 | 75 | while (true) { 76 | 77 | while (i <= r && arr[i] <= v) i++; 78 | while (j >= l + 1 && arr[j] >= v) j--; 79 | 80 | if (i > j) break; 81 | 82 | swap(arr, i, j); 83 | i++; 84 | j--; 85 | } 86 | //j 最后角标停留在 i > j 即为 比 v 小的最后一个一元素位置 87 | swap(arr, l, j); 88 | 89 | return j; 90 | } 91 | 92 | 93 | private static void swap(Integer[] arr, int i, int j) { 94 | int temp = arr[i]; 95 | arr[i] = arr[j]; 96 | arr[j] = temp; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/Sorting_Basic/quick_sort/QuickSort3ways.java: -------------------------------------------------------------------------------- 1 | package Sorting_Basic.quick_sort; 2 | 3 | import Sorting_Basic.Selection_Sort.SortTestHelper; 4 | 5 | import java.util.Arrays; 6 | 7 | /*** 8 | * 三路快速排序 9 | * 将 arr[l...r] 分为 三部分 10 | * 11 | * arr[l+1...lt] v 14 | */ 15 | public class QuickSort3ways { 16 | 17 | public static void main(String[] args) { 18 | int N = 20; 19 | Integer[] num = SortTestHelper.generateRandomArray(N, 0, N); 20 | quickSort3(num, num.length); 21 | System.out.println(Arrays.toString(num)); 22 | } 23 | 24 | 25 | private static void quickSort3(Integer[] num, int length) { 26 | __quickSort(num, 0, length - 1); 27 | } 28 | 29 | private static void __quickSort(Integer[] arr, int l, int r) { 30 | 31 | if (l >= r) { 32 | return; 33 | } 34 | 35 | // 为了提高效率,减少造成快速排序的递归树不均匀的概率, 36 | // 对于一个数组,每次随机选择的数为当前 partition 操作中最小最大元素的可能性 降低 1/n! 37 | 38 | int randomNum = (int) (Math.random() * (r - l + 1) + l); 39 | swap(arr, l, randomNum); 40 | 41 | int v = arr[l]; 42 | // 三路快速排序即把数组划分为大于 小于 等于 三部分 43 | //arr[l+1...lt] v 三部分 44 | // 定义初始值得时候依旧可以保证这初始的时候这三部分都为空 45 | int lt = l; 46 | int gt = r; 47 | int i = l + 1; 48 | 49 | while (i < gt) { 50 | if (arr[i] < v) { 51 | swap(arr, i, lt + 1); 52 | i++; 53 | lt++; 54 | } else if (arr[i] == v) { 55 | i++; 56 | } else { 57 | swap(arr, i, gt - 1); 58 | gt--; 59 | //i++ 注意这里 i 不需要加1 因为这次交换后 i 的值仍不等于 v 可能小于 v 也可能等于 v 所以交换完成后 i 的角标不变 60 | } 61 | } 62 | //循环结束的后 lt 所处的位置为 "); 46 | 47 | String st = s.substring(0, s.length() - 4); 48 | 49 | System.out.println("原链表为:" + st); 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/day1/LeetCode461.java: -------------------------------------------------------------------------------- 1 | package day1; 2 | 3 | /*** 4 | * day1.LeetCode461 汉明距离 即两个数 汉明距离是以理查德·卫斯里·汉明的名字命名的。在信息论中,两个等长字符串之间的汉明距离是两个字符串对应位置的不同字符的个数。 5 | * 换句话说,它就是将一个字符串变换成另外一个字符串所需要替换的字符个数。 6 | * 7 | * The Hamming distance between two integers is the number of positions at which the corresponding bits are different. 8 | * Given two integers x and y, calculate the Hamming distance. 9 | * Note: 10 | * 0 ≤ x, y < 231. 11 | * 12 | * Example: 13 | * 14 | * Input: x = 1, y = 4 15 | * 16 | * Output: 2 17 | * 18 | * Explanation: 19 | * 1 (0 0 0 1) 20 | * 4 (0 1 0 0) 21 | * ↑ ↑ 22 | * 23 | * The above arrows point to positions where the corresponding bits are different. 24 | */ 25 | public class LeetCode461 { 26 | 27 | public static void main(String[] args) { 28 | 29 | System.out.printf("hamming:: " + leetCodeHammingDistance(1, 3)); 30 | System.out.printf("hamming:: " + hammingDistance(1, 3)); 31 | } 32 | 33 | //我的解法 34 | private static int hammingDistance(int x, int y) { 35 | int z = x ^ y; 36 | char[] chars = Integer.toBinaryString(z).toCharArray(); 37 | int distance = 0; 38 | for (char a : chars) { 39 | if (a == '1') { 40 | distance++; 41 | } 42 | } 43 | return distance; 44 | } 45 | 46 | /** 47 | * 48 | * 异或运算符 如果对应位置相同则是0, 不同则是1 49 | * 50 | * 4的二进制形式为0000 0000 0000 0000 0000 0000 0000 0100 51 | * 10的二进制形式为0000 0000 0000 0000 0000 0000 0000 1010 52 | * 53 | * 按照计算规则,结果为0000 0000 0000 0000 0000 0000 0000 1110 54 | * 55 | * 56 | * z>>1 右移1位 do while 中判断如果右移后的数字为0 则停止执行 即判断有多少位是1 57 | */ 58 | private static int leetCodeHammingDistance(int x, int y) { 59 | int z = x ^ y;// 60 | int distance = 0; 61 | do { 62 | distance += z & 1; 63 | z = z >> 1; 64 | } while (z != 0); 65 | return distance; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/day1/LeetCode657.java: -------------------------------------------------------------------------------- 1 | package day1; 2 | 3 | /*** 4 | * Initially, there is a Robot at position (0, 0). Given a sequence of its moves, judge if this robot makes a circle, 5 | * which means it moves back to the original place. 6 | * The move sequence is represented by a string. And each move is represent by a character. 7 | * The valid robot moves are R (Right), L (Left), U (Up) and D (down). 8 | * The output should be true or false representing whether the robot makes a circle. 9 | * 10 | * Example 1: 11 | * Input: "UD" 12 | * Output: true 13 | * Example 2: 14 | * Input: "LL" 15 | * Output: false 16 | * 17 | */ 18 | public class LeetCode657 { 19 | public static void main(String[] args) { 20 | System.out.println("is Circle:: " + judgeCircle("LRLRDUUD")); 21 | System.out.println("is Circle:: " + leetCodeJudgeCircle("LRLRDUUD")); 22 | } 23 | 24 | private static boolean judgeCircle(String moves) { 25 | char[] chars = moves.toCharArray(); 26 | int upSet = 0; 27 | int downSet = 0; 28 | int leftSet = 0; 29 | int rightSet = 0; 30 | for (char charAt : chars) { 31 | if (charAt == 'U') { 32 | upSet++; 33 | } else if (charAt == 'D') { 34 | downSet++; 35 | } else if (charAt == 'L') { 36 | leftSet++; 37 | } else if (charAt == 'R') { 38 | rightSet++; 39 | } 40 | } 41 | 42 | return upSet == downSet && leftSet == rightSet; 43 | } 44 | 45 | /** 46 | * 该题 比较简单 但是区别于自己的做法,下面这个做法更能节约变量空间 也更形象 47 | * @param moves 48 | * @return 49 | */ 50 | private static boolean leetCodeJudgeCircle(String moves) { 51 | int x = 0, y = 0; 52 | for (char move : moves.toCharArray()) { 53 | if (move == 'U') y--; 54 | else if (move == 'D') y++; 55 | else if (move == 'L') x--; 56 | else if (move == 'R') x++; 57 | } 58 | return x == 0 && y == 0; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/day1/leetCode760.java: -------------------------------------------------------------------------------- 1 | package day1; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | /*** 8 | * 9 | * Given two lists Aand B, and B is an anagram of A. B is an anagram of A means B is made by randomizing the order of the elements in A. 10 | * We want to find an index mapping P, from A to B. A mapping P[i] = j means the ith element in A appears in B at index j. 11 | * These lists A and B may contain duplicates. If there are multiple answers, output any of them. 12 | * 13 | * For example, given 14 | * 15 | * A = [12, 28, 46, 32, 50] 16 | * B = [50, 12, 32, 46, 28] 17 | * We should return 18 | * [1, 4, 3, 2, 0] 19 | * 20 | * as P[0] = 1 because the 0th element of A appears at B[1], and P[1] = 4 because the 1st element of A appears at B[4], and so on. 21 | * 22 | * Note: 23 | * 24 | * A, B have equal lengths in range [1, 100]. 25 | * A[i], B[i] are integers in range [0, 10^5]. 26 | * 27 | * 该题目的意思是给定两个数组 A B ,B 数组是 A 数组元素 随机排列的后的新数组, 以你认为的最优解给出 A 数组元素在 B 数组中元素的角标数组 28 | */ 29 | public class leetCode760 { 30 | public static void main(String[] args) { 31 | 32 | int[] A = {12, 28, 46, 32, 50}; 33 | int[] B = {50, 12, 32, 46, 28}; 34 | 35 | System.out.println("A 中 元素在 B 数组中的角标 " + Arrays.toString(anagramMappings(A, B))); 36 | System.out.println("A 中 元素在 B 数组中的角标 " + Arrays.toString(leetCode_anagramMappings(A, B))); 37 | 38 | } 39 | 40 | /** 41 | * 我的解法 42 | */ 43 | private static int[] anagramMappings(int[] A, int[] B) { 44 | int[] indexOfB = new int[A.length]; 45 | int index = 0; 46 | for (int i = 0; i < A.length; i++) { 47 | int cellOfA = A[i]; 48 | for (int j = 0; j < B.length; j++) { 49 | if (B[j] == cellOfA) { 50 | indexOfB[index++] = j; 51 | } 52 | } 53 | } 54 | return indexOfB; 55 | } 56 | 57 | /** 58 | * 官方最优解 59 | */ 60 | private static int[] leetCode_anagramMappings(int[] A, int[] B) { 61 | Map D = new HashMap(); 62 | for (int i = 0; i < B.length; ++i) 63 | D.put(B[i], i); 64 | 65 | int[] ans = new int[A.length]; 66 | int t = 0; 67 | for (int x : A) 68 | ans[t++] = D.get(x); 69 | return ans; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/day10/zcy2_10.java: -------------------------------------------------------------------------------- 1 | package day10; 2 | 3 | import TestUItls.Node; 4 | import TestUItls.TestUtils; 5 | 6 | import java.util.Stack; 7 | 8 | /*** 9 | * 单链表每 K 个节点之间逆序 10 | */ 11 | public class zcy2_10 { 12 | 13 | public static void main(String[] args) { 14 | Node head = TestUtils.getList(8); 15 | TestUtils.printList(head); 16 | 17 | TestUtils.printList(reverseKNode1(head, 3)); 18 | TestUtils.printList(reverseKNode2(TestUtils.getList(8), 3)); 19 | } 20 | 21 | private static Node reverseKNode1(Node head, int K) { 22 | if (K < 2) { 23 | return head; 24 | } 25 | Stack stack = new Stack<>(); 26 | Node newHead = head; 27 | Node cur = head; 28 | Node pre = null; 29 | Node next = null; 30 | 31 | while (cur != null) { 32 | next = cur.next; 33 | stack.push(cur); 34 | if (stack.size() == K) { 35 | pre = resign1(stack, pre, next); 36 | newHead = newHead == head ? cur : newHead; 37 | } 38 | cur = next; 39 | } 40 | return newHead; 41 | } 42 | 43 | private static Node resign1(Stack stack, Node left, Node right) { 44 | Node cur = stack.pop(); 45 | if (left != null) { 46 | left.next = cur; 47 | } 48 | 49 | Node next; 50 | while (!stack.isEmpty()) { 51 | next = stack.pop();//2 52 | cur.next = next;//3->2 53 | cur = next;//2 54 | } 55 | //1-> 4 56 | cur.next = right; 57 | 58 | return cur; 59 | } 60 | 61 | private static Node reverseKNode2(Node head, int K) { 62 | if (K < 2) { 63 | return head; 64 | } 65 | Node cur = head; 66 | Node start = null; 67 | Node pre = null; 68 | Node next = null; 69 | int count = 1; 70 | 71 | while (cur != null) { 72 | next = cur.next; 73 | if (count == K) { 74 | //start 永远等于翻转的第一个节点 75 | start = pre == null ? head : pre.next; 76 | //只在第一次的时候赋值 77 | head = pre == null ? cur : head; 78 | 79 | resign2(pre, start, cur, next); 80 | 81 | // pre 翻转后的最后一个节点 82 | pre = start; 83 | 84 | count = 0; 85 | } 86 | count++; 87 | cur = next; 88 | } 89 | 90 | return head; 91 | } 92 | 93 | /** 94 | * @param left 反转的部分链表的前一个节点 95 | * @param start 开始反转的第一个节点 96 | * @param end 结束反转的节点 97 | * @param right 结束翻转节点的下一个节点 98 | */ 99 | private static void resign2(Node left, Node start, Node end, Node right) { 100 | Node pre = start; 101 | Node cur = start.next; 102 | Node next = null; 103 | 104 | while (cur != right) { 105 | next = cur.next; 106 | cur.next = pre; 107 | pre = cur; 108 | cur = next; 109 | } 110 | 111 | if (left != null) { 112 | left.next = end; 113 | } 114 | 115 | start.next = right; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/day10/zcy2_11.java: -------------------------------------------------------------------------------- 1 | package day10; 2 | 3 | import TestUItls.Node; 4 | import TestUItls.TestUtils; 5 | 6 | import java.util.HashMap; 7 | import java.util.HashSet; 8 | 9 | /** 10 | * 删除无序链表中重复出现的节点 11 | */ 12 | public class zcy2_11 { 13 | 14 | public static void main(String[] args) { 15 | TestUtils.printList(getList()); 16 | TestUtils.printList(removeRep1(getList())); 17 | TestUtils.printList(removeRep2(getList())); 18 | } 19 | 20 | private static Node getList(){ 21 | Node head = TestUtils.getList(3); 22 | head.next = new Node(3); 23 | head.next.next = new Node(4); 24 | head.next.next.next = new Node(4); 25 | head.next.next.next.next = new Node(2); 26 | head.next.next.next.next.next = new Node(1); 27 | head.next.next.next.next.next.next = new Node(1); 28 | return head; 29 | } 30 | private static Node removeRep1(Node head) { 31 | if (head == null) { 32 | return null; 33 | } 34 | 35 | HashSet set = new HashSet<>(); 36 | Node pre = head; 37 | Node cur = head.next; 38 | set.add(head.value); 39 | 40 | while (cur != null) { 41 | if (set.contains(cur.value)) { 42 | pre.next = cur.next; 43 | } else { 44 | set.add(cur.value); 45 | pre = cur; 46 | } 47 | cur = cur.next; 48 | } 49 | 50 | return head; 51 | } 52 | 53 | 54 | private static Node removeRep2(Node head) { 55 | if (head == null) { 56 | return null; 57 | } 58 | Node cur = head; 59 | Node pre = null; 60 | Node next = null; 61 | 62 | while (cur != null) { 63 | pre = cur; 64 | next = cur.next; 65 | 66 | while (next != null) { 67 | if (cur.value == next.value) { 68 | pre.next = next.next; 69 | } else { 70 | pre = next; 71 | } 72 | next = next.next; 73 | } 74 | 75 | cur = cur.next; 76 | } 77 | return head; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/day10/zcy2_9.java: -------------------------------------------------------------------------------- 1 | package day10; 2 | 3 | import TestUItls.Node; 4 | import TestUItls.TestUtils; 5 | 6 | import java.util.Stack; 7 | 8 | /** 9 | * 两个单链表生成相加链表 10 | */ 11 | public class zcy2_9 { 12 | public static void main(String[] args) { 13 | Node head = TestUtils.getList(8); 14 | TestUtils.printList(head); 15 | Node head1 = TestUtils.getList(2); 16 | TestUtils.printList(head1); 17 | 18 | TestUtils.printList(addList(head1,head)); 19 | TestUtils.printList(addList2(head1,head)); 20 | } 21 | 22 | /** 23 | * 使用栈结构 24 | * 25 | * @return 26 | */ 27 | private static Node addList(Node head1, Node head2) { 28 | Stack s1 = new Stack<>(); 29 | Stack s2 = new Stack<>(); 30 | 31 | while (head1 != null) { 32 | s1.push(head1.value); 33 | head1 = head1.next; 34 | } 35 | 36 | while (head2 != null) { 37 | s2.push(head2.value); 38 | head2 = head2.next; 39 | } 40 | 41 | int ca = 0; 42 | int n1 = 0; 43 | int n2 = 0; 44 | int n = 0; 45 | 46 | Node node = null; 47 | Node pre = null; 48 | 49 | //两个都有都为为空则终止循环 50 | while (!s1.isEmpty() || !s2.isEmpty()) { 51 | //获取链表倒数第一个数的数值 52 | n1 = s1.isEmpty() ? 0 : s1.pop(); 53 | n2 = s2.isEmpty() ? 0 : s2.pop(); 54 | //求两者的和 ca 表示进位 因为这里算当前位置的节点值 ca 哪上一位进来 55 | n = n1 + n2 + ca; 56 | pre = node; 57 | node = new Node(n % 10); 58 | node.next = pre; 59 | ca = n / 10; 60 | } 61 | 62 | if (ca == 1) { 63 | pre = node; 64 | node = new Node(1); 65 | node.next = pre; 66 | } 67 | 68 | return node; 69 | } 70 | 71 | private static Node addList2(Node head1, Node head2) { 72 | head1 = reverseList(head1); 73 | head2 = reverseList(head2); 74 | 75 | int ca = 0; 76 | int n1 = 0; 77 | int n2 = 0; 78 | int n = 0; 79 | Node c1 = head1; 80 | Node c2 = head2; 81 | Node pre = null; 82 | Node node = null; 83 | 84 | while (c1 != null || c2 != null) { 85 | 86 | n1 = c1 != null ? c1.value : 0; 87 | n2 = c2 != null ? c2.value : 0; 88 | 89 | n = n1 + n2 + ca; 90 | // 91 | pre = node; 92 | node = new Node(n % 10); 93 | node.next = pre; 94 | ca = n / 10; 95 | 96 | 97 | c1 = c1 != null ? c1.next : null; 98 | c2 = c2 != null ? c2.next : null; 99 | } 100 | 101 | 102 | if (ca == 1) { 103 | pre = node; 104 | node = new Node(1); 105 | node.next = pre; 106 | } 107 | 108 | head1 = reverseList(head1); 109 | head2 = reverseList(head2); 110 | 111 | return node; 112 | } 113 | 114 | private static Node reverseList(Node head) { 115 | 116 | Node next = null; 117 | Node pre = null; 118 | 119 | while (head != null) { 120 | next = head.next;//记住下一个 121 | head.next = pre;//将当前的下一个赋值为上一个 122 | pre = head;//移动指针 123 | head = next; 124 | } 125 | TestUtils.printList(pre); 126 | return pre; 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/day11/zcy2_12.java: -------------------------------------------------------------------------------- 1 | package day11; 2 | 3 | import TestUItls.Node; 4 | import TestUItls.TestUtils; 5 | 6 | /** 7 | * 向有序环形链单链表中插入新节点 8 | */ 9 | public class zcy2_12 { 10 | 11 | 12 | private Node getSortedList() { 13 | Node head = new Node(1); 14 | head.next = new Node(3); 15 | head.next.next = new Node(4); 16 | head.next.next.next = new Node(5); 17 | head.next.next.next.next = new Node(7); 18 | head.next.next.next.next.next = head; 19 | return head; 20 | } 21 | 22 | private static Node instertNode(Node head, int num) { 23 | Node node = new Node(num); 24 | if (head == null) { 25 | node.next = node; 26 | return node; 27 | } 28 | Node cur = head.next; 29 | Node pre = head; 30 | while (cur != head) { 31 | if (pre.value <= num && cur.value >= num) { 32 | break; 33 | } 34 | pre = cur; 35 | cur = cur.next; 36 | } 37 | pre.next = node; 38 | node.next = cur; 39 | 40 | //如果 head 的值 小于要插入的值 则node 是插入了链表中 所以返回 head 如果 head 值大于 node 则插入了链表最前端或者最后端(环形链表其实是一个位置) 41 | return head.value < num ? head : node; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/day11/zcy2_13.java: -------------------------------------------------------------------------------- 1 | package day11; 2 | 3 | import TestUItls.Node; 4 | 5 | import java.util.Stack; 6 | 7 | /** 8 | * 从单链表中删除指定值得节点 9 | */ 10 | public class zcy2_13 { 11 | 12 | private static Node removeValue1(Node head, int num) { 13 | Stack stack = new Stack<>(); 14 | //将左右不等于 num 的节点放入栈中 15 | while (head != null) { 16 | if (head.value != num) { 17 | stack.push(head); 18 | } 19 | head = head.next; 20 | } 21 | 22 | while (!stack.isEmpty()) { 23 | stack.peek().next = head; 24 | head = stack.pop(); 25 | } 26 | 27 | return head; 28 | } 29 | 30 | 31 | private static Node removeValue2(Node head, int num) { 32 | 33 | // 找到第一个不等于 num 的节点 34 | while (head != null) { 35 | if (head.value != num) { 36 | break; 37 | } 38 | head = head.next; 39 | } 40 | Node pre = head; 41 | 42 | //注意起始指针在同一个地方 如果cur 指针移动,如果不等 num 两个第一次循环后就相差了一个节点,实际上此时的头节点不可能相同 43 | Node cur = head; 44 | while (cur != null) { 45 | if (cur.value == num) { 46 | pre.next = cur.next; 47 | } else { 48 | pre = cur; 49 | } 50 | cur = cur.next; 51 | } 52 | 53 | return head; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/day11/zcy2_14.java: -------------------------------------------------------------------------------- 1 | package day11; 2 | 3 | import TestUItls.Node; 4 | 5 | /*** 6 | * 单链表的选择排序 要求空间复杂度为 O(1) 7 | */ 8 | public class zcy2_14 { 9 | 10 | 11 | private static Node selectionSort(Node head) { 12 | Node tail = null;//排序部分的末尾节点 13 | Node cur = head;//未排序的链表头部 14 | Node smallPre = null;//最小节点的前一个节点 15 | Node small = null;//最小节点 16 | 17 | //思路 1.每次循环找到最小元素 和最小元素的上一个节点 2. 删除最小元素 并另 tail 等于此次循环的最小元素 18 | //3 cur 一直保持 未排序的头部 19 | 20 | while (cur != null) { 21 | small = cur; 22 | smallPre = getSmallestPreNode(cur); 23 | //samllPre = null 代表链表已经排序完成 24 | if (smallPre != null) { 25 | small = smallPre.next; 26 | smallPre.next = small.next; 27 | } 28 | // 只有在第一循环时三元表达式成立 29 | cur = cur == small ? cur.next : cur; 30 | //链表head = 链表最小值 31 | if (tail == null) { 32 | head = small; 33 | } else { 34 | tail.next = small; 35 | } 36 | tail = small; 37 | } 38 | 39 | return head; 40 | } 41 | 42 | /** 43 | * 获取当前剩余链表最小值得前一个元素 44 | * @param head 45 | * @return 46 | */ 47 | private static Node getSmallestPreNode(Node head) { 48 | Node smallPre = null; 49 | Node small = head; 50 | Node pre = head; 51 | Node cur = head.next; 52 | while (cur != null) { 53 | if (cur.value < small.value) { 54 | smallPre = pre; 55 | small = cur; 56 | } 57 | pre = cur; 58 | cur = cur.next; 59 | } 60 | return smallPre; 61 | } 62 | 63 | 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/day11/zcy2_15.java: -------------------------------------------------------------------------------- 1 | package day11; 2 | 3 | import TestUItls.Node; 4 | 5 | /** 6 | * 一种怪异的删除节点的方式 7 | * 链表节点值均为 int 值,给定链表中的一个节点 node ,但不知道这个链表的头节点 如何在链表中删除 node 8 | * 要求时间复杂度为 O(1) 9 | */ 10 | public class zcy2_15 { 11 | 12 | /** 13 | * 实现思路将当前元素的换成下一个元素 14 | * 15 | * @param node 16 | * @return 17 | */ 18 | private static void deleteNode(Node node) { 19 | if (node == null) { 20 | return; 21 | } 22 | 23 | Node next = node.next; 24 | if (next == null){ 25 | throw new RuntimeException("无法删除最后一个节点"); 26 | } 27 | 28 | node.value = next.value; 29 | node.next = next.next; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/day12/PrintMatrix.java: -------------------------------------------------------------------------------- 1 | package day12; 2 | 3 | /** 4 | * 给出一个整数N,打印出N*N的转圈矩阵,或者是给出一个这样的转圈矩阵,如何将其转圈输出 5 | */ 6 | public class PrintMatrix { 7 | public static void main(String args[]) { 8 | 9 | printArrayWisely(buildCircleMatrix(7)); 10 | System.out.println(); 11 | 12 | 13 | printResult(buildCircleMatrix(7)); 14 | } 15 | 16 | public static void printArrayWisely(int[][] num) { 17 | if (num == null) { 18 | return; 19 | } 20 | 21 | int x = 0; 22 | int y = 0; 23 | 24 | // 行号最大是(num.length-1)/2 25 | // 列号最大是(num[0].length-1)/2 26 | 27 | while (x * 2 < num.length && y * 2 < num[0].length) { 28 | printNewInCircle(num, x, y); 29 | x++; 30 | y++; 31 | } 32 | } 33 | 34 | public static void printNewInCircle(int[][] num, int x, int y) { 35 | int row = num.length; // 矩阵的行数 36 | int col = num[0].length; // 矩阵的列数 37 | 38 | // 第一步输出最上面一行的所以元素 39 | // 是按列来输出的 40 | for (int i = y; i <= col - y - 1; i++) { 41 | System.out.print(num[x][i] + " "); 42 | } 43 | 44 | // row-x-1:表示的是环最下的那一行的行号 45 | // 第二步,打印最右边, 使用自上向下的方式 46 | if (row - x - 1 > x) { 47 | for (int i = x + 1; i <= row - x - 1; i++) { 48 | System.out.print(num[i][col - y - 1] + " "); 49 | } 50 | } 51 | 52 | // 第三步,打印最下面的一行,但是去除第一个元素 53 | // col - 1 - y > y 这句是保证有列数 54 | if (row - x - 1 > x && col - 1 - y > y) { 55 | // 因为环的左下角的位置已经输出了,所以列号从col-y-2开始 56 | for (int i = col - y - 2; i >= y; i--) { 57 | System.out.print(num[row - 1 - x][i] + " "); 58 | } 59 | } 60 | 61 | // 第四步,在保证有列数的同时,还要保证有行数, 62 | // 环的宽度至少是2并且环的高度至少是3才会输出最左边那一列 63 | // rows-x-1:表示的是环最下的那一行的行号 64 | if (col - 1 - y > y && row - 1 - x > x + 1) { 65 | // 因为最左边那一列的第一个和最后一个已经被输出了 66 | for (int i = row - 1 - x - 1; i >= x + 1; i--) { 67 | System.out.print(num[i][y] + " "); 68 | } 69 | } 70 | } 71 | 72 | private static int[][] buildCircleMatrix(int N) { 73 | int[][] matix = new int[N][N]; 74 | int k = 0; 75 | int i = 0; 76 | int j = 0; 77 | int z = 1; 78 | 79 | while (k < N) { 80 | while (j < N - k) { 81 | matix[i][j] = z; 82 | j++; 83 | z++; 84 | } 85 | j--; 86 | i++; 87 | 88 | while (i < N - k) { 89 | matix[i][j] = z; 90 | i++; 91 | z++; 92 | } 93 | i--; 94 | j--; 95 | 96 | while (j >= k) { 97 | matix[i][j] = z; 98 | j--; 99 | z++; 100 | } 101 | j++; 102 | k++; 103 | i--; 104 | 105 | while (i >= k) { 106 | matix[i][j] = z; 107 | i--; 108 | z++; 109 | } 110 | i++; 111 | j++; 112 | } 113 | 114 | return matix; 115 | } 116 | 117 | private static void printResult(int[][] matrix) { 118 | for (int i = 0; i < matrix.length; i++) { 119 | for (int j = 0; j < matrix.length; j++) { 120 | System.out.print(matrix[i][j] +" "); 121 | } 122 | System.out.println(); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/day2/leetCode561.java: -------------------------------------------------------------------------------- 1 | package day2; 2 | 3 | import java.util.Arrays; 4 | 5 | /*** 6 | * Given an array of 2n integers, your task is to group these integers into n pairs of integer, say (a1, b1), (a2, b2), ..., (an, bn) 7 | * which makes sum of min(ai, bi) for all i from 1 to n as large as possible. 8 | * 9 | * Input: [1,4,3,2] 10 | * Output: 4 11 | * Explanation: n is 2, and the maximum sum of pairs is 4. 12 | * 13 | * Note: 14 | * 15 | * n is a positive integer, which is in the range of [1, 10000]. 16 | * All the integers in the array will be in the range of [-10000, 10000]. 17 | * 18 | * 这道题让我们分割数组,两两一对,让每对中较小的数的和最大。这题难度不大,用 贪婪算法 就可以了。 19 | * 由于我们要最大化每对中的较小值之和,那么肯定是每对中两个数字大小越接近越好,因为如果差距过大,而我们只取较小的数字,那么大数字就浪费掉了。 20 | * 明白了这一点,我们只需要给数组排个序,然后按顺序的每两个就是一对,我们取出每对中的第一个数即为较小值累加起来即可, 21 | * 22 | */ 23 | public class leetCode561 { 24 | public static void main(String[] args) { 25 | int[] nums = {1, 4, 3, 2}; 26 | System.out.println(arrayPairSum(nums)); 27 | } 28 | 29 | private static int arrayPairSum(int[] nums) { 30 | 31 | int arrayPairSum = 0; 32 | Arrays.sort(nums); 33 | 34 | for (int i = 0; i < nums.length; i = i + 2) { 35 | arrayPairSum += nums[i]; 36 | } 37 | 38 | return arrayPairSum; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/day2/leetCode617.java: -------------------------------------------------------------------------------- 1 | package day2; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | import java.util.Stack; 6 | 7 | /*** 8 | * 617. Merge Two Binary Trees 9 | * Given two binary trees and imagine that when you put one of them to cover the other, 10 | * some nodes of the two trees are overlapped while the others are not. 11 | * You need to merge them into a new binary tree. The merge rule is that if two nodes overlap, 12 | * then sum node values up as the new value of the merged node. 13 | * Otherwise, the NOT null node will be used as the node of new tree. 14 | 15 | * Example 1: 16 | * Input: 17 | * Tree 1 Tree 2 18 | * 1 2 19 | * / \ / \ 20 | * 3 2 1 3 21 | * / \ \ 22 | * 5 4 7 23 | * 24 | * 25 | * Output: 26 | * Merged tree: 27 | * 3 28 | * / \ 29 | * 4 5 30 | * / \ \ 31 | * 5 4 7 32 | */ 33 | public class leetCode617 { 34 | public static void main(String[] args) { 35 | TreeNode treeNode1 = stringToTreeNode("[1,3,2,5]"); 36 | System.out.println(treeNodeToString(treeNode1)); 37 | TreeNode treeNode2 = stringToTreeNode("[2,1,3,null,4,null,7]"); 38 | System.out.println(treeNodeToString(treeNode2)); 39 | 40 | System.out.println("合并结果: " + treeNodeToString(mergeTrees(treeNode1, treeNode2))); 41 | System.out.println("合并结果: " + treeNodeToString(LeetCode_mergeTreesA(treeNode1, treeNode2))); 42 | System.out.println("合并结果: " + treeNodeToString(LeetCode_mergeTreesB(treeNode1, treeNode2))); 43 | } 44 | 45 | /** 46 | * 47 | * @param t1 48 | * @param t2 49 | * @return 50 | */ 51 | private static TreeNode mergeTrees(TreeNode t1, TreeNode t2) { 52 | 53 | if (t1 == null && t2 == null) { 54 | return null; 55 | } 56 | if (t1 == null | t2 == null) { 57 | return t1 == null ? t2 : t1; 58 | } 59 | 60 | TreeNode node = new TreeNode(0); 61 | node.val = t1.val + t2.val; 62 | node.left = mergeTrees(t1.left, t2.left); 63 | node.right = mergeTrees(t1.right, t2.right); 64 | 65 | return node; 66 | } 67 | 68 | /** 69 | * 官方解法 70 | */ 71 | private static TreeNode LeetCode_mergeTreesA(TreeNode t1, TreeNode t2) { 72 | if (t1 == null) 73 | return t2; 74 | if (t2 == null) 75 | return t1; 76 | t1.val += t2.val; 77 | t1.left = mergeTrees(t1.left, t2.left); 78 | t1.right = mergeTrees(t1.right, t2.right); 79 | return t1; 80 | } 81 | 82 | private static TreeNode LeetCode_mergeTreesB(TreeNode t1, TreeNode t2) { 83 | if (t1 == null) 84 | return t2; 85 | Stack< TreeNode[] > stack = new Stack < > (); 86 | stack.push(new TreeNode[] {t1, t2}); 87 | while (!stack.isEmpty()) { 88 | TreeNode[] t = stack.pop(); 89 | if (t[0] == null || t[1] == null) { 90 | continue; 91 | } 92 | t[0].val += t[1].val; 93 | if (t[0].left == null) { 94 | t[0].left = t[1].left; 95 | } else { 96 | stack.push(new TreeNode[] {t[0].left, t[1].left}); 97 | } 98 | if (t[0].right == null) { 99 | t[0].right = t[1].right; 100 | } else { 101 | stack.push(new TreeNode[] {t[0].right, t[1].right}); 102 | } 103 | } 104 | return t1; 105 | } 106 | 107 | /*** 官方定义 ****/ 108 | static class TreeNode { 109 | int val; 110 | TreeNode left; 111 | TreeNode right; 112 | 113 | TreeNode(int x) { 114 | val = x; 115 | } 116 | } 117 | 118 | 119 | private static TreeNode stringToTreeNode(String input) { 120 | input = input.trim(); 121 | input = input.substring(1, input.length() - 1); 122 | if (input.length() == 0) { 123 | return null; 124 | } 125 | 126 | String[] parts = input.split(","); 127 | String item = parts[0]; 128 | TreeNode root = new TreeNode(Integer.parseInt(item)); 129 | Queue nodeQueue = new LinkedList<>(); 130 | nodeQueue.add(root); 131 | 132 | int index = 1; 133 | while (!nodeQueue.isEmpty()) { 134 | TreeNode node = nodeQueue.remove(); 135 | 136 | if (index == parts.length) { 137 | break; 138 | } 139 | 140 | item = parts[index++]; 141 | item = item.trim(); 142 | if (!item.equals("null")) { 143 | int leftNumber = Integer.parseInt(item); 144 | node.left = new TreeNode(leftNumber); 145 | nodeQueue.add(node.left); 146 | } 147 | 148 | if (index == parts.length) { 149 | break; 150 | } 151 | 152 | item = parts[index++]; 153 | item = item.trim(); 154 | if (!item.equals("null")) { 155 | int rightNumber = Integer.parseInt(item); 156 | node.right = new TreeNode(rightNumber); 157 | nodeQueue.add(node.right); 158 | } 159 | } 160 | return root; 161 | } 162 | 163 | 164 | private static String treeNodeToString(TreeNode root) { 165 | if (root == null) { 166 | return "[]"; 167 | } 168 | 169 | StringBuilder output = new StringBuilder(); 170 | Queue nodeQueue = new LinkedList<>(); 171 | nodeQueue.add(root); 172 | while (!nodeQueue.isEmpty()) { 173 | TreeNode node = nodeQueue.remove(); 174 | 175 | if (node == null) { 176 | output.append("null, "); 177 | continue; 178 | } 179 | 180 | output.append(String.valueOf(node.val)).append(", "); 181 | nodeQueue.add(node.left); 182 | nodeQueue.add(node.right); 183 | } 184 | return "[" + output.substring(0, output.length() - 2) + "]"; 185 | } 186 | 187 | /*** 官方定义结束 ****/ 188 | } 189 | 190 | -------------------------------------------------------------------------------- /src/day2/leetCode728.java: -------------------------------------------------------------------------------- 1 | package day2; 2 | 3 | import com.sun.xml.internal.fastinfoset.util.CharArray; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | /** 10 | * 求给定两个数之间的 自分数,自分数:可以被自己本身的每一位整除(每一位不包括该为 0 ),也就是说包括0的数都不是自分数 11 | * 1、Char是无符号型的,可以表示一个整数,不能表示负数;而byte是有符号型的,可以表示-128—127 的 12 | * 2、char可以表中文字符,byte不可以 13 | * 3. char、byte、int对于英文字符,可以相互转化 14 | */ 15 | public class leetCode728 { 16 | public static void main(String[] args) { 17 | System.out.println(selfDividingNumbers(1, 22)); 18 | System.out.println(leetCode_selfDividingNumbers(1, 22)); 19 | } 20 | 21 | public static List selfDividingNumbers(int left, int right) { 22 | List result = new ArrayList(); 23 | for (int i = left; i < right + 1; i++) { 24 | if (i > 12) { 25 | //判断一个数是否能被自己的的每一位整除 26 | boolean isSelfDividingNum = true; 27 | char[] chars = String.valueOf(i).toCharArray(); 28 | // System.out.println(Arrays.toString(chars)); 29 | for (int j = 0; j < chars.length; j++) { 30 | //一开始忘记了 char 本身就可以做运算 所以多做了很多转换 31 | // int parseInt = Integer.parseInt(String.valueOf(chars[j])); 32 | // if (parseInt != 0) { 33 | // if (i % parseInt != 0) { 34 | // isSelfDividingNum = false; 35 | // } 36 | // } 37 | if (chars[j] == '0' || i % chars[j] != '0') { 38 | isSelfDividingNum = false; 39 | } 40 | } 41 | 42 | if (isSelfDividingNum) { 43 | result.add(i); 44 | } 45 | } else { 46 | result.add(i); 47 | } 48 | 49 | } 50 | 51 | return result; 52 | } 53 | 54 | 55 | private static List leetCode_selfDividingNumbers(int left, int right) { 56 | List ans = new ArrayList(); 57 | for (int n = left; n <= right; ++n) { 58 | if (selfDividing(n)) ans.add(n); 59 | } 60 | return ans; 61 | } 62 | 63 | private static boolean selfDividing(int n) { 64 | for (char c : String.valueOf(n).toCharArray()) { 65 | if (c == '0' || (n % (c - '0') > 0)) 66 | return false; 67 | } 68 | return true; 69 | } 70 | 71 | 72 | public static byte[] intToByteArray(int a) { 73 | return new byte[]{ 74 | (byte) ((a >> 24) & 0xFF), 75 | (byte) ((a >> 16) & 0xFF), 76 | (byte) ((a >> 8) & 0xFF), 77 | (byte) (a & 0xFF) 78 | }; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/day3/leetCode339.java: -------------------------------------------------------------------------------- 1 | package day3; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | import java.util.Stack; 7 | 8 | /*** 9 | * Given a nested list of integers, return the sum of all integers in the list weighted by their depth. 10 | * Each element is either an integer, or a list -- whose elements may also be integers or other lists. 11 | * 12 | * Example 1: 13 | * Given the list [[1,1],2,[1,1]], return 10. (four 1's at depth 2, one 2 at depth 1) 14 | * 15 | * Example 2: 16 | * Given the list [1,[4,[6]]], return 27. (one 1 at depth 1, one 4 at depth 2, and one 6 at depth 3; 1 + 4*2 + 6*3 = 27) 17 | * 18 | * 19 | */ 20 | 21 | /** 22 | * // This is the interface that allows for creating nested lists. 23 | * // You should not implement it, or speculate about its implementation 24 | * public interface NestedInteger { 25 | *

26 | * // @return true if this NestedInteger holds a single integer, rather than a nested list. 27 | * public boolean isInteger(); 28 | *

29 | * // @return the single integer that this NestedInteger holds, if it holds a single integer 30 | * // Return null if this NestedInteger holds a nested list 31 | * public Integer getInteger(); 32 | *

33 | * // @return the nested list that this NestedInteger holds, if it holds a nested list 34 | * // Return null if this NestedInteger holds a single integer 35 | * public List getList(); 36 | * } 37 | */ 38 | public class leetCode339 { 39 | 40 | public static void main(String[] args) { 41 | List aNestList = getANestList(); 42 | System.out.println("mSum :: " + mhelper(aNestList, 1)); 43 | System.out.println("sum :: " + depthSum(aNestList)); 44 | System.out.println("sum :: " + getWeightSum(aNestList,1)); 45 | } 46 | 47 | private static int mhelper(List aNestList, int depth) { 48 | int sum = 0; 49 | for (NestedInteger nestedInteger : aNestList) { 50 | if (nestedInteger.isInteger()) { 51 | System.out.println("getInteger:: " + nestedInteger.getInteger() +":: " + depth); 52 | sum += nestedInteger.integer * depth; 53 | } else { 54 | sum += helper(nestedInteger.getNestedIntegers(), depth + 1); 55 | } 56 | 57 | } 58 | return sum; 59 | } 60 | 61 | public static int depthSum(List nestedList) { 62 | return helper(nestedList, 1); 63 | } 64 | 65 | private static int helper(List list, int depth) { 66 | int ret = 0; 67 | for (NestedInteger e : list) { 68 | 69 | if (e.isInteger()) { 70 | ret += e.getInteger() * depth; 71 | System.out.println("getInteger:: " + e.getInteger() +":: " + depth); 72 | } else { 73 | ret += helper(e.getNestedIntegers(), depth + 1); 74 | } 75 | } 76 | return ret; 77 | } 78 | public static int getWeightSum(List nestedIntegers, int weight) { 79 | if (nestedIntegers == null || nestedIntegers.size() <= 0 || weight <= 0) { 80 | return 0; 81 | } 82 | int weightSum = 0; 83 | for (int i = 0; i < nestedIntegers.size(); i++) { 84 | if (nestedIntegers.get(i) != null && nestedIntegers.get(i).isInteger()) { 85 | System.out.println("getInteger:: " + nestedIntegers.get(i).getInteger() +":: " + weight); 86 | weightSum += nestedIntegers.get(i).getInteger() * weight; 87 | } else { 88 | weightSum += getWeightSum(nestedIntegers.get(i).getNestedIntegers(), weight + 1); 89 | } 90 | } 91 | return weightSum; 92 | } 93 | 94 | public static List getANestList() { 95 | NestedInteger nestedInteger6 = new NestedInteger(6); 96 | List list1 = new LinkedList<>(); 97 | list1.add(nestedInteger6); 98 | 99 | NestedInteger nestedInteger4 = new NestedInteger(4); 100 | nestedInteger4.setNestedIntegers(list1); 101 | 102 | NestedInteger nestedInteger1 = new NestedInteger(1); 103 | List list2 = new LinkedList<>(); 104 | list2.add(new NestedInteger(4)); 105 | list2.add(nestedInteger4); 106 | 107 | nestedInteger1.setNestedIntegers(list2); 108 | 109 | List nestedIntegerList = new ArrayList<>(); 110 | nestedIntegerList.add(new NestedInteger(1)); 111 | nestedIntegerList.add(nestedInteger1); 112 | 113 | System.out.println(nestedIntegerList); 114 | 115 | return nestedIntegerList ; 116 | } 117 | 118 | 119 | /** 120 | * 自定义嵌套数组 121 | * Created by Administrator on 2017/7/19. 122 | * [ 1, [ 4 , [ 6 ] ] ] 123 | */ 124 | public static class NestedInteger { 125 | //包含一个整数 126 | private Integer integer; 127 | //以及一个数组 128 | private List nestedIntegers; 129 | 130 | //初始化Int 131 | public NestedInteger(Integer integer) { 132 | this.integer = integer; 133 | } 134 | 135 | //初始化数组 136 | public NestedInteger(List nestedIntegers) { 137 | this.nestedIntegers = nestedIntegers; 138 | } 139 | 140 | //判断是否int 141 | public Boolean isInteger() { 142 | if (integer != null && nestedIntegers == null) { 143 | return Boolean.TRUE; 144 | } else { 145 | return Boolean.FALSE; 146 | } 147 | } 148 | 149 | //判断是否数组 150 | public Boolean isNestedIntger() { 151 | if (integer == null && nestedIntegers != null) { 152 | return Boolean.TRUE; 153 | } else { 154 | return Boolean.FALSE; 155 | } 156 | } 157 | 158 | //Getter Setter 159 | public Integer getInteger() { 160 | return integer; 161 | } 162 | 163 | public void setInteger(Integer integer) { 164 | this.integer = integer; 165 | this.nestedIntegers = null; 166 | } 167 | 168 | public List getNestedIntegers() { 169 | return nestedIntegers; 170 | } 171 | 172 | public void setNestedIntegers(List nestedIntegers) { 173 | this.nestedIntegers = nestedIntegers; 174 | this.integer = null; 175 | } 176 | 177 | @Override 178 | public String toString() { 179 | return "NestedInteger{" + 180 | "integer=" + integer + 181 | ", nestedIntegers=" + nestedIntegers + 182 | '}'; 183 | } 184 | String st = "[NestedInteger{integer=1, nestedIntegers=null}," + 185 | "" + 186 | "" + 187 | " NestedInteger{integer=1, nestedIntegers=[NestedInteger{integer=4, nestedIntegers=null}," + 188 | "" + 189 | " NestedInteger{integer=4, nestedIntegers=[NestedInteger{integer=6, nestedIntegers=null}]}]}]\n"; 190 | } 191 | 192 | } 193 | 194 | -------------------------------------------------------------------------------- /src/day3/leetCode359.java: -------------------------------------------------------------------------------- 1 | package day3; 2 | 3 | import java.io.InputStream; 4 | import java.util.HashMap; 5 | import java.util.LinkedHashMap; 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | /*** 10 | * Design a logger system that receive stream of messages along with its timestamps, each message should be printed if and only if it is not printed in the last 10 seconds. 11 | 12 | * Given a message and a timestamp (in seconds granularity), return true if the message should be printed in the given timestamp, otherwise returns false. 13 | * 14 | * It is possible that several messages arrive roughly at the same time. 15 | * 16 | * Example: 17 | * 18 | * Logger logger = new Logger(); 19 | * 20 | * // logging string "foo" at timestamp 1 21 | * logger.shouldPrintMessage(1, "foo"); returns true; 22 | * 23 | * // logging string "bar" at timestamp 2 24 | * logger.shouldPrintMessage(2,"bar"); returns true; 25 | * 26 | * // logging string "foo" at timestamp 3 27 | * logger.shouldPrintMessage(3,"foo") returns false; 28 | * 29 | * // logging string "bar" at timestamp 8 30 | * logger.shouldPrintMessage(8,"bar"); returns false; 31 | * 32 | * // logging string "foo" at timestamp 10 33 | * logger.shouldPrintMessage(10,"foo"); returns false; 34 | * 35 | * // logging string "foo" at timestamp 11 36 | * logger.shouldPrintMessage(11,"foo"); returns true; 37 | * 38 | * Credits: 39 | * Special thanks to @memoryless for adding this problem and creating all test cases. 40 | * 41 | */ 42 | public class leetCode359 { 43 | 44 | public static void main(String[] args) { 45 | 46 | Map integerStringMap = gengerateMsg(); 47 | 48 | integerStringMap.forEach((key, value) -> { 49 | 50 | boolean shouldPrintln = true; 51 | 52 | //随便取一个数这个数的 key 向前数10s 内如果有同样的 value 则不打印,如果没有相同的则打印 53 | 54 | for (int i = 1; i < 11; i++) { 55 | key = key - i; 56 | if (integerStringMap.containsKey(key)) { 57 | String temp = integerStringMap.get(key); 58 | if (temp.equals(value)) { 59 | shouldPrintln = false; 60 | break; 61 | } 62 | } 63 | } 64 | 65 | printData(key, value, shouldPrintln); 66 | }); 67 | 68 | 69 | } 70 | 71 | private static void printData(int key, String value, boolean shouldPrint) { 72 | System.out.println("logger.shouldPrintMessage(" + key + "," + value + ");" + "returns " + shouldPrint); 73 | } 74 | 75 | private static Map gengerateMsg() { 76 | Map timeDatas = new LinkedHashMap<>(); 77 | timeDatas.put(1, "foo"); 78 | timeDatas.put(2, "bar"); 79 | timeDatas.put(3, "foo"); 80 | timeDatas.put(8, "foo"); 81 | timeDatas.put(10, "bar"); 82 | timeDatas.put(11, "foo"); 83 | timeDatas.put(13, "foo"); 84 | timeDatas.put(16, "bar"); 85 | timeDatas.put(16, "foo"); 86 | timeDatas.put(46, "foo"); 87 | timeDatas.put(61, "bar"); 88 | return timeDatas; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/day3/leetCode476.java: -------------------------------------------------------------------------------- 1 | package day3; 2 | 3 | /*** 4 | * Given a positive integer, output its complement number. The complement strategy is to flip the bits of its binary representation. 5 | 6 | * Note: 7 | * The given integer is guaranteed to fit within the range of a 32-bit signed integer. 8 | * You could assume no leading zero bit in the integer’s binary representation. 9 | * 10 | * Example 1: 11 | * Input: 5 12 | * Output: 2 13 | * Explanation: The binary representation of 5 is 101 (no leading zero bits), and its complement is 010. So you need to output 2. 14 | * 15 | * Example 2: 16 | * 17 | * Input: 1 18 | * Output: 0 19 | * Explanation: The binary representation of 1 is 1 (no leading zero bits), and its complement is 0. So you need to output 0. 20 | */ 21 | public class leetCode476 { 22 | public static void main(String[] args) { 23 | System.out.println("result:: " + findComplement(5)); 24 | System.out.println("result:: " + leetCode_findComplement(5)); 25 | 26 | highestOneBit(5); 27 | } 28 | 29 | private static int findComplement(int num) { 30 | String binaryString = Integer.toBinaryString(num); 31 | char[] complement = new char[binaryString.length()]; 32 | for (int i = 0; i < binaryString.toCharArray().length; i++) { 33 | complement[i] = binaryString.toCharArray()[i] == '1' ? '0' : '1'; 34 | } 35 | 36 | return Integer.valueOf(String.valueOf(complement),2); 37 | } 38 | 39 | 40 | /** 41 | * 这个函数调用。使用的第一感觉就是这个函数是干什么用的,通过查看文档得知,这个函数的作用是取 i 这个数的二进制形式最左边的最高一位且高位后面全部补零, 42 | * 最后返回int型的结果。 43 | * @param num 44 | * @return 45 | */ 46 | private static int leetCode_findComplement(int num) { 47 | return ~num & (Integer.highestOneBit(num) - 1); 48 | } 49 | 50 | /** 51 | * 如果一个数是0, 则返回0; 52 | * 如果是负数, 则返回 -2147483648:【1000,0000,0000,0000,0000,0000,0000,0000】(二进制表示的数); 53 | * 如果是正数, 返回的则是跟它最靠近的比它小的2的N次方 54 | * @param i 55 | * @return 56 | */ 57 | public static int highestOneBit(int i) { 58 | // HD, Figure 3-1 59 | i |= (i >> 1); 60 | i |= (i >> 2); 61 | i |= (i >> 4); 62 | i |= (i >> 8); 63 | i |= (i >> 16); 64 | return i - (i >>> 1); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/day4/zcy1.java: -------------------------------------------------------------------------------- 1 | package day4; 2 | 3 | import java.util.Stack; 4 | 5 | /*** 6 | * 由于本人算法基础较差,所以从 day4 开始先刷 左程云 所写的《IT 名企算法与数据结构题目最优解》 一边学习一边巩固算法基础知识 7 | * 8 | * 第一章 第一题 9 | * 10 | * 设计一个有 getMin 功能的栈 11 | * 12 | * 实现一个特殊的栈,在实现站的基本功能的基础上,再实现返回栈中最小元素的操作 13 | * 14 | * 要求: 15 | * 16 | * 1. pop push getMin 操作的时间复杂度都为 O(1) 17 | * 2. 设计的栈类型可以使用现成的栈结构 18 | * 19 | * 学习目标:1、 时间复杂度的计算方法 2、 栈数据结构的特点 3、理解题目的实现方法 20 | */ 21 | public class zcy1 { 22 | 23 | public static void main(String[] args) { 24 | int[] testNumbers = new int[]{3, -4, 5, 1, 2, 1, 100, -1}; 25 | GetMinStack getMinStack = new GetMinStack(); 26 | OtherGetMinStack otherGetMinStack = new OtherGetMinStack(); 27 | 28 | for (int testNumber : testNumbers) { 29 | getMinStack.push(testNumber); 30 | } 31 | 32 | for (int testNumber : testNumbers) { 33 | otherGetMinStack.push(testNumber); 34 | } 35 | 36 | 37 | System.out.println("getMinStack 最小值:: " + getMinStack.getMin()); 38 | System.out.println("otherGetMinStack 最小值:: " + otherGetMinStack.getMin()); 39 | } 40 | 41 | /*** 42 | * 实现思路 43 | * 首先理解题目,要我们实现一个栈 : 栈所包含的基本操作 pop push ,特点先进后出,然后实现 getMin 实现栈中获取最小值的方法 44 | * 45 | * 这里我们假设栈中存放的元素为 Integer 即整数 46 | * 47 | * 该方法 自定义栈中有两个栈,stackData 持有所进栈的所有数据 每次进进站都会判断 stackMin 栈顶元素 与进站元素的大小, 48 | * 如果进栈元素较小 则进 stackData 的时候也进 stackMin 否则不进 stackMin 因此 stackMin 栈顶始终与进栈元素中元素最小值相同 49 | * 50 | * 毫无疑问进出和获取最小值元素的操作数都是常数,与输入的数大小无关, 也就是时间复杂度都是常数 也就是 O(1) 51 | */ 52 | private static class GetMinStack { 53 | 54 | private Stack stackData; 55 | private Stack stackMin; 56 | 57 | GetMinStack() { 58 | stackData = new Stack<>(); 59 | stackMin = new Stack<>(); 60 | } 61 | 62 | /** 63 | * 出栈方式 64 | * 65 | * @return 栈顶元素 66 | */ 67 | public Integer pop() { 68 | 69 | if (stackData.isEmpty()) { 70 | throw new RuntimeException("Your Stack is empty"); 71 | } 72 | 73 | int value = stackData.pop(); 74 | 75 | if (value == getMin()) { 76 | stackMin.pop(); 77 | } 78 | 79 | return value; 80 | } 81 | 82 | /** 83 | * 进栈方式 84 | * 85 | * @param num 进栈元素 86 | */ 87 | public void push(Integer num) { 88 | 89 | if (stackMin.isEmpty()) { 90 | stackMin.push(num); 91 | } else if (num <= getMin()) { 92 | stackMin.push(num); 93 | } 94 | stackData.push(num); 95 | } 96 | 97 | 98 | /** 99 | * 获取栈中最小元素的方式 100 | * 101 | * @return 最小元素 102 | */ 103 | public Integer getMin() { 104 | if (stackMin.isEmpty()) { 105 | throw new RuntimeException("Your Stack is empty"); 106 | } 107 | return stackMin.peek(); 108 | } 109 | } 110 | 111 | private static class OtherGetMinStack { 112 | private Stack stackData; 113 | private Stack stackMin; 114 | 115 | OtherGetMinStack() { 116 | stackData = new Stack<>(); 117 | stackMin = new Stack<>(); 118 | } 119 | 120 | /** 121 | * 出栈方式 122 | * 123 | * @return 栈顶元素 124 | */ 125 | public Integer pop() { 126 | 127 | if (stackData.isEmpty()) { 128 | throw new RuntimeException("Your Stack is empty"); 129 | } 130 | 131 | int value = stackData.pop(); 132 | //区别第一种 stackData 元素出栈时 stackMin也出 133 | stackMin.pop(); 134 | 135 | return value; 136 | } 137 | 138 | /** 139 | * 进栈方式 140 | * 141 | * @param num 进栈元素 142 | */ 143 | public void push(Integer num) { 144 | 145 | if (stackMin.isEmpty()) { 146 | stackMin.push(num); 147 | } else if (num <= getMin()) { 148 | stackMin.push(num); 149 | }else { 150 | //区别第一种 stackData 元素进站时 stackMin也进站 只是如果当前要进站的值大于 151 | // stackMin 栈顶值时 每次进站的元素是当前栈顶元素 这样保证了两个栈大小相等 出栈操作不需要太多判断 152 | stackMin.push(stackMin.peek()); 153 | } 154 | stackData.push(num); 155 | } 156 | 157 | 158 | /** 159 | * 获取栈中最小元素的方式 160 | * 161 | * @return 最小元素 162 | */ 163 | public Integer getMin() { 164 | if (stackMin.isEmpty()) { 165 | throw new RuntimeException("Your Stack is empty"); 166 | } 167 | return stackMin.peek(); 168 | } 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /src/day4/zcy2.java: -------------------------------------------------------------------------------- 1 | package day4; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * 使用两个栈实现一个队列,该队列支持基本操作(add, poll peek) 7 | *

8 | * 学习目标 9 | * 1.了解队列的基本知识 10 | * 2.学习队列的基本操作 11 | */ 12 | public class zcy2 { 13 | 14 | public static void main(String[] args) { 15 | int[] testNumbers = new int[]{3, -4, 5, 1, 2, 1, 100, -1}; 16 | TwoStackQueue queue = new TwoStackQueue(); 17 | 18 | for (int i = 0; i < testNumbers.length; i++) { 19 | queue.add(testNumbers[i]); 20 | } 21 | System.out.println("执行 peek 操作:: " + queue.peek()); 22 | System.out.println("执行 poll 操作:: " + queue.poll()); 23 | 24 | 25 | } 26 | 27 | /** 28 | * 此题不是很难首先明确栈和队列的区别: 29 | * 1. 栈 先进后出 队列先进先出 30 | * 2. 要保证在执行队列 pop 和 peek 的时候 stackPop 栈中是空的 然后将 stackPush 栈中的元素一次性压入 stackPop 31 | * 3. 出队是对 stackPop 进行操操作 32 | * 33 | * 值得思考的地方: 如果 TwoStackQueue add 3 , -4 后 调用一次 poll 或者 peek 那么 stockPop 就不为空了,此时stackPush 为空 34 | * 那么在执行一次 add 5 后,再去执行 poll 或 peek stackPop 由于 stackPop.isEmpty()为 false 并不会执行进栈,只有在 stackPop 为空后才能再次进栈 35 | * 在 stackPop 再次进站前 stackPush 是不为空的。压入的元素保存在 stackPush 。 36 | * 37 | */ 38 | private static class TwoStackQueue { 39 | 40 | private Stack stackPush; 41 | private Stack stackPop; 42 | 43 | public TwoStackQueue() { 44 | this.stackPush = new Stack<>(); 45 | this.stackPop = new Stack<>(); 46 | } 47 | 48 | /** 49 | * 如队列操作 50 | * 51 | * @param num 52 | */ 53 | public void add(Integer num) { 54 | stackPush.push(num); 55 | System.out.println("stackPush 栈中内容:: " + stackPush); 56 | } 57 | 58 | public Integer poll() { 59 | if (stackPop.isEmpty() && stackPush.isEmpty()) { 60 | throw new RuntimeException("Queue is Empty!"); 61 | } 62 | if (stackPop.isEmpty()) { 63 | while (!stackPush.isEmpty()) { 64 | stackPop.push(stackPush.pop()); 65 | } 66 | } 67 | return stackPop.pop(); 68 | } 69 | 70 | 71 | public Integer peek() { 72 | if (stackPop.isEmpty() && stackPush.isEmpty()) { 73 | throw new RuntimeException("Queue is Empty!"); 74 | } 75 | if (stackPop.isEmpty()) { 76 | while (!stackPush.isEmpty()) { 77 | stackPop.push(stackPush.pop()); 78 | } 79 | } 80 | return stackPop.peek(); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/day5/zcy3.java: -------------------------------------------------------------------------------- 1 | package day5; 2 | 3 | import java.util.Stack; 4 | 5 | /*** 6 | * 递归实现一个栈的逆序 7 | * 8 | *如 1,2,3,4,5 顺序进站 栈顶为5 现在使用递归的方法将元素重新进栈 5,4,3,2,1 9 | * 考察点 递归思想: 函数自己调用自己 10 | * 11 | * 举个简单的例子: 12 | * 13 | * 栈中现在有 1,2,3 14 | * 现在 getLastElementInStack 方法 执行的内容为注释部分 15 | * 刚开始困惑 为什么 判断条件是站为空则返回 pop 结果: 16 | * 1. 最小情况下 站内只存在一个元素 栈顶 = 栈底 所以逆序只是该元素出站在进站的过程 (出口的来源) 17 | * 2. 如果栈包含多个元素 执行的顺序为 外层n 出栈 外层 n-1 出栈 ... 第 1 层 返回栈底值出栈栈底元素 第 2 层 进栈 原栈底开始第 2 个元素 ... 18 | * 3. result 保存每次一层调用出栈的元素 这个元素只有是栈底值得时候才 作为函数结果,如果不是则再次调用本身 并在内层递归结束后一层一层 由内而外再次进站 19 | */ 20 | public class zcy3 { 21 | public static void main(String[] args) { 22 | Stack stack = new Stack<>(); 23 | stack.push(1); 24 | stack.push(2); 25 | stack.push(3); 26 | stack.push(4); 27 | stack.push(5); 28 | 29 | System.out.println("翻转前的 stack:: " + stack); 30 | reverse(stack); 31 | System.out.println("翻转后的 stack:: " + stack); 32 | } 33 | 34 | private static int getLastElementInStack(Stack stack) { 35 | Integer result = stack.pop(); 36 | if (stack.isEmpty()) { 37 | return result; 38 | } else { 39 | int element = getLastElementInStack(stack); 40 | // // 内层调用 41 | // Integer reslut1 = stack.pop();//栈中此时为空 [1] reslut1 = 2 42 | // if (stack.isEmpty()){ 43 | // return reslut1; 44 | // }else { 45 | // Integer reslut2 = stack.pop();//栈中此时为空 [] reslut1 = 2 46 | // if (stack.isEmpty()){ 47 | // return reslut2;//直接 return 1;即 element = 1; 48 | // }else { 49 | // //因为第3层 stack 为空了 那么就不会执行 push 操作 50 | // } 51 | // //内层push 2 52 | // stack.push(reslut2); 53 | // // return 1 54 | // return element; 55 | // } 56 | // 57 | // // 这里 push 的是 3 58 | // stack.push(result); 59 | // // 并且 return 1 60 | // return element; 61 | 62 | // 这里 push 的是 3 63 | stack.push(result); 64 | // 并且 return 1 65 | return element; 66 | } 67 | } 68 | 69 | private static void reverse(Stack stack) { 70 | if (stack.isEmpty()) { 71 | return; 72 | } 73 | int lastElementInStack = getLastElementInStack(stack); 74 | reverse(stack); 75 | stack.push(lastElementInStack); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/day5/zcy4.java: -------------------------------------------------------------------------------- 1 | package day5; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | 6 | /*** 7 | * 猫狗队列 8 | * 9 | * 实现一种猫狗队列 用现有的用户结构 要求 10 | * 11 | * 1. 用户可以调用 add 方法将 cat 或者 dog 类放入队列中 12 | * 2. 用户可以调用 pollAll 方法,将队列中所有的实例按照队列的先后顺序依次弹出(每次出队一个) 13 | * 3. 用户可以调用 pollDog 方法,将队列的中 Dog 类的实例按照进队列的先后顺序依次弹出 14 | * 4. 用户可以调用 pollCat 方法,将队列中的 Cat 类的实例按照进队列的先后顺序依次弹出 15 | * 5. 用户可以调用 isEmpty 方法,判断队列中是否还有 Dog 或 cat 实例 16 | * 6. 用户可以调用 isDogEmpty 方法,判断队列中是否还有 Dog 实例 17 | * 7. 用户可以调用 isCatEmpty 方法,判断队列中是否还有 Cat 实例 18 | */ 19 | public class zcy4 { 20 | 21 | 22 | private static class DogCatQueue { 23 | private Queue catQueue; 24 | private Queue dogQueue; 25 | private long count; 26 | 27 | public DogCatQueue() { 28 | catQueue = new LinkedList<>(); 29 | dogQueue = new LinkedList<>(); 30 | count = 0; 31 | } 32 | 33 | public void add(Pet pet) { 34 | if (pet.getType().equals("Dog")) { 35 | dogQueue.add(new PetEnterQueue(count++, pet)); 36 | } else if (pet.getType().equals("Cat")) { 37 | catQueue.add(new PetEnterQueue(count++, pet)); 38 | } else { 39 | throw new RuntimeException("err! not a dog or a cat"); 40 | } 41 | } 42 | 43 | public Pet pollAll() { 44 | if (!dogQueue.isEmpty() && !catQueue.isEmpty()) { 45 | if (dogQueue.peek().time < catQueue.peek().time) { 46 | return dogQueue.poll().getPet(); 47 | } else { 48 | return catQueue.poll().getPet(); 49 | } 50 | } else if (!dogQueue.isEmpty()) { 51 | return dogQueue.poll().getPet(); 52 | } else if (!catQueue.isEmpty()) { 53 | return catQueue.poll().getPet(); 54 | } else { 55 | throw new RuntimeException("err! you Queue is Empty"); 56 | } 57 | } 58 | 59 | public Pet pollDog() { 60 | if (!dogQueue.isEmpty()) { 61 | return dogQueue.poll().getPet(); 62 | } else { 63 | throw new RuntimeException("err! There is not dog in Queue"); 64 | } 65 | } 66 | 67 | public Pet pollCat() { 68 | if (!catQueue.isEmpty()) { 69 | return catQueue.poll().getPet(); 70 | } else { 71 | throw new RuntimeException("err! There is not cat in Queue"); 72 | } 73 | } 74 | 75 | public boolean isEmpty() { 76 | return catQueue.isEmpty() && dogQueue.isEmpty(); 77 | } 78 | 79 | public boolean isDogEmpty() { 80 | return dogQueue.isEmpty(); 81 | } 82 | 83 | public boolean isCatEmpty() { 84 | return catQueue.isEmpty(); 85 | } 86 | } 87 | 88 | /*** 89 | * 构建用户输入的对象 time 为输入的时间(序号) 90 | */ 91 | private static class PetEnterQueue { 92 | private long time; 93 | private Pet pet; 94 | 95 | public PetEnterQueue(long time, Pet pet) { 96 | this.time = time; 97 | this.pet = pet; 98 | } 99 | 100 | public long getTime() { 101 | return time; 102 | } 103 | 104 | public Pet getPet() { 105 | return pet; 106 | } 107 | 108 | public String getEnterPetType() { 109 | return this.pet.type; 110 | } 111 | } 112 | 113 | /*** 题目给出的实体结构 (不能改变)*/ 114 | private static class Pet { 115 | private String type; 116 | 117 | public Pet(String type) { 118 | this.type = type; 119 | } 120 | 121 | public String getType() { 122 | return type; 123 | } 124 | } 125 | 126 | private static class Cat extends Pet { 127 | public Cat() { 128 | super("Cat"); 129 | } 130 | } 131 | 132 | private static class Dog extends Pet { 133 | public Dog() { 134 | super("Dog"); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/day5/zcy5.java: -------------------------------------------------------------------------------- 1 | package day5; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * 使用一个栈实现另一个栈的排序 7 | *

8 | * 1,3,2,6,4,5 9 | */ 10 | public class zcy5 { 11 | 12 | public static void main(String[] args) { 13 | Stack stack = new Stack<>(); 14 | stack.push(1); 15 | stack.push(3); 16 | stack.push(2); 17 | stack.push(6); 18 | stack.push(4); 19 | stack.push(5); 20 | 21 | System.out.println("sortStack 前:" + stack); 22 | System.out.println("sortStack 后:" + sortStackByaAnotherStack(stack)); 23 | } 24 | 25 | /** 26 | * 结果 最小的元素在下边 最大的元素在上边 27 | * 28 | * 思路 needSortStack 出栈一个元素 与 现有的 anotherStack 栈顶元素比较 29 | * 1. 如果 anotherStack 栈顶元素比 sortNum 要小 说明 sortNum 应该在 anotherStack 栈的前边 30 | * 2. 所以此时的 anotherStack 用元素要都倒入 needSortStack 中 再把 31 | * 3. 带 anotherStack 为空的时候讲 sortNum 放进 needSortStack 中 依次类推 32 | */ 33 | private static Stack sortStackByaAnotherStack(Stack needSortStack) { 34 | Stack anotherStack = new Stack<>(); 35 | 36 | while (!needSortStack.isEmpty()) { 37 | Integer sortNum = needSortStack.pop(); 38 | while (!anotherStack.isEmpty() && anotherStack.peek() < sortNum) { 39 | needSortStack.push(anotherStack.pop()); 40 | } 41 | anotherStack.push(sortNum); 42 | } 43 | 44 | //这里得到的 anotherStack 元素最小的在上边 元素最大在下面 45 | while (!anotherStack.isEmpty()) { 46 | needSortStack.push(anotherStack.pop()); 47 | } 48 | 49 | return needSortStack; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/day6/zcy6.java: -------------------------------------------------------------------------------- 1 | package day6; 2 | 3 | import java.util.Arrays; 4 | import java.util.LinkedList; 5 | import java.util.Scanner; 6 | 7 | /** 8 | * 题目描述:给定一个数组和滑动窗口的大小,请找出所有滑动窗口里的最大值。如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3, 9 | * 那么一共存在6个滑动窗口,他们的最大值分别为{4.4,6,6,6,5}。 10 | * 输入描述:请输入一个数组:以空格隔开 11 | * 4 3 5 4 3 3 6 7 12 | * 请输入滑动窗口的大小: 13 | * 3 14 | * 程序输出: 滑动窗口的最大值为: 15 | * [5, 5, 5, 4, 6, 7] 16 | *

17 | * 问题分析:因为是寻求最优解,所以通常第一能想到的解法: for 循环全部数组,每一次右移动 找出当前窗口的最大值存入数组的 O(wn)的时间复杂度 18 | * 不足以征服面试官,所以题中给出双向链表的解法。 19 | */ 20 | public class zcy6 { 21 | 22 | public static void main(String[] args) { 23 | Scanner scanner = new Scanner(System.in); 24 | System.out.println("请输入一个数组:以空格隔开"); 25 | String str = scanner.nextLine(); 26 | 27 | System.out.println("请输入滑动窗口的大小:"); 28 | int k = scanner.nextInt(); 29 | 30 | String[] tmp = str.split(" "); 31 | int[] arrays = new int[tmp.length]; 32 | for (int i = 0; i < tmp.length; i++) { 33 | arrays[i] = Integer.parseInt(tmp[i]); 34 | } 35 | scanner.close(); 36 | 37 | SolutionMethod1 solution1 = new SolutionMethod1(); 38 | System.out.println("滑动窗口的最大值为:"); 39 | System.out.println(Arrays.toString(solution1.maxInWindows(arrays, k))); 40 | System.out.println(Arrays.toString(solution1.maxWindowsNormal(arrays, k))); 41 | } 42 | 43 | 44 | /** 45 | * 时间复杂度为 O(n) 的解法 46 | */ 47 | private static class SolutionMethod1 { 48 | 49 | public int[] maxInWindows(int[] arrays, int k) { 50 | 51 | //不存在滑动窗口的情况 52 | if (arrays == null || arrays.length < k || k < 1) { 53 | return null; 54 | } 55 | 56 | //如果 arrays 的长度 为 4 那么滑动窗口大小为3 的时候 存在 2 个窗口 可得出最终滑动窗口最大值的数组的大小 57 | int[] result = new int[arrays.length - k + 1]; 58 | 59 | //双向链表用于存放有可能成为移动窗口最大值得元素的角标 60 | LinkedList qmax = new LinkedList<>(); 61 | int index = 0; 62 | for (int i = 0; i < arrays.length; i++) { 63 | 64 | //qmax 角标放入条件 1. 当前循环i值 < qmax 队列 队末的角标对应数组中的值得时候讲角标添加到 qmax 中 65 | //反之则如果来一个比末尾大的值则说明末尾的值不可能是窗口最大值 则从队未出队 66 | while (!qmax.isEmpty() && arrays[qmax.peekLast()] < arrays[i]) { 67 | qmax.pollLast(); 68 | } 69 | qmax.addLast(i); 70 | 71 | //qmax 的第一个元素什么时候过期? 如果 qmax 的队头值为 0 那么 i 进行到 3 的时候应该舍弃 3= 3-3 72 | if (qmax.getFirst() == i - k) { 73 | qmax.pollFirst(); 74 | } 75 | //什么时候该把当前元素放入 result 中 ? i = 0 放 6 i= 1 放 6 i= 2 放 6 i= 3 放6 76 | if (i >= k - 1) { 77 | result[index++] = arrays[qmax.peekFirst()]; 78 | } 79 | 80 | } 81 | return result; 82 | } 83 | 84 | /** 85 | * 第一反应 能解出的 O(wn) 时间复杂度的解法 86 | * 87 | * @param arrays 88 | * @param w 89 | * @return 90 | */ 91 | public int[] maxWindowsNormal(int[] arrays, int w) { 92 | //不存在滑动窗口的情况 93 | if (arrays == null || arrays.length < w || w < 1) { 94 | return null; 95 | } 96 | 97 | //如果 arrays 的长度 为 4 那么滑动窗口大小为3 的时候 存在 2 个窗口 可得出最终滑动窗口最大值的数组的大小 98 | int[] result = new int[arrays.length - w + 1]; 99 | int index = 0; 100 | 101 | for (int i = 0; i < arrays.length - w + 1; i++) { 102 | int max = 0; 103 | for (int j = i; j < i + w; j++) { 104 | if (arrays[j] > max) { 105 | max = arrays[j]; 106 | } 107 | } 108 | result[index++] = max; 109 | } 110 | 111 | return result; 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/day6/zcy7.java: -------------------------------------------------------------------------------- 1 | package day6; 2 | 3 | import java.util.HashMap; 4 | import java.util.Stack; 5 | 6 | /*** 7 | * 构造数组的MaxTree 8 | * 定义二叉树结点如下: 9 | * 10 | * public class Node { 11 | * public int value; 12 | * public Node left; 13 | * public Node right; 14 | * 15 | * public Node(int data) { 16 | * this.value = data; 17 | * } 18 | * } 19 | * 20 | * 一个数组的MaxTree定义如下。 21 | * 22 | * 数组必须没有重复元素。 23 | * 24 | * 1、 MaxTree是一颗二叉树,数组的每一个值对应一个二叉树节点。 25 | * 2、 包括MaxTree树在内且在其中的每一颗树上,值最大的节点都是树的头。 26 | * 3、 给定一个没有重复元素的数组arr,写出生成这个数组的MaxTree的函数,要求如果数组长度为N,则时间复杂度为O(n)、额外空间复杂度为O(n). 27 | * 28 | * in[] arr = { 3 , 4 , 5 , 1 , 2 } 29 | * 30 | * 解法: 31 | * 1. 每一个数的父节点是它左边第一个比他大的数和右边第一个比它大的数中,较小的那个 32 | * 2. 如果一个数他左边没有比他大的数右边也没有,那么他肯定是数组中的最大值也就是 MaxTree 的 根节点 33 | * 34 | * 啊 好难!!! 35 | */ 36 | public class zcy7 { 37 | 38 | 39 | public static class Node { 40 | public int value; 41 | public Node left; 42 | public Node right; 43 | 44 | public Node(int data) { 45 | this.value = data; 46 | } 47 | } 48 | 49 | 50 | public static Node getMaxTree(int[] arr) { 51 | //1:构造原始节点数组 52 | Node[] nArr = new Node[arr.length]; 53 | for (int i = 0; i < arr.length; i++) { 54 | nArr[i] = new Node(arr[i]); 55 | } 56 | 57 | //构建获取数值左边右两边第一个比他大的数的 map 所有的栈 以及 map 58 | Stack stack = new Stack<>(); 59 | HashMap leftMap = new HashMap<>(); 60 | HashMap rightMap = new HashMap<>(); 61 | 62 | //生成 leftMap 这里边存放 value 是 key 左边比 key 大的第一个数 63 | for (int i = 0; i < nArr.length; i++) { 64 | Node curNode = nArr[i]; 65 | //如果当前值小于栈顶的值则直接入栈,如果当前大于栈顶的值则可以确定栈顶的元素左边第一个比它大的数要么 null 要是栈中下一个元素 66 | //stack.peek().value < curNode.value 可以看出 栈中元素小的在上大的在下 67 | while ((!stack.isEmpty()) && stack.peek().value < curNode.value) { 68 | popStackSetMap(stack, leftMap); 69 | } 70 | stack.push(curNode); 71 | } 72 | 73 | while (!stack.isEmpty()) { 74 | popStackSetMap(stack, leftMap); 75 | } 76 | 77 | //经过上述操作栈中元素以及入栈元素(每个数)左边第一个比他大的数已经存放在 leftMap 中 78 | 79 | //进行到这里的时候 stack 肯定为空 80 | for (int i = nArr.length - 1; i > 0; i--) { 81 | Node curNode = nArr[i]; 82 | while ((!stack.isEmpty()) && stack.peek().value < curNode.value) { 83 | popStackSetMap(stack, rightMap); 84 | } 85 | stack.push(curNode); 86 | } 87 | 88 | while (!stack.isEmpty()) { 89 | popStackSetMap(stack, rightMap); 90 | } 91 | 92 | //经过上述操作栈中元素以及入栈元素(每个数)右边第一个比他大的数已经存放在 rightMap 中 93 | 94 | Node head = null; 95 | 96 | for (int i = 0; i < nArr.length; i++) { 97 | 98 | Node curNode = nArr[i]; 99 | 100 | Node left = leftMap.get(curNode); 101 | Node right = rightMap.get(curNode); 102 | 103 | //如果一个数他左边没有比他大的数右边也没有,那么他肯定是数组中的最大值也就是 MaxTree 的 根节点 104 | if (left == null && right == null) {//5 105 | head = curNode; 106 | } else if (left == null) {// 当一个数左边没有比他更大的数的收优先安排左节点? 107 | 108 | //如果一个数的左边没有比他更大的数 右边有,那么他肯定是这个更大数的子节点, 109 | // 如果比他大的这个数左节点为空 那么这个数是这个比他更大数的左节点 110 | if (right.left == null) {//3 左大 null 右大 4 ,4 左大 null 右大 5 当3走到这里的时候 4 的左节点被安排成 3 (为什么不是右节点?) 111 | right.left = curNode; 112 | } else { 113 | right.right = curNode; 114 | } 115 | 116 | } else if (right == null) {//2: 2 左大 5 右大 null 117 | 118 | if (left.left == null) { 119 | left.left = curNode; 120 | } else { 121 | left.right = curNode; 122 | } 123 | // 走完之后5的右节点为2 为什么不是左?此例子中4 已经先占了 5 左 124 | 125 | } else {// 1 左边大 5 右大 2 126 | 127 | Node parent = left.value < right.value ? left : right; 128 | 129 | if (parent.left == null) { 130 | parent.left = curNode; 131 | } else { 132 | parent.right = curNode; 133 | } 134 | 135 | // 走完以后 2 的左键节点为 1 136 | } 137 | } 138 | return head; 139 | } 140 | 141 | /** 142 | * 取出栈顶元素,如果此时栈内为空,则表示对应node 左边(右边)没有比它大的元素 143 | * 144 | * @param stack 145 | * @param map 146 | */ 147 | private static void popStackSetMap(Stack stack, HashMap map) { 148 | Node popNode = stack.pop(); 149 | if (stack.isEmpty()) { 150 | map.put(popNode, null); 151 | } else { 152 | map.put(popNode, stack.peek()); 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/day7/zcy2_1.java: -------------------------------------------------------------------------------- 1 | package day7; 2 | 3 | import TestUItls.Node; 4 | 5 | /** 6 | * 打印两个有序链表的公共部分 7 | * 8 | * 给定两个有序链表的头指针 head1 和 head2 ,打印两个链表的公共部分 9 | * 10 | * 因为链表是有序的: 11 | * 如果 head1 的值小于 head2 的值 那么 head1 应该拿下一个值与 head2 比较 12 | * 如果 head2 的值小于 head1 的值 那么 head2 应该拿下一个值与 head1 比较 13 | * 如果两者相同那么打印元素,并同时移动两个指针。 14 | * 如果有一个移动以后为 null 那么说明已经比较完了 15 | * 16 | */ 17 | public class zcy2_1 { 18 | 19 | 20 | /** 21 | * 22 | * @param head1 有序链表1 23 | * @param head2 有序链表2 24 | */ 25 | private static void printCommonPart(Node head1, Node head2){ 26 | System.out.printf("Common part:: "); 27 | while (head1 != null && head2 !=null){ 28 | if (head1.value < head2.value){ 29 | head1 = head1.next; 30 | }else if (head1.value > head2.value){ 31 | head2 = head2.next; 32 | }else { 33 | System.out.printf(" " + head1.value); 34 | head1 = head1.next; 35 | head2 = head2.next; 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/day7/zcy2_2.java: -------------------------------------------------------------------------------- 1 | package day7; 2 | 3 | import TestUItls.DoubleNode; 4 | import TestUItls.Node; 5 | 6 | /** 7 | * 删除单链表和双链表中的倒数第 K 个元素 8 | *

9 | * 注意链表的元素删除要注意 next 的指向的位置 双链表则还要注意 last 即上一个元素的的位置 10 | */ 11 | public class zcy2_2 { 12 | public static void main(String[] args) { 13 | Node head = new Node(1); 14 | head.next = new Node(2); 15 | head.next.next = new Node(3); 16 | head.next.next.next = new Node(4); 17 | 18 | Node node = deleteIndexOfKNum(head, 2); 19 | System.out.println("Node: " + node); 20 | 21 | 22 | DoubleNode doubleNode = new DoubleNode(1); 23 | DoubleNode doubleNode1 = new DoubleNode(2); 24 | DoubleNode doubleNode2 = new DoubleNode(3); 25 | DoubleNode doubleNode3 = new DoubleNode(4); 26 | 27 | doubleNode.last = null; 28 | doubleNode.next = doubleNode1; 29 | 30 | doubleNode1.next = doubleNode2; 31 | doubleNode1.last = doubleNode; 32 | 33 | doubleNode2.next = doubleNode3; 34 | doubleNode2.last = doubleNode1; 35 | 36 | doubleNode3.next = null; 37 | doubleNode3.last = doubleNode2; 38 | 39 | DoubleNode doubleNodeResult = deleteIndexOfKNumInDoubleList(doubleNode, 2); 40 | 41 | System.out.println("DoubleNode: " + doubleNodeResult); 42 | } 43 | 44 | private static Node deleteIndexOfKNum(Node head, int k) { 45 | 46 | if (head == null || k < 1) { 47 | return head; 48 | } 49 | 50 | //思路 找到要删除的元素的上一个元素,并修改其执行当前所指向元素的下一个元素 51 | Node cur = head; 52 | 53 | while (cur != null) { 54 | cur = cur.next; 55 | k--; 56 | } 57 | 58 | // 表示要删除的值不存在 59 | if (k == 0) { 60 | //如果 K= 0 表示要删除第1个元素,则只需要将第第二个元素返回即可 61 | head = head.next; 62 | } 63 | 64 | // 如果 K < 0 则表示要删除中间的一个元素 此时要做的是将这个元素的上一个元素找到,并将其指向下一个元素 65 | // 如 k = 2 链表长度为 4 倒数第 k 个元素 即为链表中的第 3 个元素,其前一个元素为 4-2 = 2 个元素 66 | 67 | if (k < 0) { 68 | //循环完成后记得将 cur 再次赋值为 head 否则 cur = null; 69 | cur = head; 70 | // k 自加1后跟 0 比较 因为 head 为 链表中第 0 个元素 71 | // while (k + 1 != 0) { 72 | // cur = cur.next; 73 | // k ++; 74 | // } 75 | while (++k != 0) { 76 | cur = cur.next; 77 | } 78 | cur.next = cur.next.next; 79 | } 80 | 81 | return head; 82 | } 83 | 84 | 85 | private static DoubleNode deleteIndexOfKNumInDoubleList(DoubleNode head, int k) { 86 | 87 | if (head == null || k < 1) { 88 | return head; 89 | } 90 | 91 | //思路 找到要删除的元素的上一个元素,并修改其执行当前所指向元素的下一个元素 92 | DoubleNode cur = head; 93 | 94 | while (cur != null) { 95 | cur = cur.next; 96 | k--; 97 | } 98 | 99 | // 表示要删除的值不存在 100 | if (k == 0) { 101 | //如果 K= 0 表示要删除第1个元素,则只需要将第第二个元素返回即可 102 | head = head.next; 103 | head.last = null; 104 | } 105 | 106 | if (k < 0) { 107 | //循环完成后记得将 cur 再次赋值为 head 否则 cur = null; 108 | cur = head; 109 | // k 自加1后跟 0 比较 因为 head 为 链表中第 0 个元素 110 | // while (k + 1 != 0) { 111 | // cur = cur.next; 112 | // k ++; 113 | // } 114 | while (++k != 0) { 115 | cur = cur.next; 116 | } 117 | DoubleNode newNext = cur.next.next; 118 | //双向链表还要进行前一个元素的赋值 119 | cur.next = newNext; 120 | if (newNext != null) { 121 | newNext.last = cur; 122 | } 123 | 124 | } 125 | 126 | return head; 127 | 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/day7/zcy2_3.java: -------------------------------------------------------------------------------- 1 | package day7; 2 | 3 | import TestUItls.DoubleNode; 4 | import TestUItls.Node; 5 | 6 | /** 7 | * 翻转单向链表和双向链表 8 | */ 9 | public class zcy2_3 { 10 | 11 | 12 | private static Node revaseList(Node head) { 13 | Node pre = null; 14 | Node next = null; 15 | 16 | while (head != null) { 17 | // 暂存当前元素的下一个元素 作为下一个要反转的元素 18 | next = head.next; 19 | //翻转指向 20 | head.next = pre; 21 | //要翻转的下个元素的前一个元素为当前元素 22 | pre = head; 23 | //下一个要比较的元素为当前元素的下个元素 24 | head = next; 25 | } 26 | //注意这里返回的是赋值当前比较元素 27 | return pre; 28 | } 29 | 30 | 31 | private static DoubleNode revaseDoubleList(DoubleNode head) { 32 | DoubleNode pre = null; 33 | DoubleNode next = null; 34 | 35 | while (head != null) { 36 | // 暂存当前元素的下一个元素 作为下一个要反转的元素 37 | next = head.next; 38 | 39 | //翻转当前元素的指向 40 | head.next = pre; 41 | head.last = next; 42 | 43 | //要翻转的下个元素的前一个元素为当前元素 44 | pre = head; 45 | //下一个要比较的元素为当前元素的下个元素 46 | head = next; 47 | } 48 | 49 | return pre; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/day8/zcy2_4.java: -------------------------------------------------------------------------------- 1 | package day8; 2 | 3 | import TestUItls.Node; 4 | import TestUItls.TestUtils; 5 | 6 | /*** 7 | * 翻转单链表的部分 8 | */ 9 | public class zcy2_4 { 10 | 11 | public static void main(String[] args) { 12 | System.out.println(TestUtils.getList(6)); 13 | System.out.println("翻转结果:; " + reversePart(TestUtils.getList(6), 2, 4)); 14 | 15 | System.out.println("翻转结果:; " + reversePart(TestUtils.getList(6), 1, 3)); 16 | 17 | } 18 | 19 | // 1 2(node1) 3(next) 4 5 6 1 3 2 4 5 6 20 | private static Node reversePart(Node head, int from, int to) { 21 | 22 | Node node1 = head; 23 | 24 | int len = 0;//用来计算链表的长度 25 | 26 | Node preNode = null; 27 | Node toNode = null; 28 | 29 | while (node1 != null) { 30 | 31 | len++; 32 | 33 | if (len == from - 1) { 34 | preNode = node1; 35 | } 36 | 37 | if (len == to + 1) { 38 | toNode = node1; 39 | } 40 | 41 | node1 = node1.next; 42 | } 43 | 44 | if (from < 1 || to > len || from > to) { 45 | return head; 46 | } 47 | 48 | node1 = preNode == null ? head : preNode.next; 49 | 50 | Node node2 = node1.next; 51 | node1.next = toNode; 52 | Node next = null; 53 | 54 | while (node2 != toNode) { 55 | next = node2.next; 56 | node2.next = node1; 57 | node1 = node2; 58 | node2 = next; 59 | } 60 | 61 | if (preNode != null) { 62 | preNode.next = node1; 63 | return head; 64 | } 65 | 66 | //如果 preNode 等于 null 那么翻转后的第一个元素就是 head 67 | return node1; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/day8/zcy2_5.java: -------------------------------------------------------------------------------- 1 | package day8; 2 | 3 | import TestUItls.Node; 4 | import TestUItls.TestUtils; 5 | 6 | /** 7 | * 环形单链表的约瑟夫问题 8 | */ 9 | public class zcy2_5 { 10 | 11 | 12 | public static void main(String[] args) { 13 | Node roundList = TestUtils.getRoundList(); 14 | Node remain = jusephuskill1(roundList, 3); 15 | System.out.println("活下来的节点::" + remain.value); 16 | } 17 | 18 | /** 19 | * @param head 环形单链表 20 | * @param m 报 m 的人将被 kill 21 | * @return 最后幸存下来的节点 22 | */ 23 | private static Node jusephuskill1(Node head, int m) { 24 | //如果环形单链表 中就一个元素 head.next == head 它就是幸存者 25 | if (head == null || head.next == head || m < 1) { 26 | return head; 27 | } 28 | 29 | Node last = head; 30 | while (last.next != head) { 31 | last = last.next; 32 | } 33 | int count = 0; 34 | // head 环形开始的节点 last 环形结束的节点 35 | while (head != last) { 36 | if (count == m) { 37 | last.next = head.next; 38 | //删除一个节点后 从0 开始报数 39 | count = 0; 40 | } else { 41 | last = head; 42 | } 43 | count = count+1; 44 | head = last.next; 45 | System.out.println("last: " + last + " head:: " + head); 46 | } 47 | 48 | return head; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/day9/zcy2_6.java: -------------------------------------------------------------------------------- 1 | package day9; 2 | 3 | import TestUItls.Node; 4 | import TestUItls.TestUtils; 5 | 6 | import java.util.Stack; 7 | 8 | /*** 9 | * 判断一个单链表是否为回文结构 10 | * 给定一个链表的头节点,判断这个链表是否为回文结构 11 | * 例如: 12 | * 1->2->3->2->1 是回文结构 13 | * 1->2->3->3->1 不是回文结构 14 | * 15 | * 进阶: 16 | * 如果链表长度是 N 则要求算法复杂度为 O(N) 额外的空间复杂度为 O(1) 17 | */ 18 | public class zcy2_6 { 19 | 20 | public static void main(String[] args) { 21 | Node head = TestUtils.getPalindromeList(); 22 | Node head1 = TestUtils.getList(5); 23 | 24 | System.out.println("链表是回文结构 isPalindrome1 : " + isPalindrome1(head)); 25 | System.out.println("链表是回文结构 isPalindrome1 : " + isPalindrome1(head1)); 26 | 27 | System.out.println("链表是回文结构 isPalindrome2 : " + isPalindrome2(head)); 28 | System.out.println("链表是回文结构 isPalindrome2 : " + isPalindrome2(head1)); 29 | 30 | System.out.println("链表是回文结构 isPalindrome3 : " + isPalindrome3(head)); 31 | System.out.println("链表是回文结构 isPalindrome3 : " + isPalindrome3(head1)); 32 | 33 | } 34 | 35 | /** 36 | * 普通解法 使用栈结构逆序 然后判断整个链表是否值相同 37 | * 时间复杂度O(n) 空间复杂度O(n) 38 | * 39 | * @param head 根节点 40 | * @return 是否为回文结构 41 | */ 42 | private static boolean isPalindrome1(Node head) { 43 | if (head == null || head.next == null) { 44 | return true; 45 | } 46 | 47 | Node cur = head; 48 | Stack stack = new Stack<>(); 49 | while (cur != null) { 50 | stack.push(cur); 51 | cur = cur.next; 52 | } 53 | 54 | while (head != null) { 55 | if (head.value != stack.pop().value) { 56 | return false; 57 | } 58 | head = head.next; 59 | } 60 | return true; 61 | } 62 | 63 | /** 64 | * 利用链表对折思想 只将链表的右半部分放入栈中 这里用一个很巧妙的方法判断哪个节点是链表中间点 65 | * 如 1 2 2 1 则将指针1 指向 1,指针 2 指向2,指针1 每次移动两个位置,指针2 每次移动1个位置 66 | * 当指针1 不可移动(下一个为空 或者 下下个数字为空的时候) 指针2 指向的位置及时链表中点 67 | * 当链表为奇数的时候会指向中间一个节点,如果为偶数则会指向右侧第一个节点 68 | *

69 | * 时间复杂度O(n) 空间复杂度O(n/2) 70 | * 71 | * @param head 72 | * @return 73 | */ 74 | private static boolean isPalindrome2(Node head) { 75 | if (head == null || head.next == null) { 76 | return true; 77 | } 78 | Node right = head.next; 79 | Node cur = head; 80 | 81 | while (cur.next != null && cur.next.next != null) { 82 | right = right.next; 83 | cur = cur.next.next; 84 | } 85 | 86 | Stack stack = new Stack<>(); 87 | while (right != null) { 88 | stack.push(right); 89 | right = right.next; 90 | } 91 | while (!stack.isEmpty()) { 92 | if (head.value != stack.pop().value) { 93 | return false; 94 | } 95 | head = head.next; 96 | } 97 | return true; 98 | } 99 | 100 | /** 101 | * 进阶版 102 | * 翻转 中间节点之后的节点 然后比较 两段列表是否相同 如果相同则为回文链表 103 | * 如: 1 -> 2 -> 3 -> 2 -> 1 104 | * 翻转后半部分: 1 -> 2 -> 3 -> null <- 3 <- 2 <- 1 105 | * 106 | * 难点在于两次链表部分翻转 107 | * @param head 108 | * @return 109 | */ 110 | private static boolean isPalindrome3(Node head) { 111 | 112 | if (head == null || head.next == null) { 113 | return true; 114 | } 115 | 116 | // 先找到中间节点 117 | Node n1 = head.next; 118 | Node n2 = head; 119 | while (n2.next != null && n2.next.next != null) { 120 | n1 = n1.next;//中间点 3 121 | n2 = n2.next.next;//最右点 122 | } 123 | 124 | //翻转 n1 之后的节点 125 | n2 = n1.next; 126 | n1.next = null; 127 | Node temp = null; 128 | 129 | while (n2 != null) { 130 | temp = n2.next; 131 | n2.next = n1; 132 | n1 = n2; 133 | n2 = temp; 134 | } 135 | 136 | temp = n1;// temp 为原链表最后一个节点 137 | n2 = head; // 重置为左边第一个节点 138 | boolean result = true; 139 | while (n2 != null && n1 != null) { 140 | if (n2.value != n1.value) { 141 | result = false; 142 | break; 143 | } 144 | n1 = n1.next; 145 | n2 = n2.next; 146 | } 147 | 148 | //判断完成后翻转回来 149 | n1 = temp.next; 150 | temp.next = null; 151 | while (n1 != null) { 152 | n2 = n1.next; 153 | n1.next = temp; 154 | temp = n1; 155 | n1 = n2; 156 | } 157 | 158 | return result; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/day9/zcy2_7.java: -------------------------------------------------------------------------------- 1 | package day9; 2 | 3 | import TestUItls.Node; 4 | import TestUItls.TestUtils; 5 | 6 | /*** 7 | * 将单向链表按照某值划分成左小,中间相等 右边大的新链表 8 | * 例如 9 0 4 5 1 给定数字 5 则新链表的顺序为 1 0 4 5 9 9 | * 10 | * 进阶: 11 | * 原题要求下并满足:从左到右与原来链表中的顺序一致 时间复杂度为 O(N) 空间复杂度为 O(1) 12 | */ 13 | public class zcy2_7 { 14 | 15 | public static void main(String[] args) { 16 | Node head = generaList(); 17 | System.out.print("旧链表:"); 18 | TestUtils.printList(head); 19 | 20 | 21 | Node cur = head; 22 | while (cur.next != null) { 23 | cur = cur.next; 24 | } 25 | quickSort(head, cur); 26 | 27 | System.out.print("新链表:"); 28 | 29 | TestUtils.printList(head); 30 | // TestUtils.printList(partition(head, 5)); 31 | 32 | // System.out.print("新链表:"); 33 | // TestUtils.printList(listPartition2(generaList(), 5)); 34 | 35 | } 36 | 37 | private static Node listPartition1(Node head, int partition) { 38 | if (head == null) { 39 | return head; 40 | } 41 | 42 | Node cur = head; 43 | 44 | //获取 链表长度 45 | int len = 0; 46 | while (cur != null) { 47 | len++; 48 | cur = cur.next; 49 | } 50 | 51 | //获取链表对应的数组表示 52 | Node[] nodes = new Node[len]; 53 | cur = head; 54 | len = 0; 55 | while (cur != null) { 56 | nodes[len] = cur; 57 | len++; 58 | cur = cur.next; 59 | } 60 | partition(nodes, partition); 61 | int i = 0; 62 | for (i = 1; i != len; i++) { 63 | nodes[i - 1].next = nodes[i]; 64 | } 65 | 66 | nodes[i - 1].next = null; 67 | 68 | return nodes[0]; 69 | } 70 | 71 | 72 | /** 73 | * arr[l + 1 ... j] < v 74 | * arr[j+1,i) >= v 75 | * 76 | * @param left 77 | * @param right 78 | */ 79 | private static void quickSort(Node left, Node right) { 80 | if (left == null || right == null || left == right) { 81 | return; 82 | } 83 | //快拍的标志值 84 | int v = left.value; 85 | //j = l 86 | Node l = left; 87 | Node start = left.next; 88 | // for (int i = l + 1; i <= r; i++) { 89 | while (start != right.next && start != null) { 90 | if (start.value < v) { 91 | l = l.next; 92 | //swap(arr,j+1,i) 93 | int temp = l.value; 94 | l.value = start.value; 95 | start.value = temp; 96 | } 97 | start = start.next; 98 | } 99 | 100 | //交换 l 与 与 left 的值 101 | int temp = l.value; 102 | l.value = left.value; 103 | left.value = temp; 104 | 105 | quickSort(left, l); 106 | quickSort(l.next, right); 107 | } 108 | 109 | 110 | private static Node partition(Node head, int x) { 111 | Node left = new Node(0); 112 | Node right = new Node(0); 113 | Node mid = new Node(0); 114 | 115 | Node leftDummy = left; 116 | Node rightDummy = right; 117 | Node midDummy = mid; 118 | 119 | while (head != null) { 120 | if (head.value < x) { 121 | left.next = head; 122 | left = left.next; 123 | } else if (head.value == x) { 124 | mid.next = head; 125 | mid = mid.next; 126 | } else { 127 | right.next = head; 128 | right = right.next; 129 | } 130 | head = head.next; 131 | } 132 | 133 | left.next = midDummy.next; 134 | mid.next = rightDummy.next; 135 | right.next = null; 136 | 137 | return leftDummy.next; 138 | } 139 | 140 | 141 | /** 142 | * 数组 一次快速排序的实现 143 | * 144 | * @param nodes 145 | * @param partition 146 | */ 147 | private static void partition(Node[] nodes, int partition) { 148 | int samll = -1; 149 | int big = nodes.length; 150 | int index = 0; 151 | 152 | while (index != big) { 153 | if (nodes[index].value < partition) { 154 | swap(nodes, ++samll, index++); 155 | } else if (nodes[index].value == partition) { 156 | index++; 157 | } else { 158 | swap(nodes, --big, index); 159 | } 160 | } 161 | } 162 | 163 | private static void swap(Node[] nodes, int a, int b) { 164 | Node temp = nodes[a]; 165 | nodes[a] = nodes[b]; 166 | nodes[b] = temp; 167 | } 168 | 169 | private static Node generaList() { 170 | Node head = new Node(7); 171 | head.next = new Node(9); 172 | head.next.next = new Node(1); 173 | head.next.next.next = new Node(8); 174 | head.next.next.next.next = new Node(5); 175 | head.next.next.next.next.next = new Node(2); 176 | head.next.next.next.next.next.next = new Node(5); 177 | return head; 178 | } 179 | 180 | /** 181 | * 进阶版 要求两边的内容排好序 并且 空间复杂度为 O(1) 182 | * 183 | * @param head 184 | * @param partition 185 | * @return 186 | */ 187 | private static Node listPartition2(Node head, int partition) { 188 | Node sH = null;//小的头 189 | Node sT = null;//小的尾 190 | Node eH = null;//相等的头 191 | Node eT = null;//相等的尾 192 | Node bH = null;//大的头 193 | Node bT = null;//大的未 194 | 195 | //所有的节点分为三个链表中 196 | Node next = null; 197 | while (head != null) { 198 | next = head.next; 199 | //这一步很重要 如果不把 head.next 置为 null 则所有的赋值操作都将是个死循环 200 | head.next = null; 201 | if (head.value < partition) { 202 | if (sH == null) { 203 | sH = head; 204 | sT = head; 205 | } else { 206 | sT.next = head;//把小的末尾下一个节点指向 head 207 | sT = head;//把小的末尾指向 head 208 | } 209 | } else if (head.value == partition) { 210 | if (eH == null) { 211 | eH = head; 212 | eT = head; 213 | } else { 214 | eT.next = head;//把小的末尾下一个节点指向 head 215 | eT = head;//把小的末尾指向 head 216 | } 217 | } else { 218 | if (bH == null) { 219 | bH = head; 220 | bT = head; 221 | } else { 222 | bT.next = head;//把小的末尾下一个节点指向 head 223 | bT = head;//把小的末尾指向 head 224 | } 225 | } 226 | 227 | head = next; 228 | } 229 | 230 | if (sT != null) { 231 | sT.next = eH; 232 | eT = eT == null ? sT : eT;//eT == null 说明该链表中没有与指定数字相同的节点,此时只要将 sT 当做 eT 233 | } 234 | 235 | if (eT != null) { 236 | eT.next = bH; 237 | } 238 | 239 | return sH != null ? sH : eH != null ? eH : bH; 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/day9/zcy2_8.java: -------------------------------------------------------------------------------- 1 | package day9; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * 赋值含有随机指针节点的链表 7 | */ 8 | public class zcy2_8 { 9 | 10 | 11 | private static class Node { 12 | public int value; 13 | public Node next; 14 | public Node rand; 15 | 16 | public Node(int value) { 17 | this.value = value; 18 | } 19 | } 20 | 21 | 22 | /** 23 | * 使用 HashMap 时间复杂度O(1) 24 | * @param head 25 | * @return 26 | */ 27 | public static Node copyListWithRand1(Node head) { 28 | HashMap map = new HashMap<>(); 29 | Node cur = head; 30 | while (cur != null) { 31 | map.put(cur,new Node(cur.value)); 32 | cur = cur.next; 33 | } 34 | 35 | cur = head; 36 | while (cur != null){ 37 | map.get(cur).next = cur.next; 38 | map.get(cur).rand = cur.rand; 39 | } 40 | 41 | return map.get(head); 42 | } 43 | 44 | public static Node copyListWithRand2(Node head){ 45 | if (head == null){ 46 | return head; 47 | } 48 | Node cur = head; 49 | Node next = null; 50 | while (cur != null){ 51 | next = cur.next; 52 | cur.next = new Node(cur.value); 53 | cur.next.next = next; 54 | cur = next; 55 | } 56 | cur = head; 57 | Node curCopy = null; 58 | 59 | while(cur!= null){ 60 | next = cur.next.next; 61 | curCopy = cur.next; 62 | curCopy.rand = cur.rand == null ? cur.rand.next : null; 63 | cur = next; 64 | } 65 | 66 | Node res = head.next; 67 | cur = head; 68 | while (cur!=null){ 69 | next = cur.next.next; 70 | curCopy = cur.next; 71 | cur.next = next; 72 | curCopy.next = next != null ? next.next : null; 73 | cur = next; 74 | } 75 | return res; 76 | } 77 | 78 | 79 | } 80 | -------------------------------------------------------------------------------- /剑指offer/src/FindTheDupliNum.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.LinkedList; 3 | import java.util.ListIterator; 4 | 5 | /** 6 | * 数组中重复的数字 7 | * 题目:在一个长度为n的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的, 8 | * 但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。 9 | *

10 | * 思路: 如果一个长度为 n 的数组存储的整数全部在 0 ~ n-1 范围内,那如果数组中没有重复数字,如果重排数组,那么可以刚好使得 0 ~ n-1 11 | * 数组为 i 的元素 在 arr[i-1]的位置上。如果有重复数字,当第二次往 arr[i-1] 上放置的时候,就会重复,那么这个元素就是我们要找的那个元素。 12 | */ 13 | public class FindTheDupliNum { 14 | public static void main(String[] args) { 15 | int[] numbers1 = {2, 1, 3, 1, 4}; 16 | System.out.println(findOneDuplicateNum(numbers1)); 17 | int[] numbers2 = {2, 4, 3, 1, 4}; 18 | System.out.println(findOneDuplicateNum(numbers2)); 19 | int[] numbers3 = {2, 4, 2, 1, 4}; 20 | System.out.println(findOneDuplicateNum(numbers3)); 21 | int[] numbers4 = {2, 1, 3, 0, 4}; 22 | System.out.println(findOneDuplicateNum(numbers4)); 23 | } 24 | 25 | private static int findOneDuplicateNum(int[] arr) { 26 | if (arr == null || arr.length < 1) { 27 | return -1; 28 | } 29 | 30 | for (int i = 0; i < arr.length; i++) { 31 | //重排数组的过程,使 i 的位置值为 i 32 | while (arr[i] != i) { 33 | if (arr[i] == arr[arr[i]]) { 34 | return arr[i]; 35 | } else { 36 | swap(arr, i, arr[i]); 37 | } 38 | } 39 | } 40 | 41 | return -1; 42 | } 43 | 44 | private static void swap(int[] arr, int x, int y) { 45 | int temp = arr[x]; 46 | arr[x] = arr[y]; 47 | arr[y] = temp; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /剑指offer/src/PrintLinkListReverse.java: -------------------------------------------------------------------------------- 1 | public class PrintLinkListReverse { 2 | 3 | public static void main(String[] args) { 4 | Node head = buildLinkList(); 5 | printLinkList(head); 6 | } 7 | 8 | private static void printLinkList(Node head) { 9 | Node node = reverseNode(head); 10 | while (node != null){ 11 | System.out.print(" " + node.value); 12 | node = node.next; 13 | } 14 | } 15 | 16 | private static Node reverseNode(Node head) { 17 | Node next = null; 18 | Node pre = null; 19 | while (head != null) { 20 | next = head.next; 21 | head.next = pre; 22 | pre = head; 23 | head = next; 24 | } 25 | 26 | return pre; 27 | } 28 | 29 | private static Node buildLinkList() { 30 | 31 | Node head = new Node(1); 32 | head.next = new Node(2); 33 | head.next.next = new Node(3); 34 | head.next.next.next = new Node(4); 35 | head.next.next.next.next = new Node(5); 36 | 37 | return head; 38 | 39 | } 40 | 41 | private static class Node { 42 | int value; 43 | Node next; 44 | 45 | public Node(int value) { 46 | this.value = value; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /剑指offer/src/RebuildBinaryTree.java: -------------------------------------------------------------------------------- 1 | /*** 2 | * 题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 3 | * 例如:前序遍历序列{ 1, 2, 4, 7, 3, 5, 6, 8}和中序遍历序列{4, 7, 2, 1, 5, 3, 8,6},重建出下图所示的二叉树并输出它的头结点。 4 | * 5 | * 1 6 | * / \ 7 | * 2 3 8 | * / / \ 9 | * 4 5 6 10 | * \ / 11 | * 7 8 12 | */ 13 | 14 | public class RebuildBinaryTree { 15 | 16 | // 普通二叉树 17 | // 1 18 | // / \ 19 | // 2 3 20 | // / / \ 21 | // 4 5 6 22 | // \ / 23 | // 7 8 24 | private static void test1() { 25 | int[] preorder = {1, 2, 4, 7, 3, 5, 6, 8}; 26 | int[] inorder = {4, 7, 2, 1, 5, 3, 8, 6}; 27 | BinaryTreeNode root = construct(preorder, inorder); 28 | printTree(root); 29 | } 30 | 31 | 32 | 33 | // 所有结点都没有右子结点 34 | // 1 35 | // / 36 | // 2 37 | // / 38 | // 3 39 | // / 40 | // 4 41 | // / 42 | // 5 43 | private static void test2() { 44 | int[] preorder = {1, 2, 3, 4, 5}; 45 | int[] inorder = {5, 4, 3, 2, 1}; 46 | BinaryTreeNode root = construct(preorder, inorder); 47 | printTree(root); 48 | } 49 | 50 | // 所有结点都没有左子结点 51 | // 1 52 | // \ 53 | // 2 54 | // \ 55 | // 3 56 | // \ 57 | // 4 58 | // \ 59 | // 5 60 | private static void test3() { 61 | int[] preorder = {1, 2, 3, 4, 5}; 62 | int[] inorder = {1, 2, 3, 4, 5}; 63 | BinaryTreeNode root = construct(preorder, inorder); 64 | printTree(root); 65 | } 66 | 67 | // 树中只有一个结点 68 | private static void test4() { 69 | int[] preorder = {1}; 70 | int[] inorder = {1}; 71 | BinaryTreeNode root = construct(preorder, inorder); 72 | printTree(root); 73 | } 74 | 75 | // 完全二叉树 76 | // 1 77 | // / \ 78 | // 2 3 79 | // / \ / \ 80 | // 4 5 6 7 81 | private static void test5() { 82 | int[] preorder = {1, 2, 4, 5, 3, 6, 7}; 83 | int[] inorder = {4, 2, 5, 1, 6, 3, 7}; 84 | BinaryTreeNode root = construct(preorder, inorder); 85 | printTree(root); 86 | } 87 | 88 | // 输入空指针 89 | private static void test6() { 90 | construct(null, null); 91 | } 92 | 93 | // 输入的两个序列不匹配 94 | private static void test7() { 95 | int[] preorder = {1, 2, 4, 5, 3, 6, 7}; 96 | int[] inorder = {4, 2, 8, 1, 6, 3, 7}; 97 | BinaryTreeNode root = construct(preorder, inorder); 98 | printTree(root); 99 | } 100 | 101 | 102 | public static void main(String[] args) { 103 | 104 | test1(); 105 | System.out.println(); 106 | test2(); 107 | System.out.println(); 108 | test3(); 109 | System.out.println(); 110 | test4(); 111 | System.out.println(); 112 | test5(); 113 | System.out.println(); 114 | test6(); 115 | System.out.println(); 116 | // test7(); 117 | 118 | } 119 | 120 | private static BinaryTreeNode construct(int[] preorder, int[] inorder) { 121 | // 输入的合法性判断,两个数组都不能为空,并且都有数据,而且数据的数目相同 122 | if (preorder == null || inorder == null || preorder.length != inorder.length || inorder.length < 1) { 123 | return null; 124 | } 125 | 126 | return construct(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1); 127 | } 128 | 129 | private static BinaryTreeNode construct(int[] preorder, int ps, int pe, int[] inorder, int is, int ie) { 130 | // 开始位置大于结束位置说明已经没有需要处理的元素了 131 | if (ps > pe) { 132 | return null; 133 | } 134 | // 取前序遍历的第一个数字,就是当前的根结点 135 | int value = preorder[ps]; 136 | int index = is; 137 | 138 | // 在中序遍历的数组中找根结点的位置 139 | while (index <= ie && inorder[index] != value) { 140 | index++; 141 | } 142 | 143 | 144 | // 如果在整个中序遍历的数组中没有找到,说明输入的参数是不合法的,抛出异常 145 | if (index > ie) { 146 | throw new RuntimeException("Invalid input"); 147 | } 148 | 149 | // 创建当前的根结点,并且为结点赋值 150 | BinaryTreeNode node = new BinaryTreeNode(); 151 | node.value = value; 152 | 153 | // 递归构建当前根结点的左子树,左子树的元素个数:index-is+1个 154 | // 左子树对应的前序遍历的位置在[ps+1, ps+index-is] 155 | // 左子树对应的中序遍历的位置在[is, index-1] 156 | node.left = construct(preorder, ps + 1, ps + index - is, inorder, is, index - 1); 157 | // 递归构建当前根结点的右子树,右子树的元素个数:ie-index个 158 | // 右子树对应的前序遍历的位置在[ps+index-is+1, pe] 159 | // 右子树对应的中序遍历的位置在[index+1, ie] 160 | node.right = construct(preorder, ps + index - is + 1, pe, inorder, index + 1, ie); 161 | 162 | // 返回创建的根结点 163 | return node; 164 | } 165 | 166 | // 中序遍历二叉树 167 | public static void printTree(BinaryTreeNode root) { 168 | if (root != null) { 169 | printTree(root.left); 170 | System.out.print(root.value + " "); 171 | printTree(root.right); 172 | } 173 | } 174 | 175 | 176 | /** 177 | * 二叉树节点类 178 | */ 179 | public static class BinaryTreeNode { 180 | int value; 181 | BinaryTreeNode left; 182 | BinaryTreeNode right; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /剑指offer/src/ReplaceBlank.java: -------------------------------------------------------------------------------- 1 | public class ReplaceBlank { 2 | 3 | public static void main(String[] args) { 4 | String str = " We are happy."; 5 | String replaceBlank = replaceBlank(str); 6 | 7 | System.out.println("replaceBlank = " + replaceBlank); 8 | } 9 | 10 | private static String replaceBlank(String str) { 11 | if (str == null || str.length() < 1) { 12 | return str; 13 | } 14 | 15 | int blackCount = 0; 16 | //获取空格的数量 17 | for (int i = 0; i < str.length(); i++) { 18 | if (str.charAt(i) == ' ') { 19 | blackCount++; 20 | } 21 | } 22 | //新的字符串长度 23 | int newLength = str.length() + blackCount * 2; 24 | int originLength = str.length(); 25 | //用于构造新字符串的 char 数组 26 | char[] newString = new char[newLength]; 27 | //拷贝字符串进数组 28 | System.arraycopy(str.toCharArray(), 0, newString, 0, originLength); 29 | //指向新数组末尾的指针 30 | int indexNew = newLength - 1; 31 | //指向原数组长度的指针 32 | int indexOrigin = originLength - 1; 33 | //从末尾遍历数组每移动一个字符将 indexOrigin 位置的 char 赋值到新长度的末尾,如果 indexOrigin 指向空格则 indexNew 开始替换为 %20 34 | //只移动 indexOrigin 35 | while (indexOrigin >= 0 && indexOrigin != indexNew) { 36 | if (newString[indexOrigin] == ' ') { 37 | newString[indexNew--] = '0'; 38 | newString[indexNew--] = '2'; 39 | newString[indexNew--] = '%'; 40 | } else { 41 | newString[indexNew] = newString[indexOrigin]; 42 | indexNew--; 43 | } 44 | 45 | indexOrigin--; 46 | } 47 | 48 | return new String(newString,0,newLength); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /剑指offer/src/SimpleStack.java: -------------------------------------------------------------------------------- 1 | import java.util.Arrays; 2 | import java.util.EmptyStackException; 3 | 4 | public class SimpleStack { 5 | //默认容量 6 | private static final int DEFAULT_CAPACITY = 10; 7 | //栈中存放元素的数组 8 | private Object[] elements; 9 | //栈中元素的个数 10 | private int size = 0; 11 | //栈顶指针 12 | private int top; 13 | 14 | 15 | public SimpleStack() { 16 | this(DEFAULT_CAPACITY); 17 | } 18 | 19 | public SimpleStack(int initialCapacity) { 20 | elements = new Object[initialCapacity]; 21 | top = -1; 22 | } 23 | 24 | public boolean isEmpty() { 25 | return size == 0; 26 | } 27 | 28 | public int size() { 29 | return size; 30 | } 31 | 32 | @SuppressWarnings("unchecked") 33 | public E pop() throws Exception { 34 | if (isEmpty()) { 35 | throw new EmptyStackException(); 36 | } 37 | 38 | E element = (E) elements[top]; 39 | elements[top--] = null; 40 | size--; 41 | return element; 42 | } 43 | 44 | @SuppressWarnings("unchecked") 45 | public E peek() throws Exception { 46 | if (isEmpty()) { 47 | throw new Exception("当前栈为空"); 48 | } 49 | return (E) elements[top]; 50 | } 51 | 52 | public void push(E element) throws Exception { 53 | //添加之前确保容量是否满足条件 54 | ensureCapacity(size + 1); 55 | elements[size++] = element; 56 | top++; 57 | } 58 | 59 | private void ensureCapacity(int minSize) { 60 | if (minSize - elements.length > 0) { 61 | grow(); 62 | } 63 | } 64 | 65 | private void grow() { 66 | int oldLength = elements.length; 67 | // 更新容量操作 扩充为原来的1.5倍 68 | int newLength = oldLength + (oldLength >> 1); 69 | elements = Arrays.copyOf(elements, newLength); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /剑指offer/剑指offer.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | --------------------------------------------------------------------------------