diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..cd3d2f4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.idea/
+.gradle/
+build/
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..78c998a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,45 @@
+# leetcode 刷题 Log
+在线刷题的 Debug 实在是太难用了,对于我们这样提交次次不过的同学来说需要顺手的 Debug 工具啦。
+
+在 test 文件下每个题目都有对应的测试用例,Run 即可运行哦。
+
+![截图](./screenshots/screen_shot.png)
+
+# 学习计划:
+每一个学习计划对应一个 Class 文件。
+例如`SwordAimingOfferI`对应`leetcode`中剑指Offer题单I,注释中对应题目的链接,点击可以跳转查看题解。
+
+# 学习笔记:
+很好,我是个金鱼,我今天刷过的题,明天就不会了。所以在 Readme 文件记录一些笔记。
+
+## 判断奇偶
+### 1、传统的利用取余进行判断
+```
+if (n % 2 == 1) {
+//n为奇数
+}
+if (n % 2 == 0) {
+//n为奇数
+}
+```
+### 2、利用位运算&进行判断
+```
+if((n & 1) == 1){
+// n 是个奇数。
+}
+if((n & 1) == 0){
+// n 是个偶数。
+}
+```
+### 3、利用异或运算^进行判断
+```
+if (1 ^ n == n - 1) {
+//n为奇数
+}
+if (1 ^ n == n + 1) {
+//n为偶数
+```
+---
+
+版权声明:本文为CSDN博主「wenyixicodedog」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
+原文链接:https://blog.csdn.net/wenyiCodeDog/article/details/105525681
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 0000000..ffc40d9
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ id("java")
+}
+
+group = "org.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation("org.junit.jupiter:junit-jupiter:5.8.1")
+ implementation("org.jetbrains:annotations:23.0.0")
+ testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
+ testImplementation("org.junit.jupiter:junit-jupiter:5.8.1")
+ testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1")
+}
+
+tasks.getByName("test") {
+ useJUnitPlatform()
+}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..249e583
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..ae04661
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..a69d9cb
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,240 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..f127cfd
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,91 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/screenshots/screen_shot.png b/screenshots/screen_shot.png
new file mode 100644
index 0000000..1c19812
Binary files /dev/null and b/screenshots/screen_shot.png differ
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 0000000..5b2a9cc
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,2 @@
+rootProject.name = "leetcode"
+
diff --git a/src/main/java/org/example/BackTrackingExercises.java b/src/main/java/org/example/BackTrackingExercises.java
new file mode 100644
index 0000000..3e0b9a1
--- /dev/null
+++ b/src/main/java/org/example/BackTrackingExercises.java
@@ -0,0 +1,96 @@
+package org.example;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 回溯练习
+ * 剑指 Offer 38. 字符串的排列
+ */
+public class BackTrackingExercises {
+ /**
+ * 剑指 Offer 38. 字符串的排列
+ * 输入一个字符串,打印出该字符串中字符的所有排列。
+ * 你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
+ * 示例:
+ * 输入:s = "abc"
+ * 输出:["abc","acb","bac","bca","cab","cba"]
+ * 限制:
+ * 1 <= s 的长度 <= 8
+ * link
+ */
+ List res = new LinkedList<>();
+ char[] c;
+
+ public String[] permutation(String s) {
+ c = s.toCharArray();
+ dfs(0);
+ return res.toArray(new String[res.size()]);
+ }
+
+ void dfs(int x) {
+ if (x == c.length - 1) {
+ res.add(String.valueOf(c)); // 添加排列方案
+ return;
+ }
+ HashSet set = new HashSet<>();
+ for (int i = x; i < c.length; i++) {
+ if (set.contains(c[i])) {
+ continue; // 重复,因此剪枝
+ }
+ set.add(c[i]);
+ swap(i, x); // 交换,将 c[i] 固定在第 x 位
+ dfs(x + 1); // 开启固定第 x + 1 位字符
+ swap(i, x); // 恢复交换
+ }
+ }
+
+ void swap(int a, int b) {
+ char tmp = c[a];
+ c[a] = c[b];
+ c[b] = tmp;
+ }
+
+ /**
+ * 该题类似于 全排列2,本题使用set来去除重复元素
+ * 除了使用set去重外,还可以对数组进行排序,使用visited数组进行剪枝!
+ */
+ Set resSet = new HashSet();
+
+ /**
+ * 剑指 Offer 38. 字符串的排列
+ * 回溯模板,但是时间复杂度很高
+ */
+ public String[] permutationBackTrackingTemplate(String s) {
+ backtrack(s.toCharArray(), new StringBuilder(), new boolean[s.length()]);
+ return resSet.toArray(new String[0]);
+ }
+
+ // 回溯函数
+ public void backtrack(char[] ch, StringBuilder sb, boolean[] visited) {
+ // 终止条件
+ if (sb.length() == ch.length) {
+ resSet.add(sb.toString());
+ return;
+ }
+ // 选择列表
+ for (int i = 0; i < ch.length; i++) {
+ // 剪枝,如果当前位置的元素已经使用过,则跳过进入下一个位置
+ if (visited[i]) continue;
+ // 做出选择
+ sb.append(ch[i]);
+ // 更新标记
+ visited[i] = true;
+ // 进入下层回溯
+ backtrack(ch, sb, visited);
+ // 撤销选择
+ sb.deleteCharAt(sb.length() - 1);
+ visited[i] = false;
+
+ }
+ }
+
+
+}
diff --git a/src/main/java/org/example/BasicDateStructureTraining.java b/src/main/java/org/example/BasicDateStructureTraining.java
new file mode 100644
index 0000000..b7a00f6
--- /dev/null
+++ b/src/main/java/org/example/BasicDateStructureTraining.java
@@ -0,0 +1,9 @@
+package org.example;
+
+/**
+ * @Desc : 数据结构练习II
+ * @Author : Liz
+ * @Date : 2023/3/22
+ */
+public class BasicDateStructureTraining {
+}
diff --git a/src/main/java/org/example/Beans/ListNode.java b/src/main/java/org/example/Beans/ListNode.java
new file mode 100644
index 0000000..dc07804
--- /dev/null
+++ b/src/main/java/org/example/Beans/ListNode.java
@@ -0,0 +1,79 @@
+package org.example.Beans;
+
+import java.util.Objects;
+
+/**
+ * @Desc : 链表的节点
+ * @Author : Liz
+ * @Date : 2023/3/22
+ */
+public class ListNode {
+ public Integer val;
+ public ListNode next;
+
+ public ListNode() {
+ }
+
+ public ListNode(int val) {
+ this.val = val;
+ }
+
+ public ListNode(ListNode node) {
+ this.val = node.val;
+ this.next = node.next;
+ }
+
+ public static ListNode initListNode(int[] numbers) {
+ if (numbers == null || numbers.length == 0) {
+ return null;
+ }
+ ListNode dummyNode = new ListNode(-1);
+ ListNode preNode = dummyNode;
+ for (int number : numbers) {
+ preNode.next = new ListNode(number);
+ preNode = preNode.next;
+ }
+ return dummyNode.next;
+ }
+
+ @Override
+ public String toString() {
+ if (val == null) {
+ return "[]";
+ } else if (next == null) {
+ return "[" + val + "]";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ ListNode tmp = new ListNode(this);
+ while (tmp.next != null) {
+ sb.append(tmp.val);
+ if (tmp.val != null) {
+ sb.append(",");
+ }
+ tmp = tmp.next;
+ }
+ sb.append(val).append("]");
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ListNode)) return false;
+ ListNode compareNode = (ListNode) obj;
+ return recurCompare(compareNode);
+ }
+
+ private boolean recurCompare(ListNode node) {
+ if (node == null && this.val == null) return true;
+ if (node == null) return false;
+ if (Objects.equals(node.val, this.val)) {
+ if (node.next != null) {
+ recurCompare(node.next);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/org/example/Beans/TreeNode.java b/src/main/java/org/example/Beans/TreeNode.java
new file mode 100644
index 0000000..54a3819
--- /dev/null
+++ b/src/main/java/org/example/Beans/TreeNode.java
@@ -0,0 +1,69 @@
+package org.example.Beans;
+
+import java.util.LinkedList;
+import java.util.Objects;
+
+public class TreeNode {
+ public Integer val;
+ public TreeNode left;
+ public TreeNode right;
+
+ public TreeNode() {
+ }
+
+ public TreeNode(int val) {
+ this.val = val;
+ }
+
+ //TODO: 目前不支持 Null TreeNode
+ public static TreeNode initTreeNode(Integer[] array) {
+ if (array.length == 0) return new TreeNode();
+ LinkedList queue = new LinkedList<>();
+ TreeNode root = new TreeNode(array[0]);
+ queue.offer(root);
+ int n = array.length - 1;
+ for (int i = 1; i < n; i = i + 2) {
+ TreeNode nodeLeft = new TreeNode(array[i]);
+ if (n - i >= 2) {
+ TreeNode nodeRight = new TreeNode(array[i + 1]);
+ if (!queue.isEmpty()) {
+ TreeNode rootChild = queue.poll();
+ rootChild.left = nodeLeft;
+ queue.offer(nodeLeft);
+ rootChild.right = nodeRight;
+ queue.offer(nodeRight);
+ }
+ } else {
+ if (!queue.isEmpty()) {
+ TreeNode rootChild = queue.poll();
+ rootChild.left = nodeLeft;
+ queue.offer(nodeLeft);
+ }
+ }
+ }
+ return root;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof TreeNode)) return false;
+ TreeNode compareToTree = (TreeNode) obj;
+ return dfsCompare(compareToTree);
+ }
+
+ private boolean dfsCompare(TreeNode root) {
+ if (root == null && this.val == null) return true;
+ if (root == null) return false;
+ if (Objects.equals(root.val, this.val)) {
+ if (root.left != null) {
+ dfsCompare(root.left);
+ }
+ if (root.right != null) {
+ dfsCompare(root.right);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/org/example/ByteDanceInterviewI.java b/src/main/java/org/example/ByteDanceInterviewI.java
new file mode 100644
index 0000000..a92c3ab
--- /dev/null
+++ b/src/main/java/org/example/ByteDanceInterviewI.java
@@ -0,0 +1,54 @@
+package org.example;
+
+/**
+ * 字节跳动春招题单
+ * 121. 买卖股票的最佳时机
+ * 213. 打家劫舍 II
+ */
+public class ByteDanceInterviewI {
+ /**
+ * 打家劫舍 II
+ * 你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。
+ * 同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
+ * 给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
+ * 示例 1:
+ * 输入:nums = [2,3,2]
+ * 输出:3
+ * 解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
+ * 示例 2:
+ * 输入:nums = [1,2,3,1]
+ * 输出:4
+ * 解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
+ * 偷窃到的最高金额 = 1 + 3 = 4 。
+ * 示例 3:
+ * 输入:nums = [1,2,3]
+ * 输出:3
+ * 提示:
+ * 1 <= nums.length <= 100
+ * 0 <= nums[i] <= 1000
+ * 链接
+ */
+ public int rob(int[] nums) {
+ if (nums.length == 1) {
+ return nums[0];
+ }
+ if (nums.length == 2) {
+ return Math.max(nums[0], nums[1]);
+ }
+ return Math.max(
+ robRange(nums, 0, nums.length - 2),
+ robRange(nums, 1, nums.length - 1)
+ );
+ }
+
+ private int robRange(int[] nums, int start, int end) {
+ int first = nums[start], second = Math.max(nums[start], nums[start + 1]);
+ for (int i = start + 2; i <= end; i++) {
+ int temp = second;
+ second = Math.max(first + nums[i], second);
+ first = temp;
+ }
+ return second;
+ }
+
+}
diff --git a/src/main/java/org/example/CodingAblityTraining.java b/src/main/java/org/example/CodingAblityTraining.java
new file mode 100644
index 0000000..89d8a05
--- /dev/null
+++ b/src/main/java/org/example/CodingAblityTraining.java
@@ -0,0 +1,9 @@
+package org.example;
+
+/**
+ * @Desc : 编程能力
+ * @Author : Liz
+ * @Date : 2023/3/22
+ */
+public class CodingAblityTraining {
+}
diff --git a/src/main/java/org/example/Main.java b/src/main/java/org/example/Main.java
new file mode 100644
index 0000000..29e92ff
--- /dev/null
+++ b/src/main/java/org/example/Main.java
@@ -0,0 +1,9 @@
+package org.example;
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("Hello world!");
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/example/PrefixSumExercises.java b/src/main/java/org/example/PrefixSumExercises.java
new file mode 100644
index 0000000..9972eb1
--- /dev/null
+++ b/src/main/java/org/example/PrefixSumExercises.java
@@ -0,0 +1,167 @@
+package org.example;
+
+import java.util.HashMap;
+
+/**
+ * 前缀和练习
+ * ✔ 1480. 一维数组的动态和
+ * ✔ 560. 和为 K 的子数组
+ * 209. 长度最小的子数组
+ * ✔ 面试题 17.05. 字母与数字
+ * 930. 和相同的二元子数组
+ * 974. 和可被 K 整除的子数组
+ * 1590. 使数组和能被 P 整除
+ * 523. 连续的子数组和
+ * 525. 连续数组
+ * 724. 寻找数组的中心索引
+ * 1248. 统计「优美子数组」
+ */
+public class PrefixSumExercises {
+ /**
+ * 1480. 一维数组的动态和
+ * 给你一个数组 nums 。数组「动态和」的计算公式为:runningSum[i] = sum(nums[0]…nums[i]) 。
+ * 请返回 nums 的动态和。
+ * 示例 1:
+ * 输入:nums = [1,2,3,4]
+ * 输出:[1,3,6,10]
+ * 解释:动态和计算过程为 [1, 1+2, 1+2+3, 1+2+3+4] 。
+ * 示例 2:
+ * 输入:nums = [1,1,1,1,1]
+ * 输出:[1,2,3,4,5]
+ * 解释:动态和计算过程为 [1, 1+1, 1+1+1, 1+1+1+1, 1+1+1+1+1] 。
+ * 示例 3:
+ * 输入:nums = [3,1,2,10,1]
+ * 输出:[3,4,6,16,17]
+ * 提示:
+ * 1 <= nums.length <= 1000
+ * -10^6 <= nums[i] <= 10^6
+ */
+ public int[] runningSum(int[] nums) {
+ for (int i = 1; i < nums.length; i++) {
+ nums[i] += nums[i - 1];
+ }
+ return nums;
+ }
+
+ /**
+ * 面试题 17.05.字母与数字
+ * 给定一个放有字母和数字的数组,找到最长的子数组,且包含的字母和数字的个数相同。
+ * 返回该子数组,若存在多个最长子数组,返回左端点下标值最小的子数组。若不存在这样的数组,返回一个空数组。
+ * 示例 1:
+ * 输入: ["A","1","B","C","D","2","3","4","E","5","F","G","6","7","H","I","J","K","L","M"]
+ * 输出: ["A","1","B","C","D","2","3","4","E","5","F","G","6","7"]
+ * 示例 2:
+ * 输入: ["A","A"]
+ * 输出: []
+ * array.length <= 100000
+ * 链接:面试题 17.05.字母与数字
+ */
+ public String[] findLongestSubArray(String[] array) {
+ int n = array.length;
+ int[] s = new int[n + 1]; // 前缀和
+ for (int i = 0; i < n; ++i)
+ s[i + 1] = s[i] + (array[i].charAt(0) >> 6 & 1) * 2 - 1;
+
+ int begin = 0, end = 0; // 符合要求的子数组 [begin,end)
+ HashMap first = new HashMap();
+ for (int i = 0; i <= n; ++i) {
+ int j = first.getOrDefault(s[i], -1);
+ if (j < 0) // 首次遇到 s[i]
+ first.put(s[i], i);
+ else if (i - j > end - begin) { // 更长的子数组
+ begin = j;
+ end = i;
+ }
+ }
+ String[] sub = new String[end - begin];
+ System.arraycopy(array, begin, sub, 0, sub.length);
+ return sub;
+ }
+
+ /**
+ * 560. 连续和为K的子数组
+ * 给你一个整数数组 nums 和一个整数 k ,请你统计并返回该数组中和为 k 的连续子数组的个数。
+ * 示例 1:
+ * 输入:nums = [1,1,1], k = 2
+ * 输出:2
+ * 示例 2:
+ * 输入:nums = [1,2,3], k = 3
+ * 输出:2
+ * 提示:
+ * 1 <= nums.length <= 2 * 104
+ * -1000 <= nums[i] <= 1000
+ * -107 <= k <= 107
+ */
+ public int subArraySumEnum(int[] nums, int k) {
+ int count = 0;
+ for (int start = 0; start < nums.length; ++start) {
+ int sum = 0;
+ for (int end = start; end >= 0; --end) {
+ sum += nums[end];
+ if (sum == k) {
+ count++;
+ }
+ }
+ }
+ return count;
+ }
+
+ public int subArraySum(int[] sum, int k) {
+ int n = sum.length;
+ int[] prefixSum = new int[sum.length + 1];
+ for (int i = 0; i < n; i++) {
+ prefixSum[i + 1] = prefixSum[i] + sum[i];
+ }
+ int count = 0;
+ for (int i = 0; i < n; i++) {
+ for (int j = i; j < n; j++) {
+ //这里因为前缀和数组比原数组长1, 所以是 j+1
+ if (prefixSum[j + 1] - prefixSum[i] == k) {
+ count++;
+ }
+ }
+ }
+ return count;
+ }
+
+ public int subArraySumHash(int[] nums, int k) {
+ int count = 0, preSum = 0;
+ HashMap preSumFreq = new HashMap<>();
+ preSumFreq.put(0, 1);
+ for (int num : nums) {
+ preSum += num;
+ if (preSumFreq.containsKey(preSum - k)) {
+ count += preSumFreq.get(preSum - k);
+ }
+ preSumFreq.put(preSum, preSumFreq.getOrDefault(preSum, 0) + 1);
+ }
+ return count;
+ }
+
+ /**
+ * 209. 长度最小的子数组
+ * 给定一个含有n个正整数的数组和一个正整数 target 。
+ * 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组[numsl, numsl+1, ..., numsr-1, numsr] ,
+ * 并返回其长度。如果不存在符合条件的子数组,返回 0 。
+ * 示例 1:
+ * 输入:target = 7, nums = [2,3,1,2,4,3]
+ * 输出:2
+ * 解释:子数组[4,3]是该条件下的长度最小的子数组。
+ * 示例 2:
+ * 输入:target = 4, nums = [1,4,4]
+ * 输出:1
+ * 示例 3:
+ * 输入:target = 11, nums = [1,1,1,1,1,1,1,1]
+ * 输出:0
+ * 提示:
+ * 1 <= target <= 109
+ * 1 <= nums.length <= 105
+ * 1 <= nums[i] <= 105
+ * 进阶:
+ * 如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。
+ */
+ public int minSubArrayLen(int target, int[] nums) {
+ return 0;
+ }
+
+}
diff --git a/src/main/java/org/example/SwordAimingOfferI.java b/src/main/java/org/example/SwordAimingOfferI.java
new file mode 100644
index 0000000..a13c5ab
--- /dev/null
+++ b/src/main/java/org/example/SwordAimingOfferI.java
@@ -0,0 +1,383 @@
+package org.example;
+
+
+import org.example.Beans.ListNode;
+import org.example.Beans.TreeNode;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * 剑指Offer I
+ * 剑指 Offer 26. 树的子结构
+ * 剑指 Offer 27. 二叉树的镜像
+ * 剑指 Offer 28. 对称的二叉树
+ * 剑指 Offer 10- I. 斐波那契数列
+ * 剑指 Offer 10- II. 青蛙跳台阶问题
+ * 剑指 Offer 63. 股票的最大利润
+ * × 剑指 Offer 42. 连续子数组的最大和
+ * 剑指 Offer 47. 礼物的最大价值
+ * 剑指 Offer 46. 把数字翻译成字符串
+ * 剑指 Offer 48. 最长不含重复字符的子字符串
+ * 剑指 Offer 18. 删除链表的节点
+ * 剑指 Offer 22. 链表中倒数第k个节点
+ */
+public class SwordAimingOfferI {
+ /**
+ * 剑指 Offer 26. 树的子结构
+ * 输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
+ * B是A的子结构, 即 A中有出现和B相同的结构和节点值。
+ * 示例 1:
+ * 输入:A = [1,2,3], B = [3,1]
+ * 输出:false
+ * 示例 2:
+ * 输入:A = [3,4,5,1,2], B = [4,1]
+ * 输出:true
+ * 限制:
+ * 0 <= 节点个数 <= 10000
+ * link
+ */
+ public boolean isSubStructureBFS(TreeNode A, TreeNode B) {
+ if (A == null || B == null) return false;
+ LinkedList queue = new LinkedList<>();
+ queue.offer(A);
+ while (!queue.isEmpty()) {
+ TreeNode node = queue.poll();
+ if (node.val == B.val) {
+ if (match(node, B)) {
+ return true;
+ }
+ }
+ if (node.left != null) {
+ queue.add(node.left);
+ }
+ if (node.right != null) {
+ queue.add(node.right);
+ }
+ }
+ return false;
+ }
+
+ private boolean match(TreeNode A, TreeNode B) {
+ LinkedList queueA = new LinkedList<>();
+ LinkedList queueB = new LinkedList<>();
+ queueA.offer(A);
+ queueB.offer(B);
+ while (!queueB.isEmpty()) {
+ TreeNode b = queueB.poll();
+ TreeNode a = queueA.poll();
+ if (a == null || b.val != a.val) {
+ return false;
+ }
+ if (b.left != null) {
+ queueB.add(b.left);
+ queueA.add(a.left);
+ }
+ if (b.right != null) {
+ queueB.add(b.right);
+ queueA.add(a.right);
+ }
+ }
+ return true;
+ }
+
+ public boolean isSubStructureDFS(TreeNode A, TreeNode B) {
+ return (A != null && B != null) && (dfs(A, B) || isSubStructureDFS(A.right, B) || isSubStructureDFS(A.left, B));
+ }
+
+ private boolean dfs(TreeNode A, TreeNode B) {
+ if (B == null) return true;
+ if (A == null || A.val != B.val) return false;
+ return dfs(A.left, B.left) && dfs(A.right, B.right);
+ }
+
+ /**
+ * 剑指 Offer 27. 二叉树的镜像
+ * 请完成一个函数,输入一个二叉树,该函数输出它的镜像。
+ * 示例 1:
+ * 输入:root = [4,2,7,1,3,6,9]
+ * 输出:[4,7,2,9,6,3,1]
+ * 限制:
+ * 0 <= 节点个数 <= 1000
+ */
+ public TreeNode mirrorTree(TreeNode root) {
+ if (root == null) {
+ return null;
+ }
+ TreeNode left = mirrorTree(root.left);
+ TreeNode right = mirrorTree(root.right);
+ root.right = left;
+ root.left = right;
+ return root;
+ }
+
+ /**
+ * 剑指 Offer 28. 对称的二叉树
+ * 请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
+ * 示例 1:
+ * 输入:root = [1,2,2,3,4,4,3]
+ * 输出:true
+ * 示例 2:
+ * 输入:root = [1,2,2,null,3,null,3]
+ * 输出:false
+ * 限制:
+ * 0 <= 节点个数 <= 1000
+ * 链接:link
+ */
+ public boolean isSymmetric(TreeNode root) {
+ return root == null ? true : recur(root.left, root.right);
+ }
+
+ boolean recur(TreeNode L, TreeNode R) {
+ if (L == null && R == null) return true;
+ if (L == null || R == null || L.val != R.val) return false;
+ return recur(L.left, R.right) && recur(L.right, R.left);
+ }
+
+ /**
+ * 剑指 Offer 10- I. 斐波那契数列
+ * 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
+ * F(0) = 0, F(1)= 1
+ * F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
+ * 斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
+ * 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
+ * 示例 1:
+ * 输入:n = 2
+ * 输出:1
+ * 示例 2:
+ * 输入:n = 5
+ * 输出:5
+ * 提示:
+ * 0 <= n <= 100
+ * 链接:click
+ */
+ public int fib(int n) {
+ int a = 0, b = 1, sum;
+ for (int i = 0; i < n; i++) {
+ sum = (a + b) % 1000000007;
+ a = b;
+ b = sum;
+ }
+ return a;
+ }
+
+ /**
+ * 剑指 Offer 10- II. 青蛙跳台阶问题
+ * 一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
+ * 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
+ * 示例 1:
+ * 输入:n = 2
+ * 输出:2
+ * 示例 2:
+ * 输入:n = 7
+ * 输出:21
+ * 示例 3:
+ * 输入:n = 0
+ * 输出:1
+ * 提示:
+ * 0 <= n <= 100
+ * 链接:click
+ * tips: 滚动数组
+ */
+ public int numWays(int n) {
+ int a = 1, b = 1, sum;
+ for (int i = 0; i < n; i++) {
+ sum = (a + b) % 1000000007;
+ a = b;
+ b = sum;
+ }
+ return a;
+ }
+
+ public int numWaysDp(int n) {
+ int[] dp = new int[n + 1];
+ if (n <= 1) return 1;
+ dp[1] = 1;
+ dp[2] = 2;
+ for (int i = 3; i <= n; i++) {
+ dp[i] = (dp[i - 1] + dp[i - 2]) % 1000000007;
+ }
+ return dp[n];
+ }
+
+ /**
+ * 剑指 Offer 63. 股票的最大利润
+ * 假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?
+ * 示例 1:
+ * 输入: [7,1,5,3,6,4]
+ * 输出: 5
+ * 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
+ * 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
+ * 示例 2:
+ * 输入: [7,6,4,3,1]
+ * 输出: 0
+ * 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
+ * 限制:
+ * 0 <= 数组长度 <= 10^5
+ * 链接:click
+ */
+ public int maxProfit(int[] prices) {
+ int min = Integer.MAX_VALUE;
+ int maxProfit = 0;
+ for (int price : prices) {
+ if (min > price) {
+ min = price;
+ } else {
+ maxProfit = Math.max(maxProfit, price - min);
+ }
+ }
+ return maxProfit;
+ }
+
+ /**
+ * 剑指 Offer 47. 礼物的最大价值
+ * 在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。
+ * 你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。
+ * 给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
+ * 示例 1:
+ * 输入:
+ * [
+ * [1,3,1],
+ * [1,5,1],
+ * [4,2,1]
+ * ]
+ * 输出: 12
+ * 解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物
+ * 提示:
+ * 0 < grid.length <= 200
+ * 0 < grid[0].length <= 200
+ * 链接:click
+ */
+ public int maxValue(int[][] grid) {
+ //空间复杂度O(1)
+// int m = grid.length, n = grid[0].length;
+// for (int i = 0; i < m; i++) {
+// for (int j = 0; j < n; j++) {
+// if (i == 0 && j == 0) continue;
+// if (i == 0) grid[i][j] += grid[i][j - 1];
+// else if (j == 0) grid[i][j] += grid[i - 1][j];
+// else grid[i][j] += Math.max(grid[i][j - 1], grid[i - 1][j]);
+// }
+// }
+// return grid[m - 1][n - 1];
+ int row = grid.length;
+ int col = grid[0].length;
+ int[][] dp = new int[row + 1][col + 1];
+ for (int i = 1; i <= row; i++) {
+ for (int j = 1; j <= col; j++) {
+ dp[i][j] = grid[i - 1][j - 1] + Math.max(dp[i - 1][j], dp[i][j - 1]);
+ }
+ }
+ return dp[row][col];
+ }
+
+ /**
+ * 剑指 Offer 46. 把数字翻译成字符串
+ * 给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。
+ * 请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
+ * 示例 1:
+ * 输入: 12258
+ * 输出: 5
+ * 解释: 12258有5种不同的翻译,分别是"bccfi", "bwfi", "bczi", "mcfi"和"mzi"
+ * 提示:
+ * 0 <= num < 231
+ * 链接:click
+ * 分析:有点像青蛙跳台阶,当数字在 [10,25] 的区间内有两种翻译方法,并且要注意以数字 0 开头的两位数无法翻译。
+ */
+ public int translateNum(int num) {
+ //dp[0]=1,dp[1]=1, 使用滚动数组
+ int a = 1, b = 1;
+ String s = String.valueOf(num);
+ for (int i = 2; i <= s.length(); i++) {
+ String tmp = s.substring(i - 2, i);
+ int c = tmp.compareTo("10") >= 0 || tmp.compareTo("25") <= 0 ? a + b : a;
+ b = a;
+ a = c;
+ }
+ return a;
+ }
+
+ /**
+ * 剑指 Offer 48. 最长不含重复字符的子字符串
+ * 请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
+ * 示例1:
+ * 输入: "abcabcbb"
+ * 输出: 3
+ * 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
+ * 示例 2:
+ * 输入: "bbbbb"
+ * 输出: 1
+ * 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
+ * 示例 3:
+ * 输入: "pwwkew"
+ * 输出: 3
+ * 解释: 因为无重复字符的最长子串是"wke",所以其长度为 3。
+ * 请注意,你的答案必须是 子串 的长度,"pwke"是一个子序列,不是子串。
+ * 提示:
+ * s.length <= 40000
+ * 链接:click
+ */
+ public int lengthOfLongestSubstring(String s) {
+ Map dic = new HashMap<>();
+ int i = -1, res = 0;
+ for (int j = 0; j < s.length(); j++) {
+ if (dic.containsKey(s.charAt(j)))
+ i = Math.max(i, dic.get(s.charAt(j))); // 更新左指针 i
+ dic.put(s.charAt(j), j); // 哈希表记录
+ res = Math.max(res, j - i); // 更新结果
+ }
+ return res;
+ }
+
+ /**
+ * 给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
+ * 返回删除后的链表的头节点。
+ * 注意:此题对比原题有改动
+ * 示例 1:
+ * 输入: head = [4,5,1,9], val = 5
+ * 输出: [4,1,9]
+ * 解释: 给定你链表中值为5的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
+ * 示例 2:
+ * 输入: head = [4,5,1,9], val = 1
+ * 输出: [4,5,9]
+ * 解释: 给定你链表中值为1的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
+ * 说明:
+ * 题目保证链表中节点的值互不相同
+ * 若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点
+ * 链接:click
+ */
+ public ListNode deleteNode(ListNode head, int val) {
+ if (head.val == val) return head.next;
+ ListNode pre = head, cur = head.next;
+ while (cur != null && cur.val != val) {
+ pre = cur;
+ cur = cur.next;
+ }
+ if (cur != null) pre.next = cur.next;
+ return head;
+
+ }
+
+ /**
+ * 剑指 Offer 22. 链表中倒数第k个节点
+ * 输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
+ * 例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
+ * 示例:
+ * 给定一个链表: 1->2->3->4->5, 和 k = 2.
+ * 返回链表 4->5.
+ * 链接:click
+ * tags : 双指针、链表
+ */
+ public ListNode getKthFromEnd(ListNode head, int k) {
+ ListNode former = head, latter = head;
+ for (int i = 0; i < k; i++) {
+ former = former.next;
+ }
+ while (former != null) {
+ latter = latter.next;
+ former = former.next;
+ }
+ return latter;
+ }
+}
diff --git a/src/main/java/org/example/TheDiningPhilosophers.java b/src/main/java/org/example/TheDiningPhilosophers.java
new file mode 100644
index 0000000..506bf8d
--- /dev/null
+++ b/src/main/java/org/example/TheDiningPhilosophers.java
@@ -0,0 +1,80 @@
+package org.example;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * 1226. 哲学家进餐
+ * 5 个沉默寡言的哲学家围坐在圆桌前,每人面前一盘意面。叉子放在哲学家之间的桌面上。(5 个哲学家,5 根叉子)
+ * 所有的哲学家都只会在思考和进餐两种行为间交替。哲学家只有同时拿到左边和右边的叉子才能吃到面,而同一根叉子在同一时间只能被一个哲学家使用。
+ * 每个哲学家吃完面后都需要把叉子放回桌面以供其他哲学家吃面。只要条件允许,哲学家可以拿起左边或者右边的叉子,但在没有同时拿到左右叉子时不能进食。
+ * 假设面的数量没有限制,哲学家也能随便吃,不需要考虑吃不吃得下。
+ * 设计一个进餐规则(并行算法)使得每个哲学家都不会挨饿;也就是说,在没有人知道别人什么时候想吃东西或思考的情况下,每个哲学家都可以在吃饭和思考之间一直交替下去。
+ * 哲学家从0 到 4 按 顺时针 编号。请实现函数void wantsToEat(philosopher, pickLeftFork, pickRightFork, eat, putLeftFork, putRightFork):
+ * philosopher 哲学家的编号。
+ * pickLeftFork 和 pickRightFork 表示拿起左边或右边的叉子。
+ * eat 表示吃面。
+ * putLeftFork 和 putRightFork 表示放下左边或右边的叉子。
+ * 由于哲学家不是在吃面就是在想着啥时候吃面,所以思考这个方法没有对应的回调。
+ * 给你 5 个线程,每个都代表一个哲学家,请你使用类的同一个对象来模拟这个过程。在最后一次调用结束之前,可能会为同一个哲学家多次调用该函数。
+ * 示例:
+ * 输入:n = 1
+ * 输出:[[4,2,1],[4,1,1],[0,1,1],[2,2,1],[2,1,1],[2,0,3],[2,1,2],[2,2,2],[4,0,3],[4,1,2],[0,2,1],[4,2,2],[3,2,1],[3,1,1],
+ * [0,0,3],[0,1,2],[0,2,2],[1,2,1],[1,1,1],[3,0,3],[3,1,2],[3,2,2],[1,0,3],[1,1,2],[1,2,2]]
+ * 解释:
+ * n 表示每个哲学家需要进餐的次数。
+ * 输出数组描述了叉子的控制和进餐的调用,它的格式如下:
+ * output[i] = [a, b, c] (3个整数)
+ * - a 哲学家编号。
+ * - b 指定叉子:{1 : 左边, 2 : 右边}.
+ * - c 指定行为:{1 : 拿起, 2 : 放下, 3 : 吃面}。
+ * 如 [4,2,1] 表示 4 号哲学家拿起了右边的叉子。
+ * 提示:
+ * 1 <= n <= 60
+ * 链接
+ */
+public class TheDiningPhilosophers {
+ private final ReentrantLock[] lockList = {
+ new ReentrantLock(),
+ new ReentrantLock(),
+ new ReentrantLock(),
+ new ReentrantLock(),
+ new ReentrantLock()
+ };
+ //限制最多4个哲学家持有叉子
+ private Semaphore eatLimit = new Semaphore(4);
+
+ public TheDiningPhilosophers(int id, int forkStatus, int action) {
+ }
+
+ // call the run() method of any runnable to execute its code
+ public void wantsToEat(int philosopher,
+ Runnable pickLeftFork,
+ Runnable pickRightFork,
+ Runnable eat,
+ Runnable putLeftFork,
+ Runnable putRightFork) throws InterruptedException {
+ int leftFork = (philosopher + 1) % 5;
+ int rightFork = philosopher;
+
+ eatLimit.acquire();
+
+ lockList[leftFork].lock();
+ lockList[rightFork].lock();
+
+ pickLeftFork.run();
+ pickRightFork.run();
+
+ eat.run();
+
+ putLeftFork.run();
+ putRightFork.run();
+
+ lockList[leftFork].unlock();
+ lockList[rightFork].unlock();
+
+ eatLimit.release();
+ }
+
+
+}
diff --git a/src/main/java/org/example/XiaoMiSpringInterviewI.java b/src/main/java/org/example/XiaoMiSpringInterviewI.java
new file mode 100644
index 0000000..b209084
--- /dev/null
+++ b/src/main/java/org/example/XiaoMiSpringInterviewI.java
@@ -0,0 +1,289 @@
+package org.example;
+
+import java.util.*;
+
+/**
+ * 小米春招备战题单
+ * 1.数组、双指针、排序
+ * 88.合并两个有序数组
+ * 15. 三数之和
+ * 242. 有效的字母异位词
+ * 3. 无重复字符的最长子串 (双指针的类型一:同向双指针,类似题目: 713. 乘积小于 K 的子数组,长度最小的子数组)
+ * 17. 电话号码的字母组合
+ * 231. 2 的幂
+ * 78. 子集
+ */
+public class XiaoMiSpringInterviewI {
+ /**
+ * 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,
+ * 同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
+ * 注意:答案中不可以包含重复的三元组。
+ * 示例 1:
+ * 输入:nums = [-1,0,1,2,-1,-4]
+ * 输出:[[-1,-1,2],[-1,0,1]]
+ * 解释:
+ * nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
+ * nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
+ * nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
+ * 不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
+ * 注意,输出的顺序和三元组的顺序并不重要。
+ * 示例 2:
+ * 输入:nums = [0,1,1]
+ * 输出:[]
+ * 解释:唯一可能的三元组和不为 0 。
+ * 示例 3:
+ * 输入:nums = [0,0,0]
+ * 输出:[[0,0,0]]
+ * 解释:唯一可能的三元组和为 0 。
+ * 提示:
+ * 3 <= nums.length <= 3000
+ * -10^5 <= nums[i] <= 10^5
+ * 链接:15. 三数之和
+ */
+ public List> threeSum(int[] nums) {
+ List> lists = new ArrayList<>();
+ //排序 O=nlogn
+ Arrays.sort(nums);
+ //双指针
+ int len = nums.length;
+ for (int i = 0; i < len; ++i) {
+ if (nums[i] > 0) return lists;
+ if (i > 0 && nums[i] == nums[i - 1]) continue;
+ int curr = nums[i];
+ int L = i + 1, R = len - 1;
+ while (L < R) {
+ int tmp = curr + nums[L] + nums[R];
+ if (tmp == 0) {
+ List list = new ArrayList<>();
+ list.add(curr);
+ list.add(nums[L]);
+ list.add(nums[R]);
+ lists.add(list);
+ while (L < R && nums[L + 1] == nums[L]) ++L;
+ while (L < R && nums[R - 1] == nums[R]) --R;
+ ++L;
+ --R;
+ } else if (tmp < 0) {
+ ++L;
+ } else {
+ --R;
+ }
+ }
+ }
+ return lists;
+ }
+
+ /**
+ * 242. 有效的字母异位词
+ * 链接
+ * 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
+ * 注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
+ * 示例1:
+ * 输入: s = "anagram", t = "nagaram"
+ * 输出: true
+ * 示例 2:
+ * 输入: s = "rat", t = "car"
+ * 输出: false
+ * 提示:
+ * 1 <= s.length, t.length <= 5 * 104
+ * s 和 t 仅包含小写字母
+ * 进阶:如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?
+ */
+ public boolean isAnagram(String s, String t) {
+ if (s.length() != t.length()) return false;
+ int[] alpha = new int[26];
+ for (int i = 0; i < s.length(); i++) {
+ alpha[s.charAt(i) - 'a']++;
+ alpha[t.charAt(i) - 'a']--;
+ }
+ for (int i = 0; i < s.length(); i++) {
+ if (alpha[i] != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。
+ * 链接
+ * 示例1:
+ * 输入: s = "abcabcbb"
+ * 输出: 3
+ * 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
+ * 示例 2:
+ * 输入: s = "bbbbb"
+ * 输出: 1
+ * 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
+ * 示例 3:
+ * 输入: s = "pwwkew"
+ * 输出: 3
+ * 解释: 因为无重复字符的最长子串是"wke",所以其长度为 3。
+ * 请注意,你的答案必须是 子串 的长度,"pwke"是一个子序列,不是子串。
+ * 提示:
+ * 0 <= s.length <= 5 * 104
+ * s由英文字母、数字、符号和空格组成
+ * 类似题目:
+ * 713.乘积小于 K 的子数组 乘积小于 K 的子数组
+ * 209.长度最小的子数组 长度最小的子数组
+ */
+ public int lengthOfLongestSubstring(String s) {
+ int n = s.length();
+ int right = 0;
+ int ans = 0;
+ HashSet window = new HashSet<>();
+ for (int i = 0; i < n; i++) {
+ if (i != 0) {
+ window.remove(s.charAt(i - 1));
+ }
+ while (right < n && !window.contains(s.charAt(right))) {
+ window.add(s.charAt(right));
+ right++;
+ }
+ ans = Math.max(ans, right - i);
+ }
+ return ans;
+ }
+
+ /**
+ * 17. 电话号码的字母组合
+ * 链接
+ * 考点:回溯
+ * 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
+ * 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
+ * 示例 1:
+ * 输入:digits = "23"
+ * 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
+ * 示例 2:
+ * 输入:digits = ""
+ * 输出:[]
+ * 示例 3:
+ * 输入:digits = "2"
+ * 输出:["a","b","c"]
+ * 提示:
+ * 0 <= digits.length <= 4
+ * digits[i] 是范围 ['2', '9'] 的一个数字。
+ */
+ public List letterCombinations(String digits) {
+ List ans = new ArrayList<>();
+ if (digits.length() == 0) return ans;
+ HashMap dict = new HashMap<>();
+ dict.put('2', "abc");
+ dict.put('3', "def");
+ dict.put('4', "ghi");
+ dict.put('5', "jkl");
+ dict.put('6', "mno");
+ dict.put('7', "pqrs");
+ dict.put('8', "tuv");
+ dict.put('9', "wxyz");
+ backtrack(ans, dict, digits, 0, new StringBuffer());
+ return ans;
+ }
+
+ /**
+ * 回溯三问
+ * 1. 当前操作?枚举`path[i]`要填入的字母
+ * 2. 子问题?构造字符串`>=i`的部分
+ * 3. 下一个子问题?构造字符串`>=i+1`的部分
+ * ---------------------------------------
+ * 17. 电话号码的字母组合 https://leetcode.cn/problems/letter-combinations-of-a-phone-number/solutions/2059416/hui-su-bu-hui-xie-tao-lu-zai-ci-pythonja-3orv/
+ * 78. 子集 https://leetcode.cn/problems/subsets/solutions/2059409/hui-su-bu-hui-xie-tao-lu-zai-ci-pythonja-8tkl/
+ * 131. 分割回文串 https://leetcode.cn/problems/palindrome-partitioning/solutions/2059414/hui-su-bu-hui-xie-tao-lu-zai-ci-pythonja-fues/
+ * 784. 字母大小写全排列 https://leetcode.cn/problems/letter-case-permutation/
+ * 1601. 最多可达成的换楼请求数目 https://leetcode.cn/problems/maximum-number-of-achievable-transfer-requests/
+ * 2397. 被列覆盖的最多行数 https://leetcode.cn/problems/maximum-rows-covered-by-columns/
+ */
+ private void backtrack(List ans, HashMap dict, String digits, int path, StringBuffer combination) {
+ if (path == digits.length()) {
+ ans.add(combination.toString());
+ } else {
+ char digit = digits.charAt(path);
+ String letters = dict.get(digit);
+ int lettersCount = letters.length();
+ for (int i = 0; i < lettersCount; i++) {
+ combination.append(letters.charAt(i));
+ //在path后面添加元素,所以在递归之前什么样,之后就要`恢复现场`
+ //path表示路径上的数字。比如枚举1,然后枚举2,1和2在不同的分支上,那么必然不会在同一条路径中。先递归1,把1加到path中,
+ // 然后递归2,如果不把1去掉的话,1和2就都在path中,这就错了,期望是path中只有2,那么把1去掉把2加进去,就可以满足要求了。
+ backtrack(ans, dict, digits, path + 1, combination);
+ //这里`恢复现场`
+ combination.deleteCharAt(path);
+ }
+ }
+ }
+
+ /**
+ * 231. 2 的幂
+ * 链接
+ * 给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。
+ * 如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。
+ * 示例 1:
+ * 输入:n = 1
+ * 输出:true
+ * 解释:2^0 = 1
+ * 示例 2:
+ * 输入:n = 16
+ * 输出:true
+ * 解释:2^4 = 16
+ * 示例 3:
+ * 输入:n = 3
+ * 输出:false
+ * 示例 4:
+ * 输入:n = 4
+ * 输出:true
+ * 示例 5:
+ * 输入:n = 5
+ * 输出:false
+ * 提示:
+ * -231 <= n <= 231 - 1
+ * 进阶:你能够不使用循环/递归解决此问题吗?
+ */
+ public boolean isPowerOfTwo(int n) {
+ //方法一
+ return n > 0 && (n & (n - 1)) == 0;
+ //方法二
+ //return n > 0 && (n & -n) == n;
+ }
+
+ /**
+ * 78. 子集
+ * 链接
+ * 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
+ * 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
+ * 示例 1:
+ * 输入:nums = [1,2,3]
+ * 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
+ * 示例 2:
+ * 输入:nums = [0]
+ * 输出:[[],[0]]
+ * 提示:
+ * 1 <= nums.length <= 10
+ * -10 <= nums[i] <= 10
+ * nums 中的所有元素 互不相同
+ */
+ public List> subsets(int[] nums) {
+ this.nums = nums;
+ dfs(0);
+ return ans;
+ }
+
+ List> ans = new ArrayList<>();
+ List path = new ArrayList<>();
+ int[] nums;
+
+ private void dfs(int i) {
+ if (i == nums.length) {
+ ans.add(new ArrayList<>(path));
+ return;
+ }
+ //先写非边界条件,不选,直接跳到i+1
+ dfs(i + 1);
+ //选
+ path.add(nums[i]);
+ dfs(i + 1);
+ //恢复现场
+ path.remove(path.size() - 1);
+ }
+
+
+}
diff --git a/src/main/java/test/org/example/BackTrackingExercisesTest.java b/src/main/java/test/org/example/BackTrackingExercisesTest.java
new file mode 100644
index 0000000..b68edbe
--- /dev/null
+++ b/src/main/java/test/org/example/BackTrackingExercisesTest.java
@@ -0,0 +1,48 @@
+package org.example;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+class BackTrackingExercisesTest {
+ BackTrackingExercises backTrackingExercises = new BackTrackingExercises();
+
+ @DisplayName("剑指 Offer 38. 字符串的排列")
+ @ParameterizedTest(name = "输入:{0},期望输出:{1}")
+ @MethodSource("testPermutationParamsProvider")
+ void testPermutation(String s, String[] expectedAns) {
+ String[] ans = backTrackingExercises.permutation(s);
+ assertEquals(expectedAns.length, ans.length);
+ HashSet setExp = new HashSet<>(Arrays.asList(expectedAns));
+ HashSet setAns = new HashSet<>(Arrays.asList(ans));
+ assertTrue(setAns.containsAll(setExp));
+ }
+
+ @DisplayName("剑指 Offer 38. 字符串的排列")
+ @ParameterizedTest(name = "输入:{0},期望输出:{1}")
+ @MethodSource("testPermutationParamsProvider")
+ void testpermutationBackTrackingTemplate(String s, String[] expectedAns) {
+ String[] ans = backTrackingExercises.permutationBackTrackingTemplate(s);
+ assertEquals(expectedAns.length, ans.length);
+ HashSet setExp = new HashSet<>(Arrays.asList(expectedAns));
+ HashSet setAns = new HashSet<>(Arrays.asList(ans));
+ assertTrue(setAns.containsAll(setExp));
+ }
+
+ public static Stream testPermutationParamsProvider() {
+ return Stream.of(
+ arguments("abc", new String[]{"abc", "acb", "bac", "bca", "cab", "cba"}),
+ arguments("abb", new String[]{"abb", "bab", "bba"}),
+ arguments("ab", new String[]{"ab", "ba"}),
+ arguments("a", new String[]{"a"})
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/test/org/example/ByteDanceInterviewITest.java b/src/main/java/test/org/example/ByteDanceInterviewITest.java
new file mode 100644
index 0000000..764b32d
--- /dev/null
+++ b/src/main/java/test/org/example/ByteDanceInterviewITest.java
@@ -0,0 +1,32 @@
+package org.example;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+class ByteDanceInterviewITest {
+ private ByteDanceInterviewI byteDance = new ByteDanceInterviewI();
+
+ @DisplayName("打家劫舍 II")
+ @ParameterizedTest(name = "输入:{0},期望输出:{1}")
+ @MethodSource("testRobParamProvider")
+ void testRob(int[] nums, int expectedAns) {
+ assertEquals(expectedAns, byteDance.rob(nums));
+ }
+
+ public static Stream testRobParamProvider() {
+ return Stream.of(
+ arguments(new int[]{2, 3, 2}, 3),
+ arguments(new int[]{1, 2, 3, 1}, 4),
+ arguments(new int[]{1, 2, 3}, 3),
+ arguments(new int[]{2}, 2),
+ arguments(new int[]{1, 2}, 2)
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/test/org/example/PrefixSumExercisesTest.java b/src/main/java/test/org/example/PrefixSumExercisesTest.java
new file mode 100644
index 0000000..951deff
--- /dev/null
+++ b/src/main/java/test/org/example/PrefixSumExercisesTest.java
@@ -0,0 +1,77 @@
+package org.example;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+
+class PrefixSumExercisesTest {
+ // @DisplayName("PrefixSumTest")
+// @ParameterizedTest
+// @CsvFileSource(resources = "./PrefixSumTest.csv")
+// void findLongestSubArray() {
+// PrefixSumExercises test = new PrefixSumExercises();
+// test.findLongestSubArray()
+// }
+ private final PrefixSumExercises p = new PrefixSumExercises();
+
+ @DisplayName("一维数组的动态和")
+ @ParameterizedTest(name = "输入:{0}, 输出: {1}")
+ @MethodSource("arrayInputAndOutPutProvider")
+ void testRunningSum(int[] nums, int[] ans) {
+ assertArrayEquals(p.runningSum(nums), ans);
+ }
+
+ public static Stream arrayInputAndOutPutProvider() {
+ return Stream.of(
+ arguments(new int[]{1, 2, 3, 4}, new int[]{1, 3, 6, 10}),
+ arguments(new int[]{1, 1, 1, 1, 1}, new int[]{1, 2, 3, 4, 5}),
+ arguments(new int[]{3, 1, 2, 10, 1}, new int[]{3, 4, 6, 16, 17}),
+ arguments(new int[]{1}, new int[]{1})
+ );
+ }
+
+ @DisplayName("返回字母与数字个数相同的子数组")
+ @ParameterizedTest(name = "输入 {0}, 输出 {1}")
+ @MethodSource("stringArrayProvider")
+ void testFindLongestSubArray(String[] input, String[] output) {
+ assertArrayEquals(p.findLongestSubArray(input), output);
+ }
+
+ static Stream stringArrayProvider() {
+ return Stream.of(
+ arguments(new String[]{"A", "1", "B", "C", "D", "2", "3", "4", "E", "5", "F", "G", "6", "7", "H", "I", "J", "K", "L", "M"}, new String[]{"A", "1", "B", "C", "D", "2", "3", "4", "E", "5", "F", "G", "6", "7"}),
+ arguments(new String[]{"A", "A"}, new String[]{}),
+ arguments(new String[]{}, new String[]{})
+ );
+ }
+
+ @DisplayName("连续和为K的子数组")
+ @ParameterizedTest(name = "输入 nums = {0}, k = {1}")
+ @MethodSource("stringArrayProvider_testSubArraySum")
+ void testSubArraySumEnum(int[] nums, int k, int result) {
+ assertEquals(p.subArraySumEnum(nums, k), result);
+ assertEquals(p.subArraySumHash(nums, k), result);
+ assertEquals(p.subArraySum(nums, k), result);
+ }
+
+ static Stream stringArrayProvider_testSubArraySum() {
+ return Stream.of(
+ arguments(new int[]{1, 1, 1}, 2, 2),
+ arguments(new int[]{1, 2, 3}, 3, 2)
+ );
+ }
+
+ @DisplayName("长度最小的子数组")
+ @ParameterizedTest(name = "")
+ void testMinSubArrayLen(){
+
+ }
+}
diff --git a/src/main/java/test/org/example/SwordAimingOfferITest.java b/src/main/java/test/org/example/SwordAimingOfferITest.java
new file mode 100644
index 0000000..6d92108
--- /dev/null
+++ b/src/main/java/test/org/example/SwordAimingOfferITest.java
@@ -0,0 +1,190 @@
+package org.example;
+
+import org.example.Beans.ListNode;
+import org.example.Beans.TreeNode;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.example.Beans.ListNode.initListNode;
+import static org.example.Beans.TreeNode.initTreeNode;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+/**
+ * @Desc : 剑指 Offer test cases
+ * @Author : Liz
+ * @Date : 2023/3/22
+ */
+class SwordAimingOfferITest {
+ SwordAimingOfferI sword = new SwordAimingOfferI();
+
+ @DisplayName("剑指 Offer 26. 树的子结构")
+ @ParameterizedTest(name = "输入:{0},{1} 期望输出:{2}")
+ @MethodSource("testIsSubStructureParamProvider")
+ void testIsSubStructureBFS(TreeNode A, TreeNode B, boolean expectedValue) {
+ assertEquals(expectedValue, sword.isSubStructureBFS(A, B));
+ }
+
+ @DisplayName("剑指 Offer 26. 树的子结构")
+ @ParameterizedTest(name = "输入:{0},{1} 期望输出:{2}")
+ @MethodSource("testIsSubStructureParamProvider")
+ void testIsSubStructureDFS(TreeNode A, TreeNode B, boolean expectedValue) {
+ assertEquals(expectedValue, sword.isSubStructureDFS(A, B));
+ }
+
+ public static Stream testIsSubStructureParamProvider() {
+ return Stream.of(
+ arguments(initTreeNode(new Integer[]{1, 2, 3}), initTreeNode(new Integer[]{3, 1}), false),
+ arguments(initTreeNode(new Integer[]{3, 4, 5, 1, 2}), initTreeNode(new Integer[]{4, 1, 2}), true)
+ );
+ }
+
+ @DisplayName("剑指 Offer 27. 二叉树的镜像")
+ @ParameterizedTest(name = "输入:{0}, 期望输出: {1}")
+ @MethodSource("testMirrorTreeParamsProvider")
+ void testMirrorTree(TreeNode node, TreeNode exp) {
+ assertEquals(sword.mirrorTree(node), exp);
+ }
+
+ public static Stream testMirrorTreeParamsProvider() {
+ return Stream.of(
+ arguments(initTreeNode(new Integer[]{4, 2, 7, 1, 3, 6, 9}), initTreeNode(new Integer[]{4, 7, 2, 9, 6, 3, 1}))
+ );
+ }
+
+
+ //TODO 二叉树不支持 null node 初始化
+// @DisplayName("剑指 Offer 28. 对称的二叉树")
+// @ParameterizedTest(name = "输入:{0},期望输出:{1}")
+// @MethodSource("testIsSymmetricParamsProvider")
+// void testIsSymmetric(TreeNode root, boolean exp) {
+// assertEquals(exp, sword.isSymmetric(root));
+// }
+//
+// public static Stream testIsSymmetricParamsProvider() {
+// return Stream.of(
+// arguments(initTreeNode(new Integer[]{1, 2, 2, 3, 4, 4, 3}), true),
+// arguments(initTreeNode(new Integer[]{1, 2, 2, null, 3, null, 3}), false)
+// );
+// }
+ @DisplayName("剑指 Offer 10- I. 斐波那契数列")
+ @ParameterizedTest(name = "输入:{0},期望输出:{1}")
+ @MethodSource("fibParamsProvider")
+ void fib(int n, int exp) {
+ assertEquals(exp, sword.fib(n));
+ }
+
+ public static Stream fibParamsProvider() {
+ return Stream.of(
+ arguments(2, 1),
+ arguments(1, 1),
+ arguments(3, 2),
+ arguments(5, 5)
+ );
+ }
+
+ @DisplayName("剑指 Offer 10- II. 青蛙跳台阶问题")
+ @ParameterizedTest(name = "输入:{0},期望输出:{1}")
+ @MethodSource("numWaysParamsProvider")
+ void numWays(int n, int exp) {
+ assertEquals(exp, sword.numWays(n));
+ assertEquals(exp, sword.numWaysDp(n));
+ }
+
+ public static Stream numWaysParamsProvider() {
+ return Stream.of(
+ arguments(3, 3),
+ arguments(7, 21),
+ arguments(2, 2),
+ arguments(0, 1)
+ );
+ }
+
+ @DisplayName("剑指 Offer 63. 股票的最大利润")
+ @ParameterizedTest(name = "输入:{0},期望输出:{1}")
+ @MethodSource("maxProfitParamsProvider")
+ void maxProfit(int[] prices, int exp) {
+ assertEquals(exp, sword.maxProfit(prices));
+ }
+
+ public static Stream maxProfitParamsProvider() {
+ return Stream.of(
+ arguments(new int[]{7, 1, 5, 3, 6, 4}, 5),
+ arguments(new int[]{7, 6, 4, 3, 1}, 0)
+ );
+ }
+
+ @DisplayName("剑指 Offer 47. 礼物的最大价值")
+ @ParameterizedTest(name = "输入:{0},期望输出:{1}")
+ @MethodSource("maxValueParamsProvider")
+ void maxValue(int[][] grid, int exp) {
+ assertEquals(exp, sword.maxValue(grid));
+ }
+
+ public static Stream maxValueParamsProvider() {
+ return Stream.of(
+ arguments(new int[][]{{1, 3, 1}, {1, 5, 1}, {4, 2, 1}}, 12)
+ );
+ }
+
+ @DisplayName("剑指 Offer 46. 把数字翻译成字符串")
+ @ParameterizedTest(name = "输入:{0},期望输出:{1}")
+ @MethodSource("translateNumParamsProvider")
+ void translateNum(int num, int exp) {
+ assertEquals(exp, sword.translateNum(num));
+ }
+
+ public static Stream translateNumParamsProvider() {
+ return Stream.of(
+ arguments(12258, 5)
+ );
+ }
+
+ @DisplayName("剑指 Offer 48. 最长不含重复字符的子字符串")
+ @ParameterizedTest(name = "输入:{0},期望输出:{1}")
+ @MethodSource("lengthOfLongestSubstringParamsProvider")
+ void lengthOfLongestSubstring(String s, int exp) {
+ assertEquals(exp, sword.lengthOfLongestSubstring(s));
+ }
+
+ public static Stream lengthOfLongestSubstringParamsProvider() {
+ return Stream.of(
+ arguments("abcabcbb", 3),
+ arguments("pwwkew", 3),
+ arguments("bbbbb", 1)
+ );
+ }
+
+ @DisplayName("剑指 Offer 18. 删除链表的节点")
+ @ParameterizedTest(name = "输入:{0},期望输出:{2}")
+ @MethodSource("deleteNodeParamsProvider")
+ void deleteNode(ListNode head, int val, ListNode exp) {
+ assertEquals(exp, sword.deleteNode(head, val));
+ }
+
+ public static Stream deleteNodeParamsProvider() {
+ return Stream.of(
+ arguments(initListNode(new int[]{4, 5, 1, 9}), 5, initListNode(new int[]{4, 1, 9})),
+ arguments(initListNode(new int[]{4, 5, 1, 9}), 1, initListNode(new int[]{4, 5, 9}))
+ );
+ }
+
+
+ @DisplayName("剑指 Offer 22. 链表中倒数第k个节点")
+ @ParameterizedTest(name = "输入:{0},期望输出:{2}")
+ @MethodSource("getKthFromEndParamsProvider")
+ void getKthFromEnd(ListNode head, int k, ListNode exp) {
+ assertEquals(exp, sword.getKthFromEnd(head, k));
+ }
+
+ public static Stream getKthFromEndParamsProvider() {
+ return Stream.of(
+// arguments(initListNode(new int[]{1, 2, 3, 4, 5, 6}), 3, initListNode(new int[]{4, 5})),
+ arguments(initListNode(new int[]{1, 2, 3, 4, 5}), 2, initListNode(new int[]{4, 5}))
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/test/org/example/XiaoMiSpringInterviewITest.java b/src/main/java/test/org/example/XiaoMiSpringInterviewITest.java
new file mode 100644
index 0000000..d4b9645
--- /dev/null
+++ b/src/main/java/test/org/example/XiaoMiSpringInterviewITest.java
@@ -0,0 +1,134 @@
+package org.example;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+class XiaoMiSpringInterviewITest {
+
+ public XiaoMiSpringInterviewI xiaomi = new XiaoMiSpringInterviewI();
+
+ @DisplayName("三数之和为0")
+ @ParameterizedTest(name = "输入:{0}, 期望输出:{1}")
+ @MethodSource("threeSumParameterProvider")
+ void testThreeSumExpectSumEqualsZero(int[] nums, List> expectedList) {
+ List> resultList = xiaomi.threeSum(nums);
+ assertTrue(resultList.size() == expectedList.size() && resultList.containsAll(expectedList));
+ }
+
+ public static Stream threeSumParameterProvider() {
+ return Stream.of(
+ arguments(new int[]{-1, 0, 1, 2, -1, -4}, Arrays.asList(Arrays.asList(-1, -1, 2), Arrays.asList(-1, 0, 1))),
+ arguments(new int[]{0, 1, 1}, new ArrayList>()),
+ arguments(new int[]{0, 0, 0}, Arrays.asList(Arrays.asList(0, 0, 0))),
+ arguments(new int[]{-2, 0, 1, 1, 2}, Arrays.asList(Arrays.asList(-2, 0, 2), Arrays.asList(-2, 1, 1)))
+ );
+ }
+
+ @DisplayName("有效的字母异位词")
+ @ParameterizedTest(name = "s: {0},s: {1},expectedReturnResult: {2}")
+ @MethodSource("testIsAnagramParamProvider")
+ void testIsAnagram(String s, String t, boolean expectedReturnResult) {
+ assertEquals(xiaomi.isAnagram(s, t), expectedReturnResult);
+ }
+
+ public static Stream testIsAnagramParamProvider() {
+ return Stream.of(
+ arguments("anagram", "nagaram", true),
+ arguments("rat", "car", false),
+ arguments("", "car", false),
+ arguments("fess", "car", false),
+ arguments("fess", "ssef", true)
+ );
+ }
+
+ @DisplayName("无重复字符的最长子串")
+ @ParameterizedTest(name = "输入:{0}, 期望结果:{1}")
+ @MethodSource("testLengthOfLongestSubstringParamProvider")
+ void testLengthOfLongestSubstring(String s, int expectedAns) {
+ assertEquals(expectedAns, xiaomi.lengthOfLongestSubstring(s));
+ }
+
+ public static Stream testLengthOfLongestSubstringParamProvider() {
+ return Stream.of(
+ arguments("abcabcbb", 3),
+ arguments("bbbbb", 1),
+ arguments("pwwkew", 3)
+ );
+ }
+
+ @DisplayName("电话号码的字母组合")
+ @ParameterizedTest(name = "输入:{0},期望输出 {1}")
+ @MethodSource("testletterCombinationsParamProvider")
+ void testletterCombinations(String digits, List expected) {
+ List ansList = xiaomi.letterCombinations(digits);
+ assertTrue(ansList.size() == expected.size() && expected.containsAll(ansList));
+ }
+
+ public static Stream testletterCombinationsParamProvider() {
+ return Stream.of(
+ arguments("23", Arrays.asList("ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf")),
+ arguments("", Collections.emptyList()),
+ arguments("2", Arrays.asList("a", "b", "c")),
+ arguments("79", Arrays.asList("pw", "px", "py", "pz", "qw", "qx", "qy", "qz", "rw", "rx", "ry", "rz", "sw", "sx", "sy", "sz"))
+ );
+ }
+
+ @DisplayName("2的幂")
+ @ParameterizedTest(name = "输入:{0},期望输出: {1}")
+ @MethodSource("testIsPowerOfTwoParamProvider")
+ void testIsPowerOfTwo(int n, boolean ansExpected) {
+ assertEquals(ansExpected, xiaomi.isPowerOfTwo(n));
+ }
+
+ public static Stream testIsPowerOfTwoParamProvider() {
+ return Stream.of(
+ arguments(1, true),
+ arguments(16, true),
+ arguments(3, false),
+ arguments(4, true),
+ arguments(5, false),
+ arguments(-5, false),
+ arguments(-4, false),
+ arguments(0, false)
+ );
+ }
+
+ @DisplayName("子集")
+ @ParameterizedTest(name = "输入{0},期待输出:{1}")
+ @MethodSource("testSubsetsParamProvider")
+ void testSubsets(int[] nums, List> ansExpected) {
+ List> tmp = xiaomi.subsets(nums);
+ assertTrue(tmp.size() == ansExpected.size() && ansExpected.containsAll(tmp));
+ }
+
+ public static Stream testSubsetsParamProvider() {
+ return Stream.of(
+ arguments(new int[]{1, 2, 3}, Arrays.asList(
+ Collections.emptyList(),
+ Collections.singletonList(1),
+ Collections.singletonList(2),
+ Arrays.asList(1, 2),
+ Collections.singletonList(3),
+ Arrays.asList(1, 3),
+ Arrays.asList(2, 3),
+ Arrays.asList(1, 2, 3)
+ )),
+ arguments(new int[]{0}, Arrays.asList(
+ Collections.emptyList(),
+ Collections.singletonList(0)
+ ))
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/test/org/example/csv/PrefixSumTest.csv b/src/main/java/test/org/example/csv/PrefixSumTest.csv
new file mode 100644
index 0000000..760d6cc
--- /dev/null
+++ b/src/main/java/test/org/example/csv/PrefixSumTest.csv
@@ -0,0 +1,2 @@
+"[""A"",""1"",""B"",""C"",""D"",""2"",""3"",""4"",""E"",""5"",""F"",""G"",""6"",""7"",""H"",""I"",""J"",""K"",""L"",""M""]","[""A"",""1"",""B"",""C"",""D"",""2"",""3"",""4"",""E"",""5"",""F"",""G"",""6"",""7""]"
+"[""A"",""A""]",[]
\ No newline at end of file