- 字符串
- 数组
- 11111 - 非重复数字的全排列(46)- 181
- 22222 - 删除有序数组中的重复项(26)- 33
- 11111 - 多数元素(169)- 60
- 最大数(179)- 48
- 把数组排成最小的数(面试题45)- 17
- 1 - 合并两个有序数组(88)- 185
- 0 - 三数之和(15)- 267
- 最长公共前缀(14)- 56
- 最大子数组和(53)- 236
- 0 - 最长重复子数组(718) - 58
- 1 - 二分查找(704)- 114
- 22222 - 长度为n的数组乱序存放着0至n-1,现在只能进行0与其他数的交换,完成数组排序
- 22222 - 小于n的最大数
- 搜索插入位置(35)- 4
- 11111 - 合并区间(56)- 106
- 11111 - 组合总和(39)- 62
- 11111 - 组合总和II(40)- 35
- 二叉树
- 队列
- 链表
- 其他
- 理论
字符串
无重复字符的最长子串(3)- 543
// https://leetcode.cn/problems/longest-substring-without-repeating-characters/
// 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度
// 输入: s = "abcabcbb"
// 输出: 3
// 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3
// 输入: s = "bbbbb"
// 输出: 1
// 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1
class Solution {
// 滑动窗口
public int lengthOfLongestSubstring(String s) {
// 窗口的最大长度
int maxLength = 0;
// 窗口集合
Set<Character> window = new HashSet<>();
int left = 0; // 窗口的左边界
int right = 0; // 窗口的右边界
while (right < s.length()) {
// 如果窗口中包含当前元素,说明出现了重复,
// 把窗口最左端的给移除掉,直到窗口不包含当前元素即可
while (window.contains(s.charAt(right))){
window.remove(s.charAt(left));
left++;
}
// 把当前元素添加到窗口中
window.add(s.charAt(right));
// 更新窗口的长度
maxLength = Math.max(maxLength, right - left + 1);
right++;
}
return maxLength;
}
// 使用队列
public int lengthOfLongestSubstring2(String s) {
// 创建队列
Queue<Character> queue = new LinkedList<>();
int maxLength = 0;
for (char ch : s.toCharArray()) {
// 如果队列中有重复的就把最先添加的字符给移除,
// 直到队列中没有重复元素为止
while (queue.contains(ch))
queue.poll();
// 把当前字符添加到队列中
queue.add(ch);
// 记录最大值
maxLength = Math.max(maxLength, queue.size());
}
return maxLength;
}
}
最长回文子串(5)- 188
// https://leetcode.cn/problems/longest-palindromic-substring/
// 给你一个字符串 s,找到 s 中最长的回文子串。
// 如果字符串的反序与原始字符串相同,则该字符串称为回文字符串
// 输入:s = "babad"
// 输出:"bab"
// 解释:"aba" 同样是符合题意的答案
// 输入:s = "cbbd"
// 输出:"bb"
class Solution {
// 暴力解法:时间复杂度:两层for循环O(n²),for循环里边判断是否为回文O(n),所以时间复杂度为O(n³)
// 空间复杂度:O(1),常数个变量
public String longestPalindrome(String s) {
String ans = "";
int max = 0;
int len = s.length();
for (int i = 0; i < len; i++)
for (int j = i + 1; j <= len; j++) {
String test = s.substring(i, j);
if (isPalindromic(test) && test.length() > max) {
ans = s.substring(i, j);
max = Math.max(max, ans.length());
}
}
return ans;
}
public boolean isPalindromic(String s) {
int len = s.length();
for (int i = 0; i < len / 2; i++) {
if (s.charAt(i) != s.charAt(len - i - 1)) {
return false;
}
}
return true;
}
}
22222 最长回文串(409)- 6
// https://leetcode.cn/problems/longest-palindrome/
// 给定一个包含大写字母和小写字母的字符串 s ,返回 通过这些字母构造成的 最长的回文串 。
// 在构造过程中,请注意 区分大小写 。比如 "Aa" 不能当做一个回文字符串
// 输入:s = "abccccdd"
// 输出:7
// 解释:
// 我们可以构造的最长的回文串是"dccaccd", 它的长度是 7
// 输入:s = "a"
// 输入:1
class Solution {
public static int longestPalindrome(String s) {
int res = 0;
// set存放字符个数为奇数的字符
Set<Character> set = new HashSet<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// 若字符在集合中存在,则删除集合中的元素,同时不向集合中添加该元素(表明该元素的个数为偶数)
if (!set.remove(c)) {
set.add(c);
}
}
// set.size() == 0表示整个字符串都是回文字符串
// s.length() - set.size() + 1表示剔除奇数个字符后,所有偶数字符的个数,最后加上一个奇数字符放最中间
return set.size() == 0 ? s.length() : s.length() - set.size() + 1;
}
}
字符串相加(415)- 148
// https://leetcode.cn/problems/add-strings/
// 给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
// 你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
// 输入:num1 = "11", num2 = "123"
// 输出:"134"
// 输入:num1 = "456", num2 = "77"
// 输出:"533"
class Solution {
public String addStrings(String num1, String num2) {
// 指针i指向num1的末尾
int i = num1.length() - 1;
// 指针j指向num2的末尾
int j = num2.length() - 1;
// add 维护当前是否有进位
int add = 0;
StringBuffer ans = new StringBuffer();
while (i >= 0 || j >= 0 || add != 0) {
// 获取num1第i位置上的字符,通过-'0',转换为int
int x = i >= 0 ? num1.charAt(i) - '0' : 0;
int y = j >= 0 ? num2.charAt(j) - '0' : 0;
int result = x + y + add;
// ans只保留个位数
ans.append(result % 10);
// add表示十位数,要向后进位
add = result / 10;
// num1和num2都向前移
i--;
j--;
}
// 计算完以后的答案需要翻转过来
ans.reverse();
return ans.toString();
}
}
0 - 最长公共子序列(1143)- 97
// https://leetcode.cn/problems/longest-common-subsequence/description/
// 给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0
// 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串
// 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列
// 输入:text1 = "abcde", text2 = "ace"
// 输出:3
// 解释:最长公共子序列是 "ace" ,它的长度为 3
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length(), n = text2.length();
// dp[i][j]表示text1[0:i] 和 text2[0:j]的最长公共子序列的长度
int[][] dp = new int[m + 1][n + 1];
// 边界情况:
// 当i = 0时,text1[0:i]为空,空字符串和任何字符串的最长公共子序列的长度都是0,
// 因此对任意 0 <= j <= n,有dp[0][j] = 0
dp[0][j] = 0;
dp[i][0] = 0;
for (int i = 1; i <= m; i++) {
char c1 = text1.charAt(i - 1);
for (int j = 1; j <= n; j++) {
char c2 = text2.charAt(j - 1);
if (c1 == c2) {
// 当text1[0:i-1] = text2[0:j-1]的最长公共子序列再增加一个字符(即公共字符)即可得到text1[0:i]和text2[0:j]的最长公共子序列
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
}
// 时间复杂度:O(mn),其中m和n分别是字符串text1和text2的长度。二维数组dp有m+1行和n+1列,需要对dp中的每个元素进行计算
// 空间复杂度:O(mn),其中m和n分别是字符串text1和text2的长度。创建了m+1行n+1列的二维数组 dp
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length();
int n = text2.length();
// 第一步:状态定义dp[i][j]:长度为[0, i-1]的字符串text1 与 长度为[0, j-1]的字符串text2的最长公共子序列
int[][] dp = new int[m+1][n+1];
// 第二步:初始化:text1[0, i-1]和空串的最长公共子序列自然是0,所以dp[i][0] = 0。同理dp[0][j]也是0
// 第四步:确定遍历顺序
for(int i = 1; i <= m; i++){
char char1 = text1.charAt(i-1);
for(int j = 1; j <= n; j++){
char char2 = text2.charAt(j-1);
// 第三步:状态转移方程
if(char1 == char2){
// 如果text1[i-1] 与 text2[j-1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i-1][j-1] + 1
dp[i][j] = dp[i-1][j-1] + 1;
}else {
// 如果text1[i-1] 与 text2[j-1]不相同,那就看看text1[0, i-2] 与 text2[0, j-1]的最长公共子序列 和 text1[0, i-1]与text2[0, j-2]的最长公共子序列,取最大的
dp[i][j] =Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
// 第五步:确定最大值
return dp[m][n];
}
}
11111 - 验证回文串(125)- 26
// 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串
// 给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false
// 输入: s = "A man, a plan, a canal: Panama"
// 输出:true
// 解释:"amanaplanacanalpanama" 是回文串
// https://leetcode.cn/problems/valid-palindrome/
class Solution {
public boolean isPalindrome(String s) {
char[] c = s.toLowerCase().toCharArray();
int i = 0, j = c.length - 1;
while(i < j){
while(!isValid(c[i]) && i < j){
i++;
}
while(!isValid(c[j]) && i < j){
j--;
}
if(c[i] != c[j]){
return false;
}
i++;
j--;
}
return true;
}
private boolean isValid(char c){
return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
}
}
11111 - 验证回文串II(680)- 11
// https://leetcode.cn/problems/valid-palindrome-ii/description/
// 给你一个字符串s,最多可以从中删除一个字符
// 请你判断 s 是否能成为回文字符串:如果能,返回true ;否则,返回false
// 输入:s = "aba"
// 输出:true
// 输入:s = "abc"
// 输出:false
class Solution {
public boolean validPalindrome(String s) {
// 双指针
int left = 0;
int right = s.length()-1;
while(left < right){
int leftValue = s.charAt(left);
int rightValue = s.charAt(right);
// 若首尾字符相同,则移动前后指针
if(leftValue == rightValue){
left++;
right--;
}else{
// 若首尾字符不同,则判断(首+1 与 尾)或者(首 与 尾+1)
return valid(s, left + 1, right) || valid(s, left, right - 1);
}
}
return true;
}
// 此方法判断回文字符串,完全匹配
public boolean valid(String s, int left, int right){
for(int i = left, j = right; i < j; i++, j--){
int a = s.charAt(i);
int b = s.charAt(j);
if(a != b) return false;
}
return true;
}
}
22222 - 最长公共子串
https://blog.csdn.net/ly0724ok/article/details/119876759
22222 - 最小覆盖子串(76)- 77
// https://leetcode.cn/problems/minimum-window-substring/
// 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""
// 困难:https://leetcode.cn/problems/minimum-window-substring/
// 输入:s = "ADOBECODEBANC", t = "ABC"
// 输出:"BANC"
class Solution {
public String minWindow(String s, String t) {
// 1.维护两个map记录窗口中的符合条件的字符以及need的字符
Map<Character, Integer> window = new HashMap<>();
// need中存储的是需要的字符以及需要的对应的数量
Map<Character, Integer> need = new HashMap<>();
for(char c : t.toCharArray()){
need.put(c, need.getOrDefault(c, 0) + 1);
}
int left = 0, right = 0; //双指针
int count = 0; // count记录当前窗口中符合need要求的字符的数量,当count == need.size()时即可shrik窗口
int start = 0; // start表示符合最优解的substring的起始位序
int len = Integer.MAX_VALUE; // len用来记录最终窗口的长度,并且以len作比较,淘汰选出最小的substring的len
// 一次遍历找“可行解”
while(right < s.length()){
// 更新窗口
char c = s.charAt(right);
right++; // 窗口扩大
// window.put(c,window.getOrDefault(c,0)+1);其实并不需要将s中所有的都加入windowsmap,只需要将need中的加入即可
if(need.containsKey(c)){
window.put(c, window.getOrDefault(c, 0) + 1);
if(need.get(c).equals(window.get(c))){
count++;
}
}
// System.out.println****Debug位置
// shrink左边界,找符合条件的最优解
while(count == need.size()){
// 不断“打擂”找满足条件的len最短值, 并记录最短的子串的起始位序start
if(right - left < len){
len = right - left;
start = left;
}
// 更新窗口——这段代码逻辑几乎完全同上面的更新窗口
char d = s.charAt(left);
left++; //窗口缩小
if(need.containsKey(d)){
//window.put(d,window.get(d)-1);——bug:若一进去就将window对应的键值缩小,就永远不会满足下面的if,while也会一直执行,知道left越界,因此,尽管和上面对窗口的处理几乎一样,但是这个处理的顺序还是很关键的!要细心!
if(need.get(d).equals(window.get(d))){
count--;
}
window.put(d,window.get(d)-1);
}
}
}
return len == Integer.MAX_VALUE ? "" : s.substring(start,start+len);
}
}
给一个字符串,输出这个字符串的一个最短子串,这个子串重复拼接能形成整个字符串
- (如字符串ababab,求得子串ab)若无这种子串,则输出整个字符串;写完有bug没调出来,就问整体思路,说思路是对的但细节有点问题
- https://blog.csdn.net/taozhi867243023/article/details/124048733
反转字符串中的单词III(557)- 17
// https://leetcode.cn/problems/reverse-words-in-a-string-iii/
// 给定一个字符串 s ,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序
// 输入:s = "Let's take LeetCode contest"
// 输出:"s'teL ekat edoCteeL tsetnoc"
// s 不包含任何开头或结尾空格
// s 中的所有单词都用一个空格隔开
class Solution {
public String reverseWords(String s) {
String[] split = s.split(" ");
for (int i = 0; i < split.length; i++) {
split[i] = new StringBuffer(split[i]).reverse().toString();
}
return String.join(" ", split);
}
public String reverseWords1(String s) {
StringBuffer ret = new StringBuffer();
int length = s.length();
int i = 0;
while (i < length) {
int start = i;
while (i < length && s.charAt(i) != ' ') {
i++;
}
for (int p = start; p < i; p++) {
ret.append(s.charAt(start + i - 1 - p));
}
while (i < length && s.charAt(i) == ' ') {
i++;
ret.append(' ');
}
}
return ret.toString();
}
}
最长连续递增序列(674)- 11
// https://leetcode.cn/problems/longest-continuous-increasing-subsequence/
// 给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度
// 连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列
// 输入:nums = [1,3,5,4,7]
// 输出:3
// 解释:最长连续递增序列是 [1,3,5], 长度为3
// 尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开
class Solution {
public int findLengthOfLCIS(int[] nums) {
int maxlength = 1;
int premax = 1;
for(int i = 0; i < nums.length - 1; i++){
if(nums[i+1] > nums[i]) premax++;
else premax = 1;
maxlength = Math.max(maxlength, premax);
}
return maxlength;
}
public int findLengthOfLCIS1(int[] nums) {
int ans = 0;
int n = nums.length;
int start = 0;
for (int i = 0; i < n; i++) {
if (i > 0 && nums[i] <= nums[i - 1]) {
start = i;
}
ans = Math.max(ans, i - start + 1);
}
return ans;
}
}
字符串中找出连续最长的数字串
// https://blog.csdn.net/ren__wei_/article/details/116030619
// 读入一个字符串str,输出字符串str中的连续最长的数字串
// 给一个输入abc123nj5nk88990wze这里面最长的数字串是88990,并将其输出
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
String cur = "";
String result = "";
for(int i = 0; i < str.length(); i++){
char ch = str.charAt(i);
// 当前字符是数字的话,就把这个字符放进cur中,加""的原因是变成字符串
if(ch >= '0' && ch <= '9'){
cur = cur + ch + "";
}else{
if(cur.length() > result.length()){
result = cur;
}else{
cur = "";
}
}
}
// 循环结束后,再次进行判断,防止输入的字符串全是数字
if(cur.length() > result.length()){
result = cur;
}
System.out.println(result);
}
}
22222 - 根据字符出现频率排序(451)- 6
// https://leetcode.cn/problems/sort-characters-by-frequency/
// 给定一个字符串 s ,根据字符出现的 频率 对其进行 降序排序 。一个字符出现的 频率 是它出现在字符串中的次数
// 返回 已排序的字符串 。如果有多个答案,返回其中任何一个
// 输入: s = "Aabb"
// 输出: "bbAa"
// 解释: 此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的
// 注意'A'和'a'被认为是两种不同的字符
class Solution {
public String frequencySort(String s) {
Map<Character, Integer> map = new HashMap<Character, Integer>();
int maxFreq = 0;
int length = s.length();
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
int frequency = map.getOrDefault(c, 0) + 1;
map.put(c, frequency);
maxFreq = Math.max(maxFreq, frequency);
}
StringBuffer[] buckets = new StringBuffer[maxFreq + 1];
for (int i = 0; i <= maxFreq; i++) {
buckets[i] = new StringBuffer();
}
for (Map.Entry<Character, Integer> entry : map.entrySet()) {
char c = entry.getKey();
int frequency = entry.getValue();
buckets[frequency].append(c);
}
StringBuffer sb = new StringBuffer();
for (int i = maxFreq; i > 0; i--) {
StringBuffer bucket = buckets[i];
int size = bucket.length();
for (int j = 0; j < size; j++) {
for (int k = 0; k < i; k++) {
sb.append(bucket.charAt(j));
}
}
}
return sb.toString();
}
}
// 时间复杂度:O(n + k)
// 空间复杂度:O(n + k)
- 求一个字符串中的出现最大次数的字符并返回该次数
- 驼峰子串的去除,如字符串AaADdDEeEbcvQv,AaA、DdD、EeE和vQv均为驼峰,需要去除,输出结果bc,使用栈进行一次遍历即可
- 给一个String字符串abcabcdabddd……………iii…….iii,然后给abc iii abcd ab,根据下面的去切分上面的字符串,输出结果abc|abcd|ab|iii|iii
- 代码题:一串字符串,判断第一个不重复的单词
- 编程:完整的括号,能否完全配对
- 检查字符串是否满足ipv4的要求
数组
11111 - 非重复数字的全排列(46)- 181
// https://leetcode.cn/problems/permutations/
// 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案
// 输入:nums = [1,2,3]
// 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
class Solution {
List<List<Integer>> result = new ArrayList<List<Integer>>();
List<Integer> path = new ArrayList<Integer>();
public List<List<Integer>> permute(int[] nums) {
process(nums);
return result;
}
public void process(int[] nums){
// 此处不要加终止条件,根据纵向递归判断终止条件
if(path.size() == nums.length){
result.add(new ArrayList<>(path));
}
// 纵向递归:有终止条件
for(int i = 0; i < nums.length; i++){
// path中已存在,则跳过
if(path.contains(nums[i])) continue;
path.add(nums[i]);
process(nums);
path.remove(path.size()-1);
}
}
}
22222 - 删除有序数组中的重复项(26)- 33
// https://leetcode.cn/problems/remove-duplicates-from-sorted-array/
// 给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致
// 由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果
// 将最终结果插入 nums 的前 k 个位置后返回 k
// 不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成
// 判题标准:
// 系统会用下面的代码来测试你的题解:
// int[] nums = [...]; // 输入数组
// int[] expectedNums = [...]; // 长度正确的期望答案
// int k = removeDuplicates(nums); // 调用
// assert k == expectedNums.length;
// for (int i = 0; i < k; i++) {
// assert nums[i] == expectedNums[i];
// }
// 如果所有断言都通过,那么您的题解将被 通过
class Solution {
public int removeDuplicates(int[] nums) {
if(nums == null || nums.length == 0) return 0;
int slow = 0;
int fast = 1;
while(fast < nums.length){
// 若慢指针指向的元素 与 快指针指向的元素不相等,表示慢指针指向元素不是重置项
if(nums[slow] != nums[fast]){
// 向后慢指针,并将快指针元素 赋值给 慢指针后一位元素,进入下一次循环判断
slow++;
nums[slow] = nums[fast];
}
// 每判断一次都向后移动快指针
fast++;
}
return slow + 1;
}
}
// 时间复杂度:O(n)。 空间复杂度:O(1)
11111 - 多数元素(169)- 60
// https://leetcode.cn/problems/majority-element/
class Solution {
public int majorityElement(int[] nums) {
Map<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
if(map.containsKey(num)){
map.put(num, map.get(num) + 1);
}else {
map.put(num, 1);
}
}
Integer s = map.values().stream().max(Integer::compareTo).orElse(1);
System.out.println(s);
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if(entry.getValue().equals(s)){
return entry.getKey();
}
}
return -1;
}
public int majorityElement1(int[] nums) {
int cand_num = nums[0];
int count = 1;
for (int i = 1; i < nums.length; ++i) {
if (cand_num == nums[i])
++count;
else if (--count == 0) {
cand_num = nums[i];
count = 1;
}
}
return cand_num;
}
public int majorityElement2(int[] nums) {
Arrays.sort(nums);
return nums[nums.length / 2];
}
}
最大数(179)- 48
// https://leetcode.cn/problems/largest-number/
// 给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数
// 输入:nums = [3,30,34,5,9]
// 输出:"9534330"
class Solution {
public String largestNumber(int[] nums) {
int n = nums.length;
String[] numsToWord = new String[n];
for(int i = 0; i < n; i++){
numsToWord[i] = String.valueOf(nums[i]);
}
// compareTo()方法比较的时候是按照ASCII码逐位比较的
// 通过比较(a+b)和(b+a)的大小,就可以判断出a,b两个字符串谁应该在前面
// 所以[3,30,34]排序后变为[34,3,30]
// [233,23333]排序后变为[23333,233]
Arrays.sort(numsToWord, (a, b)->{
return (b + a).compareTo(a + b);
});
// 如果排序后的第一个元素是0,那后面的元素肯定小于或等于0,则可直接返回0
if(numsToWord[0].equals("0")){
return "0";
}
StringBuilder sb = new StringBuilder();
for(int i = 0; i < n; i++){
sb.append(numsToWord[i]);
}
return sb.toString();
}
}
把数组排成最小的数(面试题45)- 17
// https://leetcode.cn/problems/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof/
// 输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个
// 输入: [3,30,34,5,9]
// 输出: "3033459"
class Solution {
public String minNumber(int[] nums) {
String[] strs = new String[nums.length];
for (int i = 0; i < nums.length; i++) {
strs[i] = String.valueOf(nums[i]);
}
Arrays.sort(strs, (o1, o2) -> (o1 + o2).compareTo(o2 + o1));
StringBuilder sb = new StringBuilder();
for (String str : strs) {
sb.append(str);
}
return sb.toString();
}
}
1 - 合并两个有序数组(88)- 185
// https://leetcode.cn/problems/merge-sorted-array/description/
// 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
// 请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
// 注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n
// 输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
// 输出:[1,2,2,3,5,6]
// 解释:需要合并 [1,2,3] 和 [2,5,6] 。
// 合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int[] sorted = new int[m + n];
// 双指针,p1指向nums1数组,p2指向nums2数组
int p1 = 0, p2 = 0;
// cur表示当前指针对应的元素值
int cur;
while (p1 < m || p2 < n) {
if (p1 == m) {
// nums1数组已经取完,此时完全取nums2数组的值即可
cur = nums2[p2++];
} else if (p2 == n) {
// nums2数组已经取完,此时完全取nums1数组的值即可
cur = nums1[p1++];
} else if (nums1[p1] < nums2[p2]) {
cur = nums1[p1++];
} else {
cur = nums2[p2++];
}
// 由于上述步骤已经将p1或p2完成的+1,所以此处的下标要-1
sorted[p1 + p2 - 1] = cur;
}
for (int i = 0; i < m + n; i++) {
nums1[i] = sorted[i];
}
}
}
0 - 三数之和(15)- 267
// https://leetcode.cn/problems/3sum/
// 给你一个整数数组nums,判断是否存在三元组[nums[i],nums[j],nums[k]],满足i != j、i != k且j != k,
// 同时还满足nums[i] + nums[j] + nums[k] == 0。请你返回所有和为0且不重复的三元组
// 注意:答案中不可以包含重复的三元组
// 输入: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] 。
// 注意,输出的顺序和三元组的顺序并不重要
class Solution {
// 定义三个指针,保证遍历数组中的每一个结果
// 画图,解答
public List<List<Integer>> threeSum(int[] nums) {
// 定义一个结果集
List<List<Integer>> res = new ArrayList<>();
// 数组的长度
int len = nums.length;
// 当前数组的长度为空,或者长度小于3时,直接退出
if(nums == null || len < 3){
return res;
}
// 将数组进行排序
Arrays.sort(nums);
// 遍历数组中的每一个元素
for(int i = 0; i<len;i++){
// 如果遍历的起始元素大于0,就直接退出
// 原因,此时数组为有序的数组,最小的数都大于0了,三数之和肯定大于0
if(nums[i]>0){
break;
}
// 去重,当起始的值等于前一个元素,那么得到的结果将会和前一次相同
if(i > 0 && nums[i] == nums[i-1]) continue;
int l = i +1;
int r = len-1;
// 当 l 不等于 r时就继续遍历
while(l < r){
// 将三数进行相加
int sum = nums[i] + nums[l] + nums[r];
// 如果等于0,将结果对应的索引位置的值加入结果集中
if(sum==0){
// 将三数的结果集加入到结果集中
res.add(Arrays.asList(nums[i], nums[l], nums[r]));
// 在将左指针和右指针移动的时候,先对左右指针的值,进行判断
// 如果重复,直接跳过。
// 去重,因为 i 不变,当此时 l取的数的值与前一个数相同,所以不用在计算,直接跳
while(l < r && nums[l] == nums[l+1]) {
l++;
}
// 去重,因为 i不变,当此时 r 取的数的值与前一个相同,所以不用在计算
while(l< r && nums[r] == nums[r-1]){
r--;
}
// 将 左指针右移,将右指针左移。
l++;
r--;
// 如果结果小于0,将左指针右移
}else if(sum < 0){
l++;
// 如果结果大于0,将右指针左移
}else if(sum > 0){
r--;
}
}
}
return res;
}
}
最长公共前缀(14)- 56
// https://leetcode.cn/problems/longest-common-prefix/
// 编写一个函数来查找字符串数组中的最长公共前缀。
// 输入:strs = ["flower","flow","flight"]
// 输出:"fl"
class Solution {
public String longestCommonPrefix(String[] strs) {
String shortest = strs[0];
for (String str : strs) {
if (str.length() < shortest.length()) {
shortest = str;
}
}
String res = shortest;
for (int i = 0; i < shortest.length(); i++) {
for (String str : strs) {
if (!str.startsWith(res)) {
res = res.substring(0, res.length() - 1);
}
}
}
return res;
}
}
最大子数组和(53)- 236
// https://leetcode.cn/problems/maximum-subarray/
// 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
// 子数组 是数组中的一个连续部分
// 输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
// 输出:6
// 解释:连续子数组 [4,-1,2,1] 的和最大,为 6
class Solution {
public int maxSubArray(int[] nums) {
int len = nums.length;
// 第一步:定义状态:dp[i]表示第i个元素为尾结点构成的子数组的和
int[] dp = new int[len];
// 第二步:初始化
dp[0] = nums[0];
int maxSum = dp[0];
// 第四步:确定遍历顺序
for(int i = 1; i < len; i++){
// 第三步:状态转移
// dp[i-1] > 0,则dp[i] = dp[i-1] + nums[i]。此处不能判断dp[i-1] + nums[i] > 0,需考虑dp[i-1]为负数场景
// dp[i-1] <= 0,则dp[i] = nums[i];
if(dp[i-1] > 0){
dp[i] = dp[i-1] + nums[i];
}else {
dp[i] = nums[i];
}
maxSum = Math.max(maxSum, dp[i]);
}
// 第五步:确定最大值
return maxSum;
}
public int maxSubArray1(int[] nums) {
int ans = nums[0]; // 最终求出的连续子数组的最大和
int sum = 0; // 当前元素前面的连续子数组的最大和
for(int num: nums) {
if(sum > 0) {
sum += num; // 说明sum对结果有增益效果,则sum保留并加上当前遍历数字
} else {
sum = num; // 说明sum对结果无增益效果,需要舍弃,则sum直接更新为当前遍历数字
}
ans = Math.max(ans, sum);
}
return ans;
}
}
0 - 最长重复子数组(718) - 58
// https://leetcode.cn/problems/maximum-length-of-repeated-subarray/
// 给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 。
// 输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
// 输出:3
// 解释:长度最长的公共子数组是 [3,2,1]
class Solution {
public int findLength(int[] A, int[] B) {
int m = A.length;
int n = B.length;
int maxLength = 0;
// 第一步:定义状态:dp[i][j]表示A[i-1],B[j-1]重复子数组
int[][] dp = new int[m+1][n+1];
// 第二步:初始化
// Arrays.fill(dp, 0);
// 第四步:确认遍历顺序
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
// 第三步:状态转移
// A[i-1] = B[j-1],dp[i][j] = dp[i-1][j-1] + 1
// A[i-1] != B[j-1],dp[i][j] = 0
if(A[i-1] == B[j-1]){
dp[i][j] = dp[i-1][j-1] + 1;
}
maxLength = Math.max(maxLength, dp[i][j]);
}
}
// 第五步:确认最大值
return maxLength;
}
public int findLength1(int[] A, int[] B) {
int m = A.length;
int n = B.length;
int[] dp = new int[n+1];
int ans = 0;
for(int i = m-1; i >= 0; i--){
int p = 0;
int q = 0;
for(int j = n-1; j >= 0; j--){
p = q;
q = dp[j];
dp[j] = (A[i] == B[j]) ? p + 1 : 0;
ans = Math.max(ans,dp[j]);
}
}
return ans;
}
}
1 - 二分查找(704)- 114
// 给定一个n个元素有序的(升序)整型数组nums和一个目标值target,写一个函数搜索nums中的target,
// 如果目标值存在返回下标,否则返回 -1
// https://leetcode.cn/problems/binary-search/
class Solution {
public int search(int[] nums, int target) {
int left = 0, right = nums.length-1;
while(left <= right){
int mid = left + (right - left) / 2;
if(target == nums[mid]){ // 目标值刚好在中间位置
return mid;
}else if(target > nums[mid]) { // 目标值在后半部分
left = mid + 1;
}else{
right = mid - 1; // 目标值在前半部分
}
}
return -1;
}
}
// 时间复杂度:O(logn),其中n是数组的长度
// 空间复杂度:O(1)
22222 - 长度为n的数组乱序存放着0至n-1,现在只能进行0与其他数的交换,完成数组排序
// 问题描述:长度为n的数组乱序存放着0至n-1. 现在只能进行0与其他数的交换,完成数组排序
// 思路:由于乱序数组各元素亮亮不同,且已知数组内容即为0-(n-1),所以只要考虑将0-(n-1)这n个数依次填入数组array即可。
// 一个简单的思路是,由于数组最终内容是array[i]=i,考虑将0与数组第i个位置互换,然后将0与i互换,则i填入位置array[i]
22222 - 小于n的最大数
6. 给定一个数n,如23121;给定一组数字A如{2,4,9}求由A中元素组成的、小于n的最大数,如小于23121的最大数为22999.
7. 给一个数组a[],一个数n 232342,求用数组a[]里面数字组合成最大的不大于n的数
// https://blog.csdn.net/weixin_42041027/article/details/125269790
分组的最大数量(2358)
// https://leetcode.cn/problems/maximum-number-of-groups-entering-a-competition/description/
// 给你一个正整数数组 grades ,表示大学中一些学生的成绩。你打算将 所有 学生分为一些 有序 的非空分组,其中分组间的顺序满足以下全部条件:
// 第 i 个分组中的学生总成绩 小于 第 (i + 1) 个分组中的学生总成绩,对所有组均成立(除了最后一组)
// 第 i 个分组中的学生总数 小于 第 (i + 1) 个分组中的学生总数,对所有组均成立(除了最后一组)
// 返回可以形成的 最大 组数
// 输入:grades = [10,6,12,7,3,5]
// 输出:3
// 解释:下面是形成 3 个分组的一种可行方法:
// - 第 1 个分组的学生成绩为 grades = [12] ,总成绩:12 ,学生数:1
// - 第 2 个分组的学生成绩为 grades = [6,7] ,总成绩:6 + 7 = 13 ,学生数:2
// - 第 3 个分组的学生成绩为 grades = [10,3,5] ,总成绩:10 + 3 + 5 = 18 ,学生数:3
// 可以证明无法形成超过 3 个分组
搜索插入位置(35)- 4
// https://leetcode.cn/problems/search-insert-position/
// 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置
// 请必须使用时间复杂度为 O(log n) 的算法
// 输入: nums = [1,3,5,6], target = 5
// 输出: 2
// 输入: nums = [1,3,5,6], target = 2
// 输出: 1
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
int left = 0, right = n - 1, ans = n;
while (left <= right) {
int mid = ((right - left) >> 1) + left;
if (target <= nums[mid]) {
ans = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return ans;
}
}
11111 - 合并区间(56)- 106
// https://leetcode.cn/problems/merge-intervals/
// 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间
// 输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
// 输出:[[1,6],[8,10],[15,18]]
// 解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]
class Solution {
public int[][] merge(int[][] intervals) {
List<int[]> result = new ArrayList<>();
// 将数组中的每个子数组按照第一个元素大小进行排序
Arrays.sort(intervals, (o1, o2) -> (o1[0] - o2[0]));
for(int[] interval : intervals){
// 当前集合为空 或者 当前遍历数组的第一个元素 大于 集合中最后一个数组的第二个元素,表明此时数组不需要合并,加上该新数组即可
if(result.size() == 0 || interval[0] > result.get(result.size()-1)[1]){
result.add(interval);
}else{
// 当前遍历数组的第一个元素 不大于 集合中最后一个数组的第二个元素,表明此时两个数组会有重叠,需要合并这两个数组。第二个元素取较大值
result.get(result.size()-1)[1] = Math.max(interval[1], result.get(result.size()-1)[1]);
}
}
// 集合转数组
return result.toArray(new int[result.size()][]);
}
}
// 时间复杂度:O(nlogn)
// 空间复杂度:O(nlogn)
11111 - 组合总和(39)- 62
// https://leetcode.cn/problems/combination-sum/
// 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
// candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
// 对于给定的输入,保证和为 target 的不同组合数少于 150 个。
class Solution {
List<List<Integer>> result = new ArrayList<List<Integer>>();
List<Integer> path = new ArrayList<Integer>();
// 存储计算过程中的值
int sum = 0;
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
backtrack(candidates, target, 0);
return result;
}
public void backtrack(int[] candidates, int target, int start){
if(sum == target){
// 此处不能直接使用path
result.add(new ArrayList<>(path));
return;
}
// 剪枝操作:若sum + candidates[i] > target, 则不进入循环
for(int i = start; i < candidates.length && sum + candidates[i] <= target; i++){
sum = sum + candidates[i];
path.add(candidates[i]);
// 因为candidates中的元素可以无限制重复被选取,所以此处递归传入i,而不是start+1
backtrack(candidates, target, i);
// 去除路径中最后添加的数字
path.remove(path.size() - 1);
// 减去最后添加的数字(此时可能刚好满足条件,或者和超过target)
sum = sum - candidates[i];
}
}
}
11111 - 组合总和II(40)- 35
NC46加起来和为目标值的组合 - 组合总和 II(40)
// https://www.cnblogs.com/zhengxch/p/15302333.html
// https://leetcode.cn/problems/combination-sum-ii/
// 给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合
// candidates 中的每个数字在每个组合中只能使用 一次
// 注意:解集不能包含重复的组合
// 输入: candidates = [10,1,2,7,6,1,5], target = 8
// 输出:
// [
// [1,1,6],
// [1,2,5],
// [1,7],
// [2,6]
// ]
class Solution {
List<List<Integer>> result = new ArrayList<List<Integer>>();
List<Integer> path = new ArrayList<Integer>();
int sum = 0;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
process(candidates, target, 0);
return result;
}
public void process(int[] candidates, int target, int start){
if(sum == target){
result.add(new ArrayList<>(path));
return;
}
for(int i = start; i < candidates.length && sum + candidates[i] <= target; i++){
// 去重,若candidates中存在重复元素,只需要取其中一个元素进行递归操作即可
// 后续相同的元素就不需要再进行求和操作,因为肯定与前一个相同元素求出的结果一致,此时就重复了
if(i > start && candidates[i] == candidates[i-1]) continue;
sum = sum + candidates[i];
path.add(candidates[i]);
process(candidates, target, i + 1);
// 回溯
path.remove(path.size() - 1);
sum = sum - candidates[i];
}
}
}
- 给定一个二维数组,数值表示高度,球只能往低处或者数值相等处走,给定球的初始位置,判断球是否能到达边界?
- 给定一个数组,让数组中的奇数在前,偶数在后,奇数正序,偶数逆序
- 找出数组中第二大的数,不能排序,不能用库
- 一个山峰型数组(先单调增后单调减),对数组去重排序并输出,比如输入[1,3,5,6,4,3,2,1],去重并排序后输出[1,2,3,4,5,6],要求时间复杂度O(n),空间复杂度O(1),我的解法是先找到最大值对应下标,从而把数组分成前半段与后半段,然后从右向左的顺序取后半部分数组每个数字,并将其依次插入到前半部分,遇到重复则丢弃
- 编程实现两个数组相乘,输出一个数组
- 牛牛有n堆石子堆。 牛牛可以对任意一堆石子数量大于1的石子堆进行分裂操作,分裂成两堆新的石子数量都大于等于1的石子堆。 现在牛牛需要通过分裂得到m堆石子,他想知道这m堆石子的最小值最大可以是多少?
6 8 17
3 3 4 4 17。。。 3
4 4 6 8 9。。。 4
5 5 6 7 8 。。。5
二叉树
二叉树的中序遍历(94)- 5
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
inorder(root, res);
return res;
}
public void inorder(TreeNode root, List<Integer> res) {
if (root == null) {
return;
}
inorder(root.left, res);
res.add(root.val);
inorder(root.right, res);
}
}
二叉树中序非递归
public class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
// 栈 先进后出
// 前序遍历,出栈顺序:根左右; 入栈顺序:右左根
// 中序遍历,出栈顺序:左根右; 入栈顺序:右根左
// 后序遍历,出栈顺序:左右根; 入栈顺序:根右左
List<Integer> ans = new ArrayList<Integer>();
Stack<TreeNode> stack = new Stack<>();
// root为空且stack为空,遍历结束
while (root != null || !stack.isEmpty()) {
// 先根节点、然后左子树入栈
while (root != null) {
stack.push(root);
root = root.left;
}
// 此时root==null,说明上一步的root没有左子树
// 1. 执行左出栈。因为此时root==null,导致root.right一定为null
// 2. 执行下一次外层while代码块,根出栈。此时root.right可能存在
// 3a. 若root.right存在,右入栈,再出栈
// 3b. 若root.right不存在,重复步骤2
root = stack.pop();
ans.add(root.val);
root = root.right;
}
return ans;
}
}
平衡二叉树(110)- 4
// 1. 平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树,可以保证查询效率较高(是在二叉排序树基础上实现的)
// 2. 具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等
// https://leetcode.cn/problems/balanced-binary-tree/description/
class Solution {
boolean ans = true;
public boolean isBalanced(TreeNode root) {
dfs(root);
return ans;
}
private int dfs(TreeNode root){
if(root == null){
return 0;
}
int left = dfs(root.left);
int right = dfs(root.right);
if(Math.abs(left - right) > 1){
ans = false;
}
return Math.max(left, right) + 1;
}
}
二叉树的层序遍历(102)- 3
// 给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
// https://leetcode.cn/problems/binary-tree-level-order-traversal/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if(root == null){
return result;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
while (!queue.isEmpty()){
// 当前层元素集合
List<Integer> level = new ArrayList<>();
// 当前层元素个数
int currentLevelSize = queue.size();
for (int i = 1; i <= currentLevelSize; i++) {
// 从队列中取出当前层元素放入level
TreeNode node = queue.poll();
level.add(node.val);
// 将当前层所有元素的左右子节点放入队列中
if(node.left != null){
queue.add(node.left);
}
if(node.right != null){
queue.add(node.right);
}
}
result.add(level);
}
return result;
}
}
11111 - 二叉树中的最大路径和(124)- 3
// https://leetcode.cn/problems/binary-tree-maximum-path-sum/description/
class Solution {
int maxSum = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
maxGain(root);
return maxSum;
}
public int maxGain(TreeNode node) {
if (node == null) {
return 0;
}
// 递归计算左右子节点的最大贡献值
// 只有在最大贡献值大于 0 时,才会选取对应子节点
int leftGain = Math.max(maxGain(node.left), 0);
int rightGain = Math.max(maxGain(node.right), 0);
// 当前节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值
int priceNewpath = node.val + leftGain + rightGain;
// 更新答案
maxSum = Math.max(maxSum, priceNewpath);
// 返回节点的最大贡献值
return node.val + Math.max(leftGain, rightGain);
}
}
11111 - 对称二叉树(101)- 2
// https://leetcode.cn/problems/symmetric-tree/description/
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null) {
return true;
}
// 调用递归函数,比较左节点,右节点
return dfs(root.left, root.right);
}
boolean dfs(TreeNode left, TreeNode right) {
// 递归的终止条件是两个节点都为空
// 或者两个节点中有一个为空
// 或者两个节点的值不相等
if(left == null && right == null) {
return true;
}
if(left == null || right == null) {
return false;
}
if(left.val != right.val) {
return false;
}
// 再递归的比较 左节点的左孩子 和 右节点的右孩子
// 以及比较 左节点的右孩子 和 右节点的左孩子
return dfs(left.left, right.right) && dfs(left.right, right.left);
}
}
二叉树的直径(543)- 2
// https://leetcode.cn/problems/diameter-of-binary-tree/
class Solution {
int ans = 0;
public int diameterOfBinaryTree(TreeNode root) {
ans = 1;
depth(root);
return ans - 1;
}
// 计算该节点的深度
int depth(TreeNode root){
if(root == null){
return 0;
}
int left = depth(root.left); // 左节点为根的子树的深度
int right = depth(root.right); // 右节点为根的子树的深度
// 计算经过该节点的直径
ans = Math.max(ans, left + right + 1);
// 返回该节点为根的子树的深度
return Math.max(left, right) + 1;
}
}
class Solution {
public int diameterOfBinaryTree(TreeNode root) {
return process(root).maxDistance;
}
public static Info process(TreeNode node){
if(node == null){
return new Info(0, 0);
}
// 获取当前节点的左右子树的info信息
Info leftInfo = process(node.left);
Info rightInfo = process(node.right);
// 根据当前节点的左右子树的高度 得到 当前节点的高度
int height = Math.max(leftInfo.height, rightInfo.height) + 1;
// 根据当前节点的左右子树的最大距离 得到 当前节点的距离
int maxDistance = Math.max(Math.max(leftInfo.maxDistance, rightInfo.maxDistance),
leftInfo.height + rightInfo.height);
return new Info(maxDistance, height);
}
}
public class Info{
public int maxDistance;
public int height;
public Info(int dis, int h){
maxDistance = dis;
height = h;
}
}
二叉树的最大深度(543)- 1
// https://leetcode.cn/problems/maximum-depth-of-binary-tree/description/
class Solution {
public int maxDepth(TreeNode root) {
if (root == null){
return 0;
}
int leftDepth = root.left == null ? 0 : maxDepth(root.left);
int rightDepth = root.right == null ? 0 : maxDepth(root.right);
return Math.max(leftDepth, rightDepth) + 1;
}
}
二叉树遍历
整棵二叉树的最大距离
package class008;
// 测试链接:https://leetcode.com/problems/balanced-binary-tree
public class Code08_MaxDistance {
public static class Node {
public int val;
public Node left;
public Node right;
Node(int val) {
this.val = val;
}
}
public static class Info {
public int maxDistance; // 当前节点及其左右子树的最大距离
public int height; // 当前节点及其左右子树的高度
public Info(int dis, int h) {
maxDistance = dis;
height = h;
}
}
public static int maxDistance2(Node head) {
return process(head).maxDistance;
}
public static Info process(Node node) {
if (node == null) {
return new Info(0, 0);
}
// 获取当前节点的左右子树的info信息
Info leftInfo = process(node.left);
Info rightInfo = process(node.right);
// 根据当前节点的左右子树的高度 得到 当前节点的高度
int height = Math.max(leftInfo.height, rightInfo.height) + 1;
// 根据当前节点的左右子树的最大距离 得到 当前节点的距离
int maxDistance = Math.max(Math.max(leftInfo.maxDistance, rightInfo.maxDistance),
leftInfo.height + rightInfo.height + 1);
return new Info(maxDistance, height);
}
}
二叉排序(搜索)树(BST)
对于二叉排序树的任何一个非叶子节点,要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大
顺序存储二叉树
顺序存储二叉树的特点:
- 顺序二叉树通常只考虑完全二叉树
- 第 n 个元素的左子节点为第 2 * n + 1 个元素
- 第 n 个元素的右子节点为第 2 * n + 2 个元素
- 第 n 个元素的父节点为第 (n - 1) / 2 个元素
- n:表示二叉树中的第几个元素(按0开始编号)
二叉树深度优先、广度优先区别
- 常见的深度优先搜索(DFS):先序遍历、中序遍历、后序遍历
- 深度优先搜索就是沿着每一个分支路径遍历直到不能再深入为止,也就是到达了叶节点。如果到达叶节点,那我们就向上回溯,回到叶节点之前的那一个节点,接着遍历该节点未被访问过的子节点。一直重复这个过程直到所有的节点把遍历完
常见的广度优先搜索(BFS):层序遍历(按层遍历)
广度优先搜索就是层序遍历,就是按照树的层次结构一层一层遍历,访问完一层我们就进入下一层,且每个节点只能访问一遍
队列
用栈实现队列(232)- 112
// https://leetcode.cn/problems/implement-queue-using-stacks/
class MyQueue {
// 输入栈,用于压入push传入的数据
Stack<Integer> inStack;
// 输出栈,用于pop和peek操作
Stack<Integer> outStack;
public MyQueue() {
inStack = new Stack<>();
outStack = new Stack<>();
}
// 将元素x推到队列的末尾
public void push(int x) {
inStack.push(x);
}
// 从队列的开头移除并返回元素
public int pop() {
if(outStack.isEmpty()){
while(!inStack.isEmpty()){
// 从inStack(先进后出)取出元素,push到outStack(先进后出)。两者刚好使得元素先进先出
outStack.push(inStack.pop());
}
}
return outStack.pop();
}
// 返回队列开头的元素
public int peek() {
if(outStack.isEmpty()){
while(!inStack.isEmpty()){
outStack.push(inStack.pop());
}
}
return outStack.peek();
}
// 如果队列为空,返回true
public boolean empty() {
return outStack.isEmpty() && inStack.isEmpty();
}
}
两个队列找相同元素;编写测试用例进行测试;优化代码;复杂度;如果是无限队列怎么办
- 查找有限队列中重复数据并输出,编写测试用例测试,计算自己代码的时间复杂度,如果是无限队列怎么办。
链表
相交链表(160)- 170
```java // https://leetcode.cn/problems/intersection-of-two-linked-lists/description/
class Solution {
// 双指针解法
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
ListNode pA = headA, pB = headB;
// 当pA指向末尾时,重新指向headB
// 当pB指向末尾时,重新指向headA,此时能够保证pA与pB最终走的长度是一致相同的
while (pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}
// 哈希集合
public ListNode getIntersectionNode1(ListNode headA, ListNode headB) {
if(headA == null || headB == null){
return null;
}
ListNode temp1 = headA;
ListNode temp2 = headB;
List<ListNode> listNodes = new ArrayList<>();
while (true){
listNodes.add(temp1);
if(temp1.next == null){
break;
}
temp1 = temp1.next;
}
while (true){
if(listNodes.contains(temp2)){
return temp2;
}else {
if(temp2.next == null){
break;
}
temp2 = temp2.next;
}
}
return null;
}
}
<a name="UaSmH"></a>
### 反转链表(206)- 532
```java
// https://leetcode.cn/problems/reverse-linked-list/description/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
public ListNode reverseList1(ListNode head) {
// 递归终止条件是当前为空,或者下一个节点为空
if(head==null || head.next==null) {
return head;
}
// 这里的cur就是最后一个节点
ListNode cur = reverseList(head.next);
// 如果链表是 1->2->3->4->5,那么此时的cur就是5
// 而head是4,head的下一个是5,下下一个是空
// 所以head.next.next 就是 5->4
head.next.next = head;
// 防止链表循环,需要将head.next设置为空
head.next = null;
// 每层递归函数都返回cur,也就是最后一个节点
return cur;
}
public ListNode reverseList2(ListNode head) {
if(head == null){
return null;
}
Stack<ListNode> stack = new Stack<>();
ListNode temp = head;
while (true){
stack.push(temp);
if(temp.next == null){
break;
}
temp = temp.next;
}
ListNode listNode = stack.pop();
ListNode listNode1 = listNode;
while (!stack.empty()){
ListNode pop = stack.pop();
pop.next = null;
listNode.next = pop;
listNode = listNode.next;
}
return listNode1;
}
}
class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
}
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
22222 - K个一组翻转链表(25)- 286
https://leetcode.cn/problems/reverse-nodes-in-k-group/
给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
if(head == null || head.next == null) return head;
ListNode tail = head;
for(int i = 0; i < k; i++){
if(tail == null) return head;
tail = tail.next; // 此时tail指向第k个节点的后一节点
}
ListNode newHead = reverse(head, tail);
// 递归翻转从tail为起始节点的k个节点(上一步翻转不包含tail节点),并将返回值链接到head.next
head.next = reverseKGroup(tail, k);
return newHead;
}
// 链表反转,head 至 tailNext上一个节点,左闭右开
private ListNode reverse(ListNode head, ListNode tailNext) {
ListNode pre = null;
ListNode next = null;
while(head != tailNext){
next = head.next;
head.next = pre;
pre = head; // 指针下移
head = next; // 指针下移
}
// 返回最后一个节点,此时head指向最后一个节点的下一节点,即tailNext
return pre;
}
}
删除排序链表中的重复元素(83)- 7
// https://leetcode.cn/problems/remove-duplicates-from-sorted-list/description/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if (head == null) {
return null;
}
ListNode cur = head;
while (cur.next != null) {
if (cur.val == cur.next.val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return head;
}
}
判断链表中是否有环(141)- 7
// https://leetcode.cn/problems/linked-list-cycle/description/
class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode slow = head; // 慢指针
ListNode fast = head.next; // 快指针
while (slow != fast) {
if (fast == null || fast.next == null) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
public boolean hasCycle1(ListNode head) {
if(head == null){
return false;
}
List<ListNode> listNodes = new ArrayList<>();
listNodes.add(head);
ListNode temp = head;
while (true){
if(temp.next == null){
return false;
}
temp = temp.next;
if(listNodes.contains(temp)){
return true;
}
listNodes.add(temp);
}
}
}
class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
next = null;
}
}
合并两个有序链表(21)- 4
// https://leetcode.cn/problems/merge-two-sorted-lists/description/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) return l2;
if (l2 == null) return l1;
ListNode resultNode = new ListNode(0);
ListNode temp = resultNode;
while (l1 != null && l2 != null){
if(l1.val < l2.val){
temp.next = l1;
l1 = l1.next;
}else {
temp.next = l2;
l2 = l2.next;
}
temp = temp.next;
}
if(l1 != null) temp.next = l1;
if(l2 != null) temp.next = l2;
return resultNode.next;
}
public ListNode mergeTwoLists1(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
}
else if (l2 == null) {
return l1;
}
else if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
}
else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
public ListNode mergeTwoLists2(ListNode list1, ListNode list2) {
if(list1 == null && list2 == null){
return null;
}
if(list1 == null){
return list2;
}
if(list2 == null){
return list1;
}
List<Integer> list = new ArrayList<>();
ListNode temp1 = list1;
ListNode temp2 = list2;
while (true){
if(temp1 == null){
break;
}
list.add(temp1.val);
temp1 = temp1.next;
}
while (true){
if(temp2 == null){
break;
}
list.add(temp2.val);
temp2 = temp2.next;
}
Collections.sort(list);
ListNode root = new ListNode(list.get(0));
ListNode node = root;
for (int i = 1; i < list.size(); i++) {
node.next = new ListNode(list.get(i));
node = node.next;
}
return root;
}
}
class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
}
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
环形链表,返回入环的那个节点(142)- 4
// https://leetcode.cn/problems/linked-list-cycle-ii/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode temp = head;
Set<ListNode> set = new HashSet<ListNode>();
while (temp != null) {
if (set.contains(temp)) {
return temp;
} else {
set.add(temp);
}
temp = temp.next;
}
return null;
}
public ListNode detectCycle(ListNode head) {
if (head == null) {
return null;
}
ListNode slow = head, fast = head;
while (fast != null) {
slow = slow.next;
if (fast.next != null) {
fast = fast.next.next;
} else {
return null;
}
if (fast == slow) {
fast = head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
return null;
}
}
class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
next = null;
}
}
1 - 反转链表 II(92)- 3
// https://leetcode.cn/problems/reverse-linked-list-ii/description/
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
// 重新设置头节点
ListNode dummyNode = new ListNode(-1, head);
ListNode pre = dummyNode;
// pre最终指向left上一个节点
for (int i = 0; i < left - 1; i++) {
pre = pre.next;
}
// cur指向left节点
ListNode cur = pre.next;
ListNode next;
for (int i = 0; i < right - left; i++) {
// 下移指针
next = cur.next;
cur.next = next.next;
next.next = pre.next;
pre.next = next;
}
return dummyNode.next;
}
}
22222 - 链表中倒数第k个节点(剑指 Offer 22)- 2
// https://leetcode.cn/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/description/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode first = head;
ListNode second = head;
// first最终指向第k个节点
for(int i = 0; i < k; i++){
first = first.next;
}
// second与first的距离始终保持K个节点,当first遍历到最后时,second刚好在倒数第k个节点
while(first != null){
first = first.next;
second = second.next;
}
return second;
}
}
22222 - 删除链表的倒数第N个结点(leetcode19)- 2
// https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
// 重新定义头节点
ListNode dummy = new ListNode(0, head);
// 快指针
ListNode first = head;
// 慢指针
ListNode second = dummy;
for (int i = 0; i < n; ++i) {
first = first.next;
}
// 当快指针到链表尾部时,慢指针到达被删除节点的上一节点
while (first != null) {
first = first.next;
second = second.next;
}
// 将慢指针对应的下一个节点重新指定
second.next = second.next.next;
ListNode ans = dummy.next;
return ans;
}
public ListNode removeNthFromEnd1(ListNode head, int n) {
ListNode dummy = new ListNode(0, head);
int length = getLength(head);
ListNode cur = dummy;
for (int i = 1; i < length - n + 1; ++i) {
cur = cur.next;
}
cur.next = cur.next.next;
ListNode ans = dummy.next;
return ans;
}
// 获取链表长度
public int getLength(ListNode head) {
int length = 0;
while (head != null) {
++length;
head = head.next;
}
return length;
}
}
33333 - 合并并且去重两个有序链表
其他
两个大数相加(1)- 15
// https://leetcode.cn/problems/two-sum/
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] result = new int[2];
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if((nums[i] + nums[j]) == target){
result[0] = i;
result[1] = j;
return result;
}
}
}
return result;
}
public int[] twoSum1(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j < nums.length; j++) {
if(i != j){ // 必须先判断外层循环与内层循环下标不能相同
if((nums[i] + nums[j]) == target){
return new int[]{i, j};
}
}
}
}
return null;
}
}
买卖股票的最佳时机(121)- 14
// https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/
class Solution {
public int maxProfit(int prices[]) {
int minprice = Integer.MAX_VALUE;
int maxprofit = 0;
for (int i = 0; i < prices.length; i++) {
if (prices[i] < minprice) {
minprice = prices[i];
} else if (prices[i] - minprice > maxprofit) {
maxprofit = prices[i] - minprice;
}
}
return maxprofit;
}
// 执行结果超出时间限制
public int maxProfit1(int[] prices) {
int temp = 0;
for (int i = 0; i < prices.length; i++) {
for (int j = i + 1; j < prices.length; j++) {
temp = Math.max(prices[j] - prices[i], temp);
}
}
return temp;
}
}
爬楼梯(121)- 6
// https://leetcode.cn/problems/climbing-stairs/
class Solution {
public int climbStairs(int n) {
// 记录从1个台阶(下标为0)到n个台阶(下标为n-1)中,每个台阶所需要的方法数
int[] dp = new int[n];
dp[0] = 1; // 表示第一个台阶
dp[1] = 2; // 表示第二个台阶
// i = 2是第三个台阶,i = n-1是第n个台阶
for(int i = 2; i < n; i++){
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n-1]; // 数组的最后一个值
}
public int climbStairs1(int n) {
if(n < 3){
return n;
}
int prePre = 1;
int pre = 2;
int result = 0;
for (int i = 2; i < n; i++) {
// f(n) = f(n-1) + f(n-2):
// 若第一步爬1个台阶,则后续需要存在n-1个台阶的方法数
// 若第一步爬2个台阶,则后续需要存在n-2个台阶的方法数
result = pre + prePre;
prePre = pre;
pre = result;
}
return result;
}
}
有效的括号(20)- 20
// leetcode20:有效的括号:https://leetcode.cn/problems/valid-parentheses/description/
class Solution {
public boolean isValid(String s) {
while(true){
int l = s.length();
s = s.replace("()", "");
s = s.replace("{}", "");
s = s.replace("[]", "");
if(s.length() == l){
return l == 0;
}
}
}
public boolean isValid1(String s) {
Stack<Character> stack = new Stack<>();
stack.push(s.charAt(0));
for (int i = 1; i < s.length(); i++) {
char c = s.charAt(i);
if(c == ')' && !stack.empty()){
Character pop = stack.pop();
if(pop != '('){
return false;
}else {
continue;
}
}
if(c == ']' && !stack.empty()){
Character pop = stack.pop();
if(pop != '['){
return false;
}else {
continue;
}
}
if(c == '}' && !stack.empty()){
Character pop = stack.pop();
if(pop != '{'){
return false;
}else {
continue;
}
}
stack.push(s.charAt(i));
}
return stack.empty();
}
}
括号生成(22)- 1
// leetcode22:括号生成:https://leetcode.cn/problems/generate-parentheses/description/
class Solution {
public List<String> generateParenthesis(int n) {
List<String> res = new ArrayList<>();
getParenthesis(res, "", n, n);
return res;
}
// str表示生成的有效括号,最后都放入res集合
// left表示左括号数量、right表示右括号数量
private void getParenthesis(List<String> res, String str,int left, int right) {
// 当左括号与右括号数量都为0时,将生成的有效括号放入res集合
if(left == 0 && right == 0 ){
res.add(str);
return;
}
if(left > right){
return;
}
if(left > 0){
getParenthesis(res, str+"(", left-1, right);
}
if(right > 0){
getParenthesis(res, str+")", left, right-1);
}
}
private void getParenthesis1(List<String> res, String str, int left, int right) {
if(left == 0 && right == 0 ){
res.add(str);
return;
}
if(left > right){
return;
}else if(left == right){
// 剩余左右括号数相等,下一个只能用左括号
getParenthesis(res, str+"(", left-1, right);
}else if(left < right){
// 剩余左括号小于右括号,下一个可以用左括号也可以用右括号
if(left > 0){
getParenthesis(res, str+"(", left-1, right);
}
getParenthesis(res, str+")", left, right-1);
}
}
}
22222 - 斐波那契数列(剑指 Offer 10- I)- 33
// https://leetcode.cn/problems/fibonacci-number/
// 写一个函数,输入 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
// 输入:n = 2
// 输出:1
// 输入:n = 5
// 输出:5
class Solution {
public int fib(int n) {
final int MOD = 1000000007;
if(n < 2){
return n;
}
int[] dp = new int[n+1];
dp[0] = 0;
dp[1] = 1;
for(int i = 2; i <= n; i++){
dp[i] = dp[i-1] + dp[i-2];
dp[i] = dp[i] % MOD;
}
return dp[n];
}
}
111111- 比较版本号(165)- 79
// https://leetcode.cn/problems/compare-version-numbers/
// 给你两个版本号 version1 和 version2 ,请你比较它们。
// 版本号由一个或多个修订号组成,各修订号由一个 '.' 连接。每个修订号由 多位数字 组成,可能包含 前导零 。
// 每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。
// 比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。
// 也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 0 和 1 ,0 < 1
// 返回规则如下:
// 如果 version1 > version2 返回 1,
// 如果 version1 < version2 返回 -1,
// 除此之外返回 0
// 输入:version1 = "1.01", version2 = "1.001"
// 输出:0
// 解释:忽略前导零,"01" 和 "001" 都表示相同的整数 "1"
// 输入:version1 = "1.0", version2 = "1.0.0"
// 输出:0
// 解释:version1 没有指定下标为 2 的修订号,即视为 "0"
class Solution {
// 时间与空间复杂度均为:O(n+m)
public int compareVersion1(String version1, String version2) {
String[] v1 = version1.split("\\.");
String[] v2 = version2.split("\\.");
for (int i = 0; i < v1.length || i < v2.length; i++) {
// 遍历每个元素时,将v1Vaule与v2Vaule均重新赋值为0,因为此时v1 或 v2已经遍历完,要用0代替与另一个的值进行比较
// 如果版本号没有指定某个下标处的修订号,则该修订号视为0
int v1Value = 0, v2Value = 0;
// 依次遍历v1 与 v2,取出对应元素进行比较
if (i < v1.length) v1Value = Integer.parseInt(v1[i]);
if (i < v2.length) v2Value = Integer.parseInt(v2[i]);
if (v1Value > v2Value) return 1;
if (v1Value < v2Value) return -1;
}
return 0;
}
// 时间复杂度:O(n+m)、空间复杂度:O(1)
public int compareVersion(String version1, String version2) {
int v1Len = version1.length(), v2Len = version2.length();
int v1Index = 0, v2Index = 0;
while (v1Index < v1Len || v2Index < v2Len) {
int v1Value = 0;
for (; v1Index < v1Len && version1.charAt(v1Index) != '.'; v1Index++) {
v1Value = v1Value * 10 + (version1.charAt(v1Index) - '0');
}
v1Index++; // 上一个for循环后就是点号,此步骤目的是跳过点号
int v2Value = 0;
for (; v2Index < v2Len && version2.charAt(v2Index) != '.'; v2Index++) {
v2Value = v2Value * 10 + (version2.charAt(v2Index) - '0');
}
v2Index++; // 跳过点号
if (v1Value != v2Value) {
return v1Value > v2Value ? 1 : -1;
}
}
return 0;
}
}
11111 - x的n次方(50)- 33
// https://leetcode.cn/problems/powx-n/description/
// 实现 pow(x, n) ,即计算x的整数n次幂函数(即,x^n )
class Solution {
public double myPow(double x, int n) {
if(n == 0) return 1;
if(n == 1) return x;
if(x == 0) return 0;
if(x == 1) return 1;
// 处理负数越界的问题
if (n < 0){
if(n == Integer.MIN_VALUE){
if(x < 0) return 1;
else return 0;
}
return 1 / myPow(x, -n);
}
// 需要进行奇偶判断,否则执行出错StackOverflowError
if (n % 2 == 1){
return x * myPow(x, n-1);
}
return myPow(x*x, n/2);
}
// 超出时间限制
public double myPow1(double x, int n) {
if (n < 0 && n == Integer.MIN_VALUE){
if(x < 0) return 1;
else if(x == 1) return 1;
else return 0;
}
if(n == 0) return 1;
if(n == 1) return x;
if(x == 0) return 0;
if(x == 1) return x;
double result = x;
for(int i = 1; i < Math.abs(n); i++){
result = result * x;
}
return n > 0 ? result : 1 / result;
}
}
11111 - 复原 IP 地址(93)- 95
// https://leetcode.cn/problems/restore-ip-addresses/
// 有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔
// 给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案
// 输入:s = "101023"
// 输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
class Solution {
List<String> result = new ArrayList<String>();
Deque<String> path = new ArrayDeque<>(4);
public List<String> restoreIpAddresses(String s) {
process(s, 0, 4);
return result;
}
// start表示当前索引位置,reside表示剩余段数
public void process(String s, int start, int reside){
// 终止条件
if(start == s.length()){
// 当变量到最后一个字符 且 剩余段数为0时,将此时的path添加到结果集中
if(reside == 0){
result.add(String.join(".", path));
}
return;
}
for(int i = start; i < start + 3 && i < s.length(); i++){
// 若字符串剩余长度 大于 剩余分段所需最大长度,表明先前的分段不合理,最后会存在剩余字符
if(s.length() - i > reside * 3){
continue;
}
// 截取的字符串满足条件
if(judgeNumber(s, start, i)){
// 截取部分字符添加到path中
String cur = s.substring(start, i+1);
path.addLast(cur);
// 纵向递归
process(s, i+1, reside-1);
path.removeLast();
}
}
}
// 判断截取的字符串是否满足要求
public boolean judgeNumber(String s, int left, int right){
int len = right - left + 1;
// 当前为0开头 且 长度大于1的数字需要剪枝
if(s.charAt(left) == '0' && len > 1){
return false;
}
// 将当前截取的字符串转化成数字
int res = Integer.parseInt(s.substring(left, right+1));
// 判断截取到的数字是否符合
return res >= 0 && res <= 255;
}
}
33333 - 分发糖果(135)- 29
圆圈中最后剩下的数字(剑指 Offer 62) - 29
https://leetcode.cn/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/
1-13号纸牌,第一个放在牌堆底部,第二个放在桌子上,以此类推,当手里没牌时桌面上牌的顺序
(约瑟夫环或者叫丢手绢问题 力扣 剑指 Offer 62. 圆圈中最后剩下的数字)
- 微信拼手气红包,输入两个参数:人数、金额数,输出每个人所得的红包金额
- 有序序列插入数据索引
- 笛卡尔坐标系给五个坐标挑出欧几里得距离最近的一对
- kmp了解吗
- 哈夫曼编码了解吗?
- 关键路径了解吗
- 输入3个数,判断是否能构成直角三角形(用测试的思想)
- 存一堆无序数据用链表还是队列
实现多线程从A、B、C三个文件中读取数据并写到log.txt中
理论
快速排序
快速排序的实现的原理:
- 快速排序实现的重点在于数组的拆分,通常我们将数组的第一个元素定义为比较元素,然后将数组中小于比较元素的数放到左边,将大于比较元素的放到右边,这样我们就将数组拆分成了左右两部分:小于比较元素的数组;大于比较元素的数组。我们再对这两个数组进行同样的拆分,直到拆分到不能再拆分,数组就自然而然地以升序排列了
- 快速排序的过程:
- 先找一个基准,将这个基准赋值给临时变量tmp中
- 从后往前找第一个比基准小的数据赋值到i位置
- 从前往后找第一个比基准大的数据赋值到j位置
- 重复步骤1和步骤2直到i和j相遇,然后将tmp的值赋给i和j相遇的位置
- 将基准左边和基准右边的数据继续按照步骤1,2,3,4进行,直到数据全部排完。
时间复杂度:O(nlogn)。空间复杂度:O(logn)。快速排序是一个不稳定的排序算法
堆排序
堆排序是指利用堆(heap)这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子节点的键值或索引总是小于(或者大于)它的父节点
- 大根堆/大顶堆:每个节点的值均大于等于其左右孩子节点的值;
小根堆/小顶堆:每个节点的值均小于等于其左右孩子节点的值。 - 大根堆:nums[i] >= nums[2i+1] & nums[i] >= nums[2i+2]
小根堆:nums[i] <= nums[2i+1] & nums[i] <= nums[2i+2] 对于「升序排列」数组,需用到大根堆
对于「降序排列」数组,则需用到小根堆数组和链表的区别
数组静态分配内存,链表动态分配内存;
- 数组在内存中连续,链表不连续;
- 数组元素在栈区,链表元素在堆区;
- 数组利用下标定位,时间复杂度为O(1),链表定位元素时间复杂度O(n);
数组插入或删除元素的时间复杂度O(n),链表的时间复杂度O(1)。
递归
递归的原理:一个大问题可以分解成几个小问题,其中有的小问题可以直接实现,另一部分小问题与大问题的实现方式相同,从而进入循环,递归到最后会有一个直接实现方式,即递归出口
递归三要素:
栈:栈是一种线性的数据结构,读取规则是先进后出。栈中的数据占用的内存空间的大小是确定的,便于代码执行时的入栈、出栈操作,并由系统自动分配和自动释放内存可以及时得到回收,相对于堆来说,更加容易管理内存空间。
- 堆:堆是一种树形数据结构,读取相对复杂。堆是动态分配内存,内存大小不一,也不会自动释放。栈中的数据长度不定,且占空间比较大。便于开辟内存空间,更加方便存储。
- 堆栈内存分配:程序运行时,每个线程分配一个栈,每个进程分配一个堆。也就是说,栈是线程独占的,堆是线程共用的。此外,栈创建的时候,大小是确定的,数据超过这个大小,就发生stack overflow错误,而堆的大小是不确定的,需要的话可以不断增加。
- 队列是一种先进先出的数据结构。 队列在列表的末端增加项,在首端移除项。它允许在表的首端(队列头)进行删除操作,在表的末端(队列尾)进行插入操作
- 栈是一种后进先出的数据结构,也就是说最新添加的项最早被移出;它是一种运算受限的线性表,只能在栈顶进行插入和删除操作。向一个栈插入新元素叫入栈(进栈),就是把新元素放入到栈顶的上面,成为新的栈顶;从一个栈删除元素叫出栈,就是把栈顶的元素删除掉,相邻的成为新栈顶
应用场景:
平衡二叉树树有以下规则:
- 规则1:每个节点最多只有两个子节点(二叉)
- 规则2:每个节点的值比它的左子树所有的节点大,比它的右子树所有节点小(有序)
- 规则3:每个节点左子树的高度与右子树高度之差的绝对值不超过1
- 红黑树的规则:它一种特殊的二叉查找树
- 规则1:每个节点不是黑色就是红色
- 规则2:根节点为黑色
- 规则3:红色节点的父节点和子节点不能为红色
- 规则4:所有的叶子节点都是黑色(空节点视为叶子节点NIL)
- 规则5:每个节点到叶子节点的每个路径黑色节点的个数都相等
- 平衡二叉树和红黑树的区别:
- 平衡二叉树的左右子树的高度差绝对值不超过1,但是红黑树在某些时刻可能会超过1,只要符合红黑树的五个条件即可。
- 二叉树只要不平衡就会进行旋转,而红黑树不符合规则时,有些情况只用改变颜色不用旋转,就能达到平衡。
- 红黑树的时间复杂度为:O(logn)