Merge remote-tracking branch 'origin/master'

This commit is contained in:
markilue 2023-04-13 10:02:51 +08:00
commit 764aae3459
35 changed files with 3707 additions and 105 deletions

View File

@ -9,6 +9,33 @@
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
<artifactId>DataStructures</artifactId> <artifactId>DataStructures</artifactId>
<build> <build>
<plugins> <plugins>

View File

@ -0,0 +1,73 @@
package com.atguigu.sort.selftry;
import org.junit.Test;
import java.util.Arrays;
/**
*@BelongsProject: DataStructures_Algorithm
*@BelongsPackage: com.atguigu.sort.selftry
*@Author: markilue
*@CreateTime: 2023-03-31 10:26
*@Description: TODO 尝试堆排序
*@Version: 1.0
*/
public class HeapSort {
@Test
public void test(){
int[] arr = {20, 50, 45, 40, 35,10,30,15,25};
//测试80000个数据进行测试
// int[] arr = new int[200];
// for (int i = 0; i < arr.length; i++) {
// arr[i] = (int) (Math.random() * 200); //生成一个[0,80000]的数
// }
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
public void heapSort(int[] arr) {
if (arr == null) return;
//从最后一个非叶子节点开始进行堆化
for (int i = arr.length / 2 - 1; i >= 0; i--) {
adjustHeap(arr, i, arr.length);
}
//进行交换
int temp;
for (int i = arr.length - 1; i > 0; i--) {
temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
adjustHeap(arr, 0, i);//把交换完的i放在合适的位置
}
}
public void adjustHeap(int[] arr, int i, int length) {
int temp = arr[i];//记录下以前位置的值
for (int j = 2 * i + 1; j < length; j++) {
//比较左右节点哪一个更大
if (j + 1 < length && arr[j] < arr[j + 1]) {
j++;
}
if (arr[j] > temp) {
//需要进行交换
arr[i] = arr[j];
i = j;//后续比较j位置的值了
} else {
break;
}
}
arr[i] = temp;//找到合适的位置放temp
}
}

View File

@ -1,5 +1,7 @@
package com.atguigu.tree; package com.atguigu.tree;
import org.junit.Test;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
@ -17,44 +19,57 @@ public class HeapSort {
// System.out.println(Arrays.toString(arr)); // System.out.println(Arrays.toString(arr));
//测试80000个数据进行测试 //测试80000个数据进行测试
int[] arr =new int[8000000]; int[] arr = new int[8000000];
for (int i = 0; i < arr.length; i++) { for (int i = 0; i < arr.length; i++) {
arr[i] = (int)(Math.random()*8000000); //生成一个[0,80000]的数 arr[i] = (int) (Math.random() * 8000000); //生成一个[0,80000]的数
} }
Date date1 = new Date(); Date date1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(date1); String date1Str = simpleDateFormat.format(date1);
System.out.println("排序前的时间:"+date1Str); //排序前的时间:2022-02-15 21:19:36 System.out.println("排序前的时间:" + date1Str); //排序前的时间:2022-02-15 21:19:36
heapSort(arr); heapSort(arr);
Date date2 = new Date(); Date date2 = new Date();
String date2Str = simpleDateFormat.format(date2); String date2Str = simpleDateFormat.format(date2);
System.out.println("排序后的时间:"+date2Str); //排序后的时间:2022-02-15 21:19:40 System.out.println("排序后的时间:" + date2Str); //排序后的时间:2022-02-15 21:19:40
} }
@Test
public void test() {
int[] arr = {20, 50, 45, 40, 35,10,30,15,25};
//测试80000个数据进行测试
// int[] arr = new int[200];
// for (int i = 0; i < arr.length; i++) {
// arr[i] = (int) (Math.random() * 200); //生成一个[0,80000]的数
// }
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
/** /**
* 编写一个堆排序的方法 * 编写一个堆排序的方法
*/ */
public static void heapSort(int[] arr) { public static void heapSort(int[] arr) {
//TODO (1)将无序序列构建成一个堆,根据升序降序需求选择大顶堆或者小顶堆 //TODO (1)将无序序列构建成一个堆,根据升序降序需求选择大顶堆或者小顶堆
//arr.length/2-1求出来的是完全二叉树的最后一个非叶子节点 // arr.length/2-1求出来的是完全二叉树的最后一个非叶子节点;
for (int i = arr.length/2-1; i >= 0; i--) { // arr.length/2-1是关键是adjustHeap前提的满足条件的前提
adjustHeap(arr,i,arr.length); for (int i = arr.length / 2 - 1; i >= 0; i--) {
adjustHeap(arr, i, arr.length);
} }
int temp=0; int temp = 0;
//TODO (2)将顶对元素与末尾元素交换,将最大元素放置在数组末尾 //TODO (2)将顶对元素与末尾元素交换,将最大元素放置在数组末尾
//TODO (3)重新调整结构,使其满足堆定义,然后继续交换对顶元素与当前末尾元素,贩毒执行调整+交换步骤,直到整个序列有序 //TODO (3)重新调整结构,使其满足堆定义,然后继续交换对顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序
for (int i = arr.length-1; i >0; i--) { for (int i = arr.length - 1; i > 0; i--) {
//交换 //交换
temp=arr[i]; temp = arr[i];
arr[i]=arr[0]; arr[i] = arr[0];
arr[0]=temp; arr[0] = temp;
adjustHeap(arr,0,i); adjustHeap(arr, 0, i);//主要就是利用堆顶元素就是最大值来进行处理的
} }
@ -62,7 +77,7 @@ public class HeapSort {
} }
/** /**
* 前提:该节点的所有子节点都已经是大顶堆了 * TODO 前提:该节点的所有子节点都已经是大顶堆了因为这里需要break
* 将以第i个位置为根节点时以下树,调整成一个大顶堆 * 将以第i个位置为根节点时以下树,调整成一个大顶堆
* *
* @param arr 待调整的数组 * @param arr 待调整的数组
@ -76,20 +91,22 @@ public class HeapSort {
//开始调整 //开始调整
//说明:j = i * 2 + 1 ;这里k就是i节点的左子节点 //说明:j = i * 2 + 1 ;这里k就是i节点的左子节点
for (int j = i * 2 + 1; j < length; j = j * 2 + 1) { for (int j = i * 2 + 1; j < length; j = j * 2 + 1) {
//TODO 1.寻找左右节点哪一个更大
if (j + 1 < length && arr[j] < arr[j + 1]) { if (j + 1 < length && arr[j] < arr[j + 1]) {
//左子节点的值小于右子节点的值 //左子节点的值小于右子节点的值
j++; //j指向右子节点 j++; //j指向右子节点
} }
//TODO 2.用左右节点中大值去和他的父节点比较如果小了直接将父节点变成那个最大的值
if (arr[j] > temp) { if (arr[j] > temp) {
//如果子节点大于父节点 //如果子节点大于父节点
arr[i] = arr[j];//把较大的值赋给当前节点 arr[i] = arr[j];//把较大的值赋给当前节点
i = j; //将i指向j继续循环比较 i = j; //将i指向j继续循环比较
}else { } else {
break; break;//左右子节点都比父节点小直接break
} }
} }
//当for循环结束后,我们已经将以i为父节点的树的最大值,放在了i的位置 //当for循环结束后,我们已经将以i为父节点的树的最大值,放在了i的位置
arr[i]=temp; //将temp值放在调整后的位置 arr[i] = temp; //将temp值放在调整后的位置
} }

View File

@ -0,0 +1,217 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-13 17:13
*@Description:
* TODO 力扣146题 LRU缓存:
* 请你为 最不经常使用LFU缓存算法设计并实现数据结构
* 实现 LFUCache
* LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象
* int get(int key) - 如果键 key 存在于缓存中则获取键的值否则返回 -1
* void put(int key, int value) - 如果键 key 已存在则变更其值如果键不存在请插入键值对当缓存达到其容量 capacity 则应该在插入新项之前移除最不经常使用的项在此问题中当存在平局即两个或更多个键具有相同使用频率应该去除 最近最久未使用 的键
* 为了确定最不常使用的键可以为缓存中的每个键维护一个 使用计数器 使用计数最小的键是最久未使用的键
* 当一个键首次插入到缓存中时它的使用计数器被设置为 1 (由于 put 操作)对缓存中的键执行 get put 操作使用计数器的值将会递增
* 函数 get put 必须以 O(1) 的平均时间复杂度运行
*@Version: 1.0
*/
public class T55_LFUCache {
Map<Integer, Node> cache;
Map<Integer, NodeList> freqMap;
int capacity;
int min;
int size;
public T55_LFUCache() {
}
public T55_LFUCache(int capacity) {
this.capacity = capacity;
cache = new HashMap<>();
freqMap = new HashMap<>();
min = 0;
size = 0;
}
public int get(int key) {
Node node = cache.get(key);
if (node == null) {
return -1;
}
freqInc(node);
return node.value;
}
public void put(int key, int value) {
if (capacity == 0) {
return;
}
Node node = cache.get(key);
if (node != null) {
//更新即可
node.value = value;
freqInc(node);
} else {
if (size == capacity) {
//如果已经等于了就要进行删除
removeNode();
}
Node node1 = new Node(key, value, 1);
cache.put(key, node1);
addNode(node1);
size++;
}
}
public void freqInc(Node node) {
int freq = node.freq;
NodeList nodeList = freqMap.get(freq);
if (nodeList != null) {
nodeList.removeNode(node);
} else {
nodeList = new NodeList();
freqMap.put(freq, nodeList);
}
if (freq == min && nodeList.isEmpty()) {
min = freq + 1;
}
node.freq++;
NodeList nodeList1 = freqMap.get(freq + 1);
if (nodeList1 == null) {
nodeList1 = new NodeList();
freqMap.put(freq + 1, nodeList1);
}
nodeList1.addNode(node);
}
public void addNode(Node node) {
int freq = node.freq;
NodeList nodeList = freqMap.get(freq);
if (nodeList == null) {
nodeList = new NodeList();
freqMap.put(freq, nodeList);
}
nodeList.addNode(node);
min = 1;
}
public void removeNode() {
NodeList nodeList = freqMap.get(min);
Node node = nodeList.removeNode();
cache.remove(node.key);
size--;
}
@Test
public void test() {
T55_LFUCache lRUCache = new T55_LFUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
System.out.println(lRUCache.get(1)); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废缓存是 {1=1, 3=3}
System.out.println(lRUCache.get(2)); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废缓存是 {4=4, 3=3}
System.out.println(lRUCache.get(1)); // 返回 -1 (未找到)
System.out.println(lRUCache.get(3)); // 返回 3
System.out.println(lRUCache.get(4)); // 返回 4
}
public class Node {
int key;
int value;
int freq;
Node pre;
Node next;
public Node() {
}
public Node(int key, int value) {
this.key = key;
this.value = value;
this.freq = 1;
}
public Node(int key, int value, int freq) {
this.key = key;
this.value = value;
this.freq = freq;
}
public Node(int key, int value, int freq, Node pre, Node next) {
this.key = key;
this.value = value;
this.freq = freq;
this.pre = pre;
this.next = next;
}
}
public class NodeList {
Node head;
Node tail;
int size;
public NodeList() {
head = new Node();
tail = new Node();
head.next = tail;
tail.pre = head;
size = 0;
}
//移除链表中的最后一个节点
public Node removeNode() {
return removeNode(tail.pre);
}
//移除链表指定元素
public Node removeNode(Node node) {
node.pre.next = node.next;
node.next.pre = node.pre;
size--;
return node;
}
//加在链表中第一个位置
public void addNode(Node node) {
node.next = head.next;
head.next.pre = node;
head.next = node;
node.pre = head;
size++;
}
public boolean isEmpty() {
return size == 0;
}
}
}

View File

@ -0,0 +1,72 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
import java.util.Arrays;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-30 10:14
*@Description:
* TODO 力扣300 最长递增子序列:
* 给你一个整数数组 nums 找到其中最长严格递增子序列的长度
* 子序列 是由数组派生而来的序列删除或不删除数组中的元素而不改变其余元素的顺序例如[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列
*@Version: 1.0
*/
public class T78_LengthOfLIS {
@Test
public void test() {
// int[] nums={10,9,2,5,3,7,101,18};
int[] nums = {1, 3, 6, 7, 9, 4, 10, 5, 6};
System.out.println(lengthOfLIS(nums));
}
/**
* 思路:dp
* dp[i]表示以0开头以i结尾的数组的最长递增子序列长度
* @param nums
* @return
*/
public int lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length];
Arrays.fill(dp, 1);
int max = 0;
for (int i = 1; i < dp.length; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
if (max < dp[i]) max = dp[i];
}
}
return max;
}
public int lengthOfLIS1(int[] nums) {
int N = nums.length;
//end[i]表示i+1长度的递增子序列的最小值
int[] end = new int[N];
end[0] = nums[0];
int index = 0;
for(int i=1; i< N;i++){
if(nums[i] > end[index]){
end[++index] = nums[i];
} else {
for (int j = 0; j <= index; j++) {
if (nums[i] <= end[j]) {
end[j] = nums[i];//替换对应的位置
break;
}
}
}
}
return index + 1;
}
}

