599. 两个列表的最小索引总和
添加链接描述
假设 Andy 和 Doris 想在晚餐时选择一家餐厅,并且他们都有一个表示最喜爱餐厅的列表,每个餐厅的名字用字符串表示。
你需要帮助他们用最少的索引和找出他们共同喜爱的餐厅。 如果答案不止一个,则输出所有答案并且不考虑顺序。 你可以假设答案总是存在。
示例 1:
输入: list1 = [“Shogun”, “Tapioca Express”, “Burger King”, “KFC”],list2 = [“Piatti”, “The Grill at Torrey Pines”, “Hungry Hunter Steakhouse”, “Shogun”]
输出: [“Shogun”]
解释: 他们唯一共同喜爱的餐厅是“Shogun”。
class Solution {
public String[] findRestaurant(String[] list1, String[] list2) {
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i < list1.length; i++) {
map.put(list1[i], i);
}
int minIndexSum = Integer.MAX_VALUE;
List<String> list = new ArrayList<>();
for (int i = 0; i < list2.length; i++) {
if(map.containsKey(list2[i])) {
int j = map.get(list2[i]);
if(i + j < minIndexSum) {
list.clear();
list.add(list2[i]);
minIndexSum = i + j;
} else if (i + j == minIndexSum) {
list.add(list2[i]);
}
}
}
return list.toArray(new String[0]);
}
}
2181. 合并零之间的节点
添加链接描述
给你一个链表的头节点 head ,该链表包含由 0 分隔开的一连串整数。链表的 开端 和 末尾 的节点都满足 Node.val == 0 。
对于每两个相邻的 0 ,请你将它们之间的所有节点合并成一个节点,其值是所有已合并节点的值之和。然后将所有 0 移除,修改后的链表不应该含有任何 0 。
返回修改后链表的头节点 head 。
示例 1:
输入:head = [0,3,1,0,4,5,2,0]
输出:[4,11]
解释:
上图表示输入的链表。修改后的链表包含:
- 标记为绿色的节点之和:3 + 1 = 4
- 标记为红色的节点之和:4 + 5 + 2 = 11
class Solution {
public ListNode mergeNodes(ListNode head) {
ListNode newHead = new ListNode(-1);
ListNode tmp = newHead;
ListNode cur = head;
int val = 0;
while(cur != null) {
if(cur != head && cur.val == 0) { // 第一个头节点不加入
tmp.next = new ListNode(val);
tmp = tmp.next;
val = 0; // 下一个
} else {
val += cur.val;
}
cur = cur.next;
}
return newHead.next;
}
}
69. x 的平方根
添加链接描述
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
示例 1:
输入:x = 4
输出:2
class Solution {
// 二分
public int mySqrt(int x) {
if(x == 1) return 1;
int left = 1;
int right = x / 2; //
while(left <= right) {
int mid = left + (right - left) / 2;
if(x / mid > mid) { //不用x > mid * mid 防溢出
left = mid + 1;
} else if (x / mid < mid) {
right = mid - 1;
} else {
return mid;
}
}
return right; // -1->-1 0->1
}
// 2、根据平方数的性质——连续n个奇数相加的结果一定是平方数
public int mySqrt2(int x) {
if(x < 0) return 0;
int i = 1;
int ans = 0;
while(x >= 0) {
x -= i;
i += 2;
ans++;
}
return ans - 1;
}
public int mySqrt1(int x) {
double mul = Math.sqrt(x);
return (int)Math.floor(mul);
}
}
917. 仅仅反转字母
添加链接描述
给你一个字符串 s ,根据下述规则反转字符串:
所有非英文字母保留在原有位置。
所有英文字母(小写或大写)位置反转。
返回反转后的 s 。
示例 1:
输入:s = “ab-cd”
输出:“dc-ba”
class Solution {
public String reverseOnlyLetters(String s) {
char[] ret = s.toCharArray();
int left = 0;
int right = ret.length - 1; //
while(left < right) {
while(left < right && !Character.isLetter(ret[left])) {
left++;
}
while(left < right && !Character.isLetter(ret[right])) {
right--;
}
if(left < right) {
char tmp = ret[left];
ret[left] = ret[right];
ret[right] = tmp;
}
left++; //
right--; //
}
return new String(ret);
}
}
剑指 Offer 54. 二叉搜索树的第k大节点
添加链接描述
给定一棵二叉搜索树,请找出其中第 k 大的节点的值。
示例 1:
输入: root = [3,1,4,null,2], k = 1
3
/
1 4
2
输出: 4
class Solution {
// 中序遍历 保存的list中是从小到大排序的
public void inorder(TreeNode root, List<Integer> list) {
if(root == null) {
return;
}
inorder(root.left, list);
list.add(root.val);
inorder(root.right, list);
}
public int kthLargest(TreeNode root, int k) {
List<Integer> list = new ArrayList<>();
inorder(root, list);
return list.get(list.size() - k); // 获取
}
}
496. 下一个更大元素 I
添加链接描述
nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。
给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。
对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。
返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。
示例 1:
输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
- 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
- 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
class Solution {
// 单调栈 + map
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
Deque<Integer> stack = new ArrayDeque<>(); // 从小到大
Map<Integer, Integer> map = new HashMap<>(); // 记录映射关系
for (int i = nums2.length - 1; i >= 0; i--) { // 从后往前 记录nums2中向左第一个最大值
int num = nums2[i];
while(!stack.isEmpty() && num >= stack.peek()) {
stack.pop(); // 比栈顶大或等于 出栈 判断下一个
}
// 空-> 右边无比之大的数 存储-1 不为空 -> 存储
map.put(num, stack.isEmpty() ? -1 : stack.peek());
stack.push(num); // 入栈
}
int[] ans = new int[nums1.length];
for (int i = 0; i < nums1.length; i++) {
ans[i] = map.get(nums1[i]); // 数组元素不重复 通过map找到当前数据对应的第一个最大值
}
return ans;
}
// 暴力算法 O(n*m)
// 找在nums2中的位置->j 往后找第一个大的k 判断是否<n 若存在加入数组
public int[] nextGreaterElement1(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
int[] ans = new int[m];
for (int i = 0; i < m; i++) {
int j = 0;
while(j < n && nums2[j] != nums1[i]) {
j++;
}
int k = j + 1;
while(k < n && nums2[k] < nums1[i]) {
k++;
}
ans[i] = k < n ? nums2[k] : -1;
}
return ans;
}
}
1441. 用栈操作构建数组
添加链接描述
给你一个目标数组 target 和一个整数 n。每次迭代,需要从 list = {1,2,3…, n} 中依序读取一个数字。
请使用下述操作来构建目标数组 target :
Push:从 list 中读取一个新元素, 并将其推入数组中。
Pop:删除数组中的最后一个元素。
如果目标数组构建完成,就停止读取更多元素。
题目数据保证目标数组严格递增,并且只包含 1 到 n 之间的数字。
请返回构建目标数组所用的操作序列。
题目数据保证答案是唯一的。
示例 1:
输入:target = [1,3], n = 3
输出:[“Push”,“Push”,“Pop”,“Push”]
解释:
读取 1 并自动推入数组 -> [1]
读取 2 并自动推入数组,然后删除它 -> [1]
读取 3 并自动推入数组 -> [1,3]
class Solution {
public List<String> buildArray(int[] target, int n) {
List<String> list = new ArrayList<>();
int index = 0;
for(int j = 1; j <= n && index < target.length; j++) {
if(j == target[index]) { // 一个匹配 到 下一个元素
list.add("Push");
index++;
} else {
list.add("Push");
list.add("Pop");
}
}
return list;
}
}
328. 奇偶链表
添加链接描述
给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。
第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。
请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。
你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。
class Solution {
// 分离节点后合并
public ListNode oddEvenList(ListNode head) {
if(head == null || head.next == null) {
return head;
}
ListNode evenHead = head.next; // 连接单节点的尾
ListNode odd = head; // 单数链表的头节点
ListNode even = head.next; // 偶数链表的头节点
while(even != null && even.next != null) {
odd.next = even.next;
odd = odd.next;
even.next = odd.next;
even = even.next;
}
odd.next = evenHead; //
return head;
}
}
2. 两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
class Solution {
// 正确做法:与67题类似
// https://leetcode-cn.com/problems/add-binary/
// 由于输入的两个链表都是逆序存储数字的位数的,因此两个链表中同一位置的数字可以直接相加。
// 我们同时遍历两个链表,逐位计算它们的和,并与当前位置的进位值相加。
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode newHead = new ListNode(-1);
ListNode tmp = newHead;
int carry = 0; // 进位
while(l1 != null || l2 != null || carry != 0) {
int val1 = l1 != null ? l1.val : 0;
int val2 = l2 != null ? l2.val : 0;
int val = val1 + val2 + carry; // 还需加入的总值
carry = val / 10; // 如果是10,此次节点val为0,进位1
tmp.next = new ListNode(val % 10);
tmp = tmp.next; // 不要忘了往后走
if(l1 != null) {
l1 = l1.next;
}
if(l2 != null) {
l2 = l2.next;
}
}
return newHead.next;
}
// 修改为Long 还是有超出范围的情况
public Long getSumOfVal(ListNode cur) {
StringBuilder sb = new StringBuilder();
while(cur != null) {
sb.append(cur.val);
cur = cur.next;
}
String str = sb.reverse().toString();
return Long.valueOf(str);
}
public ListNode addTwoNumbers1(ListNode l1, ListNode l2) {
Long num1 = getSumOfVal(l1);
Long num2 = getSumOfVal(l2);
Long sum = num1 + num2; // 两链表 val 之和
// 逆置和
String str = String.valueOf(sum);
StringBuilder sb = new StringBuilder(str).reverse();
ListNode newHead = new ListNode(-1);
ListNode cur = newHead; // 连接每一个节点
for (int i = 0; i < str.length(); i++) {
ListNode node = new ListNode(sb.charAt(i) - '0');
cur.next = node;
cur = cur.next;
}
return newHead.next;
}
}
67. 二进制求和
添加链接描述
给你两个二进制字符串,返回它们的和(用二进制表示)。
输入为 非空 字符串且只包含数字 1 和 0。
示例 1:
输入: a = “11”, b = “1”
输出: “100”
class Solution {
public String addBinary(String a, String b) {
int i = a.length() - 1;
int j = b.length() - 1;
StringBuilder sb = new StringBuilder();
int carry = 0;
while(i >= 0 || j >= 0 || carry != 0) { // 进位不为0
int sum = carry;
if(i >= 0) {
sum += a.charAt(i--) - '0';
}
if(j >= 0) {
sum += b.charAt(j--) - '0';
}
sb.append(sum % 2);
carry = sum / 2;
}
/*if() { // 进位 不为0 放个位数
sb.append(carry);
}*/
return sb.reverse().toString();
}
// 先将 a 和 b 转化成十进制数,求和后再转化为二进制数
// 如果字符串超过 33 位,不能转化为 Integer
// 如果字符串超过 65 位,不能转化为 Long
// 如果字符串超过 500000001 位,不能转化为 BigInteger
/*public String addBinary1(String a, String b) {
return Integer.toBinaryString(
Integer.parseInt(a, 2) + Integer.parseInt(b, 2)
);
}*/
}
1171. 从链表中删去总和值为零的连续节点
给你一个链表的头节点 head,请你编写代码,反复删去链表中由 总和 值为 0 的连续节点组成的序列,直到不存在这样的序列为止。
删除完毕后,请你返回最终结果链表的头节点。
你可以返回任何满足题目要求的答案。
(注意,下面示例中的所有序列,都是对 ListNode 对象序列化的表示。)
示例 1:
输入:head = [1,2,-3,3,1]
输出:[3,1]
提示:答案 [1,2,1] 也是正确的。
class Solution {
// 前缀和
public ListNode removeZeroSumSublists(ListNode head) {
ListNode newHead = new ListNode(0);
newHead.next = head;
int sum = 0; // 和
ListNode cur = newHead;
Map<Integer, ListNode> map = new HashMap<>();
// 首次遍历建立 节点处链表和<->节点 哈希表
// 若同一和出现多次会覆盖,即记录该sum出现的最后一次节点
while(cur != null) {
sum += cur.val;
map.put(sum, cur);
cur = cur.next;
}
// 第二遍遍历 若当前节点处sum在下一处出现了则表明两结点之间所有节点和为0 直接删除区间所有节点
//(从前面的一个sum处的节点指向后面的另一个sum处的节点的next)
sum = 0;
cur = newHead;
while(cur != null) {
sum += cur.val;
cur.next = map.get(sum).next;
cur = cur.next;
}
return newHead.next;
}
}
717. 1比特与2比特字符
有两种特殊字符:
第一种字符可以用一个比特 0 来表示
第二种字符可以用两个比特(10 或 11)来表示、
给定一个以 0 结尾的二进制数组 bits ,如果最后一个字符必须是一位字符,则返回 true 。
示例 1:
输入: bits = [1, 0, 0]
输出: true
解释: 唯一的编码方式是一个两比特字符和一个一比特字符。
所以最后一个字符是一比特字符。
class Solution {
public boolean isOneBitCharacter(int[] bits) {
int start = 0;
while(start < bits.length - 1) {
if(bits[start] == 0) {
start++;
} else {
start += 2;
}
}
return start == bits.length - 1;
}
}
1791. 找出星型图的中心节点
有一个无向的 星型 图,由 n 个编号从 1 到 n 的节点组成。星型图有一个 中心 节点,并且恰有 n - 1 条边将中心节点与其他每个节点连接起来。
给你一个二维整数数组 edges ,其中 edges[i] = [ui, vi] 表示在节点 ui 和 vi 之间存在一条边。请你找出并返回 edges 所表示星型图的中心节点。
class Solution {
// 顶点数目为 N,中心顶点的度为 N-1
public int findCenter(int[][] edges) {
Map<Integer, Integer> map = new HashMap<>();
for(int[] e : edges) {
map.put(e[0], map.getOrDefault(e[0], 0) + 1);
map.put(e[1], map.getOrDefault(e[1], 0) + 1);
}
int size = map.size();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if(entry.getValue() == size - 1) {
return entry.getKey();
}
}
/*for (int key : map.keySet()) {
if(map.get(key) == size - 1) { // 根据k找v
return key;
}
}*/
return -1;
}
public int findCenter1(int[][] edges) {
return edges[0][0] == edges[1][0] || edges[0][0] == edges[1][1] ? edges[0][0] : edges[0][1];
}
}
1046. 最后一块石头的重量
添加链接描述
有一堆石头,每块石头的重量都是正整数。
每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。
示例:
输入:[2,7,4,1,8,1]
输出:1
解释:
先选出 7 和 8,得到 1,所以数组转换为 [2,4,1,1,1],
再选出 2 和 4,得到 2,所以数组转换为 [2,1,1,1],
接着是 2 和 1,得到 1,所以数组转换为 [1,1,1],
最后选出 1 和 1,得到 0,最终数组转换为 [1],这就是最后剩下那块石头的重量。
class Solution {
// 优先级队列
public int lastStoneWeight(int[] stones) {
// 大根堆
PriorityQueue<Integer> pr = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
for (int n : stones) {
pr.offer(n);
}
// 拿出两个 不相等y-x入队列
while(pr.size() > 1) {
int num1 = pr.poll();
int num2 = pr.poll();
if(num1 != num2) {
pr.offer(num1 - num2);
}
}
return pr.isEmpty() ? 0 : pr.poll();
}
// 排序
public int lastStoneWeight1(int[] stones) {
int n = stones.length;
if(n <= 1) {
return stones[0];
}
while(stones[n - 2] != 0) {
Arrays.sort(stones);
if(stones[n - 1] != stones[n - 2]) {
stones[n - 1] = stones[n - 1] - stones[n - 2];
stones[n - 2] = 0;
} else {
stones[n - 1] = 0;
stones[n - 2] = 0;
}
Arrays.sort(stones); // stones[n - 2] != 0
}
return stones[n - 1];
}
}
1380. 矩阵中的幸运数
添加链接描述
给你一个 m * n 的矩阵,矩阵中的数字 各不相同 。请你按 任意 顺序返回矩阵中的所有幸运数。
幸运数是指矩阵中满足同时下列两个条件的元素:
在同一行的所有元素中最小
在同一列的所有元素中最大
示例 1:
输入:matrix = [[3,7,8],[9,11,13],[15,16,17]]
输出:[15]
解释:15 是唯一的幸运数,因为它是其所在行中的最小值,也是所在列中的最大值。
class Solution {
public List<Integer> luckyNumbers1 (int[][] matrix) {
int m = matrix.length;
int n = matrix[0].length;
int[] minRow = new int[m]; // 第 i 行的最小值
Arrays.fill(minRow, Integer.MAX_VALUE);
int[] maxCol = new int[n]; // 第 j 列的最大值
// 遍历 记录行最小值,列最大值
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
minRow[i] = Math.min(minRow[i], matrix[i][j]);
maxCol[j] = Math.max(maxCol[j], matrix[i][j]);
}
}
// 再次遍历 同时满足即为答案
List<Integer> list = new ArrayList<>();
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
if(matrix[i][j] == minRow[i] && matrix[i][j] == maxCol[j]) {
list.add(matrix[i][j]);
}
}
}
return list;
}
// 遍历矩阵,判断 matrix[i][j] 是否是它所在行的最小值和所在列的最大值
public List<Integer> luckyNumbers (int[][] matrix) {
int m = matrix.length;
int n = matrix[0].length;
List<Integer> list = new ArrayList<>();
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
boolean isMin = true;
boolean isMax = true;
// 判断行最小值
for (int k = 0; k < n; k++) {
if(matrix[i][k] < matrix[i][j]) {
isMin = false;
break;
}
}
if(!isMin) {
continue;
}
// 判断列最大值
for (int k = 0; k < m; k++) {
if(matrix[k][j] > matrix[i][j]) {
isMax = false;
break;
}
}
if(isMax) {
list.add(matrix[i][j]);
}
}
}
return list;
}
}
540. 有序数组中的单一元素
添加链接描述
给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。
请你找出并返回只出现一次的那个数。
你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。
示例 1:
输入: nums = [1,1,2,3,3,4,4,8,8]
输出: 2
class Solution {
// O(logn) 二分查找
public int singleNonDuplicate2(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = (right - left) / 2 + left;
if(mid % 2 == 0) { // 偶数
if(nums[mid] == nums[mid + 1]) {
left = mid + 1;
} else {
right = mid;
}
} else { // 奇数
if(nums[mid] == nums[mid - 1]) {
left = mid + 1;
} else {
right = mid;
}
}
}
return nums[right];
}
// 用异或来进行统一,因为 偶数异或1 等于 加1,奇数异或1 等于 减1
public int singleNonDuplicate(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = (right - left) / 2 + left;
if(nums[mid] == nums[mid ^ 1]) {
left = mid + 1;
} else {
right = mid;
}
}
return nums[right];
}
// O(n) 每两个遍历
public int singleNonDuplicate1(int[] nums) {
for(int i = 0; i < nums.length - 1; i += 2) {
if(nums[i] != nums[i + 1]) {
return nums[i];
}
}
return nums[nums.length - 1];
}
}
1189. “气球” 的最大数量
添加链接描述
给你一个字符串 text,你需要使用 text 中的字母来拼凑尽可能多的单词 “balloon”(气球)。
字符串 text 中的每个字母最多只能被使用一次。请你返回最多可以拼凑出多少个单词 “balloon”。
示例 1:
输入:text = “nlaebolko”
输出:1
class Solution {
public int maxNumberOfBalloons(String text) {
// 此单词每个字符次数
int[] times = new int[5];
for(int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
if(ch == 'b') {
times[0]++;
} else if (ch == 'a') {
times[1]++;
} else if (ch == 'l') {
times[2]++;
} else if (ch == 'o') {
times[3]++;
} else if(ch == 'n'){
times[4]++;
}
}
// 'l' 'o' 有两次
times[2] /= 2;
times[3] /= 2;
int min = times[0];
for (int i = 0; i < 5; i++) {
min = Math.min(min, times[i]);
}
return min;
}
}
面试题 01.02. 判定是否互为字符重排
添加链接描述
给定两个字符串 s1 和 s2,请编写一个程序,确定其中一个字符串的字符重新排列后,能否变成另一个字符串。
示例 1:
输入: s1 = “abc”, s2 = “bca”
输出: true
class Solution {
// 3、哈希表
public boolean CheckPermutation(String s1, String s2) {
if(s1.length() != s2.length()) {
return false;
}
HashMap<Character, Integer> map = new HashMap<>();
// 如果相等,抵消为0
for(int i = 0; i < s1.length(); i++) {
map.put(s1.charAt(i), map.getOrDefault(s1.charAt(i), 0) + 1);
map.put(s2.charAt(i), map.getOrDefault(s2.charAt(i), 0) - 1);
}
// 判断
for(HashMap.Entry<Character, Integer> entry : map.entrySet()) {
if(entry.getValue() != 0) {
return false;
}
}
return true;
}
// 2、数组统计次数
public boolean CheckPermutation2(String s1, String s2) {
if(s1.length() != s2.length()) {
return false;
}
// 数组记录两字符串的字符次数、
int[] cnts1 = cntLetters(s1);
int[] cnts2 = cntLetters(s2);
for(int i = 0; i < 26; i++) {
if(cnts1[i] != cnts2[i]) {
return false;
}
}
return true;
}
public int[] cntLetters(String str) {
int[] cnts = new int[26];
for(int i = 0; i < str.length(); i++) {
cnts[str.charAt(i) - 'a']++;
}
return cnts;
}
// 1、转字符数组 排序
public boolean CheckPermutation1(String s1, String s2) {
if(s1.length() != s2.length()) {
return false;
}
char[] chars1 = s1.toCharArray();
Arrays.sort(chars1);
char[] chars2 = s2.toCharArray();
Arrays.sort(chars2);
// 字符串比较
return new String(chars1).equals(new String(chars2));
}
}
1984. 学生分数的最小差值
添加链接描述
给你一个 下标从 0 开始 的整数数组 nums ,其中 nums[i] 表示第 i 名学生的分数。另给你一个整数 k 。
从数组中选出任意 k 名学生的分数,使这 k 个分数间 最高分 和 最低分 的 差值 达到 最小化 。
返回可能的 最小差值 。
示例 1:
输入:nums = [90], k = 1
输出:0
解释:选出 1 名学生的分数,仅有 1 种方法:
- [90] 最高分和最低分之间的差值是 90 - 90 = 0
可能的最小差值是 0
// 滑动窗口最右边的值就是窗口内的最大值,滑动窗口最左边的值就是窗口内的最小值
// 因此,我们要寻找的就是已经排序的数组中,所有大小为 k 的滑动窗口中,最右端数字 - 最左端数字 的最小结果
class Solution {
public int minimumDifference(int[] nums, int k) {
Arrays.sort(nums);
int min = Integer.MAX_VALUE;
for(int i = 0; i + k - 1 < nums.length; i++) {
min = Math.min(min, nums[i + k - 1] - nums[i]);
}
return min;
}
// 滑动窗口:通过两个指针截取固定长度的数组
public int minimumDifference1(int[] nums, int k) {
// 先将数组进行排序
Arrays.sort(nums);
// 截取一定长度的数组
int left=0;
int right=k-1;
// 创建一个变量保存最小的差值
int min = Integer.MAX_VALUE;
while(right < nums.length) {
min = Math.min(min, nums[right] - nums[left]);
left++;
right = left + k - 1;
}
return min;
}
}
1447. 最简分数
添加链接描述
给你一个整数 n ,请你返回所有 0 到 1 之间(不包括 0 和 1)满足分母小于等于 n 的 最简 分数 。分数可以以 任意 顺序返回。
示例 1:
输入:n = 2
输出:[“1/2”]
解释:“1/2” 是唯一一个分母小于等于 2 的最简分数。
class Solution {
// 更相减损法
int gcd(int a, int b) {
while(true) {
if(a > b) {
a -= b;
} else if (a < b) {
b -= a;
} else {
return a;
}
}
}
// 欧几里得算法
int gcd1(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
// int c = a % b;
// while(c != 0) {
// a = b;
// b = c;
// c = a % b;
// }
// return b;
}
public List<String> simplifiedFractions(int n) {
List<String> list = new ArrayList<>();
for(int i = 1; i < n; i++) {
for(int j = i + 1; j <= n; j++) {
if(gcd(i, j) == 1) {
list.add(i + "/" + j);
}
}
}
return list;
}
}
2006. 差的绝对值为 K 的数对数目
添加链接描述
给你一个整数数组 nums 和一个整数 k ,请你返回数对 (i, j) 的数目,满足 i < j 且 |nums[i] - nums[j]| == k 。
|x| 的值定义为:
如果 x >= 0 ,那么值为 x 。
如果 x < 0 ,那么值为 -x 。
示例 1:
输入:nums = [1,2,2,1], k = 1
输出:4
解释:差的绝对值为 1 的数对为:
- [1,2,2,1]
- [1,2,2,1]
- [1,2,2,1]
- [1,2,2,1]
class Solution {
public int countKDifference(int[] nums, int k) {
int ans = 0;
HashMap<Integer, Integer> map = new HashMap<>();
for(int n : nums) {
ans += map.getOrDefault(n + k, 0) + map.getOrDefault(n - k, 0);
map.put(n, map.getOrDefault(n, 0) + 1);
}
return ans;
}
public int countKDifference1(int[] nums, int k) {
int ans = 0;
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if(nums[i] - nums[j] == k || nums[j] - nums[i] == k) {
ans++;
}
}
}
return ans;
}
}
1748. 唯一元素的和
添加链接描述
给你一个整数数组 nums 。数组中唯一元素是那些只出现 恰好一次 的元素。
请你返回 nums 中唯一元素的 和 。
示例 1:
输入:nums = [1,2,3,2]
输出:4
解释:唯一元素为 [1,3] ,和为 4 。
class Solution {
// 哈希表记录次数
public int sumOfUnique1(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
for(int n : nums) {
map.put(n, map.getOrDefault(n, 0) + 1);
}
int ans = 0;
for(HashMap.Entry<Integer, Integer> entry : map.entrySet()) {
if(entry.getValue() == 1) {
ans += entry.getKey();
}
}
return ans;
}
// 一次遍历 没有出现加起来 出现过就减 同时设为2
public int sumOfUnique2(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
int ans = 0;
for(int n : nums) {
if(!map.containsKey(n)) {
ans += n; // 没有此值
map.put(n, 1); // 同时加入哈希表
} else if(map.get(n) == 1) {
ans -= n; // 出现过一次 表示加过了 需要减掉
map.put(n, 2); // 同时设为2
}
}
return ans;
}
// 数组
public int sumOfUnique(int[] nums) {
int[] cnts = new int[101];
int ans = 0;
for(int n : nums) {
if(++cnts[n] == 1) {
ans += n;
} else if (cnts[n] == 2) {
ans -= n;
}
}
return ans;
}
}
278. 第一个错误的版本
添加链接描述
你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
示例 1:
输入:n = 5, bad = 4
输出:4
解释:
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
int left = 1;
int right = n;
while(left < right) {
int mid = left + ((right - left) >> 1);
if(isBadVersion(mid)) {
right = mid; // 答案在区间 [left, mid] 中
} else {
left = mid + 1; // 答案在区间 [mid+1, right] 中
}
}
return left;
}
}
202. 快乐数
添加链接描述
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
示例 1:
输入:n = 19
输出:true
解释:
1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1
class Solution {
// 1、用哈希集合检测循环
/* 算法分为两部分,我们需要设计和编写代码:
- 给一个数字 n,它的下一个数字是什么?
- 按照一系列的数字来判断我们是否进入了一个循环。
第 1 部分我们按照题目的要求做数位分离,求平方和。
第 2 部分可以使用哈希集合完成。每次生成链中的下一个数字时,我们都会检查它是否已经在哈希集合中。
如果它不在哈希集合中,我们应该添加它。
如果它在哈希集合中,这意味着我们处于一个循环中,因此应该返回 false。*/
public int getNextNum(int n) {
int num = 0;
while(n > 0) {
num += Math.pow(n % 10, 2);
n /= 10;
}
return num;
}
public boolean isHappy1(int n) {
Set<Integer> set = new HashSet<>();
while(n != 1 && !set.contains(n)) {
set.add(n);
n = getNextNum(n);
}
return n == 1;
}
// 2、快慢指针法判断是否有环 龟兔赛跑
public boolean isHappy(int n) {
int slow = n;
int fast = getNextNum(n);
while(fast != 1 && slow != fast) {
slow = getNextNum(slow);
fast = getNextNum(getNextNum(fast));
}
return fast == 1;
}
}
1816. 截断句子
添加链接描述
句子 是一个单词列表,列表中的单词之间用单个空格隔开,且不存在前导或尾随空格。每个单词仅由大小写英文字母组成(不含标点符号)。
例如,“Hello World”、“HELLO” 和 “hello world hello world” 都是句子。
给你一个句子 s 和一个整数 k ,请你将 s 截断 ,使截断后的句子仅含 前 k 个单词。返回 截断 s 后得到的句子。
示例 1:
输入:s = “Hello how are you Contestant”, k = 4
输出:“Hello how are you”
解释:
s 中的单词为 [“Hello”, “how” “are”, “you”, “Contestant”]
前 4 个单词为 [“Hello”, “how”, “are”, “you”]
因此,应当返回 “Hello how are you”
class Solution {
public String truncateSentence(String s, int k) {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < s.length(); i++) {
if(s.charAt(i) == ' ') {
k--;
}
if(k == 0) {
break;
}
sb.append(s.charAt(i));
}
return sb.toString();
}
}
1725. 可以形成最大正方形的矩形数目
添加链接描述
给你一个数组 rectangles ,其中 rectangles[i] = [li, wi] 表示第 i 个矩形的长度为 li 、宽度为 wi 。
如果存在 k 同时满足 k <= li 和 k <= wi ,就可以将第 i 个矩形切成边长为 k 的正方形。例如,矩形 [4,6] 可以切成边长最大为 4 的正方形。
设 maxLen 为可以从矩形数组 rectangles 切分得到的 最大正方形 的边长。
请你统计有多少个矩形能够切出边长为 maxLen 的正方形,并返回矩形 数目 。
示例 1:
输入:rectangles = [[5,8],[3,9],[5,12],[16,5]]
输出:3
解释:能从每个矩形中切出的最大正方形边长分别是 [5,3,5,5] 。
最大正方形的边长为 5 ,可以由 3 个矩形切分得到。
class Solution {
public int countGoodRectangles(int[][] rectangles) {
int maxLen = 0; // maxLen
int cnt = 0;
for(int x[] : rectangles) {
int min = Math.min(x[0], x[1]); // 每组中的最小边长
if(min > maxLen) {
cnt = 1; // 更新最大边长数
}
if(min == maxLen) {
cnt++;
}
maxLen = Math.max(min, maxLen);
}
return cnt;
}
}
1414. 和为 K 的最少斐波那契数字数目
添加链接描述
给你数字 k ,请你返回和为 k 的斐波那契数字的最少数目,其中,每个斐波那契数字都可以被使用多次。
斐波那契数字定义为:
F1 = 1
F2 = 1
Fn = Fn-1 + Fn-2 , 其中 n > 2 。
数据保证对于给定的 k ,一定能找到可行解。
示例 1:
输入:k = 7
输出:2
解释:斐波那契数字为:1,1,2,3,5,8,13,……
对于 k = 7 ,我们可以得到 2 + 5 = 7 。
class Solution {
// 获取1-k的数列 从大到小找
public int findMinFibonacciNumbers(int k) {
List<Integer> list = new ArrayList<>();
list.add(1);
int f1 = 1;
int f2 = 1;
while (f1 + f2 <= k) {
int f3 = f1 + f2;
list.add(f3);
f1 = f2;
f2 = f3;
}
int ans = 0;
for (int i = list.size() - 1; i >= 0 && k > 0; i--) {
int num = list.get(i);
// ans += k / num;
// k %= num;
// 斐波那契数字为:1,1,2,3,5,8,13
// k = 19
// 输出:3
// 解释:对于 k = 19 ,我们可以得到 1 + 5 + 13 = 19
if(k >= num) {
k -= num;
ans++;
}
}
return ans;
}
}
1688. 比赛中的配对次数
class Solution {
// 每一场比赛中,输的队伍无法晋级,且不会再参加后续的比赛。
// 由于最后只决出一个获胜队伍,因此就有 n-1 个无法晋级的队伍,也就是会有 n−1 场比赛。
public int numberOfMatches(int n) {
return n - 1;
}
public int numberOfMatches1(int n) {
int ans = 0;
while(n > 1) {
ans += n / 2;
if(n % 2 != 0) {
n = n / 2 + 1;
}else {
n = n / 2;
}
}
return ans;
}
}
884. 两句话中的不常见单词
class Solution {
public String[] uncommonFromSentences(String s1, String s2) {
String[] words1 = s1.split(" ");
String[] words2 = s2.split(" ");
HashMap<String, Integer> map = new HashMap<>();
for (int i = 0; i < words1.length; i++) {
map.put(words1[i], map.getOrDefault(words1[i], 0) + 1);
}
for (int i = 0; i < words2.length; i++) {
map.put(words2[i], map.getOrDefault(words2[i], 0) + 1);
}
List<String> list = new ArrayList<>();
for(Map.Entry<String, Integer> ret : map.entrySet ()) {
if(ret.getValue() == 1) {
list.add(ret.getKey());
}
}
return list.toArray(new String[0]);
}
}
1332. 删除回文子序列
class Solution {
// 如果该字符串本身为回文串,此时只需删除1次即可,删除次数为1
// 如果该字符串本身不是回文串,此时只需删除2次即可,比如可以先删除所有的'a',再删除所有的'b',删除次数为2
public int removePalindromeSub(String s) {
int len = s.length();
for(int i = 0; i < len / 2; i++) {
if(s.charAt(i) != s.charAt(len - 1 - i)) {
return 2;
}
}
return 1;
}
}
1512. 好数对的数目
添加链接描述
给你一个整数数组 nums 。
如果一组数字 (i,j) 满足 nums[i] == nums[j] 且 i < j ,就可以认为这是一组 好数对 。
返回好数对的数目。
示例 1:
输入:nums = [1,2,3,1,1,3]
输出:4
解释:有 4 组好数对,分别是 (0,3), (0,4), (3,4), (2,5) ,下标从 0 开始
class Solution {
// 哈希表 x(x-1)/2
public int numIdenticalPairs(int[] nums) {
int cnt = 0;
Map<Integer, Integer> map = new HashMap<>();
for (int n : nums) {
map.put(n, map.getOrDefault(n, 0) + 1);
}
for(Map.Entry<Integer, Integer> entry : map.entrySet()) {
int x = entry.getValue();
cnt += x * (x - 1) / 2;
}
return cnt;
}
// 数组下标 相当于排列求和
// 假设nums = [1,1,1,1]
// 第一遍 temp是[0,0,0,0] ans+=0 temp[0]++
// 第二遍 temp是[1,0,0,0] ans+=1 temp[0]++
// 第三遍 temp=[2,0,0,0] ans+=2 temp[0]++
// 第四遍 temp=[3,0,0,0] ans+=3 temp[0]++
public int numIdenticalPairs2(int[] nums) {
int cnt = 0;
int[] ret = new int[101];
for(int x : nums) {
cnt += ret[x];
ret[x]++;
}
return cnt;
}
// 暴力
public int numIdenticalPairs1(int[] nums) {
int cnt = 0;
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if(nums[i] == nums[j]) {
cnt++;
}
}
}
return cnt;
}
}
617. 合并二叉树
添加链接描述
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
// 非递归
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if(root1 == null) return root2;
if(root2 == null) return root1;
TreeNode merged = new TreeNode(root1.val + root2.val);
Queue<TreeNode> queue = new LinkedList<>(); // 合并的队列
Queue<TreeNode> queue1 = new LinkedList<>();
Queue<TreeNode> queue2 = new LinkedList<>();
queue.offer(merged);
queue1.offer(root1);
queue2.offer(root2);
while(!queue2.isEmpty() && !queue2.isEmpty()) {
TreeNode node = queue.poll(), node1 = queue1.poll(), node2 = queue2.poll();
TreeNode left1 = node1.left, left2 = node2.left;
TreeNode right1 = node1.right, right2 = node2.right;
if(left1 != null || left2 != null) {
if(left1 != null && left2 != null) {
TreeNode left = new TreeNode(left1.val + left2.val);
node.left = left;
queue.offer(left);
queue1.offer(left1);
queue2.offer(left2);
} else if (left1 != null) {
node.left = left1;
} else if (left2 != null) {
node.left = left2;
}
}
if(right1 != null || right2 != null) {
if(right1 != null && right2 != null) {
TreeNode right = new TreeNode(right1.val + right2.val);
node.right = right;
queue.offer(right);
queue1.offer(right1);
queue2.offer(right2);
} else if (right1 != null) {
node.right = right1;
} else if (right2 != null) {
node.right = right2;
}
}
}
return merged;
}
// 递归
public TreeNode mergeTrees1(TreeNode root1, TreeNode root2) {
if(root1 == null) {
return root2;
}
if(root2 == null) {
return root1;
}
TreeNode root = new TreeNode(root1.val + root2.val);
root.left = mergeTrees(root1.left, root2.left);
root.right = mergeTrees(root1.right, root2.right);
return root;
}
}
844. 比较含退格的字符串
添加链接描述
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,请你判断二者是否相等。# 代表退格字符。
如果相等,返回 true ;否则,返回 false 。
注意:如果对空文本输入退格字符,文本继续为空。
示例 1:
输入:s = “ab#c”, t = “ad#c”
输出:true
解释:S 和 T 都会变成 “ac”。
class Solution {
// 指针 逆序遍历
// 定义 skip 意为需要跳过的字符数量,skip != 0 跳过当前字符
// 直到两字符串能够各自确定一个字符,然后将这两个字符进行比较
public static boolean backspaceCompare(String s, String t) {
int i = s.length() - 1;
int j = t.length() - 1;
int skip1 = 0;
int skip2 = 0;
while(i >= 0 || j >= 0) {
while(i >= 0) {
if(s.charAt(i) == '#') { // 需要跳过的字符+1
skip1++;
i--;
} else if(skip1 > 0) { // 跳过
skip1--;
i--;
} else {
break; // 不需要删的字符 出去比较是否相等
}
}
while(j >= 0) {
if(t.charAt(j) == '#') {
skip2++;
j--;
} else if(skip2 > 0) {
skip2--;
j--;
} else {
break;
}
}
if(i >= 0 && j >= 0) {
if(s.charAt(i) != t.charAt(j)) { // 字符不相等
return false;
}
} else {
if(i >= 0 || j >= 0) { // 有一个遍历完
return false;
}
}
i--;
j--;
}
return true;
}
// 重构字符串
// 退格符和应当被删除的字符都去除 比较两字符串是否相等
public static boolean backspaceCompare2(String s, String t) {
return build(s).equals(build(t));
}
public static String build(String str) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
if(str.charAt(i) != '#') {
sb.append(str.charAt(i));
} else {
if(sb.length() > 0) {
sb.deleteCharAt(sb.length() - 1); // 删除最后一个下标的元素
}
}
}
return sb.toString();
}
// 栈
public boolean backspaceCompare1(String s, String t) {
Stack<Character> stack1 = new Stack<>();
Stack<Character> stack2 = new Stack<>();
int len = Math.max(s.length(), t.length());
for (int i = 0; i < len; i++) {
if(i < s.length()) {
if(s.charAt(i) != '#') {
stack1.push(s.charAt(i));
} else if (s.charAt(i) == '#' && !stack1.empty()) { // 退格 栈不为空
stack1.pop();
}
}
if(i < t.length()) {
if(t.charAt(i) != '#') {
stack2.push(t.charAt(i));
} else if (t.charAt(i) == '#' && !stack2.empty()){
stack2.pop();
}
}
}
if(stack1.size() != stack2.size()) {
return false;
}
int size = stack1.size(); //
for (int i = 0; i < size; i++) {
if(stack1.pop() != stack2.pop()) {
return false;
}
}
return true;
}
}
933. 最近的请求次数
class RecentCounter {
// 二分查找
List<Integer> list;
public RecentCounter() {
list = new ArrayList<>();
}
public int ping(int t) {
list.add(t);
int index = binarySearch(list, t - 3000); // 查找的是 t - 3000
return list.size() - index;
}
private int binarySearch(List<Integer> list, int k) {
int left = 0;
int right = list.size() - 1;
while (left < right) {
int mid = (right - left) / 2 + left;
if (list.get(mid) >= k) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
// 存3002的时候删除1,因为1不在[2,3002]之间,而100,3001,3002都是在[2,3002]之间,返回siz为3
// 当前请求时间入队列,最早发生的请求会最先被删除,超过 3000 毫秒的请求删除
// Queue<Integer> que;
// public RecentCounter1() {
// que = new LinkedList();
// }
// public int ping1(int t) {
// que.offer(t);
// while(que.peek() < t - 3000) {
// que.poll();
// }
// return que.size();
// }
}
682. 棒球比赛
你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。
比赛开始时,记录是空白的。你会得到一个记录操作的字符串列表 ops,其中 ops[i] 是你需要记录的第 i 项操作,ops 遵循下述规则:
整数 x - 表示本回合新获得分数 x
“+” - 表示本回合新获得的得分是前两次得分的总和。题目数据保证记录此操作时前面总是存在两个有效的分数。
“D” - 表示本回合新获得的得分是前一次得分的两倍。题目数据保证记录此操作时前面总是存在一个有效的分数。
“C” - 表示前一次得分无效,将其从记录中移除。题目数据保证记录此操作时前面总是存在一个有效的分数。
请你返回记录中所有得分的总和。
class Solution {
public int calPoints(String[] ops) {
int ans = 0;
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < ops.length; i++) {
String str = ops[i];
if(str.equals("+")) {
int num1 = stack.pop();
int num2 = stack.peek(); // 前第二个
stack.push(num1); // 重新加入
stack.push(num1 + num2);
ans += stack.peek();
} else if (str.equals("D")) {
stack.push(stack.peek() * 2);
ans += stack.peek();
} else if (str.equals("C")) {
int ret = stack.pop();
ans -= ret;
} else {
// 是数字 -> 转数字 入栈
stack.push(Integer.parseInt(str));
ans += stack.peek();
}
}
return ans;
}
}
1716. 计算力扣银行的钱
Hercy 想要为购买第一辆车存钱。他 每天 都往力扣银行里存钱。
最开始,他在周一的时候存入 1 块钱。从周二到周日,他每天都比前一天多存入 1 块钱。在接下来每一个周一,他都会比 前一个周一 多存入 1 块钱。
给你 n ,请你返回在第 n 天结束的时候他在力扣银行总共存了多少块钱。
示例 1:
输入:n = 4
输出:10
解释:第 4 天后,总额为 1 + 2 + 3 + 4 = 10 。
class Solution {
/*每周多 7 块,因此每周的钱之和的序列是一个等差数列,等差数列求和公式求所有完整周
剩下的天数里,每天存的钱也是一个等差数列,可以用相同的公式进行求和。两者相加*/
public int totalMoney(int n) {
// 所有完整周
int week = n / 7;
int firstWeek = (1 + 7) * 7 / 2; // 每周多7
int lastWeek = firstWeek + 7 * (week - 1);
int weekMoney = (firstWeek + lastWeek) * week / 2;
// 剩下不足一周
int day = n % 7;
int firstDay = 1 + week; // 最后一周周一
int lastDay = firstDay + day - 1;
int dayMoney = (firstDay + lastDay) * day / 2;
return weekMoney + dayMoney;
}
public int totalMoney1(int n) {
int week = 0;
int day = 1;
int ans = 0;
for(int i = 0; i < n; i++) {
ans += week + day;
day++; // 每天
if(day == 8) { // 每一周
day = 1;
week++;
}
}
return ans;
}
}
747. 至少是其他数字两倍的最大数
class Solution {
// 最大值 m1,和次大值 m2,如果 m1 >= m2*2,同时记录 m1 下标
public int dominantIndex(int[] nums) {
int m1 = -1;
int m2 = -1;
int index = -1;
for (int i = 0; i < nums.length; i++) {
if(nums[i] > m1) {
m2 = m1;
m1 = nums[i];
index = i;
} else if(nums[i] > m2) {
m2 = nums[i];
}
}
return m1 >= m2 * 2 ? index : -1;
}
public int dominantIndex1(int[] nums) {
// 用array找max
int[] array = new int[nums.length];
for(int i = 0; i < nums.length; i++) {
array[i] = nums[i];
}
Arrays.sort(array);
int max = array[array.length - 1];
int index = 0; // 记录下标
int flag = 0;
for (int i = 0; i < nums.length; i++) {
if(nums[i] == max) {
flag++;
index = i;
}else if(nums[i] * 2 > max) {
return -1;
}
if(flag > 1) { // 不止一个最大数
return -1;
}
}
return index;
}
}
334. 递增的三元子序列
添加链接描述
给你一个整数数组 nums ,判断这个数组中是否存在长度为 3 的递增子序列。
如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否则,返回 false 。
示例 1:
输入:nums = [1,2,3,4,5]
输出:true
解释:任何 i < j < k 的三元组都满足题意
class Solution {
public boolean increasingTriplet1(int[] nums) {
int n = nums.length;
if(n < 3) {
return false;
}
// leftMin[i] 表示 nums[0] 到 nums[i] 中的最小值,
int[] leftMin = new int[n];
leftMin[0] = nums[0];
for (int i = 1; i < n; i++) {
leftMin[i] = Math.min(leftMin[i - 1], nums[i]);
}
// rightMax[i] 表示 nums[i] 到 nums[n−1] 中的最大值。
int[] rightMax = new int[n];
rightMax[n - 1] = nums[n - 1];
for (int i = n - 2; i >= 0; i--) {
rightMax[i] = Math.max(rightMax[i + 1], nums[i]);
}
// 查找是否存在一个下标 i 满足 leftMin[i−1] < nums[i] < rightMax[i + 1]
for (int i = 1; i < n - 1; i++) {
if(leftMin[i - 1] < nums[i] && nums[i] < rightMax[i + 1]) {
return true;
}
}
return false;
}
// 赋初始值的时候,已经满足second > first了,现在找第三个数third
// (1) 如果third比second大,那就是找到了,直接返回true
// (2) 如果third比second小,但是比first大,那就把second的值设为third,然后继续遍历找thrid
// (3) 如果third比first还小,那就把first的值设为third,然后继续遍历找thrid
// (这样的话first会跑到second的后边,但是不要紧,因为在second的前边,老first还是满足的)
public static boolean increasingTriplet(int[] nums) {
int n = nums.length;
if(n < 3) {
return false;
}
int first = nums[0];
int second = Integer.MAX_VALUE;
for (int i = 1; i < n; i++) {
int num = nums[i];
if(num > second) {
return true;
} else if (num >first) { // 比second小 比first大
second = num;
} else {
first = num; // 比first小
}
}
return false;
}
}
1920. 基于排列构建数组
添加链接描述
给你一个 从 0 开始的排列 nums(下标也从 0 开始)。请你构建一个 同样长度 的数组 ans ,其中,对于每个 i(0 <= i < nums.length),都满足 ans[i] = nums[nums[i]] 。返回构建好的数组 ans 。
从 0 开始的排列 nums 是一个由 0 到 nums.length - 1(0 和 nums.length - 1 也包含在内)的不同整数组成的数组。
示例 1:
输入:nums = [0,2,1,5,3,4]
输出:[0,1,2,4,5,3]
class Solution {
// 在原数组更改
public int[] buildArray(int[] nums) {
// 范围 [0, 999] 用%1000取原值
// 最终值 原数值
for(int i = 0; i < nums.length; i++) {
nums[i] += 1000 * (nums[nums[i]] % 1000);
}
for(int i = 0; i < nums.length; i++) {
nums[i] /= 1000;
}
return nums;
}
public int[] buildArray1(int[] nums) {
int[] ans = new int[nums.length];
for(int i = 0; i < nums.length; i++) {
ans[i] = nums[nums[i]];
}
return ans;
}
}
1629. 按键持续时间最长的键
class Solution {
public char slowestKey(int[] releaseTimes, String keysPressed) {
// 字符 时间 都初识为第一个
char ans = keysPressed.charAt(0);
int time = releaseTimes[0];
for(int i = 1; i < releaseTimes.length; i++) {
int nextTime = releaseTimes[i] - releaseTimes[i - 1]; // 下一个持续时间
if(time < nextTime) {
// 下一个持续时间更长 更新字符与时间
ans = keysPressed.charAt(i);
time = nextTime;
}else if(time == nextTime && ans < keysPressed.charAt(i)) {
// 时间相同 比较字符大小是否需要更新 c>b
ans = keysPressed.charAt(i);
}
}
return ans;
}
}
89. 格雷编码
class Solution {
// 如果二进制码字的第i位和i+1位相同,则对应的格雷码的第i位为0,否则为1
public List<Integer> grayCode(int n) {
List<Integer> list = new ArrayList<>();
for(int i = 0; i < 1 << n; i++) { // 2^n
list.add((i >> 1) ^ i);
}
return list;
}
/*
1位格雷码有两个码字
(n+1)位格雷码中的前2^n个码字等于n位格雷码的码字,按顺序书写,加前缀0
(n+1)位格雷码中的后2^n个码字等于n位格雷码的码字,按逆序书写,加前缀1
n+1位格雷码的集合 = n位格雷码集合(顺序)加前缀0 + n位格雷码集合(逆序)加前缀1
*/
// n 位的格雷码序列 等于 (n-1) 位的格雷码序列 并上 (n-1) 位的格雷码序列倒序前面加1的结果
// 3位的格雷码 [000, 001, 011, 010] | [110, 111, 101, 100]
public List<Integer> grayCode1(int n) {
List<Integer> list = new ArrayList<>();
list.add(0);
if(n < 1) {
return list;
}
int head = 1;
for (int i = 0; i < n; i++) {
// 逆序 每一层
for (int j = list.size() - 1; j >= 0; j--) {
list.add(head + list.get(j));
}
// 每一层 左移一位
head <<= 1;
}
return list;
}
}
976. 三角形的最大周长
添加链接描述
给定由一些正数(代表长度)组成的数组 A,返回由其中三个长度组成的、面积不为零的三角形的最大周长。
如果不能形成任何面积不为零的三角形,返回 0。
示例 1:
输入:[2,1,2]
输出:5
class Solution {
// 排序
// a + b > c 找 c 前面符合条件的a b最大值
public int largestPerimeter(int[] nums) {
Arrays.sort(nums);
for(int i = nums.length - 1; i >= 2; --i) {
if(nums[i - 2] + nums[i - 1] > nums[i]) {
return nums[i - 2] + nums[i - 1] + nums[i];
}
}
return 0;
}
}
495. 提莫攻击
添加链接描述
当提莫攻击艾希,艾希的中毒状态正好持续 duration 秒。
正式地讲,提莫在 t 发起发起攻击意味着艾希在时间区间 [t, t + duration - 1](含 t 和 t + duration - 1)处于中毒状态。如果提莫在中毒影响结束 前 再次攻击,中毒状态计时器将会 重置 ,在新的攻击之后,中毒影响将会在 duration 秒后结束。
给你一个 非递减 的整数数组 timeSeries ,其中 timeSeries[i] 表示提莫在 timeSeries[i] 秒时对艾希发起攻击,以及一个表示中毒持续时间的整数 duration 。
返回艾希处于中毒状态的 总 秒数。
示例 1:
输入:timeSeries = [1,4], duration = 2
输出:4
解释:提莫攻击对艾希的影响如下:
- 第 1 秒,提莫攻击艾希并使其立即中毒。中毒状态会维持 2 秒,即第 1 秒和第 2 秒。
- 第 4 秒,提莫再次攻击艾希,艾希中毒状态又持续 2 秒,即第 4 秒和第 5 秒。
艾希在第 1、2、4、5 秒处于中毒状态,所以总中毒秒数是 4 。
class Solution {
// 未中毒状态,加duration,更新中毒结束时间expired,本次中毒结束时间timeSeries[i]+duration
// 中毒状态,本次中毒结束时间 - 上次中毒时间expired
public int findPoisonedDuration(int[] timeSeries, int duration) {
int anx = 0; // 每次中毒时间总和
int expired = 0; // 中毒结束时间
for(int i = 0; i < timeSeries.length; i++) {
if(timeSeries[i] >= expired) { // 未中毒
anx += duration;
}else { // 中毒
anx += timeSeries[i] + duration - expired;
}
expired = timeSeries[i] + duration; // 加每次中毒时间
}
return anx;
}
}
剑指 Offer 61. 扑克牌中的顺子
从若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。
示例 1:
输入: [1,2,3,4,5]
输出: True
class Solution {
// 排序
// 相邻判断重复 末位为max 最后一个0后为min
public boolean isStraight(int[] nums) {
int joker = 0;
Arrays.sort(nums);
for(int i = 0; i < 4; i++) {
if(nums[i] == 0) { // 大小王的数量
joker++;
} else if(nums[i] == nums[i + 1]) { // 有重复
return false;
}
}
return nums[4] - nums[joker] < 5;
}
// 除大小王 没有重复牌
// max - min < 5
public boolean isStraight1(int[] nums) {
HashSet<Integer> set = new HashSet<>();
int max = 0; //
int min = 14; //
for(int i = 0; i < 5; i++) {
if(nums[i] == 0) { // 跳过大小王
continue;
}
if(set.contains(nums[i])) { // 有重复
return false;
}
set.add(nums[i]);
max = Math.max(max, nums[i]); // 最大
min = Math.min(min, nums[i]); // 最小
}
return max - min < 5;
}
}
1646. 获取生成数组中的最大值
class Solution {
public int getMaximumGenerated(int n) {
if(n < 2) {
return n;
}
int max = 1;
int[] nums = new int[n + 1];
// nums[0] = 0;
nums[1] = 1;
for (int i = 2; i <= n; i++) {
nums[i] = nums[i / 2] +i % 2 * nums[i / 2 + 1]; // 奇数判断为0
/*if(i % 2 == 0) { // 偶数->[i/2]
nums[i] = nums[i / 2];
}else { // 奇数->[i/2] + [i/2+1]
nums[i] = nums[i / 2] + nums[i / 2 + 1];
}*/
max = Math.max(max, nums[i]);
}
return max;
}
}
1614. 括号的最大嵌套深度
class Solution {
public int getMaximumGenerated(int n) {
if(n < 2) {
return n;
}
int max = 1;
int[] nums = new int[n + 1];
// nums[0] = 0;
nums[1] = 1;
for (int i = 2; i <= n; i++) {
nums[i] = nums[i / 2] +i % 2 * nums[i / 2 + 1]; // 奇数判断为0
/*if(i % 2 == 0) { // 偶数->[i/2]
nums[i] = nums[i / 2];
}else { // 奇数->[i/2] + [i/2+1]
nums[i] = nums[i / 2] + nums[i / 2 + 1];
}*/
max = Math.max(max, nums[i]);
}
return max;
}
}
119. 杨辉三角 II
class Solution {
/*
递推:
一行一行地计算,计算到需要的那一行,返回
第一行只有一个数:1
首尾:都是1。中间的数:获取上一行的两个数相加
计算完一行加入C,计算到rowIndex,返回C的rowIndex
*/
public List<Integer> getRow1(int rowIndex) {
List<List<Integer>> C = new ArrayList<>();
for (int i = 0; i <= rowIndex; i++) {
List<Integer> row = new ArrayList<>();
for (int j = 0; j <= i; j++) {
if(j == 0 || j == i) {
row.add(1);
} else {
row.add(C.get(i - 1).get(j - 1) + C.get(i - 1).get(j));
}
}
C.add(row);
}
return C.get(rowIndex);
}
// 只需要上一行 每次更新即可
public List<Integer> getRow2(int rowIndex) {
List<Integer> prev = new ArrayList<>();
for(int i = 0; i <= rowIndex; i++) {
List<Integer> cur = new ArrayList<>();
for(int j = 0; j <= i; j++) {
if(j == 0 || j == i) {
cur.add(1);
} else {
cur.add(prev.get(j - 1) + prev.get(j));
}
}
prev = cur;
}
return prev;
}
// 从后往前加 每次加给j
public List<Integer> getRow(int rowIndex) {
List<Integer> row = new ArrayList<>();
row.add(1);
for(int i = 1; i <= rowIndex; i++) {
row.add(0);
for(int j = i; j > 0; j--) {
row.set(j, row.get(j - 1) + row.get(j)); // j的位置更新
}
}
return row;
}
}
1576. 替换所有的问号
添加链接描述
给你一个仅包含小写英文字母和 ‘?’ 字符的字符串 s,请你将所有的 ‘?’ 转换为若干小写字母,使最终的字符串不包含任何 连续重复 的字符。
示例 1:
输入:s = “?zs”
输出:“azs”
解释:该示例共有 25 种解决方案,从 “azs” 到 “yzs” 都是符合题目要求的。只有 “z” 是无效的修改,因为字符串 “zzs” 中有连续重复的两个 ‘z’ 。
class Solution {
public String modifyString(String s) {
char[] array = s.toCharArray();
int len = array.length;
for (int i = 0; i < len; i++) {
// if(!Character.isLetter(array[i])) { // 不是字母
if(array[i] == '?') {
for (char ch = 'a'; ch <= 'c'; ch++) { // 用 a b c 判断
if((i > 0 && array[i - 1] == ch) || (i < len - 1 && array[i + 1] == ch)) {
continue; // 判断前 判断后
}
array[i] = ch;
break;
}
}
}
return new String(array);
}
}
1185. 一周中的第几天
添加链接描述
给你一个日期,请你设计一个算法来判断它是对应一周中的哪一天。
输入为三个整数:day、month 和 year,分别表示日、月、年。
您返回的结果必须是这几个值中的一个 {“Sunday”, “Monday”, “Tuesday”, “Wednesday”, “Thursday”, “Friday”, “Saturday”}。
示例 1:
输入:day = 31, month = 8, year = 2019
输出:“Saturday”
class Solution {
public String dayOfTheWeek(int day, int month, int year) {
String[] weeks = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
int[] monthDays = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int days = 0;
for(int i = 1971; i < year; i++) {
days += (i % 4 == 0 && i % 100 != 0 || i % 400 == 0) ? 366 : 365;
}
if((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
monthDays[1] = 29;
}
for(int i = 0; i < month - 1; i++) {
days += monthDays[i];
}
days += day;
return weeks[(days + 3) % 7];
}
public String dayOfTheWeek2(int day, int month, int year) {
String[] weeks = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
int[] monthDays = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// 当前年份之前的天数
int days = 0;
for(int i = 1971; i < year; i++) {
days += isRun(i) ? 366 : 365; // 闰年
}
// 当前年为闰年 -> 二月加一天
if(isRun(year)) {
monthDays[1] = 29;
}
// 当前月份之前的天数
for(int i = 0; i < month - 1; i++) {
days += monthDays[i];
}
// 当前月份的天数
days += day;
return weeks[(days + 3) % 7];
}
public static boolean isRun(int year) {
return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
}
// 1970年12月31日->星期四,算出输入的日期距离1970年12月31日有几天,再加上3后对7求余
/* 求输入的日期距离 19701970 年 1212 月 3131 日的天数,可以分为三部分分别计算后求和:
(1)输入年份之前的年份的天数贡献;
(2)输入年份中,输入月份之前的月份的天数贡献;
(3)输入月份中的天数贡献。
例如,如果输入是 21002100 年 1212 月 3131 日,即可分为三部分分别计算后求和:
(1)19711971 年 11 月 11 到 20992099 年 1212 月 3131 日之间所有的天数;
(2)21002100 年 11 月 11 日到 21002100 年 1111 月 3131 日之间所有的天数;
(3)21002100 年 1212 月 11 日到 21002100 年 1212 月 3131 日之间所有的天数。*/
public String dayOfTheWeek1(int day, int month, int year) {
String[] weeks = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
int[] monthDays = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// 当前年份之前的天数
int days = 365 * (year - 1971) + (year - 1969) / 4; // 加闰年
// 当前月份之前的天数
for(int i = 0; i < month - 1; i++) {
days += monthDays[i];
}
if((year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)) && month >= 3) {
days += 1;
}
// 当前月份的天数
days += day;
return weeks[(days + 3) % 7];
}
}
930. 和相同的二元子数组
添加链接描述
给你一个二元数组 nums ,和一个整数 goal ,请你统计并返回有多少个和为 goal 的 非空 子数组。
子数组 是数组的一段连续部分。
示例 1:
输入:nums = [1,0,1,0,1], goal = 2
输出:4
解释:
有 4 个满足题目要求的子数组:[1,0,1]、[1,0,1,0]、[0,1,0,1]、[1,0,1]
class Solution {
// 滑动窗口
public int numSubarraysWithSum(int[] nums, int goal) {
int cnt = 0;
int len = nums.length;
int left1 = 0, left2 = 0, right = 0, sum1 = 0, sum2 = 0;
while(right < len) {
sum1 += nums[right];
while(left1 <= right && sum1 > goal) {
sum1 -= nums[left1];
left1++;
}
sum2 += nums[right];
while(left2 <= right && sum2 >= goal) {
sum2 -= nums[left2];
left2++;
}
cnt += left2 - left1;
right++;
}
return cnt;
}
// 滑动窗口 本题只有0/1
public int numSubarraysWithSum4(int[] nums, int goal) {
int cnt = 0;
int len = nums.length;
for(int left = 0; left < len; left++) {
if(nums[left] > goal) {
continue;
}
int sum = nums[left];
if(sum == goal) { // 单个数
cnt++;
}
int right = left + 1;
while(right < len && sum <= goal) {
sum += nums[right];
if(sum == goal) {
cnt++;
if(right + 1 < len && nums[right + 1] != 0) {
break; // 下一个为1 大于goal 提前结束本次循环
}
}
right++; //
}
}
return cnt;
}
// 数组
public int numSubarraysWithSum3(int[] nums, int goal) {
int cnt = 0;
int sum = 0;
int[] array = new int[nums.length + 1];
for(int i : nums) {
array[sum]++;
sum += i;
if(sum - goal >= 0) {
cnt += array[sum - goal];
}
}
return cnt;
}
// 哈希表 前缀和
public int numSubarraysWithSum2(int[] nums, int goal) {
int cnt = 0;
int prevsum = 0;
HashMap<Integer, Integer> map = new HashMap<>();
/*map.put(0, 1); // 前缀和为0
for(int num : nums) {
prevsum += num; // [0,0,0,0,0] 0 -->15
cnt += map.getOrDefault(prevsum - goal, 0);
map.put(prevsum, map.getOrDefault(prevsum, 0) + 1);
}*/
for(int num : nums) {
map.put(prevsum, map.getOrDefault(prevsum, 0) + 1);
prevsum += num;
cnt += map.getOrDefault(prevsum - goal, 0);
}
return cnt;
}
// 双循环
public int numSubarraysWithSum1(int[] nums, int goal) {
int cnt = 0;
int n = nums.length;
for(int i = 0; i < n; i++) {
int sum = nums[i];
if(sum == goal) {
cnt++;
}
for(int j = i + 1; j < n; j++) {
sum += nums[j];
if(sum == goal) {
cnt++;
}
}
}
return cnt;
}
}
390. 消除游戏
添加链接描述
列表 arr 由在范围 [1, n] 中的所有整数组成,并按严格递增排序。请你对 arr 应用下述算法:
从左到右,删除第一个数字,然后每隔一个数字删除一个,直到到达列表末尾。
重复上面的步骤,但这次是从右到左。也就是,删除最右侧的数字,然后剩下的数字每隔一个删除一个。
不断重复这两步,从左到右和从右到左交替进行,直到只剩下一个数字。
给你整数 n ,返回 arr 最后剩下的数字。
示例 1:
输入:n = 9
输出:6
解释:
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
arr = [2, 4, 6, 8]
arr = [2, 6]
arr = [6]
class Solution {
// https://leetcode.com/problems/elimination-game/discuss/87119/JAVA%3A-Easiest-solution-O(logN)-with-explanation
public int lastRemaining(int n) {
int head = 1; // n为1时 head就是最后一个数
int step = 1;
boolean left = true;
while(n > 1) {
// 从左移除(奇数->从右移除)
if(left || n % 2 == 1) {
head += step;
}
step *= 2; // 步长
left = !left; // 反向
n /= 2; // 减少一半
}
return head;
}
}
461. 汉明距离
添加链接描述
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。
给你两个整数 x 和 y,计算并返回它们之间的汉明距离。
class Solution {
public int hammingDistance(int x, int y) {
return Integer.bitCount(x ^ y);
}
public int hammingDistance3(int x, int y) {
int cnt = 0;
int s = x ^ y;
while(s != 0) {
s &= s - 1; // 取出最低位的 1
cnt++;
}
return cnt;
}
public int hammingDistance2(int x, int y) {
int cnt = 0;
int s = x ^ y; // 统计不同位个数
while(s != 0) {
cnt += s & 1;
s >>= 1;
}
return cnt;
}
public int hammingDistance1(int x, int y) {
int cnt = 0;
for(int i = 0; i < 32; i++) {
cnt += (x & 1) ^ (y & 1); // x y 最低位判断相等
x >>>= 1;
y >>>= 1;
}
return cnt;
}
}
405. 数字转换为十六进制数
添加链接描述
示例:
输入:
26
输出:
“1a”
class Solution {
// 分组 4个8组
public String toHex(int num) {
if(num == 0) {
return "0";
}
StringBuilder sb = new StringBuilder();
for(int i = 7; i >= 0; --i) {
int ret = (num >> (4 * i)) & 15; // 正向转
if(sb.length() > 0 || ret > 0) { // 零和正整数,可能出现前导零
char c = ret < 10 ? (char)(ret + '0') : (char)(ret - 10 + 'a');
sb.append(c);
}
}
return sb.toString();
/*while(num != 0) {
int ret = num & 15; // 每4位
char c = (char)(ret + '0');
if(ret >= 10) {
c = (char)(ret - 10 + 'a');
}
sb.append(c);
num >>>= 4; // 无符号右移
}
return sb.reverse().toString();*/
}
// 位运算 %16 /16
public String toHex1(int num2) {
if(num2 == 0) {
return "0";
}
long num = num2;
if(num < 0) {
num = (long)(Math.pow(2, 32) + num); // 负数 加2^32偏移量
}
StringBuilder sb = new StringBuilder();
while(num != 0) {
long ret = num % 16;
char c = (char)(ret + '0');
if(ret >= 10) {
c = (char)(ret - 10 + 'a'); // abcdef
}
sb.append(c);
num /= 16;
}
/*String letter = "abcdef";
while(num != 0) {
int ret = (int)(num % 16);
if(ret >= 10) {
sb.append(letter.charAt(ret-10));
} else {
sb.append(ret);
}
num /= 16;
}*/
return sb.reverse().toString();
}
}
2022. 将一维数组转变成二维数组
class Solution {
public int[][] construct2DArray(int[] original, int m, int n) {
if(original.length != m * n) {
return new int[0][];
}
int[][] ans = new int[m][n];
for(int i = 0; i < original.length; i += n) {
System.arraycopy(original, i, ans[i / n], 0, n);
}
/*for(int i = 0; i < original.length; i++) {
ans[i / n][i % n] = original[i];
}*/
/*int k = 0;
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
ans[i][j] = original[k++];
}
}*/
return ans;
}
}
1160. 拼写单词
添加链接描述
给你一份『词汇表』(字符串数组) words 和一张『字母表』(字符串) chars。
假如你可以用 chars 中的『字母』(字符)拼写出 words 中的某个『单词』(字符串),那么我们就认为你掌握了这个单词。
注意:每次拼写(指拼写词汇表中的一个单词)时,chars 中的每个字母都只能用一次。
返回词汇表 words 中你掌握的所有单词的 长度之和。
示例 1:
输入:words = [“cat”,“bt”,“hat”,“tree”], chars = “atach”
输出:6
解释:
可以形成字符串 “cat” 和 “hat”,所以答案是 3 + 3 = 6。
class Solution {
// 哈希表 统计chars字符个数 与 words每个单词字符个数 小于 加长度
public int countCharacters(String[] words, String chars) {
HashMap<Character, Integer> charsCnt = new HashMap<>();
int len = chars.length();
for (int i = 0; i < len; i++) { // 1、chars字符次数
char c = chars.charAt(i);
charsCnt.put(c, charsCnt.getOrDefault(c, 0) + 1);
}
int ans = 0;
for (String word : words) { // 2、遍历words单词
HashMap<Character, Integer> wordCnt = new HashMap<>();
int wordLength = word.length(); // 每个单词长度
for (int i = 0; i < wordLength; i++) {
char c = word.charAt(i);
wordCnt.put(c, wordCnt.getOrDefault(c, 0) + 1); // 统计每个单词的字符次数
}
boolean isAns = true;
for (int i = 0; i < wordLength; i++) {
char c = word.charAt(i);
if(charsCnt.getOrDefault(c, 0) < wordCnt.getOrDefault(c, 0)) { // 小于
isAns = false;
break;
}
}
if(isAns) {
ans += word.length();
}
}
/*for (int i = 0; i < words.length; i++) { // 2、遍历words单词
HashMap<Character, Integer> wordCnt = new HashMap<>();
for (int j = 0; j < words[i].length(); j++) { // 加入每个字符
char c = words[i].charAt(j);
wordCnt.put(c, wordCnt.getOrDefault(c, 0) + 1);
}
boolean isAns = true;
for (int j = 0; j < words[i].length(); j++) {
char c = words[i].charAt(j);
if(charsCnt.getOrDefault(c, 0) < wordCnt.getOrDefault(c, 0)) { // 字符小于
isAns = false;
break;
}
}
if(isAns) {
ans += words[i].length();
}
}*/
return ans;
}
}
1995. 统计特殊四元组
添加链接描述
给你一个 下标从 0 开始 的整数数组 nums ,返回满足下述条件的 不同 四元组 (a, b, c, d) 的 数目 :
nums[a] + nums[b] + nums[c] == nums[d] ,且
a < b < c < d
示例 1:
输入:nums = [1,2,3,6]
输出:1
解释:满足要求的唯一一个四元组是 (0, 1, 2, 3) 因为 1 + 2 + 3 == 6 。
class Solution {
// 4、数组
public int countQuadruplets(int[] nums) {
int[] cache = new int[201];
int cnt = 0;
for (int i = 2; i < nums.length; i++) {
for (int j = 0; j < i - 1; j++) {
cache[nums[i - 1] + nums[j]]++;
}
for (int j = i + 1; j < nums.length; j++) {
int diff = nums[j] - nums[i];
cnt += diff >= 0 ? cache[diff] : 0;
}
}
return cnt;
}
// 3、d-c
// nums[a]+nums[b]=nums[d]−nums[c]
public int countQuadruplets3(int[] nums) {
int cnt = 0;
int n = nums.length;
HashMap<Integer, Integer> map = new HashMap<>();
for (int b = n - 3; b >= 1; --b) {
// nums[d]−nums[c] 出现的次数
for (int d = b + 2; d < n; d++) {
map.put(nums[d] - nums[b + 1], map.getOrDefault(nums[d] - nums[b + 1], 0) + 1);
}
for (int a = 0; a < b; a++) {
cnt += map.getOrDefault(nums[a] + nums[b], 0);
}
}
return cnt;
}
// 2、哈希表
public int countQuadruplets2(int[] nums) {
int cnt = 0;
int n = nums.length;
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = n - 2; i >= 2; --i) {
map.put(nums[i + 1], map.getOrDefault(nums[i + 1],0) + 1);
for (int a = 0; a < i; a++) {
for (int b = a + 1; b < i; b++) {
cnt += map.getOrDefault(nums[a] + nums[b] + nums[i], 0);
}
}
}
return cnt;
}
// 1、暴力
public int countQuadruplets1(int[] nums) {
int cnt = 0;
int n = nums.length;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
for (int k = j + 1; k < n; k++) {
for (int l = k + 1; l < n; l++) {
if(nums[i] + nums[j] + nums[k] == nums[l]) {
cnt++;
}
}
}
}
}
return cnt;
}
}
290. 单词规律
添加链接描述
给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。
这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。
示例1:
输入: pattern = “abba”, str = “dog cat cat dog”
输出: true
class Solution {
// 循环取字符串
public static boolean wordPattern(String pattern, String str) {
Map<String, Character> str2ch = new HashMap<>(); // 单词
Map<Character, String> ch2str = new HashMap<>(); // 字符
int i = 0; // i j 取str字符串
for (int p = 0; p < pattern.length(); p++) {
char ch = pattern.charAt(p); // 遍历pattern字符
if(i >= str.length()) {
return false;
}
int j = i;
while(j < str.length() && str.charAt(j) != ' ') { // 跳过空白字符
j++;
}
String tmp = str.substring(i, j); // 返回子字符串 [i, j) 取str一个单词
if(str2ch.containsKey(tmp) && str2ch.get(tmp) != ch) {
return false;
}
if(ch2str.containsKey(ch) && !tmp.equals(ch2str.get(ch))) {
return false;
}
str2ch.put(tmp, ch);
ch2str.put(ch, tmp);
i = j + 1;
}
return i >= str.length();
}
// 哈希表 记录字符对应字符串 字符串对应字符
public static boolean wordPattern2(String pattern, String str) {
Map<String, Character> str2ch = new HashMap<>(); // 单词
Map<Character, String> ch2str = new HashMap<>(); // 字符
String[] strings = str.split(" "); // 分割
if(strings.length != pattern.length()) { // 长度不想相等
return false;
}
for (int i = 0; i < pattern.length(); i++) {
char ch = pattern.charAt(i);
if(str2ch.containsKey(strings[i]) && str2ch.get(strings[i]) != ch) { // 判断字符串对应的字符
return false;
}
if(ch2str.containsKey(ch) && !strings[i].equals(ch2str.get(ch))) { // 判断字符对应的字符串
return false;
}
str2ch.put(strings[i], ch);
ch2str.put(ch, strings[i]);
}
return true;
}
// int i -- > java 自动装箱,转成 Integer
// put 返回的是一个 Integer
// 判断对象相等的话,相当于判断的是引用的地址,即使 Integer 包含的值相等,也不会相等
// 如果key不存在,插入成功,返回null;如果key存在,返回之前对应的value
public boolean wordPattern1(String pattern, String str) {
String[] words = str.split(" ");
if(pattern.length() != words.length) {
return false;
}
Map<Object, Integer> map = new HashMap<>();
for (Integer i = 0; i < words.length; i++) {
if(map.put(words[i], i) != map.put(pattern.charAt(i), i)) {
return false;
}
}
return true;
}
}
242. 有效的字母异位词
添加链接描述
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
示例 1:
输入: s = “anagram”, t = “nagaram”
输出: true
class Solution {
// Unicode 哈希表
public boolean isAnagram(String s, String t) {
if(s.length() != t.length()) {
return false;
}
HashMap<Character, Integer> map = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
map.put(s.charAt(i), map.getOrDefault(s.charAt(i), 0) + 1);
}
for (int i = 0; i < t.length(); i++) {
map.put(t.charAt(i), map.getOrDefault(t.charAt(i), 0) - 1);
if(map.get(t.charAt(i)) < 0) {
return false;
}
}
return true;
}
// 记录字母次数
public boolean isAnagram2(String s, String t) {
if(s.length() != t.length()) {
return false;
}
int[] table = new int[26];
for (int i = 0; i < s.length(); i++) {
table[s.charAt(i) - 'a']++;
}
for (int i = 0; i < t.length(); i++) {
table[t.charAt(i) - 'a']--;
if(table[t.charAt(i) - 'a'] < 0) { // 为负
return false;
}
}
return true;
}
// 转字符数组 排序
public boolean isAnagram1(String s, String t) {
if(s.length() != t.length()) {
return false;
}
char[] ret1 = s.toCharArray();
char[] ret2 = t.toCharArray();
Arrays.sort(ret1);
Arrays.sort(ret2);
return Arrays.equals(ret1, ret2);
}
}
167. 两数之和 II - 输入有序数组
添加链接描述
给定一个已按照 非递减顺序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。
函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
示例 1:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
class Solution {
// map
public int[] twoSum(int[] numbers, int target) {
Map<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < numbers.length; i++) {
if(map.containsKey(target - numbers[i])) {
return new int[]{map.get(target- numbers[i]), i+1};
}
map.put(numbers[i], i + 1);
}
return new int[]{-1, -1};
}
// 双指针
public int[] twoSum3(int[] numbers, int target) {
for (int i = 0; i < numbers.length; i++) { // 一个
int left = i + 1;
int right = numbers.length - 1;
while(left <= right) {
int mid = (right - left) / 2 + left;
if(numbers[mid] == target - numbers[i]) { // 另一个
return new int[]{i+1, mid+1};
}else if (numbers[mid] < target- numbers[i]) {
left = mid + 1;
}else {
right = mid - 1;
}
}
}
return new int[]{-1, -1};
}
// 暴力
public int[] twoSum1(int[] numbers, int target) {
int ret = 0;
int[] ans = new int[2];
for (int i = 0; i < numbers.length; i++) {
for (int j = i + 1; j < numbers.length; j++) {
ret = numbers[i] + numbers[j];
if(ret == target) {
ans[0] = i+1;
ans[1] = j+1;
break;
}
}
}
return ans;
}
}
125. 验证回文串
添加链接描述
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: “A man, a plan, a canal: Panama”
输出: true
解释:“amanaplanacanalpanama” 是回文串
class Solution {
// 原字符串 指针 比较
public boolean isPalindrome(String s) {
int left = 0;
int right = s.length() - 1;
while(left < right) {
while(left < right && !Character.isLetterOrDigit(s.charAt(left))) {
left++;
}
while(left < right && !Character.isLetterOrDigit(s.charAt(right))) {
right--;
}
if(left < right) {
if(Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
return false;
}
left++;
right--;
}
}
return true;
}
// 字符数字拿出 1、函数判断
public boolean isPalindrome1(String s) {
StringBuilder ret = new StringBuilder();
for(int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if(Character.isLetterOrDigit(ch)) { // 同时判断字符数字
ret.append(Character.toLowerCase(ch));
}
}
StringBuilder retReverse = new StringBuilder(ret).reverse();
return retReverse.toString().equals(ret.toString());
}
// 字符数字拿出 1、双指针判断
public boolean isPalindrome2(String s) {
StringBuilder ret = new StringBuilder();
for(int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if(Character.isLetterOrDigit(ch)) { // 同时判断字符数字
ret.append(Character.toLowerCase(ch));
}
}
int len = ret.length();
int left = 0;
int right = len - 1;
while(left < right) {
if(ret.charAt(left++) != ret.charAt(right--)) {
return false;
}
}
return true;
}
}
229. 求众数 II
添加链接描述
给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
示例 1:
输入:[3,2,3]
输出:[3]
class Solution {
// 3、摩尔投票算法
// 找3/n,最多只有两个。相同->加一,不相同->抵消
public List<Integer> majorityElement(int[] nums) {
// 初始化两个候选人和机票
int cand1 = 0;
int cand2 = 0;
int vote1 = 0;
int vote2 = 0;
//
for(int num : nums) {
if(vote1 > 0 && num == cand1) { // 该元素是第一个元素 机票加一
vote1++;
} else if (vote2 > 0 && num == cand2) { // 该元素是第二个元素 机票加一
vote2++;
} else if (vote1 == 0) { // 第一个元素
cand1 = num;
vote1++;
} else if (vote2 == 0) { // 第二个元素
cand2 = num;
vote2++;
} else { // 三个都不相等 都抵消
vote1--;
vote2--;
}
}
// 判断机票是否 > len/3
int cnt1 = 0 ;
int cnt2 = 0 ;
for(int num : nums) {
if(vote1 > 0 && num == cand1) {
cnt1++;
}
if(vote2 > 0 && num == cand2) {
cnt2++;
}
}
List<Integer> list = new ArrayList<>();
if(vote1 > 0 && cnt1 > nums.length / 3) {
list.add(cand1);
}
if(vote2 > 0 && cnt2 > nums.length / 3) {
list.add(cand2);
}
return list;
}
// 2、哈希表 记录次数
public List<Integer> majorityElement2(int[] nums) {
List<Integer> list = new ArrayList<>();
HashMap<Integer, Integer> set = new HashMap<>();
for(int n : nums) {
set.put(n, set.getOrDefault(n, 0) + 1);
}
for (int n : set.keySet()) {
if(set.get(n) > nums.length / 3) {
list.add(n);
}
}
return list;
}
// 1、循环遍历次数 哈希去重
public List<Integer> majorityElement1(int[] nums) {
List<Integer> list = new ArrayList<>();
HashSet<Integer> set = new HashSet<>();
if(nums.length <= 2) {
list.add(nums[0]);
if(nums.length == 2) {
if(nums[0] != nums[1]) { // 避免重复
list.add(nums[1]);
}
}
return list;
}
Arrays.sort(nums);
int cnt = 1;
for (int i = 0; i < nums.length - 1; i++) {
if(nums[i] == nums[i+1]) {
cnt++;
} else {
cnt = 1;
}
if(cnt > nums.length / 3) { // >
if(set.add(nums[i])) {
list.add(nums[i]); // 避免重复
}
cnt = 1;
}
}
return list;
}
}
169. 多数元素
添加链接描述
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:[3,2,3]
输出:3
class Solution {
// 摩尔投票算法
// 多数元素的次数>n/2 其余<n/2 相减>=1
/*
候选人(cand_num)初始化为nums[0],票数count初始化为1。
当遇到与cand_num相同的数,则票数count = count + 1,否则票数count = count - 1。
当票数count为0时,更换候选人,并将票数count重置为1。
遍历完数组后,cand_num即为最终答案。*/
public int majorityElement(int[] nums) {
int cand = nums[0];
int cnt = 0;
for(int n : nums) {
if(cnt == 0) {
cand = n;
}
if(n == cand) {
cnt++;
}else {
cnt--;
}
}
// int cnt = 1;
// for(int i = 1; i < nums.length; i++) {
// if(nums[i] == cand) {
// cnt++;
// } else if(--cnt == 0) { // 更新
// cand = nums[i];
// cnt = 1;
// }
// }
return cand;
}
// 排序 后半位置
public int majorityElement2(int[] nums) {
Arrays.sort(nums);
return nums[nums.length / 2];
}
// 遍历 次数超过len
public int majorityElement1(int[] nums) {
Arrays.sort(nums);
int cnt = 0;
for(int i = 0; i < nums.length - 1; i++) {
if(nums[i] == nums[i+1]) {
cnt++;
}else {
cnt = 0;
}
if(cnt >= (nums.length / 2)) {
return nums[i];
}
}
return nums[0];
}
}
260. 只出现一次的数字 III
添加链接描述
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
进阶:你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?
示例 1:
输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。
class Solution {
// 位运算
public int[] singleNumber(int[] nums) {
int sum = nums[0];
for(int i = 1; i < nums.length; i++) {
sum ^= nums[i];
}
// 取最低位 1 防溢出
int lsb = sum == Integer.MIN_VALUE ? sum : sum & (-sum);
int type1 = 0;
int type2 = 0;
// 为 0 为 1 分成两组
for(int n : nums) {
if((lsb & n) != 0) {
type1 ^= n;
}else {
type2 ^= n;
}
}
return new int[]{type1, type2};
}
// 异或 暴力查找
public int[] singleNumber1(int[] nums) {
Arrays.sort(nums);
int ret = nums[0];
int len = nums.length;
for(int i = 1; i < len; i++) {
ret ^= nums[i];
}
for(int i = 0; i < len; i++) {
for(int j = i + 1; j < len; j++) {
if((nums[i] ^ nums[j]) == ret) {
return new int[]{nums[i], nums[j]};
}
}
}
return new int[]{-1, -1};
}
}
136. 只出现一次的数字
添加链接描述
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
class Solution {
// list 没有-加入 有-删除
public int singleNumber(int[] nums) {
ArrayList<Integer> list = new ArrayList<>();
for(int n : nums) {
if(list.contains(n)) {
list.remove(Integer.valueOf(n)); //
}else {
list.add(n);
}
}
return list.get(0);
}
// 哈希 存储次数
public int singleNumber2(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
for(int n : nums) {
int cnt = map.getOrDefault(n, 0) + 1;
map.put(n, cnt);
}
for(int i : map.keySet()) {
if(map.get(i) == 1) {
return i;
}
}
return -1;
}
// 按位异或
public int singleNumber1(int[] nums) {
int single = nums[0];
for(int i = 1; i < nums.length; i++) {
single ^= nums[i];
}
return single;
}
}
58. 最后一个单词的长度
添加链接描述
给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中最后一个单词的长度。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。
示例 1:
输入:s = “Hello World”
输出:5
class Solution {
// 删除头尾空白字符串
// 最后一次" "前 lastIndexOf 从后向前找子串
public int lengthOfLastWord(String s) {
return s.trim().length() - s.trim().lastIndexOf(" ") - 1;
}
// 最后一个单词 到前面的空格长度
public int lengthOfLastWord1(String s) {
int index = s.length() - 1;
while(s.charAt(index) == ' ') {
index--;
}
int wordLength = 0;
while(index >= 0 &&s.charAt(index) != ' ') {
wordLength++;
index--;
}
return wordLength;
}
}
13. 罗马数字转整数
添加链接描述
例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
class Solution {
public int romanToInt(String s) {
HashMap<Character, Integer> map = new HashMap<>();
map.put('I', 1);
map.put('V', 5);
map.put('X', 10);
map.put('L', 50);
map.put('C', 100);
map.put('D', 500);
map.put('M', 1000);
int ans = 0;
int n = s.length();
// 若一个数字右侧的数字比它大,则将该数字的符号取反。
for(int i = 0; i < n; i++) {
int val = map.get(s.charAt(i));
if(i < n - 1 && val < map.get(s.charAt(i + 1))) {
ans -= val;
}else {
ans += val;
}
}
return ans;
}
}
448. 找到所有数组中消失的数字
添加链接描述
给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。
示例 1:
输入:nums = [4,3,2,7,8,2,3,1]
输出:[5,6]
class Solution {
// 加 n 找小于n的
public List<Integer> findDisappearedNumbers(int[] nums) {
int len = nums.length;
for (int n : nums) {
int k = (n - 1) % len;
nums[k] += len;
}
ArrayList<Integer> list = new ArrayList<>();
for(int i = 0; i < len; i++) {
if(nums[i] <= len) {
list.add(i+1);
}
}
return list;
}
// 用哈希表检查 1-n 中的每一个数是否出现
public List<Integer> findDisappearedNumbers1(int[] nums) {
HashSet<Integer> set = new HashSet<>();
for(int n : nums) {
set.add(n);
}
ArrayList<Integer> list = new ArrayList<>();
for(int i = 0; i < nums.length; i++) {
if(!set.contains(i+1)) {
list.add(i+1);
}
}
return list;
}
}
88. 合并两个有序数组
添加链接描述
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
示例 1:
输入: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 p1 = m - 1;
int p2 = n - 1;
int cur = 0;
int tail = m + n - 1; // 尾巴
while(p1 >= 0 || p2 >= 0) {
if(p1 == -1) {
cur = nums2[p2--];
} else if (p2 == -1) {
cur = nums1[p1--];
} else if (nums1[p1] > nums2[p2]) {
cur = nums1[p1--];
} else {
cur = nums2[p2--];
}
nums1[tail--] = cur;
}
}
// 双指针
public void merge2(int[] nums1, int m, int[] nums2, int n) {
int p1 = 0;
int p2 = 0;
int[] sorted = new int[m+n];
int i = 0;
while(p1 < m || p2 < n) {
if(p1 == m) {
sorted[i++] = nums2[p2++];
} else if(p2 == n) {
sorted[i++] = nums1[p1++];
} else if(nums1[p1] < nums2[p2]) {
sorted[i++] = nums1[p1++];
} else {
sorted[i++] = nums2[p2++];
}
}
i = 0;
for(int k : sorted) {
nums1[i++] = k;
}
}
// 合并 排序
public void merge1(int[] nums1, int m, int[] nums2, int n) {
for(int i = 0; i < n; i++) {
nums1[m + i] = nums2[i];
}
Arrays.sort(nums1);
}
}
80. 删除有序数组中的重复项 II
添加链接描述
给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
输入
[0,0,1,1,1,1,2,3,3]
输出
[0,0,1,1,2,3,3]
预期结果
[0,0,1,1,2,3,3]
class Solution {
public int removeDuplicates(int[] nums) {
int fast = 2;
int slow = 2;
while(fast < nums.length) {
if(nums[slow-2] != nums[fast]) {
nums[slow] = nums[fast];
slow++;
}
fast++;
}
return slow;
}
}
441. 排列硬币
你总共有 n 枚硬币,并计划将它们按阶梯状排列。对于一个由 k 行组成的阶梯,其第 i 行必须正好有 i 枚硬币。阶梯的最后一行 可能 是不完整的。
给你一个数字 n ,计算并返回可形成 完整阶梯行 的总行数。
输入:n = 5
输出:2
解释:因为第三行不完整,所以返回 2 。
class Solution {
public int arrangeCoins(int n) {
long left = 1;
long right = n;
while(left <= right) {
long mid = left + (right - left) / 2;
long total = mid * (mid + 1) / 2;
if(total == n) {
return (int)mid;
}
if(total > n) {
right = mid - 1;
}else {
left = mid + 1;
}
}
return (int)right;
}
}
434. 字符串中的单词数
统计字符串中的单词个数,这里的单词指的是连续的不是空格的字符。
请注意,你可以假定字符串里不包括任何不可打印的字符。
示例:
输入: “Hello, my name is John”
输出: 5
解释: 这里的单词是指连续的不是空格的字符,所以 “Hello,” 算作 1 个单词。
class Solution {
// 该下标对应的字符不为空格;
// 该下标为初始下标或者该下标的前下标对应的字符为空格;
public int countSegments1(String s) {
int cnt = 0;
for(int i = 0; i < s.length(); i++) {
if((i == 0 || s.charAt(i-1) == ' ') && s.charAt(i) != ' ') {
cnt++;
}
}
return cnt;
}
public int countSegments(String s) {
int cnt = 0;
String[] strings = s.split(" ");
for (int i = 0; i < strings.length; i++) {
if(!strings[i].equals("")) {
cnt++;
}
}
return cnt;
}
}
415. 字符串相加
添加链接描述
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
示例 1:
输入:num1 = “11”, num2 = “123”
输出:“134”
class Solution {
public String addStrings(String num1, String num2) {
int i = num1.length() - 1;
int j = num2.length() - 1;
int carry = 0;
StringBuilder sb = new StringBuilder();
while(i >= 0 || j >= 0 || carry != 0) {
int x = i >= 0 ? num1.charAt(i) - '0' : 0;
int y = j >= 0 ? num2.charAt(j) - '0' : 0;
int result = x + y + carry;
sb.append(result % 10);
carry = result / 10; // 进位
i--;
j--;
}
sb.reverse(); // 逆置
return sb.toString();
}
}
1154. 一年中的第几天
添加链接描述
给你一个字符串 date ,按 YYYY-MM-DD 格式表示一个 现行公元纪年法 日期。请你计算并返回该日期是当年的第几天。
通常情况下,我们认为 1 月 1 日是每年的第 1 天,1 月 2 日是每年的第 2 天,依此类推。每个月的天数与现行公元纪年法(格里高利历)一致。
示例 1:
输入:date = “2019-01-09”
输出:9
class Solution {
public int dayOfYear(String date) {
String[] strings = date.split("-");
int year = Integer.parseInt(strings[0]);
int month = Integer.parseInt(strings[1]);
int day = Integer.parseInt(strings[2]);
int[] amount = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// 闰年 2月变29天
if(year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
amount[1]++;
}
int ans = 0;
for (int i = 0; i < month - 1; i++) {
ans += amount[i];
}
return ans + day;
}
}
414. 第三大的数
添加链接描述
给你一个非空数组,返回此数组中 第三大的数 。如果不存在,则返回数组中最大的数。
示例 1:
输入:[3, 2, 1]
输出:1
解释:第三大的数是 1 。
示例 2:
输入:[1, 2]
输出:2
解释:第三大的数不存在, 所以返回最大的数 2 。
示例 3:
输入:[2, 2, 3, 1]
输出:1
解释:注意,要求返回第三大的数,是指在所有不同数字中排第三大的数。
此例中存在两个值为 2 的数,它们都排第二。在所有不同数字中排第三大的数为 1 。
class Solution {
// a b c 最大值、次大值和第三大值
// 若c仍为−2^63,则不存在三个或以上的不同元素,即第三大值不存在,返回a,否则返回c
public int thirdMax(int[] nums) {
long a = Long.MIN_VALUE;
long b = Long.MIN_VALUE;
long c = Long.MIN_VALUE;
for (long n : nums) {
if(n > a) {
c = b;
b = a;
a = n;
} else if (a > n && n > b) {
c = b;
b = n;
} else if (b > n && n > c) {
c = n;
}
}
return c == Long.MIN_VALUE ? (int)a : (int)c;
}
public int thirdMax2(int[] nums) {
TreeSet<Integer> set = new TreeSet<>();
for (int n : nums) {
set.add(n);
if(set.size() > 3) { // 超过3个 删除最小
set.remove(set.first());
}
}
// 有序集合大小为3,最小值就是第三大的数;不足3,返回最大值。
return set.size() == 3 ? set.first() : set.last();
}
// 排序 逆置 找第三
public int thirdMax1(int[] nums) {
Arrays.sort(nums);
reverse(nums);
for (int i = 0, diff = 1; i < nums.length - 1; i++) {
if(nums[i] != nums[i+1] && ++diff == 3) {
return nums[i+1];
}
}
return nums[0];
}
public void reverse(int[] nums) {
int left = 0;
int right = nums.length - 1;
while(left < right) {
int tmp = nums[left];
nums[left++] = nums[right];
nums[right--] = tmp;
}
}
}
412. Fizz Buzz
添加链接描述
给你一个整数 n ,找出从 1 到 n 各个整数的 Fizz Buzz 表示,并用字符串数组 answer(下标从 1 开始)返回结果,其中:
answer[i] == “FizzBuzz” 如果 i 同时是 3 和 5 的倍数。
answer[i] == “Fizz” 如果 i 是 3 的倍数。
answer[i] == “Buzz” 如果 i 是 5 的倍数。
answer[i] == i (以字符串形式)如果上述条件全不满足。
示例 1:
输入:n = 3
输出:[“1”,“2”,“Fizz”]
class Solution {
public List<String> fizzBuzz(int n) {
List<String> answer = new ArrayList<>();
for(int i = 1; i <= n; i++) {
if(i % 15 == 0) {
answer.add("FizzBuzz");
}else if(i % 3 == 0) {
answer.add("Fizz");
}else if(i % 5 == 0) {
answer.add("Buzz");
}else {
answer.add(i+"");
}
}
return answer;
}
}
389. 找不同
添加链接描述
给定两个字符串 s 和 t,它们只包含小写字母。
字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。
请找出在 t 中被添加的字母。
示例 1:
输入:s = “abcd”, t = “abcde”
输出:“e”
解释:‘e’ 是那个被添加的字母。
class Solution {
public char findTheDifference(String s, String t) {
int[] cnt = new int[26];
for(int i = 0; i < s.length(); i++) {
cnt[s.charAt(i) - 'a']++;
}
for(int i = 0; i < t.length(); i++) {
char ch = t.charAt(i);
cnt[ch - 'a']--;
if(cnt[ch - 'a'] < 0) {
return ch;
}
}
return ' ';
}
// 位运算
public char findTheDifference2(String s, String t) {
int ret = 0;
for(int i = 0; i < s.length(); i++) {
ret ^= s.charAt(i);
ret ^= t.charAt(i);
}
ret ^= t.charAt(t.length()-1);
return (char)ret;
}
// 排序 遍历
public char findTheDifference1(String s, String t) {
char[] ss = s.toCharArray();
char[] tt = t.toCharArray();
Arrays.sort(ss);
Arrays.sort(tt);
for (int i = 0; i < ss.length; i++) {
if(ss[i] != tt[i]) {
return tt[i];
}
}
return tt[tt.length-1];
}
}
387. 字符串中的第一个唯一字符
添加链接描述
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
示例:
s = “leetcode”
返回 0
class Solution {
public int firstUniqChar(String s) {
int[] ret = new int[26];
for(int i = 0; i < s.length(); i++) {
ret[s.charAt(i) - 'a']++;
}
for(int i = 0; i < s.length(); i++) {
if(ret[s.charAt(i) - 'a'] == 1) { // 按顺序找 只出现一次的字符
return i;
}
}
return -1;
}
public int firstUniqChar1(String s) {
HashMap<Character, Integer> map = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
int cnt = map.getOrDefault(s.charAt(i), 0) + 1;
map.put(s.charAt(i), cnt);
}
for (int i = 0; i < s.length(); i++) {
if(map.get(s.charAt(i)) == 1) { // 找次数为1
return i;
}
}
return -1;
}
}
383. 赎金信
给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。
示例 1:
输入:ransomNote = “a”, magazine = “b”
输出:false
class Solution {
// 字符数
public boolean canConstruct(String ransomNote, String magazine) {
if(ransomNote.length() > magazine.length()) {
return false;
}
int[] cnt = new int[26];
for(char c : magazine.toCharArray()) {
cnt[c - 'a']++;
}
for(char c : ransomNote.toCharArray()) {
cnt[c - 'a']--;
if(cnt[c - 'a'] < 0) {
return false;
}
}
return true;
}
// 哈希
public boolean canConstruct1(String ransomNote, String magazine) {
if(ransomNote.length() > magazine.length()) { // 不够构成ransomNote
return false;
}
HashMap<Character, Integer> map = new HashMap<>();
for (int i = 0; i < magazine.length(); i++) { // 记录字符 和次数
int cnt = map.getOrDefault(magazine.charAt(i), 0) + 1;
map.put(magazine.charAt(i), cnt);
}
for (int i = 0; i < ransomNote.length(); i++) {
int cnt = 0;
if(!map.containsKey(ransomNote.charAt(i))) {
return false; // 找不到字符
}else {
cnt = map.get(ransomNote.charAt(i)) - 1;
map.put(ransomNote.charAt(i), cnt); // 次数减一
}
if(cnt == 0) { // 没有次数 移除该字符
map.remove(ransomNote.charAt(i), 0);
}
}
return true; // 遍历完 都能在magazine里找到
}
}
374. 猜数字大小
添加链接描述
We are playing the Guess Game. The game is as follows:
I pick a number from 1 to n. You have to guess which number I picked.
Every time you guess wrong, I will tell you whether the number I picked is higher or lower than your guess.
You call a pre-defined API int guess(int num), which returns 3 possible results:
-1: The number I picked is lower than your guess (i.e. pick < num).
1: The number I picked is higher than your guess (i.e. pick > num).
0: The number I picked is equal to your guess (i.e. pick == num).
Return the number that I picked.
Example 1:
Input: n = 10, pick = 6
Output: 6
public class Solution extends GuessGame {
public int guessNumber(int n) {
int left = 1;
int right = n;
while(left < right) {
int pick = left + (right - left) / 2; // 防止mid加法溢出
// The number I picked is higher than your guess ...
if(guess(pick) > 0) {
left = pick + 1;
}else if (guess(pick) < 0) {
right = pick - 1;
}else {
return pick;
}
}
return left;
}
}
367. 有效的完全平方数
添加链接描述
给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false 。
示例 1:
输入:num = 16
输出:true
class Solution {
// 二分
public boolean isPerfectSquare(int num) {
long left = 0;
long right = num / 2;
long mid = 0;
while(left <= right) {
mid = (left + right) / 2;
if(mid * mid == num) {
return true;
} else if (mid * mid < num) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left * left == num; // 1
}
// 暴力
public boolean isPerfectSquare2(int num) {
for(long i = 1; i <= num; i++) {
long x = i * i;
if(x == num) {
return true;
}else if (x > num) { // i*i > num
return false;
}
}
return false;
}
// 减
public boolean isPerfectSquare1(int num) {
// 平方数 =1+3+5+...+(2∗n−1)
int i = 1;
while(num > 0) {
num -= i;
i += 2;
}
return num == 0;
}
}
24. 两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例:
输入:head = [1,2,3,4]
输出:[2,1,4,3]
class Solution {
// 迭代
public ListNode swapPairs(ListNode head) {
ListNode newHead = new ListNode(-1);
ListNode tmp = newHead;
tmp.next = head;
while(tmp.next != null && tmp.next.next != null) {
ListNode node1 = tmp.next; // 1
ListNode node2 = tmp.next.next; // 2
tmp.next = node2; // newHead: 2
node1.next = node2.next; // 1-3
node2.next = node1; // 2-1-3
tmp = node1; // 下一组
}
return newHead.next;
}
// 递归
public ListNode swapPairs1(ListNode head) {
if(head == null || head.next == null) {
return head;
}
ListNode newHead = head.next; // 1 2 3 4 保存2
head.next = swapPairs(newHead.next); // 交换下一组4-3 把head指向4:1-4
newHead.next = head; // 2指向1
return newHead;
}
}
997. 找到小镇的法官
在一个小镇里,按从 1 到 n 为 n 个人进行编号。传言称,这些人中有一个是小镇上的秘密法官。
如果小镇的法官真的存在,那么:
- 小镇的法官不相信任何人。
- 每个人(除了小镇法官外)都信任小镇的法官。
- 只有一个人同时满足条件 1 和条件 2 。
给定数组 trust,该数组由信任对 trust[i] = [a, b] 组成,表示编号为 a 的人信任编号为 b 的人。
如果小镇存在秘密法官并且可以确定他的身份,请返回该法官的编号。否则,返回 -1。
示例 1:
输入:n = 2, trust = [[1,2]]
输出:2
class Solution {
// 法官:被信任 n-1 && 信任别人 0
public int findJudge1(int n, int[][] trust) {
int[][] array = new int[n+1][2];
for (int[] k : trust) {
array[k[0]][0]++; // k[0] 信任别人
array[k[1]][1]++; // k[1] 被信任
}
for (int i = 1; i <= n; i++) {
if(array[i][1] == n - 1 && array[i][0] == 0) {
return i;
}
}
return -1;
}
public int findJudge(int n, int[][] trust) {
int[] array = new int[n+1];
for (int[] k : trust) {
array[k[0]]--; // k[0] 信任别人
array[k[1]]++; // k[1] 被信任
}
for (int i = 1; i <= n; i++) {
if(array[i] == n - 1) {
return i;
}
}
return -1;
}
}
350. 两个数组的交集 II
给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]
class Solution {
// 哈希 数字的个数
public int[] intersect(int[] nums1, int[] nums2) {
// 较短数组放到哈希表 较长数组遍历取交集
if(nums1.length > nums2.length) {
intersect(nums2, nums1);
}
HashMap<Integer, Integer> map = new HashMap<>();
for (int n : nums1) { // 放nums1 数和个数
int cnt = map.getOrDefault(n, 0) + 1;
map.put(n, cnt);
}
int[] array = new int[nums1.length];
int index = 0;
for (int n : nums2) {
int cnt = map.getOrDefault(n, 0);
if(cnt > 0) { // 有重复
array[index++] = n;
cnt--; // 减少一次
if(cnt > 0) {
map.put(n, cnt); // 修改key
}else {
map.remove(n); // 次数为0 没有n的交集了
}
}
}
return Arrays.copyOfRange(array, 0, index);
}
// 指针
public int[] intersect1(int[] nums1, int[] nums2) {
Arrays.sort(nums1);
Arrays.sort(nums2);
int index = 0; // 要返回的数组的范围
int index1 = 0; // nums1
int index2 = 0; // nums2
int[] array = new int[nums1.length];
while(index1 < nums1.length && index2 < nums2.length) {
int x = nums1[index1];
int y = nums2[index2];
if(x == y) {
array[index++] = x;
index1++;
index2++;
} else if (x < y) { // x != y 指向较小数的指针右移
index1++;
} else {
index2++;
}
}
return Arrays.copyOfRange(array, 0, index);
}
}
349. 两个数组的交集
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
class Solution {
// 排序 双指针
public int[] intersection(int[] nums1, int[] nums2) {
Arrays.sort(nums1);
Arrays.sort(nums2);
int index = 0; // 要返回的数组的范围
int index1 = 0; // nums1
int index2 = 0; // nums2
int[] array = new int[nums1.length];
while(index1 < nums1.length && index2 < nums2.length) { // 一个遍历完 停止
int x = nums1[index1];
int y = nums2[index2];
if(x == y) {
if(index == 0 || x != array[index - 1]) { // 保证唯一性
array[index++] = x;
}
index1++;
index2++;
} else if (x < y) { // x != y 指向较小数的指针右移
index1++;
} else {
index2++;
}
}
return Arrays.copyOfRange(array, 0, index);
}
// 哈希
public int[] intersection1(int[] nums1, int[] nums2) {
int[] array = new int[nums1.length];
HashSet<Integer> set = new HashSet<>(); // 放nums1
for (int i = 0; i < nums1.length; i++) {
set.add(nums1[i]);
}
int k = 0; // 记录要返回的范围
for (int i = 0; i < nums2.length; i++) {
if(set.contains(nums2[i])) { // 有重复 放到array 删除
array[k++] = nums2[i];
set.remove(nums2[i]);
}
}
return Arrays.copyOfRange(array, 0, k);
}
}
419. 甲板上的战舰
class Solution {
// 左边为空位 board[i][j-1]
// 上方为空位 board[i-1][j]
public int countBattleships(char[][] board) {
int cnt = 0;
int row = board.length; // 行
int col = board[0].length; // 列
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if(board[i][j] == 'X') {
if(i > 0 && board[i-1][j] == 'X') {
continue;
}
if(j > 0 && board[i][j-1] == 'X') {
continue;
}
cnt++;
}
}
}
return cnt;
}
// 如果是'X' 就设为' ' 判断行列
public int countBattleships1(char[][] board) {
int cnt = 0;
int row = board.length; // 行
int col = board[0].length; // 列
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if(board[i][j] == 'X') {
board[i][j] = ' ';
for (int k = i+1; k < row && board[k][j] == 'X'; k++) {
board[k][j] = ' ';
}
for (int k = j+1; k < col && board[i][k] == 'X'; k++) {
board[i][k] = ' ';
}
cnt++;
}
}
}
return cnt;
}
}
345. 反转字符串中的元音字母
给你一个字符串 s ,仅反转字符串中的所有元音字母,并返回结果字符串。
元音字母包括 ‘a’、‘e’、‘i’、‘o’、‘u’,且可能以大小写两种形式出现。
示例 1:
输入:s = “hello”
输出:“holle”
class Solution {
public String reverseVowels(String s) {
int l = 0;
int r = s.length() - 1;
String str = "aeiouAEIOU";
char[] newS = s.toCharArray();
while(l < r) {
if(str.contains(s.charAt(l)+"")) {
if(str.contains(s.charAt(r)+"")) {
char ch = newS[l];
newS[l] = newS[r];
newS[r] = ch;
++l;
--r;
}else {
r--;
}
}else {
l++;
}
}
return new String(newS);
}
/*
public String reverseVowels1(String s) {
int l = 0;
int r = s.length() - 1;
String str = "aeiouAEIOU";
char[] newS = s.toCharArray();
while(l < r) {
if(str.contains(s.charAt(l)+"") && str.contains(s.charAt(r)+"")) {
char ch = newS[l];
newS[l] = newS[r];
newS[r] = ch;
++l;
--r;
}
if(!str.contains(s.charAt(l)+"")) {
++l;
}
if(!str.contains(s.charAt(r)+"")) {
--r;
}
}
return new String(newS);
}
*/
}
268. 丢失的数字
给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。
示例 1:
输入:nums = [3,0,1]
输出:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。
class Solution {
// 数学求和 n(n+1)/2
public int missingNumber(int[] nums) {
int n = nums.length;
int sumN = (1+n)*n/2; // 1-N 的和
int sumNums = 0; // 数组的和
for(int i = 0; i < n; ++i) {
sumNums += nums[i];
}
return sumN - sumNums;
}
// 哈希
public int missingNumber3(int[] nums) {
Arrays.sort(nums); // 排序后判断加入的nums[i]是否是i
HashSet<Integer> set = new HashSet<>();
for(int i = 0; i < nums.length; i++) {
set.add(nums[i]);
if(!set.contains(i)) {
return i;
}
}
return nums.length;
}
// 排序
public int missingNumber2(int[] nums) {
Arrays.sort(nums);
for(int i = 0; i < nums.length; i++) {
if(nums[i] != i) {
return i;
}
}
return nums.length;
}
// 按位异或 同为0 异为1
public int missingNumber1(int[] nums) {
int len = nums.length; // 长度就是最大值
int ret = nums[0];
for (int i = 1; i < len; i++) {
ret ^= nums[i]; // 数组的每一位
ret ^= i; // i
}
ret ^= len;
return ret;
}
}
219. 存在重复元素 II
给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。
示例 1:
输入: nums = [1,2,3,1], k = 3
输出: true
class Solution {
// 记录下标
public boolean containsNearbyDuplicate(int[] nums, int k) {
HashMap<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < nums.length; i++) {
if(map.containsKey(nums[i]) && i - map.get(nums[i]) <= k) {
return true;
}
map.put(nums[i], i);
}
return false;
}
public boolean containsNearbyDuplicate1(int[] nums, int k) {
// 哈希表 限定范围 只需要判断有重复直接返回true
HashSet<Integer> set = new HashSet<>();
for(int i = 0; i < nums.length; i++) {
if(set.contains(nums[i])) {
return true;
}
set.add(nums[i]);
if(set.size() > k) {
set.remove(nums[i-k]); // 移除最前面的数字
}
}
return false;
}
}
217. 存在重复元素
给定一个整数数组,判断是否存在重复元素。
如果存在一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。
示例 1:
输入: [1,2,3,1]
输出: true
class Solution {
// 排序 相邻比较相等
public boolean containsDuplicate(int[] nums) {
Arrays.sort(nums);
for(int i = 0; i < nums.length - 1; i++) {
if(nums[i] == nums[i+1]) {
return true;
}
}
return false;
}
// 哈希
public boolean containsDuplicate1(int[] nums) {
HashSet<Integer> map = new HashSet<>();
for (int n : nums) {
/*if(map.contains(n)) {
return true;
}
map.add(n);*/
// 如果 e 已经存在于 set 中,那么 add() 方法就会返回 false
if(!map.add(n)) {
return true;
}
}
return false;
}
}
70. 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1 阶 + 1 阶
2 阶
class Solution {
public int climbStairs(int n) {
int n1 = 1;
int n2 = 1;
int n3 = 1;
for(int i = 2; i <= n; i++) {
n3 = n1 + n2;
n1 = n2;
n2 = n3;
}
return n3;
/*if(n <= 2) {
return n;
}
int n1 = 1;
int n2 = 2;
int n3 = 0;
for(int i = 3; i <= n; i++) {
n3 = n1 + n2;
n1 = n2;
n2 = n3;
}
return n3;*/
}
}
1518. 换酒问题
小区便利店正在促销,用 numExchange 个空酒瓶可以兑换一瓶新酒。你购入
了 numBottles 瓶酒。
如果喝掉了酒瓶中的酒,那么酒瓶就会变成空的。
请你计算 最多 能喝到多少瓶酒。
输入:numBottles = 9, numExchange = 3
输出:13
解释:你可以用 3 个空酒瓶兑换 1 瓶酒。
所以最多能喝到 9 + 3 + 1 = 13 瓶酒。
class Solution {
public int numWaterBottles(int numBottles, int numExchange) {
return numBottles + (numBottles-1)/(numExchange-1);
}
public int numWaterBottles1(int numBottles, int numExchange) {
int total = numBottles;
while(numBottles >= numExchange) {
total += numBottles / numExchange;
// 加不够换剩下的
numBottles = numBottles/numExchange + numBottles%numExchange;
}
return total;
}
}
258. 各位相加
给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。
示例:
输入: 38
输出: 2
解释: 各位相加的过程为:3 + 8 = 11, 1 + 1 = 2。 由于 2 是一位数,所以返回 2。
class Solution {
// O(1)
public int addDigits(int num) {
if(num > 0 && num % 9 == 0) { // 防止0
return 9;
}
return num % 9; // 如果是个位数也可以直接输出
}
// 循环
public int addDigits2(int num) {
while(num >= 10) {
int sum = 0;
while(num != 0) {
sum += num % 10;
num /= 10;
}
num = sum;
}
return num;
}
// 递归
public int addDigits1(int num) {
if(num < 10) {
return num;
}
int sum = 0;
while(num != 0) {
sum += num % 10;
num /= 10;
}
return addDigits(sum);
}
}
292. Nim 游戏
添加链接描述
示例 1:
输入:n = 4
输出:false
解释:如果堆中有 4 块石头,那么你永远不会赢得比赛;
因为无论你拿走 1 块、2 块 还是 3 块石头,最后一块石头总是会被你的朋友拿走。
class Solution {
public boolean canWinNim(int n) {
if(n % 4 == 0) {
return false;
}
return true;
}
}
巴什博奕(Bash Game):
只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个.最后取光者得胜
结论:如果 n % (m + 1) == 0
,则先手必败,否则先手必胜
你和你的朋友,两个人一起玩 Nim 游戏:
桌子上有一堆石头。
你们轮流进行自己的回合,你作为先手。
每一回合,轮到的人拿掉 1 - 3 块石头。
拿掉最后一块石头的人就是获胜者。
假设你们每一步都是最优解。请编写一个函数,来判断你是否可以在给定石头数量为 n 的情况下赢得游戏。如果可以赢,返回 true;否则,返回 false 。
190. 颠倒二进制位
添加链接描述
颠倒给定的 32 位无符号整数的二进制位。
- 输入:n = 00000010100101000001111010011100
- 输出:964176192 (00111001011110000010100101000000)
- 解释:输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596,
因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。
public class Solution {
// you need treat n as an unsigned value
public int reverseBits(int n) {
int num = 0;
for (int i = 1; i <= 32 && n != 0; i++) {
// 如果最低位是1 就可以颠倒或给num
num |= (n & 1) << (32 - i);
n >>>= 1;
}
return num;
}
}
191. 位1的个数
添加链接描述
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
public class Solution {
// you need to treat n as an unsigned value
// 每次去一个1
public int hammingWeight(int n) {
int cnt = 0;
while(n != 0) {
n &= (n - 1);
cnt++;
}
return cnt;
}
public int hammingWeight1(int n) {
int cnt = 0;
for (int i = 0; i < 32; i++) {
if((n & (1 << i)) != 0) { // 用1<<i 判断每一位
cnt++;
}
}
return cnt;
}
}
231. 2 的幂
给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。
如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。
示例 1:
输入:n = 1
输出:true
解释:20 = 1
class Solution {
// 判断是否为最大 22 的幂的约数
public boolean isPowerOfTwo(int n) {
return n > 0 && (1 << 30) % n == 0;
}
// 一个数 n 是 2 的幂,当且仅当 n 是正整数,并且 n 的二进制表示中仅包含 1 个 1
// 将 n 的二进制表示中最低位的那个 1 提取出来,再判断剩余的数值是否为 0
public boolean isPowerOfTwo1(int n) {
return n > 0 && (n & (n-1)) == 0;
}
}
263. 丑数
给你一个整数 n ,请你判断 n 是否为 丑数 。如果是,返回 true ;否则,返回 false 。
丑数 就是只包含质因数 2、3 和/或 5 的正整数。
示例 1:
输入:n = 6
输出:true
解释:6 = 2 × 3
class Solution {
public boolean isUgly(int n) {
if(n <= 0) {
return false;
}
int[] factors = {2, 3, 5};
for (int factor : factors) {
while(n % factor == 0) {
n /= factor;
}
}
return n == 1;
}
}
283. 移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
class Solution {
// 2、双指针
public void moveZeroes(int[] nums) {
int left = 0;
int right = 0;
while (right < nums.length) {
if(nums[right] != 0) {
swap(nums, left, right);
left++;
}
right++;
}
}
public void swap(int[] nums, int left, int right) {
int tmp = nums[right];
nums[right] = nums[left];
nums[left] = tmp;
}
// 1、快慢指针
public void moveZeroes1(int[] nums) {
int slow = 0;
for(int fast = 0; fast < nums.length; fast++) {
if(nums[fast] != 0) {
nums[slow++] = nums[fast];
}
}
// slow-len都置为0
while(slow < nums.length) {
nums[slow++] = 0;
}
}
}
面试题 01.01. 判定字符是否唯一
实现一个算法,确定一个字符串 s 的所有字符是否全都不同。
示例 1:
输入: s = “leetcode”
输出: false
class Solution {
// ASCII码的字符个数为128个,用两个64位,8字节的long变量,
// 二进制位 1 表示出现过,0 表示没有出现过
public boolean isUnique(String astr) {
long low = 0;
long high = 0;
for (char c : astr.toCharArray()) {
if (c > 64) {
long index = 1L << (c - 64);
if ((high & index) != 0) { // 出现过
return false;
}
high |= index; // 标记为出现
} else {
long index = 1L << c;
if ((low & index) != 0) {
return false;
}
low |= index;
}
}
return true;
}
public boolean isUnique2(String astr) {
int[] array = new int[128];
for (int i = 0; i < astr.length(); i++) {
if (array[astr.charAt(i)] != 0) {
return false;
}
array[astr.charAt(i)]++;
}
return true;
}
public boolean isUnique1(String astr) {
HashMap<Character, Integer> map = new HashMap();
for (int i = 0; i < astr.length(); i++) {
if(map.containsKey(astr.charAt(i))) {
return false;
}
map.put(astr.charAt(i), i);
}
return true;
}
}
面试题 01.02. 判定是否互为字符重排
添加链接描述
给定两个字符串 s1 和 s2,请编写一个程序,确定其中一个字符串的字符重新排列后,能否变成另一个字符串。
示例 1:
输入: s1 = “abc”, s2 = “bca”
输出: true
class Solution {
public boolean CheckPermutation(String s1, String s2) {
if(s1.length() != s2.length()) {
return false;
}
// 转字符数组 排序
char[] chars1 = s1.toCharArray();
Arrays.sort(chars1);
char[] chars2 = s2.toCharArray();
Arrays.sort(chars2);
// 字符串比较
return new String(chars1).equals(new String(chars2));
}
}
面试题 01.09. 字符串轮转
添加链接描述
字符串轮转。给定两个字符串s1和s2,请编写代码检查s2是否为s1旋转而成(比如,waterbottle是erbottlewat旋转后的字符串)。
示例1:
输入:s1 = “waterbottle”, s2 = “erbottlewat”
输出:True
class Solution {
public boolean isFlipedString(String s1, String s2) {
// helloworld
// rldhellowo
// helloworldhelloworld
return s1.length() == s2.length() && (s1 + s1).contains(s2);
}
}
344. 反转字符串
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
示例 1:
输入:s = [“h”,“e”,“l”,“l”,“o”]
输出:[“o”,“l”,“l”,“e”,“h”]
class Solution {
public void reverseString(char[] s) {
int left = 0;
int right = s.length - 1;
while(left < right) {
char tmp = s[left];
s[left++] = s[right];
s[right--] = tmp;
}
System.out.println(Arrays.toString(s));
}
}
338. 比特位计数
给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。
示例 1:
输入:n = 2
输出:[0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10
class Solution {
public int[] countBits(int n) {
int[] ans = new int[n+1];
for (int i = 0; i <= n; i++) {
ans[i] = cntEveryBIt(i);
}
return ans;
}
public int cntEveryBIt(int n) {
int cnt = 0;
while(n > 0) {
/*if((n & 1) == 1) {
cnt++;
}
n >>= 1;*/
n &= (n - 1);
cnt++;
}
return cnt;
}
}
326. 3 的幂
添加链接描述
给定一个整数,写一个函数来判断它是否是 3 的幂次方。如果是,返回 true ;否则,返回 false 。
整数 n 是 3 的幂次方需满足:存在整数 x 使得 n == 3x
示例 1:
输入:n = 27
输出:true
示例 2:
输入:n = 0
输出:false
class Solution {
public boolean isPowerOfThree(int n) {
// 32位有符号整数的范围内,最大的3的幂为3^19 = 1162261467
// 判断 n 是否是 3^19的约数
return n > 0 && 1162261467 % n == 0;
}
public boolean isPowerOfThree1(int n) {
while(n > 0 && n % 3 == 0) {
n /= 3;
}
return n == 1;
}
}
66. 加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123
class Solution {
public int[] plusOne(int[] digits) {
for(int i = digits.length - 1; i >= 0; --i) {
digits[i] = (digits[i] + 1) % 10;
if(digits[i] != 0) {
return digits;
}
}
// 全是9
digits = new int[digits.length+1];
digits[0] = 1;
return digits;
}
}
35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right) {
int mid = (left + right) / 2;
if(nums[mid] < target) {
left = mid + 1;
}else if(nums[mid] > target) {
right = mid - 1;
}else {
return mid;
}
}
return left;
}
}
28. 实现 strStr()
实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
class Solution {
public int strStr(String haystack, String needle) {
if(haystack == null || needle == null) {
return -1;
}
int i = 0;
int j = 0;
int lenHay = haystack.length();
int lenNee = needle.length();
while(i < lenHay && j < lenNee) {
if(haystack.charAt(i) == needle.charAt(j)) {
i++;
j++;
}else {
i = i - j + 1;
j = 0;
}
}
if(j >= lenNee) {
return i-j;
}
return -1;
}
}
27. 移除元素
添加链接描述
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
class Solution {
public int removeElement(int[] nums, int val) {
int fast = 0;
int slow = 0;
while(fast < nums.length) {
if(nums[fast] != val) {
nums[slow++] = nums[fast];
}
fast++;
}
return slow;
}
}
26. 删除有序数组中的重复项
有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
示例 1:
输入:nums = [1,1,2]
输出:2, nums = [1,2]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
class Solution {
// 快慢指针
public int removeDuplicates(int[] nums) {
int len = nums.length;
if(len == 0) {
return 0;
}
int fast = 1;
int slow = 1;
while(fast < len) {
if(nums[fast] != nums[fast-1]) {
nums[slow++] = nums[fast];
}
fast++;
}
return slow;
}
// 两个循环遍历
public int removeDuplicates1(int[] nums) {
// Arrays.sort(nums); 已经是有序数组
int len = nums.length -1;
for (int i = 0; i < len; i++) {
if(nums[i] == nums[i+1]) { // 一样
// 往前放
for (int j = i; j < len; j++) {
nums[j] = nums[j+1];
}
i--;
len--;
}
}
return len+1;
}
}
709. 转换成小写字母
给你一个字符串 s ,将该字符串中的大写字母转换成相同的小写字母,返回新的字符串。
示例 1:
输入:s = “Hello”
输出:“hello”
Java
class Solution {
public String toLowerCase1(String s) {
return s.toLowerCase();
}
public String toLowerCase(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if(ch >= 65 && ch <= 90) {
ch |= 32;
}
sb.append(ch);
}
return sb.toString();
}
}
C
char* toLowerCase1(char* s) {
int i = 0;
int len = strlen(s);
for (i = 0; i < len; i++)
{
s[i] = tolower(s[i]);
}
return s;
}
char* toLowerCase(char* s) {
int i = 0;
int len = strlen(s);
for (i = 0; i < len; i++)
{
if (s[i] >= 65 && s[i] <= 90) {
s[i] |= 32;
}
}
return s;
}
9. 回文数
class Solution {
public boolean isPalindrome(int x) {
if(x < 0) {
return false;
}
int tmp = x;
int res = 0; // 逆转的x
while(tmp != 0) {
res = res*10 + tmp % 10;
tmp /= 10;
}
if(x == res) {
return true;
}
return false;
}
public boolean isPalindrome1(int x) {
StringBuilder sb = new StringBuilder(x+"").reverse();
if(sb.toString().equals(x+"")) {
return true;
}
return false;
}
}
7. 整数反转
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
示例 1:
输入:x = 123
输出:321
class Solution {
public int reverse(int x) {
int res = 0;
while(x != 0) {
// 判断未来 下一次的范围 否则x==0 直接返回超范围的res
if(res < Integer.MIN_VALUE/10 || res > Integer.MAX_VALUE/10) {
return 0;
}
res = res*10 + x%10;
x /= 10;
}
return res;
}
}
1. 两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> hash = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if(hash.containsKey(target - nums[i])) {
return new int[]{hash.get(target - nums[i]), i};
}
hash.put(nums[i], i);
}
return new int[0];
}
public int[] twoSum2(int[] nums, int target) {
int len = nums.length;
for (int i = 0; i < len; i++) {
for (int j = i + 1; j < len; j++) {
if(nums[i] + nums[j] == target) {
return new int[]{i, j};
}
}
}
return new int[0];
}
public int[] twoSum1(int[] nums, int target) {
int[] array = new int[2];
for (int i = 0; i < nums.length; i++) {
for (int j = i; j < nums.length; j++) {
if(i == j) continue;
if(nums[i] + nums[j] == target) {
array[0] = i;
array[1] = j;
}
}
}
return array;
}
/* public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int[] nums = new int[4];
for (int i = 0; i < nums.length; i++) {
nums[i] = scanner.nextInt();
}
int target = scanner.nextInt();
int[] ret = twoSum(nums, target);
System.out.println(Arrays.toString(ret));
} */
}
189. 轮转数组
class Solution {
// 2、逆置
public void reverse(int[] nums, int left, int right) {
while(left < right) {
int tmp = nums[left];
nums[left++] = nums[right];
nums[right--] = tmp;
}
}
public void rotate(int[] nums, int k) {
k %= nums.length;
reverse(nums, nums.length-k, nums.length-1); // 后
reverse(nums, 0, nums.length-k-1); // 前
reverse(nums, 0, nums.length-1); // 整体
}
// 1、超出时间限制
public void rotate1(int[] nums, int k) {
k %= nums.length;
while(k != 0) {
int tmp = nums[nums.length-1];
for (int i = nums.length - 2; i >= 0; i--) {
nums[i+1] = nums[i];
}
nums[0] = tmp;
k--;
}
}
}
面试题 17.04. 消失的数字
class Solution {
public int missingNumber(int[] nums) {
int x = 0;
for(int i = 0; i < nums.length; i++) {
x ^= nums[i];
}
for(int i = 0; i < nums.length + 1; i++) {
x ^= i;
}
return x;
}
public int missingNumber1(int[] nums) {
int sum1 = 0;
for(int i = 0; i < nums.length; i++) {
sum1 += nums[i];
}
int sum2 = 0;
for(int i = 0; i < nums.length + 1; i++) {
sum2 += i;
}
return sum2 -sum1;
}
}
剑指 Offer 18. 删除链表的节点
class Solution {
public ListNode deleteNode(ListNode head, int val) {
if(head == null) {
return null;
}
if(head.val == val) {
return head.next; // 头节点是
}
ListNode prev = head; // 前一个
ListNode cur = head.next;
while(cur != null) {
if(cur.val == val) {
prev.next = cur.next;
return head;
}
prev = prev.next;
cur = cur.next;
}
return head;
}
}
面试题 02.02. 返回倒数第 k 个节点
class Solution {
public int kthToLast(ListNode head, int k) {
ListNode fast = head;
ListNode slow = head;
while(k != 0) {
fast = fast.next;
k--;
}
while(fast != null) {
fast = fast.next;
slow = slow.next;
}
return slow.val;
}
}
面试题 02.01. 移除未排序重复节点
class Solution {
public ListNode removeDuplicateNodes(ListNode head) {
if(head == null) {
return null;
}
HashSet<Integer> set = new HashSet<>();
ListNode cur = head;
// set.add(cur.val); //
// while(cur != null && cur.next != null) {
// if(set.contains(cur.next.val)) { // 用下一个查找
// cur.next = cur.next.next;
// } else {
// set.add(cur.next.val); //
// cur = cur.next;
// }
// }
// 双指针
ListNode pre = null;
while(cur != null) {
if(set.contains(cur.val)) {
pre.next = cur.next;
} else {
set.add(cur.val);
pre = cur;
}
cur = cur.next;
}
return head;
}
}
剑指 Offer 06. 从尾到头打印链表
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
class Solution {
// 3、栈
public int[] reversePrint(ListNode head) {
Stack<Integer> stack = new Stack<>();
ListNode cur = head;
while(cur != null) {
stack.push(cur.val);
cur = cur.next;
}
int i = 0;
int[] ans = new int[stack.size()];
while(!stack.isEmpty()) {
ans[i++] = stack.pop();
}
return ans;
}
// 2、反转链表 遍历
public int[] reversePrint2(ListNode head) {
ListNode prev = null;
ListNode cur = head;
int size = 0; // 计算链表长度
while(cur != null) {
ListNode curNext = cur.next;
cur.next = prev;
prev = cur;
cur = curNext;
size++;
}
int[] array = new int[size];
cur = prev;
for (int i = 0; i < size; i++) {
array[i] = cur.val;
cur = cur.next;
}
return array;
}
// 1、ArrayList
public int[] reversePrint1(ListNode head) {
ArrayList<Integer> list = new ArrayList<>();
ListNode cur = head;
while(cur != null) {
list.add(cur.val);
cur = cur.next;
}
// 获取链表长度
int size = list.size();
// 遍历放到数组里
int[] array = new int[size];
for (int i = 0; i < size; i++) {
array[i] = list.get(size-i-1); // 减一
}
return array;
}
}
1290. 二进制链表转整数
每到下一个,乘2,就不用计算长度
class Solution {
public int getDecimalValue(ListNode head) {
if(head == null) {
return 0;
}
ListNode cur = head;
int sum = 0;
while(cur != null) {
sum = sum * 2 + cur.val;
cur = cur.next;
}
return sum;
}
}
237. 删除链表中的节点
class Solution {
public void deleteNode(ListNode node) {
// 把node变成它的下一个
node.val = node.next.val;
node.next = node.next.next;
}
}
83. 删除排序链表中的重复元素
和牛客删除链表重复节点一样,只是需要保留一个
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head == null || head.next == null) {
return head;
}
ListNode prev = head;
ListNode cur = head.next;
while(cur != null) {
// 相同 删除
if(cur.next != null && cur.val == cur.next.val) {
while(cur.next != null && cur.val == cur.next.val) {
cur = cur.next;
}
prev.next = cur;
prev = prev.next; //
cur = cur.next;
} else {
prev = cur;
cur = cur.next;
}
}
// 如果头节点需要删除
if(head.val == head.next.val) {
head = head.next;
}
return head;
}
}
19. 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if(n < 0 || head == null) {
return null;
}
ListNode fast = head.next;
ListNode slow = head;
// 找要删的前一个节点
while(n-1 != 0) {
fast = fast.next;
if(fast == null) {
break;
}
n--;
}
// 要删的是head
if(fast == null) {
head = head.next;
return head;
}
// 要删的是头节点的下一个
if(fast.next == null) {
head.next = head.next.next;
return head;
}
//
while(fast.next != null) {
fast = fast.next;
slow = slow.next;
}
// 删
slow.next = slow.next.next;
return head;
}
}