需要开通vip的题目暂时跳过
点击链接可跳转到所有刷题笔记的导航链接
你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币。
给定一个数字 n,找出可形成完整阶梯行的总行数。
n 是一个非负整数,并且在32位有符号整型的范围内。
解答
public int arrangeCoins(int n) {
int low = 1;
int high = n;
long mid,sum;//使用long类型是为了应对 输入:1804289383 时,计算sum值超出int的取值范围的情况
while(low<=high)
{
mid = low + (high - low )/2;
sum = mid*(mid+1)/2;
if(sum == n)
{
return (int)mid;//强制类型转换,将long类型转换为int类型
}
else if(n > sum)
{
low = (int)mid + 1;
}
else
{
high = (int)mid - 1;
}
}
return high;//return low - 1;是同样的结果。因为最后high
}
分析
提交结果
给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。
找到所有出现两次的元素。
你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?
解答
//方法一
public List<Integer> findDuplicates(int[] nums) {
List<Integer> res = new ArrayList<>();
if(nums.length == 0)return res;
Arrays.sort(nums);
int cur = nums[0];
for(int i = 1;i < nums.length;i++){
if(cur == nums[i])res.add(nums[i]);
cur = nums[i];
}
return res;
}
//方法二
public List<Integer> findDuplicates(int[] nums) {
List<Integer> res = new ArrayList<>();
int[] sum = new int[nums.length];
for(int i = 0;i < nums.length;i++){
sum[nums[i] - 1]++;
if(sum[nums[i] - 1] == 2)res.add(nums[i]);
}
return res;
}
//方法三
public List<Integer> findDuplicates(int[] nums) {
List<Integer> res = new ArrayList<>();
for (int i = 0; i < nums.length; ++i) {
int index = Math.abs(nums[i])-1;
if (nums[index] < 0)
res.add(Math.abs(index+1));
nums[index] = -nums[index];
}
return res;
}
分析
提交结果
给定一组字符,使用原地算法将其压缩。
压缩后的长度必须始终小于或等于原数组长度。
数组的每个元素应该是长度为1 的字符(不是 int 整数类型)。
在完成原地修改输入数组后,返回数组的新长度。
进阶:
解答
public int compress(char[] chars) {
char cur = chars[0];
int curIndex = 0;
int count = 1;
int removeCount = 0;
for (int i = 1; i < chars.length - removeCount; i++) {
if (chars[i] == cur) {
count++;
} else {
if (count >= 2) {
char[] counts = String.valueOf(count).toCharArray();
for (int j = 0; j < counts.length; j++) {
chars[curIndex + 1 + j] = counts[j];
}
modify(chars, curIndex + counts.length + 1, count - counts.length - 1);
removeCount += count - counts.length - 1;
i = curIndex + 2;
}
cur = chars[i];
curIndex = i;
count = 1;
}
}
if (count >= 2) {
char[] counts = String.valueOf(count).toCharArray();
for (int j = 0; j < counts.length; j++) {
chars[curIndex + 1 + j] = counts[j];
}
return curIndex + 1 + counts.length;
}
return curIndex + 1;
}
public void modify(char[] chars, int index, int step) {
for (int i = index; i < chars.length - step; i++) {
chars[i] = chars[i + step];
}
}
分析
给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
进阶:
解答
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
Stack<ListNode> stack1 = new Stack<>();
Stack<ListNode> stack2 = new Stack<>();
ListNode p = l1;
while(p!=null){
stack1.push(p);
p = p.next;
}
p = l2;
while(p != null){
stack2.push(p);
p = p.next;
}
ListNode head = new ListNode(0);
int flag = 0;
while(!stack1.isEmpty() || !stack2.isEmpty()){
ListNode p1 = null;
if(!stack1.isEmpty()){
p1 = stack1.pop();
}
ListNode p2 = null;
if(!stack2.isEmpty()){
p2 = stack2.pop();
}
int number1 = p1 != null? p1.val:0;
int number2 = p2 != null? p2.val:0;
int sum = number1 + number2 + flag;
if(sum >= 10){
sum -= 10;
flag = 1;
}else flag = 0;
ListNode next = head.next;
head.next = new ListNode(sum);
head.next.next = next;
}
if(flag == 1){
ListNode next = head.next;
head.next = new ListNode(1);
head.next.next = next;
}
return head.next;
}
分析
提交结果
如果一个数列至少有三个元素,并且任意两个相邻元素之差相同,则称该数列为等差数列。
数组 A 包含 N 个数,且索引从 0 开始。该数组子序列将划分为整数序列 (P0, P1, …, Pk),满足 0 ≤ P0 < P1 < … < Pk < N。
如果序列 A[P0],A[P1],…,A[Pk-1],A[Pk] 是等差的,那么数组 A 的子序列 (P0,P1,…,PK) 称为等差序列。值得注意的是,这意味着 k ≥ 2。
函数要返回数组 A 中所有等差子序列的个数。
输入包含 N 个整数。每个整数都在 -231 和 231-1 之间,另外 0 ≤ N ≤ 1000。保证输出小于 2的31次方-1。
解答
public int numberOfArithmeticSlices(int[] A) {
int n = A.length;
long ans = 0;
Map<Integer, Integer>[] cnt = new Map[n];
for (int i = 0; i < n; i++) {
cnt[i] = new HashMap<>(i);
for (int j = 0; j < i; j++) {
long delta = (long)A[i] - (long)A[j];
if (delta < Integer.MIN_VALUE || delta > Integer.MAX_VALUE) {
continue;
}
int diff = (int)delta;//公差
int sum = cnt[j].getOrDefault(diff, 0);// 以A[j]结束 公差为diff的个数
int origin = cnt[i].getOrDefault(diff, 0); // 以A[i]结束 公差为diff的个数
cnt[i].put(diff, origin + sum + 1);//更新 以A[i]结束 公差为diff的个数
ans += sum;
}
}
return (int)ans;
}
提交结果
给定平面上 n 对不同的点,“回旋镖” 是由点表示的元组 (i, j, k) ,其中 i 和 j 之间的距离和 i 和 k 之间的距离相等(需要考虑元组的顺序)。
找到所有回旋镖的数量。你可以假设 n 最大为 500,所有点的坐标在闭区间 [-10000, 10000] 中。
解答
public int numberOfBoomerangs(int[][] points) {
Map<Pair<Pair<Integer,Integer>,Integer>,Integer> map = new HashMap<>();
int res = 0;
for(int i = 0;i < points.length;i++){
int[] ori = points[i];
for(int j = 0;j < points.length;j++){
if(i == j)continue;
int[] cur = points[j];
int dis = (int)(Math.pow(cur[0] - ori[0],2) + Math.pow(cur[1] - ori[1],2));
Pair<Pair<Integer,Integer>,Integer> pair = new Pair(new Pair(ori[0],ori[1]),dis);
map.put(pair,map.getOrDefault(pair,0) + 1);
}
}
for(Pair<Pair<Integer,Integer>,Integer> pair : map.keySet()){
int n = map.get(pair);
if(n >= 2){
res += n * (n-1);
}
}
return res;
}
public int cal(int n){
return n * (n - 1);
}
分析
提交结果
给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。
找到所有在 [1, n] 范围之间没有出现在数组中的数字。
您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。
解答
//方法1
public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> res = new ArrayList<>();
res.add(0);
for(int i = 0;i < nums.length;i++){
res.add(0);
}
for(int i = 0;i < nums.length;i++){
res.set(nums[i],1);
}
for(int i = 1;i <= nums.length;i++){
if(res.get(i) == 0){
res.add(i);
}
}
res = res.subList(nums.length + 1,res.size());
return res;
}
//方法2
public List<Integer> findDisappearedNumbers(int[] nums) {
for (int i = 0; i < nums.length; i++) {
int newIndex = Math.abs(nums[i]) - 1;
if (nums[newIndex] > 0) {
nums[newIndex] *= -1;
}
}
List<Integer> result = new LinkedList<Integer>();
for (int i = 1; i <= nums.length; i++) {
if (nums[i - 1] > 0) {
result.add(i);
}
}
return result;
}
分析
提交结果
序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。
设计一个算法来序列化和反序列化 二叉搜索树 。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。
编码的字符串应尽可能紧凑。
解答
public StringBuilder postorder(TreeNode root, StringBuilder sb) {
if (root == null)
return sb;
postorder(root.left, sb);
postorder(root.right, sb);
sb.append(root.val);
sb.append(' ');
return sb;
}
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
StringBuilder sb = postorder(root, new StringBuilder());
if (sb.length() > 0)
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
public TreeNode helper(Integer lower, Integer upper, ArrayDeque<Integer> nums) {
if (nums.isEmpty())
return null;
int val = nums.getLast();
if (val < lower || val > upper)
return null;
nums.removeLast();
TreeNode root = new TreeNode(val);
root.right = helper(val, upper, nums);
root.left = helper(lower, val, nums);
return root;
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if (data.isEmpty())
return null;
ArrayDeque<Integer> nums = new ArrayDeque<Integer>();
for (String s : data.split("\\s+"))
nums.add(Integer.valueOf(s));
return helper(Integer.MIN_VALUE, Integer.MAX_VALUE, nums);
}
分析
提交结果
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
说明: 要求算法时间复杂度为 O(h),h 为树的高度。
解答
public int successor(TreeNode root) {
root = root.right;
while (root.left != null) root = root.left;
return root.val;
}
public int predecessor(TreeNode root) {
root = root.left;
while (root.right != null) root = root.right;
return root.val;
}
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) return null;
if (key > root.val) root.right = deleteNode(root.right, key);
else if (key < root.val) root.left = deleteNode(root.left, key);
else {
if (root.left == null && root.right == null) root = null;
else if (root.right != null) {
root.val = successor(root);//当前结点的值替换成右子树中的最小值
root.right = deleteNode(root.right, root.val);//右子树中 删掉 替换的值的结点。
}
else {
root.val = predecessor(root);//当前结点的值替换成 左子树中的最大值。
root.left = deleteNode(root.left, root.val);//左子树中 删掉 替换值的结点。
}
}
return root;
}
分析
给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
解答
public String frequencySort(String s) {
Map<Character,Integer> map = new HashMap<>();
char[] chars = s.toCharArray();
for(int i = 0;i < chars.length;i++){
map.put(chars[i],map.getOrDefault(chars[i],0) + 1);
}
PriorityQueue<Pair<Character,Integer>> queue = new PriorityQueue<>(new Comparator<Pair<Character,Integer>>(){
public int compare(Pair<Character,Integer> p1,Pair<Character,Integer> p2){
return p2.getValue() - p1.getValue();
}
});
for(Character ch : map.keySet()){
queue.add(new Pair(ch,map.get(ch)));
}
StringBuilder sb = new StringBuilder();
while(!queue.isEmpty()){
Pair<Character,Integer> p = queue.poll();
char ch = p.getKey();
for(int i = 0;i < p.getValue();i++){
sb.append(ch);
}
}
return sb.toString();
}
分析
在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以纵坐标并不重要,因此只要知道开始和结束的横坐标就足够了。开始坐标总是小于结束坐标。
一支弓箭可以沿着 x 轴从不同点完全垂直地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。
给你一个数组 points ,其中 points [i] = [xstart,xend] ,返回引爆所有气球所必须射出的最小弓箭数。
提示:
0 <= points.length <= 104
points[i].length == 2
-231 <= xstart < xend <= 231 - 1
解答
public int findMinArrowShots(int[][] points) {
Arrays.sort(points,new Comparator<int[]>(){
public int compare(int[] o1,int[] o2){
if(o1[0] < o2[0]) return -1;
else return 1;
}
});
int res = 0;
if(points.length == 0)return res;
int[] temp = points[0];
for(int i = 1;i < points.length;i++){
int[] cur = points[i];
if(cur[0] <= temp[1]){
temp[0] = cur[0];
temp[1] = Math.min(cur[1],temp[1]);
}else{
res++;
temp = cur;
}
}
return res + 1;
}
分析
给定一个长度为 n 的非空整数数组,找到让数组所有元素相等的最小移动次数。每次移动将会使 n - 1 个元素增加 1。
解答
public int minMoves(int[] nums) {
Arrays.sort(nums);
int count = 0;
for (int i = nums.length - 1; i > 0; i--) {
count += nums[i] - nums[0];
}
return count;
}
public int minMoves(int[] nums) {
int moves = 0, min = Integer.MAX_VALUE;
for (int i = 0; i < nums.length; i++) {
min = Math.min(min, nums[i]);
}
for (int i = 0; i < nums.length; i++) {
moves += nums[i] - min;
}
return moves;
}
分析
提交结果
给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。
解答
public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
Map<Integer, Integer> map = new HashMap<>() ;
int count = 0 ;
for(int i : A)
{
for(int j : B)
{
map.put((i+j), map.getOrDefault(i+j, 0)+1) ;
}
}
for(int i: C)
{
for(int j : D)
{
if(map.containsKey(0-(i+j)))
{
count += map.get(0-(i+j)) ;
}
}
}
return count;
}
分析
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
解答
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int index = 0;
int res = 0;
for(int i = 0;i < g.length;i++){
while(index < s.length && s[index] < g[i]){
index++;
}
if(index < s.length && s[index++] >= g[i])res++;
}
return res;
}
分析
给定一个整数序列:a1, a2, …, an,一个132模式的子序列 ai, aj, ak 被定义为:当 i < j < k 时,ai < ak < aj。设计一个算法,当给定有 n 个数字的序列时,验证这个序列中是否含有132模式的子序列。
解答
public boolean find132pattern(int[] nums) {
if (nums.length < 3)
return false;
Stack < Integer > stack = new Stack < > ();
int[] min = new int[nums.length];
min[0] = nums[0];
for (int i = 1; i < nums.length; i++)
min[i] = Math.min(min[i - 1], nums[i]);
for (int j = nums.length - 1; j >= 0; j--) {
if (nums[j] > min[j]) {
while (!stack.isEmpty() && stack.peek() <= min[j])
stack.pop();
if (!stack.isEmpty() && stack.peek() < nums[j])
return true;
stack.push(nums[j]);
}
}
return false;
}
分析
给定一个含有正整数和负整数的环形数组 nums。 如果某个索引中的数 k 为正数,则向前移动 k 个索引。相反,如果是负数 (-k),则向后移动 k 个索引。因为数组是环形的,所以可以假设最后一个元素的下一个元素是第一个元素,而第一个元素的前一个元素是最后一个元素。
确定 nums 中是否存在循环(或周期)。循环必须在相同的索引处开始和结束并且循环长度 > 1。此外,一个循环中的所有运动都必须沿着同一方向进行。换句话说,一个循环中不能同时包括向前的运动和向后的运动。
解答
//方法1
public boolean circularArrayLoop(int[] nums) {
int len = nums.length;
for (int i = 0; i < len; i++) {
int[] visited = new int[len];
int curStep = nums[i];
boolean before = curStep > 0;
int temp = i;
int last = i;
while (visited[temp] == 0) {
last = temp;
visited[temp] = 1;
if (before) {
int nextIndex = (temp + nums[temp]) % len;
if (nums[nextIndex] < 0) break;
else if (visited[nextIndex] == 1 && nextIndex != last) return true;
else temp = nextIndex;
} else {
int nextIndex = temp + nums[temp];
if (nextIndex < 0) nextIndex = (5000*len + nextIndex) % len;
if (nums[nextIndex] > 0) break;
else if (visited[nextIndex] == 1 && nextIndex != last) return true;
else temp = nextIndex;
}
}
}
return false;
}
//方法2
private void setZero(int[] nums, int i){
int j;
while (true) { // !(nums[j] == 0 || nums[i]*nums[j]<0)
j = (i + nums[i] + 5000*nums.length) % nums.length;
if (nums[j] == 0 || nums[i]*nums[j]<0) {
nums[i] = 0;
break;
}
nums[i] = 0;
i = j;
}
}
public boolean circularArrayLoop(int[] nums) {
if (nums.length == 0) return false;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0) continue;
int lastJ, lastK;
int j=i, k=i;
while (true) {
lastJ = j;
j = (j + nums[j] + 5000*nums.length) % nums.length;
if (nums[lastJ]*nums[j] < 0 || nums[j] == 0 || lastJ == j) {
setZero(nums, i);
break;
}
lastK = k;
k = (k + nums[k] + 5000*nums.length) % nums.length;
if (nums[lastK]*nums[k] < 0 || nums[k] == 0 || lastK == k){
setZero(nums, i);
break;
}
lastK = k;
k = (k + nums[k] + 5000*nums.length) % nums.length;
if (nums[lastK]*nums[k] < 0 || nums[k] == 0 || lastK == k){
setZero(nums, i);
break;
}
if (j == k)
return true;
}
}
return false;
}
分析
提交结果
有 1000 只水桶,其中有且只有一桶装的含有毒药,其余装的都是水。它们从外观看起来都一样。如果小猪喝了毒药,它会在 15 分钟内死去。
问题来了,如果需要你在一小时内,弄清楚哪只水桶含有毒药,你最少需要多少只猪?
回答这个问题,并为下列的进阶问题编写一个通用算法。
解答
public int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
int states = minutesToTest / minutesToDie + 1;
return (int) Math.ceil(Math.log(buckets) / Math.log(states));
}
分析
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
解答
public boolean repeatedSubstringPattern(String s) {
int len = s.length();
for(int i = 1;i <= len/2;i++){
if(len % i == 0){
int num = len / i;
StringBuilder sb = new StringBuilder();
String sub = s.substring(0,i);
while(num > 0){
sb.append(sub);
num--;
}
if(sb.toString().equals(s))return true;
}
}
return false;
}
分析