View File

@ -0,0 +1,241 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-30 10:36
*@Description:
* TODO 力扣301 删除无效的括号:
* 给你一个由若干括号和字母组成的字符串 s 删除最小数量的无效括号使得输入的字符串有效
* 返回所有可能的结果答案可以按 任意顺序 返回
*@Version: 1.0
*/
public class T79_RemoveInvalidParentheses {
//不知道该怎么去重就用set存储结果 18ms
Set<String> set = new HashSet<>();
StringBuilder str;
public List<String> removeInvalidParentheses(String s) {
int lmove = 0;
int rmove = 0;
str = new StringBuilder(s);
//遍历一遍求出要删除左括号和右括号的数量
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {
lmove++;
} else if (s.charAt(i) == ')') {
//左右括号可以匹配
if (lmove > 0) {
lmove--;
} else {
rmove++;
}
}
}
dfs(lmove, rmove, 0);
return new ArrayList<>(set);
}
public void dfs(int lmove, int rmove, int i) {
if (lmove == 0 && rmove == 0) {
//判断字符串内括号是否有效
if (isValid(str)) {
set.add(new String(str.toString()));
}
return;
}
//还需要删除的括号个数 小于 能过删除的字符个数
if (lmove + rmove > str.length() - i) {
return;
}
//不删除
dfs(lmove, rmove, i + 1);
//删除分左右括号考虑
if (str.charAt(i) == '(' && lmove > 0) {
str.deleteCharAt(i);//删除
dfs(lmove - 1, rmove, i);//进入下一层因为删除了i处括号所以不需要i+1
str.insert(i, '(');//恢复现场
} else if (str.charAt(i) == ')' && rmove > 0) {
str.deleteCharAt(i);//删除
dfs(lmove, rmove - 1, i);//进入下一层
str.insert(i, ')');//恢复现场
}
}
public boolean isValid(StringBuilder str) {
int left = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == '(') {
left++;
} else if (str.charAt(i) == ')') {
left--;
}
if (left < 0) {
return false;
}
}
return true;
}
//自己去重4ms
private List<String> res = new ArrayList<String>();
public List<String> removeInvalidParentheses1(String s) {
int lremove = 0;
int rremove = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {
lremove++;
} else if (s.charAt(i) == ')') {
if (lremove == 0) {
rremove++;
} else {
lremove--;
}
}
}
helper(s, 0, lremove, rremove);
return res;
}
private void helper(String str, int start, int lremove, int rremove) {
if (lremove == 0 && rremove == 0) {
if (isValid(str)) {
res.add(str);
}
return;
}
for (int i = start; i < str.length(); i++) {
if (i != start && str.charAt(i) == str.charAt(i - 1)) {
continue;
}
// 如果剩余的字符无法满足去掉的数量要求直接返回
if (lremove + rremove > str.length() - i) {
return;
}
// 尝试去掉一个左括号
if (lremove > 0 && str.charAt(i) == '(') {
helper(str.substring(0, i) + str.substring(i + 1), i, lremove - 1, rremove);
}
// 尝试去掉一个右括号
if (rremove > 0 && str.charAt(i) == ')') {
helper(str.substring(0, i) + str.substring(i + 1), i, lremove, rremove - 1);
}
}
}
private boolean isValid(String str) {
int cnt = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == '(') {
cnt++;
} else if (str.charAt(i) == ')') {
cnt--;
if (cnt < 0) {
return false;
}
}
}
return cnt == 0;
}
@Test
public void test(){
String s ="()())()";
System.out.println(removeInvalidParentheses2(s));
}
//自己尝试一遍
public List<String> removeInvalidParentheses2(String s) {
//统计需要去掉的左括号和右括号的数量
int left = 0;
int right = 0;
for (char c : s.toCharArray()) {
if (c == '(') {
left++;
} else if (c == ')') {
if (left == 0) {
right++;
} else {
left--;
}
}
}
helper1(s, left, right, 0);
return res;
}
public void helper1(String s, int left, int right, int start) {
if (left == 0 && right == 0) {
if (isValid1(s)) {
res.add(s);
}
return;
}
for (int i = start; i < s.length(); i++) {
//去重
if (i != start && s.charAt(i) == s.charAt(i - 1)) continue;
if (left + right > s.length() - i) {
//怎么删都不够了
return;
}
//尝试删除左括号
if (left > 0 && s.charAt(i) == '(') {
helper1(s.substring(0, i) + s.substring(i + 1), left - 1, right, i + 1);
}
//尝试删除右括号
if (right > 0 && s.charAt(i) == ')') {
helper1(s.substring(0, i) + s.substring(i + 1), left, right - 1, i + 1);
}
}
}
public boolean isValid1(String s) {
int left = 0;
for (char c : s.toCharArray()) {
if (c == '(') {
left++;
} else if (c == ')') {
if (left == 0) {
return false;
} else {
left--;
}
}
}
return left==0;
}
}

View File

@ -0,0 +1,75 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-31 11:13
*@Description:
* TODO 力扣309 最佳买卖股票时机含冷冻期:
* 给定一个整数数组prices其中第 prices[i] 表示第 i 天的股票价格
* 设计一个算法计算出最大利润在满足以下约束条件下你可以尽可能地完成更多的交易多次买卖一支股票:
* 卖出股票后你无法在第二天买入股票 (即冷冻期为 1 )
* 注意你不能同时参与多笔交易你必须在再次购买前出售掉之前的股票
*@Version: 1.0
*/
public class T80_MaxProfit {
@Test
public void test() {
int[] prices = {1, 2, 3, 0, 2};
System.out.println(maxProfit(prices));
}
/**
* 动态dp,状态分为三种:
* 今天手里没有股票且不在冷冻期dp[i][0]
* 今天手里没有股票且在冷冻期dp[i][1]
* 今天手里有股票dp[i][2]
* @param prices
* @return
*/
public int maxProfit(int[] prices) {
if (prices.length == 1) return 0;
int[][] dp = new int[prices.length][3];
dp[0][0] = 0;
dp[0][1] = 0;
dp[0][2] = -prices[0];
for (int i = 1; i < prices.length; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]);//昨天是冷冻期;或者昨天也没买
dp[i][1] = dp[i - 1][2] + prices[i];//必须是今天买入
dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][0] - prices[i]);//必须是今天买入
}
return Math.max(dp[dp.length - 1][0], dp[dp.length - 1][1]);
}
//滚动数组优化
public int maxProfit1(int[] prices) {
if (prices.length == 1) return 0;
int dp0 = 0;
int dp1 = 0;
int dp2 = -prices[0];
for (int i = 1; i < prices.length; i++) {
int temp = dp0;
dp0 = Math.max(dp0, dp1);//昨天是冷冻期;或者昨天也没买
dp1 = dp2 + prices[i];//必须是今天买入
dp2 = Math.max(dp2, temp - prices[i]);//必须是今天买入
}
return Math.max(dp0, dp1);
}
}

View File

@ -0,0 +1,125 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-03-31 11:28
*@Description:
* TODO 力扣312 戳气球:
* n 个气球编号为0 n - 1每个气球上都标有一个数字这些数字存在数组 nums
* 现在要求你戳破所有的气球戳破第 i 个气球你可以获得 nums[i - 1] * nums[i] * nums[i + 1] 枚硬币
* 这里的 i - 1 i + 1 代表和 i 相邻的两个气球的序号如果 i - 1或 i + 1 超出了数组的边界那么就当它是一个数字为 1 的气球
* 求所能获得硬币的最大数量
*@Version: 1.0
*/
public class T81_MaxCoins {
@Test
public void test() {
int[] nums = {3, 1, 5, 8};
System.out.println(maxCoins1(nums));
}
/**
* 思路:优先戳中间最小的
* 不对
* @param nums
* @return
*/
public int maxCoins(int[] nums) {
int result = 0;
int count = nums.length;
while (nums[0] != -1) {
int minIndex = 0;
int threshold = nums.length;
if (count == 3) {
boolean flag = false;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == -1) {
threshold = i;
break;
}
if (nums[i] == 1) {
minIndex = i;
flag = true;
}
}
if (!flag) {
minIndex = 1;
}
}else {
for (int i = 1; i < nums.length; i++) {
if (nums[i] == -1) {
threshold = i;
break;
}
if (nums[minIndex] > nums[i]) {
minIndex = i;
}
}
}
//戳i位置
int left;
if (minIndex - 1 < 0) {
left = 1;
} else {
left = nums[minIndex - 1];
}
int right;
if (minIndex + 1 >= threshold) {
right = 1;
} else {
right = nums[minIndex + 1];
}
result += left * nums[minIndex] * right;
System.arraycopy(nums, minIndex + 1, nums, minIndex, threshold - minIndex - 1);
nums[threshold - 1] = -1;
count--;
}
return result;
}
/**
* 官方动态规划法:
* dp[i][j]表示填满开区间(i,j)能得到的最多硬币数
* 最终答案即为 dp[0][n+1]dp[0][n+1]dp[0][n+1]实现时要注意到动态规划的次序
* 动态规划:
* 速度击败27.13% 内存击败45.56% 43ms
* @param nums
* @return
*/
public int maxCoins1(int[] nums) {
int n = nums.length;
int[][] rec = new int[n + 2][n + 2];
int[] val = new int[n + 2];
val[0] = val[n + 1] = 1;
for (int i = 1; i <= n; i++) {
val[i] = nums[i - 1];
}
for (int i = n - 1; i >= 0; i--) {
for (int j = i + 2; j <= n + 1; j++) {
for (int k = i + 1; k < j; k++) {
int sum = val[i] * val[k] * val[j];//计算戳当前点的sum
sum += rec[i][k] + rec[k][j];//计算不要这个点之后的左右点的戳开的和
rec[i][j] = Math.max(rec[i][j], sum);
}
}
}
return rec[0][n + 1];
}
}

View File

