LeetCode已刷题记录
我的刷题策略
主要策略
- 分难度层次
- 简单
- 中等
- 困难
- 按标签刷题(先就已学过的知识)
- 链表
- 树
- 图
- 排序算法
- 搜索算法
- 数组
- 字符串
- 其他
- 按方法刷题
- 算法思想
- 双指针
- 排序
- 贪心
- 分治
- 搜索
- 动态规划
- 二分查找
- 数学
- 数据结构相关
- 链表
- 树
- 栈和队列
- 哈希表
- 字符串
- 数组和矩阵
- 图
- 位运算
- 算法思想
次要策略
- 按阶段刷题:一刷,二刷,甚至三刷,不断优化算法
- 随机刷:保持刷题手感
- 写题解,记笔记
该笔记分为 简单题 中等题笔记 题解,难题暂不深究
第一部分:简单题
1. 两数之和
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
你可以按任意顺序返回答案。
class Solution {
public int[] twoSum(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) {
return new int[]{i, j};
}
}
}
return new int []{0,0};
}
}
7. 整数反转
给你一个 32 位的有符号整数 x
,返回 x
中每位上的数字反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1]
,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
class Solution {
public int reverse(int x) {
int ans = 0;
while (x != 0) {
int pop = x % 10;
if (ans > Integer.MAX_VALUE / 10 || (ans == Integer.MAX_VALUE / 10 && pop > 7))
return 0;
if (ans < Integer.MIN_VALUE / 10 || (ans == Integer.MIN_VALUE / 10 && pop < -8))
return 0;
ans = ans * 10 + pop;
x /= 10;
}
return ans;
}
}
9. 回文数
给你一个整数 x
,如果 x
是一个回文整数,返回 ture
;否则,返回 false
。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121
是回文,而 123
不是。
class Solution {
//解法二:巧妙解法,对称
public boolean isPalindrome(int x) {
if(x<0||(x%10==0&&x!=0)){
return false;
}
//定义一个取出来的数
int reverseNum=0;
while(x>reverseNum){
int y=x%10;//取出个位
reverseNum=reverseNum*10+y;
x/=10;
}
//退出循环,说明是回文数
//偶数情况和基数情况
return x==reverseNum||x==reverseNum/10;
}
}
13. 罗马数字转整数
难度简单1203
罗马数字包含以下七种字符: I
, V
, X
, L
,C
,D
和 M
。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II
,即为两个并列的 1。12 写做 XII
,即为 X
+ II
。 27 写做 XXVII
, 即为 XX
+ V
+ II
。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII
,而是 IV
。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX
。这个特殊的规则只适用于以下六种情况:
I
可以放在V
(5) 和X
(10) 的左边,来表示 4 和 9。X
可以放在L
(50) 和C
(100) 的左边,来表示 40 和 90。C
可以放在D
(500) 和M
(1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。
class Solution {
public int romanToInt(String s) {
//定义计算结果
int sum = 0;
//定义前字符数值
int preNum = getValue(s.charAt(0));
for (int i = 1; i < s.length(); i++) {
//取得当前字符串中对应字符的数值
int num = getValue(s.charAt(i));
//两两字符比较,最后一个字符加在后面
if (preNum < num) {
sum -= preNum;
} else {
sum += preNum;
}
//只剩下一个字符
preNum = num;
}
//循环结束,拿到最终结果
sum += preNum;
return sum;
}
private int getValue(char ch) {
switch (ch) {
case 'I':
return 1;
case 'V':
return 5;
case 'X':
return 10;
case 'L':
return 50;
case 'C':
return 100;
case 'D':
return 500;
case 'M':
return 1000;
default:
return 0;
}
}
}
14. 最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""
。
class Solution {
public String longestCommonPrefix(String[] strs) {
//边界判断
if(strs==null||strs.length==0){
return "";
}
//定义返回的子串
String prefix=strs[0];
int count=strs.length;
for(int i=1;i<count;i++){
prefix=longestCommonPrefix(prefix,strs[i]);
if(prefix.length()==0){
break;
}
}
return prefix;
}
public String longestCommonPrefix(String str1,String str2){
//返回两个字符串的长度最小值
int length=Math.min(str1.length(),str2.length());
//定义索引
int index=0;
//遍历比较
while(index<length&&str1.charAt(index)==str2.charAt(index)){
index++;
}
//退出循环,找到最长字串,index停留处
return str1.substring(0,index);
}
}
20. 有效的括号
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Scanner;
public class Solution {
public boolean isValid(String s){
int n=s.length();
if (n %2==1){
return false;
}
HashMap<Character, Character> pairs = new HashMap<Character, Character>(){
{
put(')', '(');
put(']', '[');
put('}', '{');
}
};
LinkedList<Character> stack = new LinkedList<>();
for (int i = 0; i < n; i++) {
char ch = s.charAt(i);
if(pairs.containsKey(ch)){
if(stack.isEmpty()||stack.peek()!=pairs.get(ch)){
return false;
}
stack.pop();
}else{
stack.push(ch);
}
}
return stack.isEmpty();
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String s = scanner.nextLine();
Solution sol = new Solution();
System.out.println(sol.isValid(s));
}
}
21. 合并两个有序链表
难度简单1523
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
} else if (l2 == null) {
return l1;
} else if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
}
26. 删除排序数组中的重复项
难度简单1821
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
public class Main {
public static void main(String[] args) {
Solution s = new Solution();
int[] nums = {1,1,2};
System.out.println(s.removeDuplicates(nums));
}
}
class Solution {
public int removeDuplicates(int[] nums) {
if (nums.length == 0) {
return 0;
}
int i = 0;
for (int j = 1; j < nums.length; j++) {
if (nums[i] != nums[j]) {
i++;
nums[i] = nums[j];
}
}
return i + 1;
}
}
27. 移除元素
难度简单766
给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1)
额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
class Solution {
public int removeElement(int[] nums, int val) {
//边界判断
if(nums==null||nums.length==0){
return 0;
}
//快慢双指针
int i=0;//慢指针
int j;//快指针
for(j=0;j<nums.length;j++){
if(nums[j]!=val){
//把不等val的值存入num[i],相当于一个新的数组
nums[i]=nums[j];
i++;
}
}
return i;
}
}
28. 实现 strStr()
难度简单693
实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1:
输入: haystack = "hello", needle = "ll"
输出: 2
示例 2:
输入: haystack = "aaaaa", needle = "bba"
输出: -1
class Solution {
public int strStr(String haystack, String needle) {
int L = needle.length(), n = haystack.length();
if (L == 0) return 0;
int pn = 0;
while (pn < n - L + 1) {
while (pn < n - L + 1 && haystack.charAt(pn) != needle.charAt(0)){
++pn;
}
int currLen = 0, pL = 0;
while (pL < L && pn < n && haystack.charAt(pn) == needle.charAt(pL)) {
++pn;
++pL;
++currLen;
}
if (currLen == L){ return pn - L;}
pn = pn - currLen + 1;
}
return -1;
}
}
35. 搜索插入位置
难度简单815
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
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(target==nums[mid]){
return mid;
}else if(target<nums[mid]){
//往左区间查找
right=mid-1;
}else if(target>nums[mid]){
//往右区间查找
left=mid+1;
}
}
//如果不存在
return left;
}
}
38. 外观数列
难度简单640
给定一个正整数 n
,输出外观数列的第 n
项。
「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。
你可以将其视作是由递归公式定义的数字字符串序列:
countAndSay(1) = "1"
countAndSay(n)
是对countAndSay(n-1)
的描述,然后转换成另一个数字字符串。
前五项如下:
1. 1
2. 11
3. 21
4. 1211
5. 111221
第一项是数字 1
描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 "11"
描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 "21"
描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 "1211"
描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 "111221"
要 描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。
例如,数字字符串 "3322251"
的描述如下图:
博客园笔记:https://www.cnblogs.com/wanwanyuan/p/14387067.html
class Solution {
public String countAndSay(int n) {
/**
* 你可以将其视作是由递归公式定义的数字字符串序列:
* countAndSay(1) = "1"
* countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。
*/
String s = "1";//传入方法的参数
for (int i = 1; i < n; i++) {
//调用方法
s = nextString(s);
}
return s;
}
//写一个返回字符串的方法
//传入字符串参数
private String nextString(String s) {
//一个可变的字符序列。用于统计与拼接字符串
StringBuilder sb = new StringBuilder();
//获取字符串长度
int n = s.length();
//将字符串转换为字符数组
char[] cs = s.toCharArray();
//标记第一个字符
char c = cs[0];
//定义计数器,已经统计一个字符cs[0]
int count = 1;
//开始模拟统计,下标从1开始,因为cs[0]已经用 char c=cs[0];标记从cs[1]开始与cs[0]比较是否相同
for (int i = 1; i < n; i++) {
//标记当前字符
char cur = cs[i];
//相等
if (c == cur) {
count++;
} else {//不相等
//拼接描述符 数量加字符
sb.append(count);
sb.append(c);
//更新数量以及当前字符统计下一个字符
count = 1;
//标记下一个字符
c = cur;
}
}
//循环结束,拼接最终结果
sb.append(count);
sb.append(c);
//转为字符串并且返回
String result = sb.toString();
return result;
}
}
53. 最大子序和
难度简单2878
给定一个整数数组 nums
,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
class Solution {
public int maxSubArray(int[] nums) {
int pre = 0, maxAns = nums[0];
for (int x : nums) {
pre = Math.max(pre + x, x);
maxAns = Math.max(maxAns, pre);
}
return maxAns;
}
}
58. 最后一个单词的长度
难度简单275
给你一个字符串 s
,由若干单词组成,单词之间用空格隔开。返回字符串中最后一个单词的长度。如果不存在最后一个单词,请返回 0 。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。
class Solution {
public int lengthOfLastWord(String s) {
if(s==null||s.length()==0){
return 0;
}
int i=s.length()-1;
int j=s.length()-1;
while(i>=0&&j>=0){
if(i==j&&s.charAt(i)==' '){
i--;
j--;
}else if(i<j&&s.charAt(i)==' '){
break;
}else {
i--;
}
}
return j-i;
}
}
66. 加一
难度简单624
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
class Solution {
public int[] plusOne(int[] digits) {
int len=digits.length;
//1.无进位或者有进位但是中间停止
for (int i = len-1; i >=0 ; i--) {
if(digits[i]!=9){
digits[i]+=1;
return digits;
}else{
digits[i]=0;
}
}
//2.有进位,且长度加1
digits=new int[len+1];
//首位置1
digits[0]=1;
return digits;
}
}
69. x 的平方根
难度简单589
实现 int sqrt(int x)
函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
class Solution {
public int mySqrt(int x) {
//0,1
if(x<=1){
return x;
}
int left=0;
int right=x;
while(left<=right){
int mid=left+(right-left)/2;
int sqrt=x/mid;
if(sqrt==mid){
return mid;
}else if(mid>sqrt){
right=mid-1;
}else{
left=mid+1;
}
}
return right;
}
}
70. 爬楼梯
难度简单1459
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
class Solution {
public int climbStairs(int n) {
//动态规划问题
int []dp =new int[n+1];
dp[0]=1;
dp[1]=1;
for(int i=2;i<=n;i++){
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
}
83. 删除排序链表中的重复元素
难度简单464
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
class Solution {
public ListNode deleteDuplicates(ListNode head) {
//递归
if(head==null||head.next==null){
return head;
}
head.next=deleteDuplicates(head.next);
return head.val==head.next.val?head.next:head;
}
}
88. 合并两个有序数组
难度简单756
给你两个有序整数数组 nums1
和 nums2
,请你将 nums2
合并到 nums1
中,使 nums1
成为一个有序数组。
初始化 nums1
和 nums2
的元素数量分别为 m
和 n
。你可以假设 nums1
的空间大小等于 m + n
,这样它就有足够的空间保存来自 nums2
的元素。
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int index1=m-1;
int index2=n-1;
int indexMerge=n+m-1;
//使用双指针从后往前遍历
while(index1>=0||index2>=0){
if(index1<0){
nums1[indexMerge--]=nums2[index2--];
}else if(index2<0){
nums1[indexMerge--]=nums1[index1--];
}else if(nums1[index1]>nums2[index2]){
nums1[indexMerge--]=nums1[index1--];
}else{
nums1[indexMerge--]=nums2[index2--];
}
}
}
}
100. 相同的树
难度简单560
给你两棵二叉树的根节点 p
和 q
,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
class Solution {
ArrayList<TreeNode> list1=new ArrayList();
ArrayList<TreeNode> list2=new ArrayList();
public boolean isSameTree(TreeNode p, TreeNode q) {
getList(p,list1);
getList(q,list2);
return list1.equals(list2);
}
public void getList(TreeNode t, ArrayList l){
if(t==null){
l.add(null);
return;
}
l.add(t.val);
getList(t.left,l);
getList(t.right,l);
}
}
104. 二叉树的最大深度
难度简单787
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
class Solution {
public int maxDepth(TreeNode root) {
if(root==null){
return 0;
}
return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
}
}
155. 最小栈
难度简单805
设计一个支持 push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。
push(x)
—— 将元素 x 推入栈中。pop()
—— 删除栈顶的元素。top()
—— 获取栈顶元素。getMin()
—— 检索栈中的最小元素。
import java.util.Stack;
class MinStack{
//方法3,自定义Stack
//以单链表形式定义栈,链表结点定义如下:
public static class Node{
int val;
int min;
Node next;
public Node(int val, int min) {
this(val,min,null);
}
public Node(int val, int min, Node next) {
this.val = val;
this.min = min;
this.next = next;
}
}
private Node head;//头节点
public MinStack(){}
//入栈
public void push(int x) {
if(head == null){
head = new Node(x,x);
}else{
head = new Node(x,Math.min(x,head.min),head);
}
}
//出栈
public void pop(){
head=head.next;
}
//栈顶
public int top(){
return head.val;
}
//最小元素
public int getMin(){
return head.min;
}
}
160. 相交链表
难度简单971
编写一个程序,找到两个单链表相交的起始节点。
如下面的两个链表:
在节点 c1 开始相交。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode A=headA,B=headB;
while(A!=B){
A=(A==null)?headB:A.next;
B=(B==null)?headA:B.next;
}
return A;
}
}
167. 两数之和 II - 输入有序数组
难度简单468
给定一个已按照 升序排列 的整数数组 numbers
,请你从数组中找出两个数满足相加之和等于目标数 target
。
函数应该以长度为 2
的整数数组的形式返回这两个数的下标值。numbers
的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length
。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
class Solution {
public int[] twoSum(int[] numbers, int target) {
if (numbers == null) return null;
int i = 0, j = numbers.length - 1;
while (i < j) {
int sum = numbers[i] + numbers[j];
if (sum == target) {
return new int[]{i + 1, j + 1};
} else if (sum < target) {
i++;
} else {
j--;
}
}
return null;
}
}
204. 计数质数
难度简单624
统计所有小于非负整数 n
的质数的数量。
class Solution {
public int countPrimes(int n) {
boolean []isPrime=new boolean[n];
Arrays.fill(isPrime,true);
for(int i=2;i*i<n;i++){
//如果是质数
if(isPrime[i]){
for(int j=i*i;j<n;j+=i){
isPrime[j]=false;
}
}
}
int count=0;
for(int i=2;i<n;i++){
if(isPrime[i]==true){
count++;
}
}
return count;
}
}
206. 反转链表
难度简单1497
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
class Solution {
public ListNode reverseList(ListNode head) {
ListNode newHead = new ListNode(-1);
while (head != null) {
ListNode next = head.next;
head.next = newHead.next;
newHead.next = head;
head = next;
}
return newHead.next;
}
}
232. 用栈实现队列
难度简单277
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列的支持的所有操作(push
、pop
、peek
、empty
):
实现 MyQueue
类:
void push(int x)
将元素 x 推到队列的末尾int pop()
从队列的开头移除并返回元素int peek()
返回队列开头的元素boolean empty()
如果队列为空,返回true
;否则,返回false
class MyQueue {
private Stack<Integer> in;
private Stack<Integer> out;
/**
* Initialize your data structure here.
*/
public MyQueue() {
this.in = new Stack<>();
this.out = new Stack<>();
}
/**
* Push element x to the back of queue.
*/
public void push(int x) {
in.push(x);
}
/**
* Removes the element from in front of queue and returns that element.
*/
public int pop() {
if (out.isEmpty()) {
while (!in.isEmpty()) {
out.push(in.pop());
}
}
return out.pop();
}
/**
* Get the front element.
*/
public int peek() {
if (out.isEmpty()) {
while (!in.isEmpty()) {
out.push(in.pop());
}
}
return out.peek();
}
/**
* Returns whether the queue is empty.
*/
public boolean empty() {
return in.isEmpty() && out.isEmpty();
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
234. 回文链表
难度简单849
请判断一个链表是否为回文链表。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
if (head == null || head.next == null) return true;
ListNode slow = head, fast = head.next;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
if (fast != null) slow = slow.next; // 偶数节点,让 slow 指向下一个节点
cut(head, slow); // 切成两个链表
return isEqual(head, reverse(slow));
}
private void cut(ListNode head, ListNode cutNode) {
while (head.next != cutNode) {
head = head.next;
}
head.next = null;
}
private ListNode reverse(ListNode head) {
ListNode newHead = null;
while (head != null) {
ListNode nextNode = head.next;
head.next = newHead;
newHead = head;
head = nextNode;
}
return newHead;
}
private boolean isEqual(ListNode l1, ListNode l2) {
while (l1 != null && l2 != null) {
if (l1.val != l2.val) return false;
l1 = l1.next;
l2 = l2.next;
}
return true;
}
}
278. 第一个错误的版本
难度简单254
你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n
个版本 [1, 2, ..., n]
,你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version)
接口来判断版本号 version
是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
int left=0;
int right=n;
while(left<right){
int mid=left+(right-left)/2;
if(isBadVersion(mid)){
right=mid;
}else{
left=mid+1;
}
}
return left;
}
}
345. 反转字符串中的元音字母
难度简单139
编写一个函数,以字符串作为输入,反转该字符串中的元音字母。
class Solution {
private final static HashSet<Character> vowels = new HashSet<>(
Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U')
);
public String reverseVowels(String s) {
if (s == null) return null;
int i = 0, j = s.length() - 1;
char[] result = new char[s.length()];
while (i <= j) {
char ci = s.charAt(i);
char cj = s.charAt(j);
if (!vowels.contains(ci)) {
result[i++] = ci;
} else if (!vowels.contains(cj)) {
result[j--] = cj;
} else {
result[i++] = cj;
result[j--] = ci;
}
}
return new String(result);
}
}
680. 验证回文字符串 Ⅱ
难度简单316
给定一个非空字符串 s
,最多删除一个字符。判断是否能成为回文字符串。
class Solution {
//有效回文字符串
public boolean validPalindrome(String s) {
for(int i = 0, j = s.length() - 1; i < j; i++, j--){
if(s.charAt(i)!=s.charAt(j)){
return isPalindrome(s,i+1,j)||isPalindrome(s,i,j-1);
}
}
return true;
}
//双指针判断是否为回文字符串
private boolean isPalindrome(String s,int i,int j){
while(i<j){
if(s.charAt(i++)!=s.charAt(j--)){
return false;
}
}
return true;
}
}
744. 寻找比目标字母大的最小字母
难度简单110
给你一个排序后的字符列表 letters
,列表中只包含小写英文字母。另给出一个目标字母 target
,请你寻找在这一有序列表里比目标字母大的最小字母。
在比较时,字母是依序循环出现的。举个例子:
- 如果目标字母
target = 'z'
并且字符列表为letters = ['a', 'b']
,则答案返回'a'
class Solution {
public char nextGreatestLetter(char[] letters, char target) {
int n=letters.length;
int l=0;
int h=n-1;
while(l<=h){
int mid=l+(h-l)/2;
if(target>=letters[mid]){
l=mid+1;
}else{
h=mid-1;
}
}
return l<n?letters[l]:letters[0];
}
}
509. 斐波那契数
难度简单239
斐波那契数,通常用 F(n)
表示,形成的序列称为 斐波那契数列 。该数列由 0
和 1
开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给你 n
,请计算 F(n)
。
class Solution {
public int fib(int n) {
int[]fib=fib();
return fib[n];
}
public static int[] fib() {
int[] f = new int[31];
f[0] = 0;
f[1] = 1;
for (int i = 2; i <31; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f;
}
}
面试题 08.06. 汉诺塔问题
难度简单76
在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:
(1) 每次只能移动一个盘子;
(2) 盘子只能从柱子顶端滑出移到下一根柱子;
(3) 盘子只能叠在比它大的盘子上。
请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子。
你需要原地修改栈。
class Solution {
public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
movePlate(A.size(),A,B,C);
}
//写一个搬运盘子的算法
/**
A:起始盘
B:辅助盘
C:目标盘
*/
private void movePlate(int num,List<Integer> A, List<Integer> B, List<Integer> C){
if(num==1){
//只有一个盘子,直接移动过去
C.add(A.remove(A.size()-1));
return ;
}
//将size-1个盘子从A移动到B
movePlate(num-1,A,C,B);
//将第size个盘子从A移动到C;
C.add(A.remove(A.size()-1));
//将剩余的size-1个盘子从B移动到C
movePlate(num-1,B,A,C);
}
}
第二部分:中等题
2. 两数相加
难度中等5620
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
//预指针
ListNode pre = new ListNode(0);
//当前指针
ListNode cur = pre;
//进位遍历标识符
int carry = 0;
//开始按位计算
while (l1 != null || l2 != null) {
//先拿到当前结点的值
int x = l1 != null ? l1.val : 0;
int y = l2 != null ? l2.val : 0;
//模拟两个链表对应结点相加
int sum = x + y + carry;
//更新进位
carry = sum / 10;
//更新对应节点sum
sum = sum % 10;
//新建结点存储sum
cur.next = new ListNode(sum);
//继续遍历下一个结点
cur = cur.next;
//继续判断两个链表下一个结点
if (l1 != null) {
l1 = l1.next;
}
if (l2 != null) {
l2 = l2.next;
}
}
//如果两个链表全部遍历完毕后,进位值为 1,则在新链表最前方添加节点 1
if (carry == 1) {
cur.next = new ListNode(carry);
}
return pre.next;
}
}
3. 无重复字符的最长子串
难度中等4943
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
Map<Character, Integer> map = new HashMap<>(); // current index of character
// try to extend the range [i, j]
for (int j = 0, i = 0; j < n; j++) {
if (map.containsKey(s.charAt(j))) {
i = Math.max(map.get(s.charAt(j)), i);
}
ans = Math.max(ans, j - i + 1);
map.put(s.charAt(j), j + 1);
}
return ans;
}
}
5. 最长回文子串
难度中等3193
给你一个字符串 s
,找到 s
中最长的回文子串。
class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() < 2) {
return s;
}
int strLen = s.length();
int maxStart = 0; //最长回文串的起点
int maxEnd = 0; //最长回文串的终点
int maxLen = 1; //最长回文串的长度
boolean[][] dp = new boolean[strLen][strLen];
for (int r = 1; r < strLen; r++) {
for (int l = 0; l < r; l++) {
if (s.charAt(l) == s.charAt(r) && (r - l <= 2 || dp[l + 1][r - 1])) {
dp[l][r] = true;
if (r - l + 1 > maxLen) {
maxLen = r - l + 1;
maxStart = l;
maxEnd = r;
}
}
}
}
return s.substring(maxStart, maxEnd + 1);
}
}
19. 删除链表的倒数第 N 个结点
难度中等1202
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
//快慢双指针
//1.定义快指针
ListNode fast = head;
while (n-- > 0) {
fast = fast.next;
}
//判断是否移动到最后
if (fast == null) {
return head.next;
}
//定义慢指针
ListNode low = head;
while (fast.next != null) {
fast = fast.next;
low = low.next;
}
//删除结点
low.next = low.next.next;
return head;
}
}
24. 两两交换链表中的节点
难度中等804
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
class Solution {
public ListNode swapPairs(ListNode head) {
//递归
if(head==null||head.next==null){
return head;
}
ListNode next=head.next;
head.next=swapPairs(next.next);
next.next=head;
return next;
}
}
34. 在排序数组中查找元素的第一个和最后一个位置
难度中等846
给定一个按照升序排列的整数数组 nums
,和一个目标值 target
。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target
,返回 [-1, -1]
。
进阶:
- 你可以设计并实现时间复杂度为
O(log n)
的算法解决此问题吗?
class Solution {
public int[] searchRange(int[] nums, int target) {
int first=FindFirst(nums,target);
int last=FindFirst(nums,target+1)-1;
if (first == nums.length || nums[first] != target) {
return new int[]{-1, -1};
} else {
return new int[]{first, Math.max(first, last)};
}
}
private int FindFirst(int[]nums,int target){
int left=0;
int rigth=nums.length;
while(left<rigth){
int mid = left+(rigth-left)/2;
if(nums[mid]>=target){
rigth=mid;
}else{
left=mid+1;
}
}
return left;
}
}
94. 二叉树的中序遍历
难度中等854
给定一个二叉树的根节点 root
,返回它的 中序 遍历。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer>list =new ArrayList<>();
Stack<TreeNode>stack=new Stack<>();
while(stack.size()>0||root!=null){
if(root!=null){
stack.push(root);
root=root.left;
}else{
TreeNode tem=stack.pop();
list.add(tem.val);步骤二,取根结点的值
root=tem.right;//步骤三,遍历右子树
}
}
return list;
}
}
153. 寻找旋转排序数组中的最小值
难度中等347
假设按照升序排序的数组在预先未知的某个点上进行了旋转。例如,数组 [0,1,2,4,5,6,7]
可能变为 [4,5,6,7,0,1,2]
。
请找出其中最小的元素。
class Solution {
public int findMin(int[] nums) {
//因为有序,所以旋转点就是最小值点
int left=0;
int right=nums.length-1;
while(left<right){
int mid=left+(right-left)/2;
if(nums[mid]<=nums[right]){
right=mid;
}else{
left=mid+1;
}
}
return nums[left];
}
}
179. 最大数
难度中等457
给定一组非负整数 nums
,重新排列它们每个数字的顺序(每个数字不可拆分)使之组成一个最大的整数。
注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。
class Solution {
private class LargerNumberComparator implements Comparator<String>{
public int compare(String a,String b){
String order1 =a+b;
String order2 =b+a;
return order2.compareTo(order1);
}
}
public String largestNumber(int[] nums) {
String[] strings = new String[nums.length];
for (int i = 0; i < nums.length; i++) {
strings[i] = String.valueOf(nums[i]);
}
Arrays.sort(strings, new LargerNumberComparator());
if (strings[0].equals("0")) {
return "0";
}
String largestNumberStr = new String();
for (String numStr : strings) {
largestNumberStr += numStr;
}
return largestNumberStr;
}
}
198. 打家劫舍
难度中等1272
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
class Solution {
public int rob(int[] nums) {
int length=nums.length;
if(length==0){
return 0;
}
int [] dp=new int[length+1];
dp[0]=0;
dp[1]=nums[0];
for(int i=2;i<=length;i++){
dp[i]=Math.max(dp[i-2]+nums[i-1],dp[i-1]);
}
return dp[length];
}
}
445. 两数相加 II
难度中等331
给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
class Solution {
int flow=0;
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
if(l1==null) return l2;
if(l2==null) return l1;
ListNode res1=l1,res2=l2;
int len1=0,len2=0;
while(l1!=null){
len1++;
l1=l1.next;
}
while(l2!=null){
len2++;
l2=l2.next;
}
ListNode res=len1>len2?add(res1,res2,len1,len2):add(res2,res1,len2,len1);
if(flow==1) {
res1=new ListNode(1);
res1.next=res;
return res1;
}
return res;
}
public ListNode add(ListNode l1, ListNode l2,int len1,int len2) {
int temp;
if((len1==1)&&(len2==1)){
temp=l1.val;
l1.val=(l1.val+l2.val)%10;
flow=(temp+l2.val)/10;
return l1;
}
if(len1>len2) {
temp=l1.val;
l1.next=add(l1.next, l2,len1-1,len2);
l1.val=(temp+flow)%10;
flow=(temp+flow)/10;
return l1;
}
l1.next=add(l1.next, l2.next,len1-1,len2-1);
temp=l1.val;
l1.val=(temp+flow+l2.val)%10;
flow=(temp+flow+l2.val)/10;
return l1;
}
}
540. 有序数组中的单一元素
难度中等196
给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
class Solution {
public int singleNonDuplicate(int[] nums) {
int left=0;
int right=nums.length-1;
while(left<right){
int mid=left+(right-left)/2;
if(mid%2==1){
mid--;
}
if(nums[mid]==nums[mid+1]){
left=mid+2;
}else{
right=mid;
}
}
return nums[left];
}
}
633. 平方数之和
难度中等161
给定一个非负整数 c
,你要判断是否存在两个整数 a
和 b
,使得 a2 + b2 = c
。
class Solution {
public boolean judgeSquareSum(int c) {
if(c<0){
return false;
}
int i=0;
int j=(int)(Math.sqrt(c));
while(i<=j){
int powSum=i*i+j*j;
if(powSum==c){
return true;
}else if(powSum<c){
i++;
}else{
j--;
}
}
return false;
}
}