面试高频算法
704. 二分查找
难度简单585收藏分享切换为英文接收动态反馈
给定一个 n
个元素有序的(升序)整型数组 nums
和一个目标值 target
,写一个函数搜索 nums
中的 target
,如果目标值存在返回下标,否则返回 -1
。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
提示:
- 你可以假设
nums
中的所有元素是不重复的。 n
将在[1, 10000]
之间。nums
的每个元素都将在[-9999, 9999]
之间。
题解:
class Solution {
public int search(int[] nums, int target) {
int nLength= nums.length;
int low = 0;
int high = nLength-1;
while(high>=low){
int mid = (high-low)/2+low;
if(nums[mid]==target){
return mid;
}
if(nums[mid]<target){
low = mid+1;
continue;
}
if(nums[mid]>target){
high = mid-1;
continue;
}
}
return -1;
}
}
//二分查找记住!!!int mid = (high-low)/2+low;
3. 无重复字符的最长子串
难度中等6744收藏分享切换为英文接收动态反馈
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
示例 4:
输入: s = ""
输出: 0
提示:
0 <= s.length <= 5 * 104
s
由英文字母、数字、符号和空格组成
题解:
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character,Integer> maptest = new HashMap<Character,Integer>();
int i=0;
int nLength = s.length();
int max = 0;
for(int j=0;j<nLength;j++){
char word = s.charAt(j);
if(maptest.containsKey(word)){
i=Math.max(maptest.get(word),i);;
}
maptest.put(word,j+1);
max = Math.max(max, j - i + 1);
}
return max;
}
}
7. 整数反转
难度中等3352收藏分享切换为英文接收动态反馈
给你一个 32 位的有符号整数 x
,返回将 x
中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1]
,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
示例 1:
输入:x = 123
输出:321
示例 2:
输入:x = -123
输出:-321
示例 3:
输入:x = 120
输出:21
示例 4:
输入:x = 0
输出:0
提示:
-231 <= x <= 231 - 1
题解:
class Solution {
public int reverse(int x) {
List<Integer> listTmp = new ArrayList<Integer>();
int high = (int)Math.pow(2,31)-1;
int low = (int)Math.pow(2,31)*-1;
if (x > high || x < low) {
return 0;
}
int nbool = 1;
if(x<0){
nbool=-1;
x = x *nbool;
}
while(x!=0){
int nTmp = x%10;
x = x/10;
if(nTmp==0&&listTmp.size()<1){
continue;
}
listTmp.add(nTmp);
}
int nLength = listTmp.size();
double dResult =0;
int j =nLength-1;
for(int i=0;i<nLength;i++){
double dTest = listTmp.get(i)*Math.pow(10,j);
dResult = dResult +dTest ;
j--;
if (dResult > high || dResult < low) {
return 0;
}
}
int nResult = (int)dResult;
return nResult*nbool;
}
}
5. 最长回文子串
难度中等4575收藏分享切换为英文接收动态反馈
给你一个字符串 s
,找到 s
中最长的回文子串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
示例 3:
输入:s = "a"
输出:"a"
示例 4:
输入:s = "ac"
输出:"a"
提示:
1 <= s.length <= 1000
s
仅由数字和英文字母(大写和/或小写)组成
题解:
//暴力模拟,但是超时了
class Solution {
public String Palindrome(int high,int low,String s) {
int nLow = low;
int nHigh = high;
while(high>low){
if(s.charAt(low++)!=s.charAt(high--)){
return s.substring(0,1);
}
}
return s.substring(nLow,nHigh+1);
}
public String longestPalindrome(String s) {
Map<Integer,Character> maptest = new HashMap<Integer,Character>();
int nLength = s.length();
int nMax = 0;
String sResult = s.substring(0,1);
for(int i=0;i<nLength;i++){
char cTmp = s.charAt(i);
if(maptest.containsValue(cTmp)){
int high = i;
int low = 0;
for(int j =0;j<i;j++){
if(maptest.get(j)==cTmp){
low = j;
//todo 判断回文
String sTmp = Palindrome(high,low,s);
if(nMax<sTmp.length()){
sResult = sTmp;
nMax=sTmp.length();
}
}
}
}
maptest.put(i,cTmp);
}
return sResult;
}
}
暴力解法
根据回文子串的定义,枚举所有长度大于等于 22 的子串,依次判断它们是否是回文;
可以只针对大于「当前得到的最长回文子串长度」的子串进行回文验证;
当得到了一个更长的回文时,不需要真的做截取。只需要记录「当前子串的起始位置」和「子串长度」。
参考代码 1:
public class Solution {
public String longestPalindrome(String s) {
int len = s.length();
if (len < 2) {
return s;
}
int maxLen = 1;
int begin = 0;
// s.charAt(i) 每次都会检查数组下标越界,因此先转换成字符数组
char[] charArray = s.toCharArray();
// 枚举所有长度大于 1 的子串 charArray[i..j]
for (int i = 0; i < len - 1; i++) {
for (int j = i + 1; j < len; j++) {
if (j - i + 1 > maxLen && validPalindromic(charArray, i, j)) {
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
/**
* 验证子串 s[left..right] 是否为回文串
*/
private boolean validPalindromic(char[] charArray, int left, int right) {
while (left < right) {
if (charArray[left] != charArray[right]) {
return false;
}
left++;
right--;
}
return true;
}
}
217. 存在重复元素
难度简单
给定一个整数数组,判断是否存在重复元素。
如果存在一值在数组中出现至少两次,函数返回 true
。如果数组中每个元素都不相同,则返回 false
。
示例 1:
输入: [1,2,3,1]
输出: true
示例 2:
输入: [1,2,3,4]
输出: false
示例 3:
输入: [1,1,1,3,3,4,3,2,4,2]
输出: true
题解:
class Solution {
public boolean containsDuplicate(int[] nums) {
Map<Integer,Integer> mTmp = new HashMap<Integer,Integer>();
for(int i=0;i<nums.length;i++){
if(mTmp.containsKey(nums[i])){
return true;
}
mTmp.put(nums[i],i);
}
return false;
}
}
14. 最长公共前缀
难度简单
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""
。
示例 1:
输入:strs = ["flower","flow","flight"]
输出:"fl"
示例 2:
输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。
提示:
1 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i]
仅由小写英文字母组成
题解:
class Solution {
public String longestCommonPrefix(String[] strs) {
int nLength = strs.length;
if (strs == null || strs.length == 0) {
return "";
}
int nSzLength = strs[0].length();
int nBool = 0;
String strResult = "";
String strTmp = strResult;
for(int j=1;j<=nSzLength;j++){
for(int i = 1;i<nLength;i++){
strTmp = strs[0].substring(0,j);
if(!strs[i].contains(strTmp)){
nBool=1;
}
}
if(nBool==0){
strResult = strTmp;
}
}
if(nLength==1&&nSzLength==1){
return strs[0];
}
return strResult;
}
}
//contains是包含,没法比判断前缀,这是错误写法,贴出来提醒一下
//["c","acc","ccc"]
class Solution {
public String longestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0) {
return "";
}
int length = strs[0].length();
int count = strs.length;
for (int i = 0; i < length; i++) {
char c = strs[0].charAt(i);
for (int j = 1; j < count; j++) {
if (i == strs[j].length() || strs[j].charAt(i) != c) {
return strs[0].substring(0, i);
}
}
}
return strs[0];
}
}
70. 爬楼梯
难度简单2121收藏分享切换为英文接收动态反馈
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
**注意:**给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
//动态规划dp
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];
}
}
175. 组合两个表 sql
难度简单1011收藏分享切换为英文接收动态反馈
SQL架构
表1: Person
+-------------+---------+
| 列名 | 类型 |
+-------------+---------+
| PersonId | int |
| FirstName | varchar |
| LastName | varchar |
+-------------+---------+
PersonId 是上表主键
表2: Address
+-------------+---------+
| 列名 | 类型 |
+-------------+---------+
| AddressId | int |
| PersonId | int |
| City | varchar |
| State | varchar |
+-------------+---------+
AddressId 是上表主键
编写一个 SQL 查询,满足条件:无论 person 是否有地址信息,都需要基于上述两表提供 person 的以下信息:
FirstName, LastName, City, State
题解:
select FirstName, LastName, City, State from Person left join Address on Person.PersonId = Address.PersonId
15. 三数之和
难度中等4199收藏分享切换为英文接收动态反馈
给你一个包含 n
个整数的数组 nums
,判断 nums
中是否存在三个元素 *a,b,c ,*使得 a + b + c = 0 ?请你找出所有和为 0
且不重复的三元组。
**注意:**答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
提示:
0 <= nums.length <= 3000
-105 <= nums[i] <= 105
题解:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> listResult = new ArrayList<List<Integer>>();
Arrays.sort(nums);
int nLength = nums.length;
for(int i =0;i<nLength;i++){
if(nums[i] > 0) break; // 如果当前数字大于0,则三数之和一定大于0,所以结束循环
if(i > 0 && nums[i] == nums[i-1]) continue; // 去重
int L = i+1;
int R = nLength-1;
while(L<R){
int sum = nums[i]+nums[L]+nums[R];
if(sum==0){
List<Integer> listTmp = new ArrayList<Integer>();
listTmp.add(nums[i]);
listTmp.add(nums[L]);
listTmp.add(nums[R]);
listResult.add(listTmp);
while (L<R && nums[L] == nums[L+1]) L++; // 去重
while (L<R && nums[R] == nums[R-1]) R--; // 去重
L++;
R--;
}
else if (sum < 0) L++;
else if (sum > 0) R--;
}
}
return listResult;
}
}
146. LRU 缓存
难度中等1847收藏分享切换为英文接收动态反馈
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache
类:
LRUCache(int capacity)
以 正整数 作为容量capacity
初始化 LRU 缓存int get(int key)
如果关键字key
存在于缓存中,则返回关键字的值,否则返回-1
。void put(int key, int value)
如果关键字key
已经存在,则变更其数据值value
;如果不存在,则向缓存中插入该组key-value
。如果插入操作导致关键字数量超过capacity
,则应该 逐出 最久未使用的关键字。
函数 get
和 put
必须以 O(1)
的平均时间复杂度运行。
示例:
输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
提示:
1 <= capacity <= 3000
0 <= key <= 10000
0 <= value <= 105
- 最多调用
2 * 105
次get
和put
题解:
class LRUCache {
Map<Integer, Integer> mCache = new LinkedHashMap();
int nCapacity;
public LRUCache(int capacity) {
this.nCapacity = capacity;
}
public int get(int key) {
if(!mCache.containsKey(key)){
return -1;
}
int nVal = mCache.remove(key);
mCache.put(key,nVal);
return nVal;
}
public void put(int key, int value) {
if(mCache.containsKey(key)){
mCache.remove(key);
}
mCache.put(key,value);
if(mCache.size()>nCapacity){
mCache.remove(mCache.entrySet().iterator().next().getKey());
}
}
}
20. 有效的括号
难度简单2895收藏分享切换为英文接收动态反馈
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
示例 1:
输入:s = "()"
输出:true
示例 2:
输入:s = "()[]{}"
输出:true
示例 3:
输入:s = "(]"
输出:false
示例 4:
输入:s = "([)]"
输出:false
示例 5:
输入:s = "{[]}"
输出:true
提示:
1 <= s.length <= 104
s
仅由括号'()[]{}'
组成
题解:
class Solution {
public boolean isValid(String s) {
int n = s.length();
if(n%2!=0){
return false;
}
Map<Character,Character> maptest = new HashMap<Character,Character>();
maptest.put('}','{');
maptest.put(')','(');
maptest.put(']','[');
Deque<Character> deTest = new LinkedList<Character>();
for(int i=0;i<n;i++){
char ch = s.charAt(i);
if(maptest.containsKey(ch)){
if(deTest.isEmpty()||deTest.peek() != maptest.get(ch)){
return false;
}
deTest.pop();
}else{
deTest.push(ch);
}
}
return deTest.isEmpty();
}
}
22. 括号生成
难度中等2287收藏分享切换为英文接收动态反馈
数字 n
代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:
输入:n = 1
输出:["()"]
提示:
1 <= n <= 8
题解:
class Solution {
List<String> res = new ArrayList<>();
public List<String> generateParenthesis(int n) {
if (n <= 0) return null;
def("", 0, 0, n);
return res;
}
public void def(String paths, int left, int right, int n) {
if (left > n || right > left) return;
if (paths.length() == n * 2) {
res.add(paths);
return;
}
def(paths + "(", left + 1, right, n);
def(paths + ")", left, right + 1, n);
}
}
121. 买卖股票的最佳时机
难度简单2052收藏分享切换为英文接收动态反馈
给定一个数组 prices
,它的第 i
个元素 prices[i]
表示一支给定股票第 i
天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
。
示例 1:
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
提示:
1 <= prices.length <= 105
0 <= prices[i] <= 104
题解:
//暴力,但是超时
class Solution {
public int maxProfit1(int[] prices) {
int nLength = prices.length;
int nMax =0;
for(int i=0;i<nLength;i++){
for(int j = nLength-1;j>i;j--){
int sum = prices[j]-prices[i];
if(sum>0){
nMax = Math.max(nMax,sum);
}
}
}
return nMax;
}
}
public int maxProfit(int[] prices) {
int len = prices.length;
int res = 0;
// 前一天卖出可以获得的最大利润
int pre = 0;
for (int i = 1; i < len; i++) {
// 利润差
int diff = prices[i] - prices[i - 1];
// 状态转移方程:第i天卖出可以获得的最大利润 = 第i-1天卖出的最大利润 + 利润差
pre = Math.max(pre + diff, 0);
res = Math.max(res, pre);
}
return res;
}
11. 盛最多水的容器
难度中等3106收藏分享切换为英文接收动态反馈
给你 n
个非负整数 a1,a2,...,a``n
,每个数代表坐标中的一个点 (i, ai)
。在坐标内画 n
条垂直线,垂直线 i
的两个端点分别为 (i, ai)
和 (i, 0)
。找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
**说明:**你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1]
输出:1
示例 3:
输入:height = [4,3,2,1,4]
输出:16
示例 4:
输入:height = [1,2,1]
输出:2
提示:
n == height.length
2 <= n <= 105
0 <= height[i] <= 104
题解:
//暴力,但是超时间了
class Solution {
public int maxArea(int[] height) {
int nLength= height.length;
int res = 0;
for(int i=0;i<nLength;i++){
for(int j = i+1;j<nLength;j++){
int area = (j-i)*Math.min(height[j],height[i]);
res = Math.max(res,area);
}
}
return res;
}
}
class Solution {
public int maxArea(int[] height) {
int i = 0, j = height.length - 1, res = 0;
while(i < j) {
res = height[i] < height[j] ?
Math.max(res, (j - i) * height[i++]):
Math.max(res, (j - i) * height[j--]);
}
return res;
}
}
在每个状态下,无论长板或短板向中间收窄一格,都会导致水槽 底边宽度 -1变短:
若向内 移动短板 ,水槽的短板 min(h[i], h[j])可能变大,因此下个水槽的面积 可能增大 。
若向内 移动长板 ,水槽的短板 min(h[i], h[j])不变或变小,因此下个水槽的面积 一定变小 。
因此,初始化双指针分列水槽左右两端,循环每轮将短板向内移动一格,并更新面积最大值,直到两指针相遇时跳出;即可获得最大面积。
13. 罗马数字转整数
难度简单1657收藏分享切换为英文接收动态反馈
罗马数字包含以下七种字符: 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:
输入: s = "III"
输出: 3
示例 2:
输入: s = "IV"
输出: 4
示例 3:
输入: s = "IX"
输出: 9
示例 4:
输入: s = "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:
输入: s = "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
提示:
1 <= s.length <= 15
s
仅含字符('I', 'V', 'X', 'L', 'C', 'D', 'M')
- 题目数据保证
s
是一个有效的罗马数字,且表示整数在范围[1, 3999]
内 - 题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
- IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
- 关于罗马数字的详尽书写规则,可以参考 罗马数字 - Mathematics 。
题解:
class Solution {
public int romanToInt(String s) {
Map<Character,Integer> mapTest = new HashMap<Character,Integer>(){{
put('I',1);
put('V',5);
put('X',10);
put('L',50);
put('C',100);
put('D',500);
put('M',1000);
}};
int nLength = s.length();
if(nLength==1){
return mapTest.get(s.charAt(0));
}
int nSum =0;
for(int i =0;i<nLength;i++){
int cur = mapTest.get(s.charAt(i));
if(i<nLength-1&&cur<mapTest.get(s.charAt(i+1))){
nSum-=cur;
}else{
nSum+=cur;
}
}
return nSum;
}
}
16. 最接近的三数之和
难度中等1012收藏分享切换为英文接收动态反馈
给你一个长度为 n
的整数数组 nums
和 一个目标值 target
。请你从 nums
中选出三个整数,使它们的和与 target
最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。
示例 1:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
示例 2:
输入:nums = [0,0,0], target = 1
输出:0
提示:
3 <= nums.length <= 1000
-1000 <= nums[i] <= 1000
-104 <= target <= 104
题解:
//先排序,后三指针逻辑比较
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int ans = nums[0] + nums[1] + nums[2];
for(int i=0;i<nums.length;i++) {
int start = i+1, end = nums.length - 1;
while(start < end) {
int sum = nums[start] + nums[end] + nums[i];
if(Math.abs(target - sum) < Math.abs(target - ans))
ans = sum;
if(sum > target)
end--;
else if(sum < target)
start++;
else
return ans;
}
}
return ans;
}
}
102. 二叉树的层序遍历
难度中等1153收藏分享切换为英文接收动态反馈
给你二叉树的根节点 root
,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
示例 2:
输入:root = [1]
输出:[[1]]
示例 3:
输入:root = []
输出:[]
提示:
- 树中节点数目在范围
[0, 2000]
内 -1000 <= Node.val <= 1000
题解:
/**
* 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<List<Integer>> levelOrder(TreeNode root) {
//用来存放最终结果
List<List<Integer>> res = new ArrayList<List<Integer>>();
if(root==null) {
return res;
}
dfs(1,root,res);
return res;
}
void dfs(int index,TreeNode root, List<List<Integer>> res) {
//假设res是[ [1],[2,3] ], index是3,就再插入一个空list放到res中
if(res.size()<index) {
res.add(new ArrayList<Integer>());
}
//将当前节点的值加入到res中,index代表当前层,假设index是3,节点值是99
//res是[ [1],[2,3] [4] ],加入后res就变为 [ [1],[2,3] [4,99] ]
res.get(index-1).add(root.val);
//递归的处理左子树,右子树,同时将层数index+1
if(root.left!=null) {
dfs(index+1, root.left, res);
}
if(root.right!=null) {
dfs(index+1, root.right, res);
}
}
}
72. 编辑距离
难度困难2053收藏分享切换为英文接收动态反馈
给你两个单词 word1
和 word2
,请你计算出将 word1
转换成 word2
所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
示例 1:
输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
示例 2:
输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')
提示:
0 <= word1.length, word2.length <= 500
word1
和word2
由小写英文字母组成
首先转换思路,题目给的三种操作插入,删除,替换;分配到两个字符串相当于有6个操作
对word1插入一个字符,其实就相当于在word2删除一个字符
例如 (word1)abc -> abcd 插入了d,相当于 (word2)abcd - > abc 删除了d 最终结果两者就是一样了
对word1删除一个字符,其实就相当于在word2中插入一个字符(和1解释一样)
对word1更换一个字符和对word2更换一个字符没什么区别
所以可以总结,其实一共就三种操作
对word1删除操作
对word2删除操作
对word1替换操作
初始化
首先我们要知道的是,如果其中有一个字符串为空串的话,那么编辑距离一定是另一个字符串的长度(只需要空串添加相应字符即可)
所以初始化数据 dp[i][0] 和 dp[0][i] 都是等于非0字符串长度值!
class Solution {
public int minDistance(String word1, String word2) {
int length1 = word1.length();
int length2 = word2.length();
int[][] dp = new int[length1 + 1][length2 + 1];
// 第一行 dp[0][i] = 1 2 3 4 5 6 ... length2
for (int j = 1; j <= length2; j++) {
dp[0][j] = j;
}
// 第一列 dp[i][0] = 1 2 3 4 5 6 ... length1
for (int i = 1; i <= length1; i++) {
dp[i][0] = i;
}
/**
* 开始循环遍历两个字符的一一匹配~
*/
for (int i = 1; i <= length1; i++) {
for (int j = 1; j <= length2; j++) {
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
/**
* 如果两个字符串的第i个字符和另一字符串的第j字符相同
* 则编辑距离就等于两个字符串前i-1个字符和另一个字符的前j-1个字符的编辑距离(这个有点类似于公共子序列!)
*/
dp[i][j] = dp[i - 1][j - 1];
}else{
/**
* 如果两个字符不相等,A,B
* 在三种情形中取较小值!
* ①A前i-1个字符 和 B前j-1个字符的编辑距离 + 1,因为更换(A or B)第i个字符可以使得两个字符相等,所以加一步替换操作
* ②A前i个字符 和 B前j-1个字符的编辑距离 + 1,因为可以删除B的第i个字符,所以加一步删除操作
* ③A前i-1个字符 和 B前j个字符的 编辑距离 + 1,因为可以删除A的第i个字符,所以加一步删除操作
*/
dp[i][j] = Math.min(Math.min(dp[i - 1][j - 1], dp[i][j - 1]), dp[i - 1][j]) + 1;
}
}
}
return dp[length1][length2];
}
}
121. 买卖股票的最佳时机
难度简单2108收藏分享切换为英文接收动态反馈
给定一个数组 prices
,它的第 i
个元素 prices[i]
表示一支给定股票第 i
天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
。
示例 1:
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
提示:
1 <= prices.length <= 105
0 <= prices[i] <= 104
class Solution {
public int maxProfit(int[] prices) {
int nLength = prices.length;
int res =0;
int pre = 0;
for(int i=1;i<nLength;i++){
int def = prices[i] - prices[i-1];
pre = Math.max(pre+def,0);
res = Math.max(res,pre);
}
return res;
}
public int maxProfit1(int[] prices) {
int nLength = prices.length;
int nMax =0;
for(int i=0;i<nLength;i++){
for(int j = nLength-1;j>i;j--){
int sum = prices[j]-prices[i];
if(sum>0){
nMax = Math.max(nMax,sum);
}
}
}
return nMax;
}
}
11. 盛最多水的容器
难度中等3173收藏分享切换为英文接收动态反馈
给你 n
个非负整数 a1,a2,...,a``n
,每个数代表坐标中的一个点 (i, ai)
。在坐标内画 n
条垂直线,垂直线 i
的两个端点分别为 (i, ai)
和 (i, 0)
。找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
**说明:**你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1]
输出:1
示例 3:
输入:height = [4,3,2,1,4]
输出:16
示例 4:
输入:height = [1,2,1]
输出:2
提示:
n == height.length
2 <= n <= 105
0 <= height[i] <= 104
class Solution {
public int maxArea(int[] height) {
int i = 0, j = height.length - 1, res = 0;
while(i < j) {
res = height[i] < height[j] ?
Math.max(res, (j - i) * height[i++]):
Math.max(res, (j - i) * height[j--]);
}
return res;
}
public int maxArea1(int[] height) {
int nLength= height.length;
int res = 0;
for(int i=0;i<nLength;i++){
for(int j = i+1;j<nLength;j++){
int area = (j-i)*Math.min(height[j],height[i]);
res = Math.max(res,area);
}
}
return res;
}
}
136. 只出现一次的数字
难度简单2239收藏分享切换为英文接收动态反馈
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
class Solution {
public int singleNumber(int[] nums) {
int sign = 0;
for(int num :nums){
sign ^= num;
}
return sign;
}
}
13. 罗马数字转整数
难度简单1689收藏分享切换为英文接收动态反馈
罗马数字包含以下七种字符: 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:
输入: s = "III"
输出: 3
示例 2:
输入: s = "IV"
输出: 4
示例 3:
输入: s = "IX"
输出: 9
示例 4:
输入: s = "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:
输入: s = "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
提示:
1 <= s.length <= 15
s
仅含字符('I', 'V', 'X', 'L', 'C', 'D', 'M')
- 题目数据保证
s
是一个有效的罗马数字,且表示整数在范围[1, 3999]
内 - 题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
- IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
- 关于罗马数字的详尽书写规则,可以参考 罗马数字 - Mathematics 。
class Solution {
public int romanToInt(String s) {
Map<Character,Integer> mapTest = new HashMap<Character,Integer>(){{
put('I',1);
put('V',5);
put('X',10);
put('L',50);
put('C',100);
put('D',500);
put('M',1000);
}};
int nLength = s.length();
if(nLength==1){
return mapTest.get(s.charAt(0));
}
int nSum =0;
for(int i =0;i<nLength;i++){
int cur = mapTest.get(s.charAt(i));
if(i<nLength-1&&cur<mapTest.get(s.charAt(i+1))){
nSum-=cur;
}else{
nSum+=cur;
}
}
return nSum;
}
}
25. K 个一组翻转链表
难度困难1466收藏分享切换为英文接收动态反馈
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
进阶:
- 你可以设计一个只使用常数额外空间的算法来解决此问题吗?
- 你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
示例 1:
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
示例 2:
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]
示例 3:
输入:head = [1,2,3,4,5], k = 1
输出:[1,2,3,4,5]
示例 4:
输入:head = [1], k = 1
输出:[1]
提示:
- 列表中节点的数量在范围
sz
内 1 <= sz <= 5000
0 <= Node.val <= 1000
1 <= k <= sz
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode pre = dummy;
ListNode end = dummy;
while (end.next != null) {
for (int i = 0; i < k && end != null; i++) end = end.next;
if (end == null) break;
ListNode start = pre.next;
ListNode next = end.next;
end.next = null;
pre.next = reverse(start);
start.next = next;
pre = start;
end = pre;
}
return dummy.next;
}
public ListNode reverse(ListNode head){
ListNode cur=head,nxt=head,pre=null;
while(cur!=null){
nxt = cur.next;
cur.next = pre;
pre = cur;
cur = nxt;
}
return pre;
}
}
53. 最大子数组和
难度简单4331收藏分享切换为英文接收动态反馈
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [5,4,-1,7,8]
输出:23
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
**进阶:**如果你已经实现复杂度为 O(n)
的解法,尝试使用更为精妙的 分治法 求解。
class Solution {
public int maxSubArray(int[] nums) {
int nLength = nums.length;
int[] dp = new int[nLength];
dp[0] = nums[0];
int res = dp[0];
for(int i=1;i<nLength;i++){
if(dp[i-1]>0){
dp[i] = dp[i-1] + nums[i];
}else{
dp[i] = nums[i];
}
res = Math.max(res,dp[i]);
}
return res;
}
}
283. 移动零
难度简单1427收藏分享切换为英文接收动态反馈
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:
输入: nums = [0]
输出: [0]
提示:
1 <= nums.length <= 104
-231 <= nums[i] <= 231 - 1
**进阶:**你能尽量减少完成的操作次数吗?
class Solution {
public void moveZeroes(int[] nums) {
if(nums==null) {
return;
}
//两个指针i和j
int j = 0;
int nLength = nums.length;
for(int i=0;i<nLength;i++){
if(nums[i]!=0) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j++] = tmp;
}
}
}
}
剑指 Offer 22. 链表中倒数第k个节点
难度简单323收藏分享切换为英文接收动态反馈
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6
个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6
。这个链表的倒数第 3
个节点是值为 4
的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
int n = 0;
for(ListNode p = head; p != null; p = p.next) n++;
if(k > n) return null;
int count = n-k+1;
ListNode cur = head;
while(count>1){
count--;
cur = cur.next;
}
return cur;
}
}
21. 合并两个有序链表
难度简单2183收藏分享切换为英文接收动态反馈
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
提示:
- 两个链表的节点数目范围是
[0, 50]
-100 <= Node.val <= 100
l1
和l2
均按 非递减顺序 排列
/**
* 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 mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null){
return l2;
}
if(l2 == null){
return l1;
}
ListNode tmp = new ListNode(-1);
ListNode l3=tmp;
while(l1!=null&&l2!=null){
if(l1.val>=l2.val){
l3.next=l2;
l2 = l2.next;
}else{
l3.next=l1;
l1 = l1.next;
}
l3 = l3.next;
}
l3.next=l1==null ? l2:l1;
return tmp.next;
}
}
剑指 Offer 03. 数组中重复的数字
难度简单695收藏分享切换为英文接收动态反馈
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
限制:
2 <= n <= 100000
class Solution {
public int findRepeatNumber(int[] nums) {
int length = nums.length;
int[] temp = new int[length];
for(int i=0;i<length;i++){
temp[nums[i]]++;
if(temp[nums[i]]>1){
return nums[i];
}
}
return -1;
}
}