@ -0,0 +1,84 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
import java.util.Arrays;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-03 09:49
*@Description:
* TODO 力扣322 零钱兑换:
* 给你一个整数数组 coins 表示不同面额的硬币以及一个整数 amount 表示总金额
* 计算并返回可以凑成总金额所需的 最少的硬币个数 如果没有任何一种硬币组合能组成总金额返回 -1
* 你可以认为每种硬币的数量是无限的
*@Version: 1.0
*/
public class T82_CoinChange {
@Test
public void test() {
int[] coins = {186, 419, 83, 408};
int amount = 6249;
System.out.println(coinChange(coins, amount));
}
/**
* 思路:硬币可以使用无数次 完全背包
* 动态DP
* @param coins
* @param amount
* @return
*/
public int coinChange(int[] coins, int amount) {
int[][] dp = new int[coins.length][amount + 1];
Arrays.sort(coins);
dp[0][0] = 0;
for (int i = 1; i < dp[0].length; i++) {
if (i >= coins[0] && i % coins[0] == 0) dp[0][i] = dp[0][i - coins[0]] + 1;
else dp[0][i] = Integer.MAX_VALUE;
}
for (int i = 1; i < dp.length; i++) {
for (int j = 1; j < dp[0].length; j++) {
if (j >= coins[i] && dp[i][j - coins[i]] != Integer.MAX_VALUE) {
dp[i][j] = Math.min(dp[i][j - coins[i]] + 1, dp[i - 1][j]);
} else dp[i][j] = dp[i - 1][j];//使用哪一个
}
}
return dp[coins.length - 1][amount] == Integer.MAX_VALUE ? -1 : dp[coins.length - 1][amount];
}
/**
* 一维dp优化
* @param coins
* @param amount
* @return
*/
public int coinChange1(int[] coins, int amount) {
int[] dp = new int[amount + 1];
// Arrays.sort(coins);
dp[0] = 0;
for (int i = 1; i < dp.length; i++) {
if (i >= coins[0] && i % coins[0] == 0) dp[i] = dp[i - coins[0]] + 1;
else dp[i] = Integer.MAX_VALUE;
}
for (int i = 1; i < coins.length; i++) {
for (int j = coins[i]; j < dp.length; j++) {
if (j >= coins[i] && dp[j - coins[i]] != Integer.MAX_VALUE) {
dp[j] = Math.min(dp[j - coins[i]] + 1, dp[j]);
}
}
}
return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
}
}

View File

@ -0,0 +1,41 @@
package com.markilue.leecode.hot100;
import com.markilue.leecode.tree.TreeNode;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-03 10:24
*@Description:
* TODO 力扣337 打家劫舍III:
* 小偷又发现了一个新的可行窃的地区这个地区只有一个入口我们称之为 root
* 除了 root 之外每栋房子有且只有一个房子与之相连一番侦察之后聪明的小偷意识到这个地方的所有房屋的排列类似于一棵二叉树
* 如果 两个直接相连的房子在同一天晚上被打劫 房屋将自动报警
* 给定二叉树的 root 返回 在不触动警报的情况下 小偷能够盗取的最高金额
*@Version: 1.0
*/
public class T83_Rob {
public int rob(TreeNode root) {
int[] dp = subRob(root);
return Math.max(dp[0],dp[1]);
}
public int[] subRob(TreeNode root) {
if (root == null) {
return new int[]{0, 0};
}
int[] left = subRob(root.left);
int[] right = subRob(root.right);
int steal = left[0] + right[0] + root.val;
int noSteal = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
return new int[]{noSteal, steal};
}
}

View File

@ -0,0 +1,124 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
import java.util.Arrays;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-03 10:38
*@Description:
* TODO 力扣338 比特位计数:
* 给你一个整数 n 对于 0 <= i <= n 中的每个 i 计算其二进制表示中 1 的个数 返回一个长度为 n + 1 的数组 ans 作为答案
*@Version: 1.0
*/
public class T84_CountBits {
@Test
public void test1() {
int n = 85723;
System.out.println(Arrays.toString(countBits(n)));
}
/**
*思路:挨个增加有则进位
* 速度击败6.9% 内存击败13.58% 59ms
* @param n
* @return
*/
public int[] countBits(int n) {
int[] nums = new int[n + 1];
long cur = 0;
for (int i = 0; i < nums.length; i++) {
long[] check = check(cur);
cur = check[0] + 1;
nums[i] = (int)check[1];
}
return nums;
}
@Test
public void test() {
int num = 1112;
System.out.println(check(num));
}
public long[] check(long n) {
//int count =0;
boolean flag = false;//是否进位
int num = 0;
int count = 0;
StringBuilder sb = new StringBuilder();
while (n != 0 || flag) {
long cur = n % 10;
if (flag) cur += 1;
if (cur == 1) count++;
if (cur > 1) {
flag = true;
sb.append(0);
} else {
flag = false;
sb.append(cur);
}
n /= 10;
}
if (sb.length() == 0) {
sb.append(0);
}
return new long[]{Long.parseLong(sb.reverse().toString()), count};
}
/**
* 官方题解:Brian Kernighan 算法
* x=x & (x1) 这一运算将x的二进制表示的最后一个1变成0
* 因此重复该操作直到x变成0,就可以得到x中1的数量
* 速度击败41.7% 内存击败28.3% 2ms
* 时间复杂度O(nlogn)
* @param n
* @return
*/
public int[] countBits1(int n) {
int[] bits = new int[n + 1];
for (int i = 0; i <= n; i++) {
bits[i] = countOnes(i);
}
return bits;
}
public int countOnes(int x) {
int ones = 0;
while (x > 0) {
x &= (x - 1);
ones++;
}
return ones;
}
/**
* 官方题解:动态规划法:最低位有效值
* 即如果x为偶数,bit[x]=bit[x/2]
* 如果x为奇数bit[x]=bit[x/2]+1
* 是否加1可以使用对2取余:x 除以 2的余数可以通过 x & 1 得到
* @param n
* @return
*/
public int[] countBits2(int n) {
int[] bits = new int[n + 1];
for (int i = 1; i <= n; i++) {
bits[i] = bits[i >> 1] + (i & 1);
}
return bits;
}
}

View File

@ -0,0 +1,98 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
import java.util.*;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-03 11:37
*@Description:
* TODO 力扣347 前k个高频元素:
* 给你一个整数数组 nums 和一个整数 k 请你返回其中出现频率前 k 高的元素你可以按 任意顺序 返回答案
*@Version: 1.0
*/
public class T85_TopKFrequent {
@Test
public void test(){
int[] nums={4,1,-1,2,-1,2,3};
System.out.println(topKFrequent(nums,2));
}
/**
* 统计每个单词出现的次数然后进行堆化
* 速度击败87.71% 内存击败12.19% 12ms
* @param nums
* @param k
* @return
*/
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();//<nums,count>
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] != o2[0] ? o2[1] - o1[1] : o2[0] - o1[0];
}
});
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
queue.add(new int[]{entry.getKey(), entry.getValue()});
}
int[] result = new int[k];
for (int i = 0; i < k; i++) {
if(!queue.isEmpty()) result[i] = queue.poll()[0];
}
return result;
}
/**
* 官方最快:先计算每个数字出现的次数使用arrayList将相同次数的数字放在一次
* @param nums
* @param k
* @return
*/
public int[] topKFrequent1(int[] nums, int k) {
int max = Integer.MIN_VALUE,min = Integer.MAX_VALUE;
for(int i : nums){//找出最大最小值
max = (max < i)?i:max;
min = (min > i)?i:min;
}
if(max==min)return new int[]{nums[0]};
int times[] = new int[max-min+1];//定义桶大小
for(int i : nums){
times[i-min]++;//记录各个数出现的次数
}
ArrayList<Integer> count[] = new ArrayList[nums.length];//不同数量对应的数字集合
for(int i = 0; i < times.length; i++){
if(times[i] > 0){
if(count[times[i]] == null){
count[times[i]] = new ArrayList();
}
count[times[i]].add(i+min);//相同次数的加在同一个count[i]这种
}
}
int res[] = new int[k];//答案
for(int i = count.length-1,j = 0; i >=0 && j < k ; i--){
if(count[i] != null){
while(!count[i].isEmpty()){
res[j++] = count[i].remove(count[i].size()-1);
}
}
}
return res;
}
}

View File

@ -0,0 +1,146 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-06 10:04
*@Description:
* TODO 力扣394 字符串解码:
* 给定一个经过编码的字符串返回它解码后的字符串
* 编码规则为: k[encoded_string]表示其中方括号内部的 encoded_string 正好重复 k 注意 k 保证为正整数
* 你可以认为输入字符串总是有效的输入字符串中没有额外的空格且输入的方括号总是符合格式要求的
* 此外你可以认为原始数据不包含数字所有的数字只表示重复的次数 k 例如不会出现像 3a 2[4] 的输入
*@Version: 1.0
*/
public class T86_DecodeString {
@Test
public void test() {
String s = "3[a]2[bc]";
System.out.println(decodeString(s));
}
@Test
public void test1() {
String s = "3[a2[c]]";
System.out.println(decodeString(s));
}
@Test
public void test2() {
String s = "3[z]2[2[y]pq4[2[jk]e1[f]]]ef";
System.out.println(decodeString(s));
}
int start = 0;
public String decodeString(String s) {
return decodeSubString(s.toCharArray(), 1);
}
public String decodeSubString(char[] chars, int count) {
if (chars[start] == '[') start++;
StringBuilder sb = new StringBuilder();
int sum = 0;
while (start < chars.length && Character.isDigit(chars[start])) {
sum = sum * 10 + (chars[start] - '0');
start++;
}
//如果前面没有数字,返回1次
if (sum == 0) {
while (start < chars.length && chars[start] >= 'a' && chars[start] <= 'z') {
sb.append(chars[start++]);
}
} else {
sb.append(decodeSubString(chars, sum));
}
// if (start < chars.length && Character.isDigit(chars[start])) {
// sb.append(decodeSubString(chars, 1));
// }
if (start < chars.length && chars[start] == ']') {
start++;
}
if (count > 1) {
String k = sb.toString();
for (int i = 0; i < count - 1; i++) {
sb.append(k);
}
}
if (start < chars.length && chars[start] != ']') {
sb.append(decodeSubString(chars, 1));
}
return sb.toString();
}
String src;
int ptr;
/**
* 官方递归法:
* 思路和本人一致但是思路更清晰和简洁
* 速度击败100% 内存击败76.28%
* @param s
* @return
*/
public String decodeString1(String s) {
src = s;
ptr = 0;
return getString();
}
public String getString() {
if (ptr == src.length() || src.charAt(ptr) == ']') {
// String -> EPS
return "";
}
char cur = src.charAt(ptr);
int repTime = 1;
StringBuilder ret = new StringBuilder();
if (Character.isDigit(cur)) {
// String -> Digits [ String ] String
// 解析 Digits
repTime = getDigits();
// 过滤左括号
++ptr;
// 解析 String
String str = getString();
// 过滤右括号
++ptr;
// 构造字符串
while (repTime-- > 0) {
ret.append(str);
}
} else if (Character.isLetter(cur)) {
// String -> Char String
// 解析 Char
ret.append(String.valueOf(src.charAt(ptr++)));
}
ret.append(getString());
return ret.toString();
}
public int getDigits() {
int ret = 0;
while (ptr < src.length() && Character.isDigit(src.charAt(ptr))) {
ret = ret * 10 + src.charAt(ptr++) - '0';
}
return ret;
}
}

View File

