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