@ -0,0 +1,186 @@
package com.markilue.leecode.hot100;
import java.util.*;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-06 11:47
*@Description:
* TODO 力扣399 除法求值:
* 给你一个变量对数组 equations 和一个实数值数组 values 作为已知条件其中 equations[i] = [Ai, Bi] values[i] 共同表示等式 Ai / Bi = values[i] 每个 Ai Bi 是一个表示单个变量的字符串
* 另有一些以数组 queries 表示的问题其中 queries[j] = [Cj, Dj] 表示第 j 个问题请你根据已知条件找出 Cj / Dj = ? 的结果作为答案
* 返回 所有问题的答案 如果存在某个无法确定的答案则用 -1.0 替代这个答案如果问题中出现了给定的已知条件中没有出现的字符串也需要用 -1.0 替代这个答案
* 注意输入总是有效的你可以假设除法运算中不会出现除数为 0 的情况且不存在任何矛盾的结果
*@Version: 1.0
*/
public class T87_CalcEquation {
/**
* 官方题解:并查集解法
* 速度击败100% 内存击败85.81% 0ms
* @param equations
* @param values
* @param queries
* @return
*/
public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
int equationSize = equations.size();
UnionFind unionFind = new UnionFind(2 * equationSize);
//第一步:预处理将变量的值与id进行映射是的并查集的底层使用数组实现方便编码
HashMap<String, Integer> hashMap = new HashMap<>(2 * equationSize);
int id = 0;
for (int i = 0; i < equationSize; i++) {
List<String> equation = equations.get(i);
String var1 = equation.get(0);
String var2 = equation.get(1);
if (!hashMap.containsKey(var1)) {
hashMap.put(var1, id);
id++;
}
if (!hashMap.containsKey(var2)) {
hashMap.put(var2, id);
id++;
}
unionFind.union(hashMap.get(var1), hashMap.get(var2), values[i]);
}
//第二部:做查询
int queriesSize = queries.size();
double[] res = new double[queriesSize];
for (int i = 0; i < queriesSize; i++) {
String var1 = queries.get(i).get(0);
String var2 = queries.get(i).get(1);
Integer id1 = hashMap.get(var1);
Integer id2 = hashMap.get(var2);
if (id1 == null || id2 == null) {
res[i] = -1.0d;
} else {
res[i] = unionFind.isConnected(id1, id2);
}
}
return res;
}
private class UnionFind {
private int[] parent;
private double[] weight;
public UnionFind(int n) {
this.parent = new int[n];
this.weight = new double[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
weight[i] = 1.0d;
}
}
public void union(int x, int y, double value) {
int rootX = find(x);
int rootY = find(y);
if (rootX == rootY) {
return;
}
parent[rootX] = rootY;
weight[rootX] = weight[y] * value / weight[x];
}
/**
* 路径压缩
* @param x
* @return
*/
private int find(int x) {
if (x != parent[x]) {//存在引用链至少是第一次遍历到才会不相等
int origin = parent[x];
parent[x] = find(parent[x]);
weight[x] *= weight[origin];
}
return parent[x];
}
public double isConnected(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootY == rootX) {
return weight[x] / weight[y];
} else {
return -1.0d;
}
}
}
/**
* 官方DFS法:本质上就是把数据加入子集当中然后如果存在引用链就一定能够被连接上则可以计算
* 速度击败54.5% 内存击败29.67% 1ms
* @param equations
* @param values
* @param queries
* @return
*/
public double[] calcEquation1(List<List<String>> equations, double[] values, List<List<String>> queries) {
double[] result = new double[queries.size()];
Map<String, Map<String, Double>> graph = buildGraph(equations, values);
for (int i = 0; i < queries.size(); i++) {
//dfs寻找对应的引用
String start = queries.get(i).get(0);
String end = queries.get(i).get(1);
if (!graph.containsKey(start) || !graph.containsKey(end)) {
result[i] = -1;//有一个没有就肯定计算不出来
} else {
Set<String> visited = new HashSet<>();
result[i] = dfs(graph, start, end, visited);
}
}
return result;
}
//构建引用链
private Map<String, Map<String, Double>> buildGraph(List<List<String>> equations, double[] values) {
Map<String, Map<String, Double>> graph = new HashMap<>();
for (int i = 0; i < equations.size(); i++) {
String v1 = equations.get(i).get(0);
String v2 = equations.get(i).get(1);
graph.putIfAbsent(v1, new HashMap<String, Double>());
graph.get(v1).put(v2, values[i]);
graph.putIfAbsent(v2, new HashMap<String, Double>());
graph.get(v2).put(v1, 1.0 / values[i]);
}
return graph;
}
private double dfs(Map<String, Map<String, Double>> graph, String start, String end, Set<String> visited) {
visited.add(start);
Map<String, Double> next = graph.get(start);
for (Map.Entry<String, Double> entry : next.entrySet()) {
if (entry.getKey().equals(end)) {
return entry.getValue();
}
if (!visited.contains(entry.getKey())) {
double nextValue = dfs(graph, entry.getKey(), end, visited);
if (nextValue > 0) {//即不可能是-1就正常返回
return entry.getValue() * nextValue;
}
}
}
return -1;
}
}

View File

@ -0,0 +1,100 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-07 10:18
*@Description:
* TODO 力扣406 根据身高重建队列:
* 假设有打乱顺序的一群人站成一个队列数组 people 表示队列中一些人的属性不一定按顺序
* 每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi 前面 正好 ki 个身高大于或等于 hi 的人
* 请你重新构造并返回输入数组 people 所表示的队列
* 返回的队列应该格式化为数组 queue 其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性queue[0] 是排在队列前面的人
*@Version: 1.0
*/
public class T88_ReconstructQueue {
@Test
public void test() {
int[][] people = {{7, 0}, {4, 4}, {7, 1}, {5, 0}, {6, 1}, {5, 2}};
// reconstructQueue1(people);
for (int[] ints : reconstructQueue1(people)) {
System.out.println(Arrays.toString(ints));
}
}
/**
* 思路:排序后使用linkedList插入
* 速度击败26.34% 内存击败70.6% 9ms
* @param people
* @return
*/
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0];
}
});
LinkedList<int[]> list = new LinkedList<>();
for (int[] person : people) {
list.add(person[1], person);
}
int[][] result = new int[people.length][];
return list.toArray(new int[people.length][]);
}
/**
* 先排序后腾挪
* 速度击败81.31% 内存击败17.3% 6ms
* @param people
* @return
*/
public int[][] reconstructQueue1(int[][] people) {
Arrays.sort(people, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] == o2[0] ? o1[1] - o2[1] : o1[0] - o2[0];
}
});
for (int i = people.length - 1; i >= 0; i--) {
int[] person = people[i];
//看看前面有几个和他相等的
int same = 0;
for (int j = i - 1; j >= 0; j--) {
if (people[j][0] == person[0]) same++;
if (people[j][0] < person[0]) break;//小了直接break
}
//不相等需要腾挪
if (same != person[1]) {
int length = person[1] - same;
System.arraycopy(people, i + 1, people, i, length);
people[i + length] = person;
}
}
return people;
}
}

View File

@ -0,0 +1,54 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-07 10:52
*@Description:
* TODO 力扣416 分割等和数组:
* 给你一个 只包含正整数 非空 数组 nums 请你判断是否可以将这个数组分割成两个子集使得两个子集的元素和相等
*@Version: 1.0
*/
public class T89_CanPartition {
@Test
public void test() {
// int[] nums={1,5,11,5};
int[] nums = {1, 2, 3, 5};
System.out.println(canPartition(nums));
}
/**
* 思路:本质上就是找一个总和一半的子序列
* @param nums
* @return
*/
public boolean canPartition(int[] nums) {
if (nums.length < 2) return false;
int sum = 0;
int max = 0;
for (int num : nums) {
sum += num;
max = Math.max(max, num);
}
if (sum % 2 != 0) return false;
int target = sum / 2;
if (max > target) return false;
boolean[] dp = new boolean[target + 1];
dp[0]=true;
for (int i = 0; i < nums.length; i++) {
for (int j = dp.length - 1; j >= nums[i]; j--) {
dp[j] |= dp[j - nums[i]];
}
}
return dp[target];
}
}

View File

@ -0,0 +1,141 @@
package com.markilue.leecode.hot100;
import com.markilue.leecode.tree.TreeNode;
import com.markilue.leecode.tree.TreeUtils;
import org.junit.Test;
import java.util.*;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-07 11:05
*@Description:
* TODO 力扣437 路径总和III:
* 给定一个二叉树的根节点 root 和一个整数 targetSum 求该二叉树里节点值之和等于 targetSum 路径 的数目
* 路径 不需要从根节点开始也不需要在叶子节点结束但是路径方向必须是向下的只能从父节点到子节点
*@Version: 1.0
*/
public class T90_PathSum {
@Test
public void test() {
TreeNode root = TreeUtils.structureTree(new ArrayList<>(Arrays.asList(10, 5, -3, 3, 2, null, 11, 3, -2, null, 1)), 0);
System.out.println(pathSum(root, 8));
}
/**
* 思路:可以把return的状态分为 不要之前的左 不要之前右 都不要
* 有问题:节点的始末不一定是终点
* @param root
* @param targetSum
* @return
*/
public int pathSum(TreeNode root, int targetSum) {
cur.add(0);
path(root, targetSum, 0);
return sum;
}
int sum = 0;
List<Integer> cur = new ArrayList<Integer>();
public void path(TreeNode root, int targetSum, int last) {
if (root == null) return;
int value = root.val;
for (Integer integer : cur) {
if (integer + value == targetSum) sum++;
}
int sum = value + last;
cur.add(value);
cur.add(sum);
path(root.left, targetSum, sum);
cur.remove(cur.size() - 1);
path(root.right, targetSum, sum);
cur.remove(cur.size() - 1);
}
/**
* 分别计算节点为根的和他为路径的之和
* 时间复杂度为O(N^2)
* 速度击败8.17% 内存击败46.54%
* @param root
* @param targetSum
* @return
*/
public int pathSum1(TreeNode root, int targetSum) {
if (root == null) {
return 0;
}
int ret = rootSum(root, targetSum);//计算他为根的
ret += pathSum1(root.left, targetSum);//计算不要他的 为路径的
ret += pathSum1(root.right, targetSum);
return ret;
}
public int rootSum(TreeNode root, int targetSum) {
int ret = 0;
if (root == null) {
return 0;
}
int val = root.val;
if (val == targetSum) {
ret++;
}
ret += rootSum(root.left, targetSum - val);
ret += rootSum(root.right, targetSum - val);
return ret;
}
/**
* 官方前缀和解法
* 时间复杂度O(n)
* * 前缀和的递归回溯思路
* * 从当前节点反推到根节点(反推比较好理解正向其实也只有一条)有且仅有一条路径因为这是一棵树
* * 如果此前有和为currSum-target,而当前的和又为currSum,两者的差就肯定为target了
* * 所以前缀和对于当前路径来说是唯一的当前记录的前缀和在回溯结束回到本层时去除保证其不影响其他分支的结果
* @param root
* @param targetSum
* @return
*/
public int pathSum2(TreeNode root, int targetSum) {
Map<Long, Integer> prefix = new HashMap<Long, Integer>();
prefix.put(0L, 1);
return dfs(root, prefix, 0, targetSum);
}
public int dfs(TreeNode root, Map<Long, Integer> prefix, long curr, int targetSum) {
if (root == null) {
return 0;
}
int ret = 0;
curr += root.val;
//---核心代码
//看看root到当前节点这条路上是否存在节点前缀和加target为currSum的路径
//当前节点->root节点反推有且仅有一条路径如果此前有和为currSum-target,而当前的和又为currSum,两者的差就肯定为target了
//currSum-target相当于找路径的起点起点的sum+target=currSum当前点到起点的距离就是target
ret = prefix.getOrDefault(curr - targetSum, 0);
prefix.put(curr, prefix.getOrDefault(curr, 0) + 1);
ret += dfs(root.left, prefix, curr, targetSum);
ret += dfs(root.right, prefix, curr, targetSum);
prefix.put(curr, prefix.getOrDefault(curr, 0) - 1);
return ret;
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,69 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-08 11:54
*@Description:
* TODO 力扣448 找到所有数组中的消失的数字:
* 给你一个含 n 个整数的数组 nums 其中 nums[i] 在区间 [1, n] 请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字并以数组的形式返回结果
*@Version: 1.0
*/
public class T92_FindDisappearedNumbers {
@Test
public void test(){
int[] nums = {4, 3, 2, 7, 8, 2, 3, 1};
System.out.println(findDisappearedNumbers(nums));
}
/**
* 思路:记录各个数字出现的次数为0则没有出现
* 速度击败99.36% 内存击败52.21% 3ms
* @param nums
* @return
*/
public List<Integer> findDisappearedNumbers(int[] nums) {
int[] counts = new int[nums.length + 1];
for (int num : nums) {
counts[num]++;
}
List<Integer> result = new ArrayList<>();
for (int i = 1; i < counts.length; i++) {
if (counts[i] == 0) result.add(i);
}
return result;
}
/**
* 官方题解:原地+n不影响实际结果
* 速度击败99.36% 内存击败26.85% 3ms
* @param nums
* @return
*/
public List<Integer> findDisappearedNumbers1(int[] nums) {
int n = nums.length;
for (int num : nums) {
int x = (num - 1) % n;
nums[x] += n;
}
List<Integer> ret = new ArrayList<Integer>();
for (int i = 0; i < n; i++) {
if (nums[i] <= n) {
ret.add(i + 1);
}
}
return ret;
}
}

View File

@ -0,0 +1,40 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-08 13:21
*@Description:
* TODO 力扣461 汉明距离:
* 两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目
* 给你两个整数 x y计算并返回它们之间的汉明距离
*@Version: 1.0
*/
public class T93_HammingDistance {
@Test
public void test() {
// System.out.println(3^1);
System.out.println(hammingDistance(1, 3));
}
/**
* 思路:异或之后得到不同位为1的值
* 然后num&(num-1)可以消除最后一个为1的值因此可以统计有几个1
* @param x
* @param y
* @return
*/
public int hammingDistance(int x, int y) {
int result = x ^ y;
int count = 0;
while (result != 0) {
count++;
result &= (result - 1);
}
return count;
}
}

View File

@ -0,0 +1,59 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-10 12:49
*@Description:
* TODO 力扣494 目标和:
* 给你一个整数数组 nums 和一个整数 target
* 向数组中的每个整数前添加 '+' '-' 然后串联起所有整数可以构造一个 表达式
* 例如nums = [2, 1] 可以在 2 之前添加 '+' 1 之前添加 '-' 然后串联起来得到表达式 "+2-1"
* 返回可以通过上述方法构造的运算结果等于 target 的不同 表达式 的数目
*@Version: 1.0
*/
public class T94_FindTargetSumWays {
@Test
public void test() {
int[] nums = {1, 1, 1, 1, 1};
int target = 3;
System.out.println(findTargetSumWays(nums, target));
}
/**
* 思路:本质上就是一个背包问题:
* add-(sum-add)=target
* add=(sum+target)/2
* 速度击败94.6% 内存击败50.13% 2ms
* @param nums
* @param target
* @return
*/
public int findTargetSumWays(int[] nums, int target) {
int sum = 0;
for (int num : nums) {
sum += num;
}
if ((sum + target) % 2 != 0) return 0;
int mid = (sum + target) / 2;
if (mid < 0) return 0;
int[] dp = new int[mid + 1];
dp[0] = 1;
for (int i = 0; i < nums.length; i++) {
for (int j = mid; j >= nums[i]; j--) {
dp[j] += dp[j - nums[i]];//用这个数不用这个数
}
}
return dp[mid];
}
}

View File

@ -0,0 +1,46 @@
package com.markilue.leecode.hot100;
import com.markilue.leecode.tree.TreeNode;
import org.junit.Test;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-10 13:02
*@Description:
* TODO 力扣538 把二叉搜索树转换为累加树:
* 给出二叉 搜索 树的根节点该树的节点值各不相同请你将其转换为累加树Greater Sum Tree使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和
* 提醒一下二叉搜索树满足下列约束条件
* 节点的左子树仅包含键 小于 节点键的节点
* 节点的右子树仅包含键 大于 节点键的节点
* 左右子树也必须是二叉搜索树
*@Version: 1.0
*
*/
public class T95_ConvertBST {
@Test
public void test() {
}
int sum = 0;
/**
* 后续遍历递归法
* 速度击败100% 内存击败85.25% 0ms
*/
public TreeNode convertBST(TreeNode root) {
if (root == null) {
return null;
}
convertBST(root.right);
sum += root.val;
root.val = sum;
convertBST(root.left);
return root;
}
}

View File

@ -0,0 +1,40 @@
package com.markilue.leecode.hot100;
import com.markilue.leecode.tree.TreeNode;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-10 13:17
*@Description:
* TODO 力扣543 二叉树的直径:
* 给定一棵二叉树你需要计算它的直径长度一棵二叉树的直径长度是任意两个结点路径长度中的最大值这条路径可能穿过也可能不穿过根结点
*@Version: 1.0
*/
public class T96_DiameterOfBinaryTree {
int max = 0;
/**
* 思路:就是计算最多一条链路上的节点数目
* 速度击败100% 内存击败79.29% 递归法
* @param root
* @return
*/
public int diameterOfBinaryTree(TreeNode root) {
sub(root);
return max;
}
public int sub(TreeNode root) {
if (root == null) {
return 0;
}
int left = sub(root.left);
int right = sub(root.right);
max = Math.max(max, left + right);
return Math.max(left, right) + 1;
}
}

View File

@ -0,0 +1,116 @@
package com.markilue.leecode.hot100;
import org.junit.Test;
import java.util.HashMap;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-10 13:28
*@Description:
* TODO 力扣560 合为k的子数组:
* 给你一个整数数组 nums 和一个整数 k 请你统计并返回 该数组中和为 k 的连续子数组的个数
*@Version: 1.0
*/
public class T97_SubarraySum {
@Test
public void test() {
int[] nums = {100,1,2,3,100,1,2,3,4};
System.out.println(subarraySum1(nums, 3));
}
/**
* 思路:滑动窗口法(维护一个窗口内总和为k的窗口)
* 有问题:因为数组里面有负数的情况这个判断就是有问题的
* @param nums
* @param k
* @return
*/
public int subarraySum(int[] nums, int k) {
if (nums.length == 1 && nums[0] == k) {
return 1;
}
int result = 0;
int cur = nums[0];
int left = 0;
int right = 1;
while (left <= right && right <= nums.length) {
if (cur == k && left != right) {
result++;
}
if (left == nums.length) break;
if (right < nums.length) {
cur += nums[right++];
} else {
cur -= nums[left++];//到终点了直接缩小窗口
}
while (left < right && cur > k) {
cur -= nums[left++];
}
}
return result;
}
/**
* 官方前缀和+hash优化:
* 时间复杂度O(n)
* 速度击败53.66% 内存击败29.86% 24ms
* @param nums
* @param k
* @return
*/
public int subarraySum1(int[] nums, int k) {
int count = 0, pre = 0;
HashMap<Integer, Integer> mp = new HashMap<>();//<pre,count>
mp.put(0, 1);
for (int i = 0; i < nums.length; i++) {
pre += nums[i];
if (mp.containsKey(pre - k)) {
count += mp.get(pre - k);
}
mp.put(pre, mp.getOrDefault(pre, 0) + 1);
}
return count;
}
/**
* 官方最快:
* 速度击败92.3% 内存击败40.6% 21ms
* @param nums
* @param k
* @return
*/
public int subarraySum2(int[] nums, int k) {
HashMap<Integer,Integer> map = new HashMap<>();
map.put(0,1);
int res = 0;
int sum = 0;
for(int i = 0;i < nums.length; i++){
sum+=nums[i];
if(map.containsKey(sum - k)){
res+=map.get(sum - k);
}
if(map.containsKey(sum)){
map.put(sum,map.get(sum)+1);
}else{
map.put(sum, 1);
}
}
return res;
}
}

View File

@ -0,0 +1,91 @@
package com.markilue.leecode.hot100;
import java.util.Arrays;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-11 12:47
*@Description:
* TODO 力扣581 最短无序连续子数组:
* 给你一个整数数组 nums 你需要找出一个 连续子数组 如果对这个子数组进行升序排序那么整个数组都会变为升序排序
* 请你找出符合题意的 最短 子数组并输出它的长度
*@Version: 1.0
*/
public class T98_FindUnsortedSubarray {
/**
* 官方排序法:
* TODO 最短的思路:
* 本质上就是考虑把数组分为三段子数组:numsA,numsB,numsC 当对numsB进行排序整个数组就会变得有序
* 所以要找到最短的numsB,就是找到最大的numsA和numsC的长度之和
* 因此将数组排序与原数组对比找到最长相同的前缀numsA和最长相同的后缀numsC,就可以最短numsB
* 时间复杂度O(nlogn)
* 时间击败36.47% 内存击败15.19% 6ms
* @param nums
* @return
*/
public int findUnsortedSubarray(int[] nums) {
if (isSorted(nums)) {
return 0;
}
int[] numsSorted = new int[nums.length];
System.arraycopy(nums, 0, numsSorted, 0, nums.length);
Arrays.sort(numsSorted);
int left = 0;
while (nums[left] == numsSorted[left]) {
left++;
}
int right = nums.length - 1;
while (nums[right] == numsSorted[right]) {
right--;
}
return right - left + 1;
}
public boolean isSorted(int[] nums) {
for (int i = 1; i < nums.length; i++) {
if (nums[i] < nums[i - 1]) {
return false;
}
}
return true;
}
/**
* 一次遍历:对于需要排序的优化:
* 时间复杂度O(n)
* 速度击败93.39% 内存击败68.96% 1ms
* @param nums
* @return
*/
public int findUnsortedSubarray1(int[] nums) {
//2,6,4,8,10,9,15
//升序开始索引:0,2,5
//降序开始索引:1,4
int n = nums.length;
int maxn = Integer.MIN_VALUE, right = -1;
int minn = Integer.MAX_VALUE, left = -1;
for (int i = 0; i < n; i++) {
// 因此 从左到右 找最大值 这期间如果有值比最大值还要小 那么这个值就有可能是 中段的右边界
//因为升序的时候左右段 最大值一直在替换所以最后一个小 它就是 中段的右边界
if (maxn > nums[i]) {
right = i;
} else {
maxn = nums[i];
}
//因此 从右到左 找最小值 这期间如果有值比最小值还要大 那么这个值就有可能是 中段的左边界
//因为降序的时候左右段 最小值一直在替换所以最后一个大 它就是 中段的左边界
if (minn < nums[n - i - 1]) {
left = n - i - 1;
} else {
minn = nums[n - i - 1];
}
}
return right == -1 ? 0 : right - left + 1;
}
}

View File

@ -0,0 +1,40 @@
package com.markilue.leecode.hot100;
import com.markilue.leecode.tree.TreeNode;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.hot100
*@Author: markilue
*@CreateTime: 2023-04-11 14:27
*@Description:
* TODO 力扣617 合并二叉树:
* 给你两棵二叉树 root1 root2
* 想象一下当你将其中一棵覆盖到另一棵之上时两棵树上的一些节点将会重叠而另一些不会你需要将这两棵树合并成一棵新二叉树合并的规则是如果两个节点重叠那么将这两个节点的值相加作为合并后节点的新值否则不为 null 的节点将直接作为新二叉树的节点
* 返回合并后的二叉树
* 注意: 合并过程必须从两个树的根节点开始
*@Version: 1.0
*/
public class T99_MergeTrees {
/**
* 递归法
* @param root1
* @param root2
* @return
*/
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) {
return root2;
} else if (root2 == null) {
return root1;
}
//两者都不等于null
TreeNode root = new TreeNode(root1.val + root2.val);
root.left = mergeTrees(root1.left, root2.left);
root.right = mergeTrees(root1.right, root2.right);
return root;
}
}

View File

@ -16,8 +16,8 @@ import org.junit.Test;
public class DeleteDuplicatesII { public class DeleteDuplicatesII {
@Test @Test
public void test(){ public void test() {
ListNode root = ListNodeUtils.build(new int[]{1, 1, 2,2,2}); ListNode root = ListNodeUtils.build(new int[]{1, 1, 2, 2, 2});
ListNodeUtils.print(deleteDuplicates(root)); ListNodeUtils.print(deleteDuplicates(root));
} }
@ -39,7 +39,7 @@ public class DeleteDuplicatesII {
temp1 = temp1.next; temp1 = temp1.next;
} }
temp.next = temp1.next; temp.next = temp1.next;
}else { } else {
temp = temp.next; temp = temp.next;
} }
@ -49,4 +49,31 @@ public class DeleteDuplicatesII {
return fake.next; return fake.next;
} }
public ListNode deleteDuplicates1(ListNode head) {
if (head == null) {
return head;
}
ListNode fake = new ListNode();
fake.next = head;
ListNode temp = fake;
while (temp.next != null) {
//存在重复
if (temp.next.next != null && temp.next.val == temp.next.next.val) {
ListNode temp1 = temp.next.next;
while (temp1.next != null && temp1.val == temp1.next.val) {
temp1 = temp1.next;
}
temp.next = temp1.next;
} else {
temp = temp.next;
}
}
return fake.next;
}
} }

View File

@ -0,0 +1,117 @@
package com.markilue.leecode.listnode.selftry;
import java.util.HashMap;
import java.util.Map;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.listnode.selftry
*@Author: markilue
*@CreateTime: 2023-04-04 13:45
*@Description:
* TODO 力扣146 LRU缓存:
* 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构
* 实现 LRUCache
* LRUCache(int capacity) 正整数 作为容量 capacity 初始化 LRU 缓存
* int get(int key) 如果关键字 key 存在于缓存中则返回关键字的值否则返回 -1
* void put(int key, int value) 如果关键字 key 已经存在则变更其数据值 value 如果不存在则向缓存中插入该组 key-value 如果插入操作导致关键字数量超过 capacity 则应该 逐出 最久未使用的关键字
* 函数 get put 必须以 O(1) 的平均时间复杂度运行
*@Version: 1.0
*/
public class LRUCache {
int size;
int capacity;
DNode head;
DNode tail;
Map<Integer, DNode> map;
//HashMap+链表:本质上就是LinkedHashMap
public LRUCache() {
}
public LRUCache(int capacity) {
this.capacity = capacity;
head = new DNode();
tail = new DNode();
map = new HashMap<>();
size = 0;
head.next = tail;
tail.prev = head;
}
public int get(int key) {
DNode dNode = map.get(key);
if (dNode == null) return -1;
deleteNode(dNode);
addToHead(dNode);
return dNode.value;
}
public void put(int key, int value) {
DNode dNode = map.get(key);
if(dNode==null){
DNode node = new DNode(key,value);
map.put(key, node);
addToHead(node);
size++;
if (size > capacity) {
DNode dNode1 = deleteTailNode();
map.remove(dNode1.key);
--size;
}
}else {
dNode.value=value;
deleteNode(dNode);
addToHead(dNode);
}
}
public void addToHead(DNode node) {
head.next.prev = node;
node.next = head.next;
head.next = node;
node.prev = head;
}
//自删除
public void deleteNode(DNode node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
//删除尾节点
public DNode deleteTailNode() {
DNode prev = tail.prev;
deleteNode(prev);
return prev;
}
public class DNode {
DNode prev;
DNode next;
int value;
int key;
public DNode() {
}
public DNode(int key , int value) {
this.key=key;
this.value = value;
}
public DNode(int value, DNode prev, DNode next) {
this.value = value;
this.prev = prev;
this.next = next;
}
}
}

View File

@ -0,0 +1,51 @@
package com.markilue.leecode.listnode.selftry;
import com.markilue.leecode.listnode.ListNode;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.listnode.selftry
*@Author: markilue
*@CreateTime: 2023-04-04 12:56
*@Description:
* TODO 力扣143 重排链表:
* 给定一个单链表 L 的头节点 head 单链表 L 表示为
* L0 L1 Ln - 1 Ln
* 请将其重新排列后变为
* L0 Ln L1 Ln - 1 L2 Ln - 2
* 不能只是单纯的改变节点内部的值而是需要实际的进行节点交换
*@Version: 1.0
*/
public class ReorderList {
/**
* 递归法
* @param head
*/
ListNode p;
public void reorderList(ListNode head) {
p = head;
recur(head);
}
public void recur(ListNode head) {
if (head == null) {
return;
}
recur(head.next);
if (p.next == null || p == head || head.next == p) {
//判断当前节点是否需要合并
p.next = null;//不需要合并了直接把p置为null
return;
}
head.next = p.next;//把p的后面拼在head的后面
p.next = head;//把head加在p的后面
p = p.next.next;
}
}

View File

@ -1,7 +1,10 @@
package com.markilue.leecode.listnode.selftry; package com.markilue.leecode.listnode.selftry;
import com.markilue.leecode.listnode.ListNode;
import com.markilue.leecode.listnode.T04_swapPairs; import com.markilue.leecode.listnode.T04_swapPairs;
import java.util.List;
public class reverseKGroup { public class reverseKGroup {
public static void main(String[] args) { public static void main(String[] args) {
@ -14,20 +17,49 @@ public class reverseKGroup {
return null; return null;
} }
public static class ListNode {
int val;
T04_swapPairs.ListNode next;
ListNode() {
/**
* 反转部分链表
* @param head
* @param k
* @return
*/
public ListNode reverseKGroup1(ListNode head, int k) {
if (head == null) {
return head;
} }
ListNode(int val) { ListNode temp = head;
this.val = val; //寻找终点
for (int i = 0; i < k; i++) {
if (temp == null) return head;//没到k个直接返回
temp = temp.next;
} }
ListNode(int val, T04_swapPairs.ListNode next) { ListNode newHead = reverse(head, temp);
this.val = val; head.next= reverseKGroup1(temp, k);
this.next = next; return newHead;
}
public ListNode reverse(ListNode start, ListNode end) {
//反转部分链表
if (start == null) return start;
ListNode fake = new ListNode();
fake.next = start;
ListNode temp = start;
while (temp != end) {
ListNode tempnext = temp.next;
temp.next = fake.next;
fake.next = temp;
temp = tempnext;
} }
return fake.next;
} }
} }

View File

@ -1,42 +1,45 @@
package com.markilue.leecode.listnode.selftry; package com.markilue.leecode.listnode.selftry;
import com.markilue.leecode.listnode.ListNode;
import com.markilue.leecode.listnode.ListNodeUtils;
public class swapPairs { public class swapPairs {
public static void main(String[] args) { public static void main(String[] args) {
ListNode l1 = new ListNode(1); ListNode l1 = new ListNode(1);
//l1.next=new ListNode(2); l1.next = new ListNode(2);
//l1.next.next=new ListNode(3); l1.next.next = new ListNode(3);
//l1.next.next.next=new ListNode(4); l1.next.next.next = new ListNode(4);
ListNode listNode = swapPairs(l1); ListNode listNode = swapPairs1(l1);
ListNodeUtils.print(listNode);
} }
public static ListNode swapPairs(ListNode head) { public static ListNode swapPairs(ListNode head) {
if(head==null){ if (head == null) {
return null; return null;
} }
ListNode dust=head; ListNode dust = head;
ListNode swap=head.next; ListNode swap = head.next;
int temp=0; int temp = 0;
while (swap!=null){ while (swap != null) {
temp=swap.val; temp = swap.val;
swap.val=head.val; swap.val = head.val;
head.val=temp; head.val = temp;
if(swap.next!=null){ if (swap.next != null) {
if(swap.next.next!=null){ if (swap.next.next != null) {
swap=swap.next.next; swap = swap.next.next;
head=head.next.next; head = head.next.next;
}else { } else {
break; break;
} }
}else { } else {
break; break;
} }
@ -44,21 +47,30 @@ public class swapPairs {
return dust; return dust;
} }
public static class ListNode {
int val;
ListNode next;
ListNode() { public static ListNode swapPairs1(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode fake = new ListNode();
fake.next = head;
ListNode tempcur = fake;
while (tempcur != null && tempcur.next != null) {
ListNode temp = tempcur.next.next;
if (tempcur.next.next != null) {
tempcur.next.next = tempcur.next.next.next;
temp.next = tempcur.next;
tempcur.next=temp;
}
tempcur = tempcur.next.next;
// tempcur = temp;
} }
ListNode(int val) { return fake.next;
this.val = val;
}
ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
} }

View File

@ -0,0 +1,719 @@
use sqlpractice;
# 学生表Student
create table Student
(
SId varchar(10),
Sname varchar(10),
Sage datetime,
Ssex varchar(10)
);
insert into Student
values ('01', '赵雷', '1990-01-01', '');
insert into Student
values ('02', '钱电', '1990-12-21', '');
insert into Student
values ('03', '孙风', '1990-05-20', '');
insert into Student
values ('04', '李云', '1990-08-06', '');
insert into Student
values ('05', '周梅', '1991-12-01', '');
insert into Student
values ('06', '吴兰', '1992-03-01', '');
insert into Student
values ('07', '郑竹', '1989-07-01', '');
insert into Student
values ('09', '张三', '2017-12-20', '');
insert into Student
values ('10', '李四', '2017-12-25', '');
insert into Student
values ('11', '李四', '2017-12-30', '');
insert into Student
values ('12', '赵六', '2017-01-01', '');
insert into Student
values ('13', '孙七', '2018-01-01', '');
# 科目表
create table Course
(
CId varchar(10),
Cname nvarchar(10),
TId varchar(10)
);
insert into Course
values ('01', '语文', '02');
insert into Course
values ('02', '数学', '01');
insert into Course
values ('03', '英语', '03');
# 教师表
create table Teacher
(
TId varchar(10),
Tname varchar(10)
);
insert into Teacher
values ('01', '张三');
insert into Teacher
values ('02', '李四');
insert into Teacher
values ('03', '王五');
# 成绩表SC
create table SC
(
SId varchar(10),
CId varchar(10),
score decimal(18, 1)
);
insert into SC
values ('01', '01', 80);
insert into SC
values ('01', '02', 90);
insert into SC
values ('01', '03', 99);
insert into SC
values ('02', '01', 70);
insert into SC
values ('02', '02', 60);
insert into SC
values ('02', '03', 80);
insert into SC
values ('03', '01', 80);
insert into SC
values ('03', '02', 80);
insert into SC
values ('03', '03', 80);
insert into SC
values ('04', '01', 50);
insert into SC
values ('04', '02', 30);
insert into SC
values ('04', '03', 20);
insert into SC
values ('05', '01', 76);
insert into SC
values ('05', '02', 87);
insert into SC
values ('06', '01', 31);
insert into SC
values ('06', '03', 34);
insert into SC
values ('07', '02', 89);
insert into SC
values ('07', '03', 98);
#TODO 1、查询" 01 "课程比" 02 "课程成绩高的学生的信息及课程分数
select Student.sid, sname, sage, Ssex, s1, s2
from student
join
(select t1.SId, s1, s2
from (select SId, score s1
from sc
where CId = '01') t1
join
(select SId, score s2
from sc
where CId = '02') t2 on t1.SId = t2.SId and t1.s1 > t2.s2) t3 on Student.SId = t3.SId;
#TODO 2.1、查询同时存在" 01 "课程和" 02 "课程的情况
select t1.SId, s1, s2
from (select SId, score s1
from sc
where CId = '01') t1
join
(select SId, score s2
from sc
where CId = '02') t2 on t1.SId = t2.SId;
#TODO 2.2、查询存在" 01 "课程但可能不存在" 02 "课程的情况(不存在时显示为 null )
select t1.sid, s1, s2
from (select SId, score s1
from sc
where CId = '01') t1
left join (select SId, score s2
from sc
where CId = '02') t2 on t1.SId = t2.SId;
#TODO 2.3、查询不存在" 01 "课程但存在" 02 "课程的情况
select t2.sid, s1, s2
from (select SId, score s1
from sc
where CId = '01') t1
right join (select SId, score s2
from sc
where CId = '02') t2 on t1.SId = t2.SId;
#TODO 3、查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩
-- 查询平均成绩大于等于 60 分的同学
select SId, sum(score) / count(score) avg
from sc
group by sid
having avg > 60;
-- 查询这些学生的相关信息
select t1.sid, Sname, avg
from student
right join (select SId, sum(score) / count(score) avg
from sc
group by sid
having avg > 60) t1 on Student.SId = t1.SId;
#TODO 4、查询在 SC 表存在成绩的学生信息
-- 存在成绩+去重
select sid
from sc
where score is not null
group by sid;
-- 查询对应的信息
select t1.sid, Sname, Sage, Ssex
from student
right join (select sid
from sc
where score is not null
group by sid) t1 on Student.SId = t1.SId;
#TODO 5、查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为 null )
-- 查询有成绩的
select sid, count(score) c1, sum(score) s1
from sc
group by sid;
select Student.sid, sname, c1, s1
from student
left join(select sid, count(score) c1, sum(score) s1
from sc
group by sid) t1 on Student.sid = t1.SId;
#TODO 6、查有成绩的学生信息
-- 有成绩的学生
select distinct(sid)
from sc
where score is not null;
-- 查询对应的信息
select Student.sid, Student.sname, Student.sage, ssex
from student
right join (select distinct(sid) sid
from sc
where score is not null) t1 on Student.SId = t1.SId;
#TODO 7、查询「李」姓老师的数量
select count(TId)
from Teacher
where Tname like '李%';
#TODO 8、查询学过「张三」老师授课的同学的信息
-- 张三老师教过的课
select cid, t.TId
from Course
join
(select tid, tname from teacher where tname = '张三') t on Course.TId = t.TId;
-- 张三老师教过的课的学生
select sid, t2.CId
from sc
join (select cid, t.TId
from Course
join
(select tid, tname from teacher where tname = '张三') t on Course.TId = t.TId) t2
on sc.CId = t2.CId;
-- 详细信息
select t3.sid, Sname, sage, Ssex
from student
join(select sid, t2.CId
from sc
join (select cid, t.TId
from Course
join
(select tid, tname from teacher where tname = '张三') t on Course.TId = t.TId) t2
on sc.CId = t2.CId) t3 on Student.SId = t3.SId;
#TODO 9、查询没有学全所有课程的同学的信息
-- 查询全部course的数目
select count(cid) num
from course;
-- 查询没有学全所有课程的同学
select sid, count(cid) c1, num
from sc,
(select count(cid) num
from course) t2
group by sid
having c1 < t2.num;
-- 查询对应学生的信息
select S.sid, sname, Sage, Ssex
from Student
right join (select sid, count(cid) c1, num
from sc,
(select count(cid) num
from course) t2
group by sid
having c1 < t2.num) S on Student.SId = S.SId;
#TODO 10、查询至少有一门课与学号为" 01 "的同学所学相同的同学的信息
-- 学号01同学所学
SELECT CId
from sc
where SId = '01';
-- 所学相同
select Student.sid, sname, Sage, Ssex
from Student
where SId in
(select distinct (sid)
from sc
join(SELECT CId
from sc
where SId = '01') t1 on sc.CId = t1.CId
where SC.SId != '01');
#TODO 11、查询和" 01 "号的同学学习的课程完全相同的其他同学的信息
-- 先join得到所有的相同的课
-- 计算所有课的数目和原本的数目是否相等
select sc.sid
from sc
join(select CId
from sc
where SId = '01') t1 on sc.CId = t1.CId
where sid != '01'
group by SId
having count(sc.cid) = (select count(CId)
from sc
where SId = '01');
select *
from student
where Student.SId in (select sc.sid
from sc
join(select CId
from sc
where SId = '01') t1 on sc.CId = t1.CId
where sid != '01'
group by SId
having count(sc.cid) = (select count(CId)
from sc
where SId = '01'));
#TODO 12、查询没学过"张三"老师讲授的任一门课程的学生姓名
-- 查询张三老师教过的课
select cid
from course
join(select TId
from teacher
where Tname = '张三') t1
on Course.TId = t1.TId;
-- 查询学过张三老师教过的课的学生
select SId
from sc
where CId in (select cid
from course
join(select TId
from teacher
where Tname = '张三') t1
on Course.TId = t1.TId);
-- 查询没有学过张三老师教过的课的学生
select *
from student
where SId not in (select SId
from sc
where CId in (select cid
from course
where tid in (select TId
from teacher
where Tname = '张三')));
# TODO 13、查询两门及其以上不及格课程的同学的学号姓名及其平均成绩
-- 查询两门及其以上不及格课程的同学的学号
select sid, count(CId) c1
from sc
where score < 60
group by sid
having c1 >= 2;
-- 查询这些人的平均成绩
select sid, avg(score) avg
from sc
where SId in
(select sid
from sc
where score < 60
group by sid
having count(CId) >= 2)
group by sid;
-- 查询全部
select t1.sid, Sname, avg
from student
join
(select sid, avg(score) avg
from sc
where SId in
(select sid
from sc
where score < 60
group by sid
having count(CId) >= 2)
group by sid) t1 on Student.SId = t1.SId;
#TODO 14、检索" 01 "课程分数小于 60按分数降序排列的学生信息
-- 降序学生
select sid, score
from sc
where CId = '01'
and score < 60
order by score desc;
select *
from Student
join
(select sid, score
from sc
where CId = '01'
and score < 60
order by score desc) t1 on Student.SId = t1.SId;
#TODO 15、按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩
select sid, CId, score, avg
from (select sid,
CId,
score,
avg(score) over (partition by sid) avg
from sc) t1
order by avg desc;
#TODO 16、查询各科成绩最高分、最低分和平均分:
select cid, max(score), min(score), avg(score)
from sc
group by cid;
#TODO 17.1、按各科成绩进行排序,并显示排名, Score 重复时保留名次空缺
-- 窗口函数
select cid, sid, score, rank() over (partition by CId order by score desc)
from sc;
-- 师兄非窗口函数法
SELECT *, COUNT(*)
FROM sc a
LEFT JOIN
sc b
ON a.cid = b.cid AND a.score <= b.score
GROUP BY a.cid, a.sid, a.score
ORDER BY a.cid DESC, a.score DESC;
#TODO 17.2、按各科成绩进行排序,并显示排名, Score 重复时合并名次
-- 窗口函数
select cid, sid, score, dense_rank() over (partition by CId order by score desc)
from sc;
-- 师兄非窗口函数法 (应该有问题)
SELECT *, COUNT(*)
FROM sc a
LEFT JOIN
sc b
ON a.cid = b.cid AND a.score < b.score
GROUP BY a.cid, a.sid, a.score
ORDER BY a.cid DESC, a.score DESC;
# TODO 17.3、查询学生的总成绩,并进行排名,总分重复时保留名次空缺
-- 非窗口函数法
select t1.sid, t1.sum, count(*) rk
from (select sid, sum(score) sum
from sc
group by sid) t1
left join
(select sid, sum(score) sum
from sc
group by sid) t2 on t1.sum <= t2.sum
group by t1.sid, t1.sum
order by t1.sum desc;
-- 窗口函数法
select sid, sum, rank() over (order by sum desc) rk
from (select sid, sum(score) sum
from sc
group by sid) t1;
#TODO 17.4、 查询学生的总成绩,并进行排名,总分重复时不保留名次空缺
-- 窗口函数法
select sid, sum, dense_rank() over (order by sum desc) rk
from (select sid, sum(score) sum
from sc
group by sid) t1;
SET @crank = 0;
SELECT b.sid, b.a, @crank := @crank + 1 AS rank1
FROM (SELECT sid, SUM(score) AS a FROM sc GROUP BY sid ORDER BY a DESC) b;
#TODO 18.统计各科成绩各分数段人数:课程编号,课程名称,[100-85][85-70][70-60][60-0] 及所占百分比
select t1.cid,cname,you,liang,zhong,cha,you/total,liang/total,zhong/total,cha/total
from course
join
(
select cid,
sum(if(score >= 85 and score < 100, 1, 0)) you,
sum(if(score >= 70 and score < 85, 1, 0)) liang,
sum(if(score >= 60 and score < 70, 1, 0)) zhong,
sum(if(score > 0 and score < 60, 1, 0)) cha,
count(score) total
from sc
group by cid
)t1 on Course.CId=t1.CId;
-- 师兄写法
SELECT sc.cid,c.cname,
SUM(CASE WHEN score>85 AND score<=100 THEN 1 ELSE 0 END) AS '[100-85]',
SUM(CASE WHEN score>70 AND score<=85 THEN 1 ELSE 0 END) AS '[85-70]',
SUM(CASE WHEN score>60 AND score<=70 THEN 1 ELSE 0 END) AS '[70-60]',
SUM(CASE WHEN score<60 THEN 1 ELSE 0 END) AS '[60-0]',
SUM(CASE WHEN score>85 AND score<=100 THEN 1 ELSE 0 END)/COUNT(1) AS '[100-85]百分比',
SUM(CASE WHEN score>70 AND score<=85 THEN 1 ELSE 0 END)/COUNT(1) AS '[85-70]百分比',
SUM(CASE WHEN score>60 AND score<=70 THEN 1 ELSE 0 END)/COUNT(1) AS '[70-60]百分比',
SUM(CASE WHEN score<60 THEN 1 ELSE 0 END)/COUNT(1) AS '[60-0]百分比'
FROM sc ,course c
WHERE sc.cid=c.cid
GROUP BY sc.cid;
# TODO 19查询各科成绩前三名的记录
-- 窗口函数法
select cid,sid,score,rk
from
(
select cid,sid,score,rank() over (partition by cid order by score desc) rk
from sc
) t1
where rk<=3;
-- 师兄非窗口函数写法
SELECT cid,sid,score FROM sc a
WHERE (SELECT COUNT(1) FROM sc b WHERE a.cid=b.cid AND a.score<=b.score)<=3
ORDER BY cid;
#TODO 20.查询每门课程被选修的学生数
select Course.cid,Cname,student_num
from course
left join
(
select cid,count(sid) student_num
from sc
group by cid
) t1 on Course.CId=t1.CId;
#TODO 21 查询出只选修两门课程的学生学号和姓名
select t1.sid,Sname
from student
join
(
select sid,count(cid) num
from sc
group by sid
having num=2
) t1 on Student.SId=t1.SId;
#TODO 22.查询男生、女生人数`student`
select sum(if(Ssex='',1,0)) '男生数量',sum(if(Ssex='',1,0)) '女生数量'
from student;
SELECT Ssex,COUNT(*) num FROM student GROUP BY ssex;
#TODO 23.查询名字中含有「风」字的学生信息
select *
from student
where Sname like '%风%';
#TODO 24.查询同名同姓学生名单,并统计同名人数
select sname ,count(sid) num
from student
group by sname
having num>1;
#TODO 25.查询 1990 年出生的学生名单
select sid,sname,Sage
from student
where year(Sage)='1990';
SELECT * FROM student WHERE sage LIKE '1990%';
#TODO 26.查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列
select cid,avg(score) avg
from sc
group by cid
order by avg desc,cid;
#TODO 27.查询平均成绩大于等于 85 的所有学生的学号、姓名和平均成绩
select t1.sid,sname,avg
from student
right join
(
select sid,avg(score) avg
from sc
group by sid
having avg>=85
) t1 on Student.SId=t1.sid;
#TODO 28.查询课程名称为「数学」,且分数低于 60 的学生姓名和分数
select t1.sid,Sname,score
from student
join
(
select cid,SId,score
from sc
where cid=
(
select cid
from Course
where Cname ='数学'
)and score<60
) t1 on Student.SId=t1.SId;
#TODO 29.查询所有学生的课程及分数情况(存在学生没成绩,没选课的情况)
select Student.sid,sname,ifnull(Cname,''),ifnull(score,0)
from student
left join
(
select sid,sc.cid,cname,score
from sc,course
where sc.cid=Course.CId
)t1 on Student.SId=t1.SId;
#TODO 30.查询任何一门课程成绩在 70 分以上的姓名、课程名称和分数
select sc.sid,sname,sc.cid,cname,score
from sc,course,student
where sc.CId=Course.CId and Student.SId=sc.SId and score>70;
SELECT t3.sname,t2.cname,t1.score FROM
(
(SELECT * FROM sc) t1
JOIN
(SELECT cid,cname FROM course) t2
JOIN
(SELECT sid,sname FROM student) t3
ON t1.cid=t2.cid AND t1.sid=t3.sid
)
WHERE t1.score > 70;
#TODO 31.查询不及格的课程
select sid,cid,score,(case when score<60 then 'fail' else 'success' end) result
from sc
where score<60;
# TODO 32.查询课程编号为 01 且课程成绩在 80 分以上的学生的学号和姓名
select sid,Sname
from Student
where sid in
(select sid
from sc
where cid='01'and score>=80);
-- 也可以join获得具体的数
SELECT student.`SId`,student.`Sname`,sc.`CId`,sc.`score`
FROM sc,student
WHERE sc.`SId`=student.`SId` AND sc.`CId`=01 AND sc.`score`>=80;
#TODO 33.求每门课程的学生人数
select cid,count(sid) num
from sc
group by cid;
#TODO 34.成绩不重复,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩
select student.sid, sname, sage, ssex,cid, score
from student
join
(
select sid,cid,score
from sc
where CId
in (select CId
from course
where TId in(select TId from teacher where Tname='张三'))
having score=max(score)
) t1 on Student.SId=t1.SId;
SELECT t2.sid,t2.cid,MAX(t2.score) FROM
(
(SELECT CId FROM course WHERE TId = (SELECT tid FROM teacher WHERE Tname='张三'))t1
JOIN
(SELECT * FROM sc) t2
ON t1.cid=t2.cid
);
#TODO 35.成绩有重复的情况下,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩
-- 本人的应该有没有重复都行
select student.sid, sname, sage, ssex,cid, score
from student
join
(
select sid,cid,score
from sc
where CId
in (select CId
from course
where TId in(select TId from teacher where Tname='张三'))
having score=max(score)
) t1 on Student.SId=t1.SId;
# TODO 36.查询不同课程成绩相同的学生的学生编号、课程编号、学生编号
-- 窗口函数
-- 查询不同课程成绩相同的学生的学生编号
select sid,score,CId,num
from
(
select sid,score,cid,count(cid) over(partition by sid,score) num
from sc
)t1
where num>1;
-- 师兄不用窗口函数法(有四门课及以上的应该不对)
SELECT * FROM sc a
WHERE sid IN
(SELECT sid FROM sc
GROUP BY sid
HAVING (COUNT(*)=2 AND COUNT(DISTINCT score)=1) -- 有两门课有重复的情况
OR (COUNT(*)=3 AND COUNT(DISTINCT score)<3)); -- 有三门课有重复的情况
-- 根据师兄的改进(去重前后不相等则有重复)
SELECT * FROM sc a
WHERE sid IN
(SELECT sid FROM sc
GROUP BY sid
HAVING COUNT(*) != COUNT(DISTINCT score));
#TODO 37.查询各学生的年龄,只按年份来算
SELECT sid,sname, YEAR(now())-YEAR(sage) age FROM student;
#TODO 38.按照出生日期来算,当前月日
SELECT sid,sname,TIMESTAMPDIFF(YEAR,sage,CURDATE()) age FROM student;
-- 自己的本办法
-- 判断年龄是否要-1
select sid,sname ,year(now())-year(Sage)-if(month(now())-month(Sage)>0||(month(now())=month(Sage)&&day(now())>=day(Sage)),0,1) age
from student;
#TODO 39.查询本周过生日的学生
SELECT * FROM student WHERE WEEKOFYEAR(sage)=WEEKOFYEAR(CURDATE())

View File

@ -0,0 +1,60 @@
package com.markilue.leecode.test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.test
*@Author: markilue
*@CreateTime: 2023-04-04 11:01
*@Description:
* TODO 力扣15题 三数之和:
* 给你一个整数数组 nums 判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k j != k 同时还满足 nums[i] + nums[j] + nums[k] == 0
* 你返回所有和为 0 且不重复的三元组
* 注意答案中不可以包含重复的三元组
*@Version: 1.0
*/
public class ThreeSum {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
if (nums.length < 3) {
return result;
}
Arrays.sort(nums);
for (int i = 0; i < nums.length-2; i++) {
if (i != 0 && nums[i] == nums[i - 1]) {
continue;
}
if (nums[i] > 0) break;
int left = i + 1;
int right = nums.length - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum < 0) {
left++;
} else if (sum > 0) {
right--;
} else {
result.add(new ArrayList<>(Arrays.asList(nums[i], nums[left], nums[right])));
while (left < right && nums[left] == nums[left + 1]) left++;
while (left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
}
}
}
return result;
}
}

View File

@ -1,5 +1,6 @@
package com.markilue.leecode.test; package com.markilue.leecode.test;
import com.markilue.leecode.hot100.T55_LFUCache;
import com.markilue.leecode.tree.TreeNode; import com.markilue.leecode.tree.TreeNode;
import org.junit.Test; import org.junit.Test;
@ -15,6 +16,32 @@ import java.util.ArrayList;
*/ */
public class test { public class test {
@Test
public void test2(){
T55_LFUCache lfuCache = new T55_LFUCache(2);
lfuCache.put(1,1);
lfuCache.put(2,2);
System.out.println(lfuCache.get(1));
lfuCache.put(3,3);
System.out.println(lfuCache.get(2));
System.out.println(lfuCache.get(3));
lfuCache.put(4,4);
System.out.println(lfuCache.get(1));
System.out.println(lfuCache.get(3));
System.out.println(lfuCache.get(4));
}
static {
System.out.println("hello sta");
}
public static void main(String[] args) {
System.out.println("hello world");
}
//测试ArrayList的index是否发生变化 //测试ArrayList的index是否发生变化
@Test @Test

View File

@ -1,6 +1,7 @@
package com.markilue.leecode.test; package com.markilue.leecode.test;
import java.util.HashMap; import java.util.*;
import java.util.Scanner; import java.util.Scanner;
/** /**
@ -14,53 +15,53 @@ import java.util.Scanner;
public class testAnt { public class testAnt {
public static void main(String[] args) { // public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String input = sc.nextLine();
new testAnt().test(input);
// StringBuilder output = new StringBuilder();
// int left = 0;
// //
// //去除前面的 // Scanner sc = new Scanner(System.in);
// while (left < input.length() && input.charAt(left) == ' ') { // String input = sc.nextLine();
// left++;
// }
// //
// boolean isCapital = true; // 首字母是否大写 // new testAnt().test(input);
// for (int i = left; i < input.length(); i++) { //// StringBuilder output = new StringBuilder();
// char c = input.charAt(i); //// int left = 0;
// if (c == ' ') { ////
// if (i > 0 && input.charAt(i - 1) == '.') { //// //去除前面的
// output.deleteCharAt(output.length() - 1); //// while (left < input.length() && input.charAt(left) == ' ') {
// } //// left++;
// // 跳过多余的空格 //// }
// while (i < input.length() - 1 && input.charAt(i + 1) == ' ') { ////
// i++; //// boolean isCapital = true; // 首字母是否大写
// } //// for (int i = left; i < input.length(); i++) {
// if (input.charAt(i + 1) != '.') { //// char c = input.charAt(i);
// output.append(' '); //// if (c == ' ') {
// } //// if (i > 0 && input.charAt(i - 1) == '.') {
// } else if (c == '.') { //// output.deleteCharAt(output.length() - 1);
// // 句号后面要加一个空格 //// }
// output.append(". "); //// // 跳过多余的空格
// isCapital = true; // 下一句话的首字母要大写 //// while (i < input.length() - 1 && input.charAt(i + 1) == ' ') {
// } else { //// i++;
// if (isCapital) { //// }
// // 首字母要大写 //// if (input.charAt(i + 1) != '.') {
// output.append(Character.toUpperCase(c)); //// output.append(' ');
// isCapital = false; //// }
// } else { //// } else if (c == '.') {
// output.append(c); //// // 句号后面要加一个空格
// } //// output.append(". ");
// } //// isCapital = true; // 下一句话的首字母要大写
// } //// } else {
//// if (isCapital) {
//// // 首字母要大写
//// output.append(Character.toUpperCase(c));
//// isCapital = false;
//// } else {
//// output.append(c);
//// }
//// }
//// }
////
//// System.out.println(output.toString());
// sc.close();
// //
// System.out.println(output.toString()); // }
sc.close();
}
public void test(String str) { public void test(String str) {
@ -106,4 +107,68 @@ public class testAnt {
} }
// public static void main(String[] args) {
// Scanner scanner = new Scanner(System.in);
// int k = scanner.nextInt();
// int count = 0;
// for (int i = 0; i < 65536; i++) {
// String hex = String.format("%04x", i); // 将整数转换成长度为 4 的十六进制数
// if (isGoodHex(hex)) {
// count++;
// if (count == k) {
// System.out.println(hex);
// break;
// }
// }
// }
// }
//
// public static boolean isGoodHex(String hex) {
// Set<Character> digits = new HashSet<>();
// for (char c : hex.toCharArray()) {
// digits.add(c);
// }
// return digits.size() == 4;
// }
public static void main(String[] args) {
String s = "????(?"; // 待匹配的字符串
int left = 0, right = 0, count = 0; // 分别记录左括号右括号和合法的括号对的数量
Stack<Integer> stack = new Stack<>(); // 用于记录左括号和 '?' 的下标
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '(' || c == '?') {
stack.push(i);
if (c == '(') {
left++;
}
} else {
if (stack.isEmpty()) {
continue;
}
stack.pop();
if (c == ')') {
right++;
}
count++;
}
}
while (!stack.isEmpty()) { // 处理剩余的左括号或 '?'
int index = stack.pop();
if (s.charAt(index) == '(') {
left--;
} else {
right--;
}
}
count += Math.min(left, right); // '?' 可以代替左括号或右括号因此左右括号数量较小的值为合法的括号对数量
System.out.println(count);
}
} }