LeetCode刷题笔记
LeetCode1.两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
//利用Hashmap存储(key,value)=(值,索引)
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] newNums = new int[2];
Map<Integer, Integer> map = new HashMap();
for(int i = 0; i < nums.length ;i++) {
int diff = target - nums[i];
if(map.get(diff) != null) {
newNums[1] = i;
newNums[0] = map.get(diff);
return newNums;
}
map.put(nums[i], i);
}
return newNums;
}
}
LeetCode2.两数相加
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
/**
* Definition for singly-linked list.
* public class ListNode {
* int val; //链表结点存储值
* ListNode next; //指向下一个结点
* ListNode(int x) { val = x; } //构造函数
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummyHead = new ListNode(0); //头结点值为0
ListNode p = l1, q = l2, curr = dummyHead;
int carry = 0;
while (p != null || q != null) {
int x = (p != null) ? p.val : 0;
int y = (q != null) ? q.val : 0;
int sum = carry + x + y;
carry = sum / 10;
curr.next = new ListNode(sum % 10); //最终结果为8070,去掉头结点
curr = curr.next;
if (p != null) p = p.next;
if (q != null) q = q.next;
}
if (carry > 0) {
curr.next = new ListNode(carry);
}
return dummyHead.next; //返回去掉头结点之后的值,即807
}
}
LeetCode7.整数反转
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例 1:
输入: 123
输出: 321
示例 2:
输入: -123
输出: -321
示例 3:
输入: 120
输出: 21
注意:
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−2^31, 2^31 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
/**
* 如果temp = rev⋅10 + pop 导致溢出,那么一定有rev ≥ INTMAX / 10
* 如果rev > INTMAX / 10,那么temp = rev⋅10 + pop 一定会溢出。
* 如果rev == INTMAX / 10,那么只要 pop > 7,temp = rev⋅10 + pop 就会溢出。
* 如果rev == INTMIN / 10,那么只要 pop <-8,temp = rev⋅10 + pop 就会溢出。
* Interger.MAX_VALUE = Math.pow(2,31)-1 = 2147483647
* Interger.MIN_VALUE = -Math.pow(2.31) = -2147483648
*/
class Solution {
public int reverse(int x) {
int rev = 0;
while(x != 0){
int pop = x % 10;
x = x / 10;
if(rev > Integer.MAX_VALUE / 10 || (rev == Integer.MAX_VALUE / 10 && pop > Integer.MAX_VALUE % 10)){
rev = 0;
break;
}else if(rev < Integer.MIN_VALUE / 10 || (rev == Integer.MIN_VALUE / 10 && x < Integer.MIN_VALUE % 10)){
rev = 0;
break;
}
rev = rev * 10 + pop;
}
return rev;
}
}
LeetCode9.回文数
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例 1:
输入: 121
输出: true
示例 2:
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
进阶:
你能不将整数转为字符串来解决这个问题吗?
class Solution {
public boolean isPalindrome(int x) {
if(x<0 || (x%10==0 && x!=0)) return false ; //先排除小于0 或者 以0结尾且不为零的数
int result=0;
while(x>result){ //判断一半长度即可
result = result*10 + x % 10; //反向取值
x /= 10;
}
return result==x || x==result/10; //前一半和后一半的反向值是否相等 或者 是否为 0< x <10 的数
}
}
LeetCode13.罗马数字转整数
罗马数字包含以下七种字符: 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 的范围内。
示例 1:
输入: “III”
输出: 3
示例 2:
输入: “IV”
输出: 4
示例 3:
输入: “MCMXCIV”
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
import java.util.HashMap;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner =new Scanner(System.in);
String s =scanner.nextLine();
System.out.println(romanToInt(s));
}
private static int romanToInt(String s) {
HashMap<Character,Integer> hm = new HashMap<>();
hm.put('I',1);
hm.put('V',5);
hm.put('X',10);
hm.put('L',50);
hm.put('C',100);
hm.put('D',500);
hm.put('M',1000);
int sum = 0;
char ss[] = s.toCharArray();
for(int i =0;i < ss.length-1;i++){
if(hm.get(ss[i]) >= hm.get(ss[i+1])){
sum = sum + hm.get(ss[i]);
}else {
sum = sum + (hm.get(ss[i+1])- hm.get(ss[i]));
i++;
}
}
if(ss.length == 1){
return hm.get(ss[0]);
}
else if(hm.get(ss[ss.length-2]) >= hm.get(ss[ss.length-1]))
return sum + hm.get(ss[ss.length-1]);
else
return sum;
}
}
LeetCode14.最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
示例 1:
输入: [“flower”,“flow”,“flight”]
输出: “fl”
示例 2:
输入: [“dog”,“racecar”,“car”]
输出: “”
解释: 输入不存在公共前缀。
说明:
所有输入只包含小写字母 a-z 。
//水平扫描法 每一个都和第一个字符串比较
class Solution {
public String longestCommonPrefix(String[] strs) {
if(strs == null || strs.length == 0) return "";
String prefix = strs[0];
for(int i = 1;i < strs.length;i++){
while(strs[i].indexOf(prefix) != 0){
prefix = prefix.substring(0, prefix.length()-1);
if(prefix.isEmpty()) return "";
}
}
return prefix;
}
}
//垂直扫描法 同时比较同一位置处字符是否相等
class Solution {
public String longestCommonPrefix(String[] strs) {
if(strs == null || strs.length == 0) return "";
for(int i = 0;i < strs[0].length();i++){
char c = strs[0].charAt(i);
for(int j = 1;j < strs.length;j++){
if(i == strs[j].length() || strs[j].charAt(i) != c)
return strs[0].substring(0,i);
}
}
return strs[0];
}
}
LeetCode20.有效的括号
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: “()”
输出: true
示例 2:
输入: “()[]{}”
输出: true
示例 3:
输入: “(]”
输出: false
示例 4:
输入: “([)]”
输出: false
示例 5:
输入: “{[]}”
输出: true
class Solution {
public boolean isValid(String s) {
char[] stack=new char[s.length()+1];
int top=1;
for(char c:s.toCharArray()){
if(c=='('||c=='['||c=='{'){
stack[top++]=c; //左括号进栈,top+1,右括号出现就与前一位比较,相同就弹出前一位,top-1
}else if(c==')'&&stack[--top]!='('){
return false;
}else if(c==']'&&stack[--top]!='['){
return false;
}else if(c=='}'&&stack[--top]!='{'){
return false;
}
}
return top==1;
}
}
LeetCode21.合并两个有序链表
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null) {
return l2;
}
if(l2 == null) {
return l1;
}
if(l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2); //递归,判断下一个节点连接在哪里
return l1; //返回表头较小的链表
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
}
LeetCode26.删除排序数组中的重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
class Solution {
public int removeDuplicates(int[] nums) {
int j = 0;
for(int i = 1; i < nums.length; i++){
if(nums[j] == nums[i]){
nums[i] = nums[i];
}else{
nums[j+1] = nums[i];
j++;
}
}
return j+1;
}
}
LeetCode121. 买卖股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
class Solution {
public int maxProfit(int[] prices) {
int len = prices.length;
if (len < 2) {
return 0;
}
// 有可能不做交易,因此结果集的初始值设置为 0
int res = 0;
// 表示在当前位置之前的最小值,假设修正法(打擂台法)
int minVal = prices[0];
// 注意:这里从 1 开始
for (int i = 1; i < len; i++) {
res = Math.max(res, prices[i] - minVal);
minVal = Math.min(minVal, prices[i]);
}
return res;
}
}
LeetCode122. 买卖股票的最佳时机II
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
解法一:谷峰法
解法二:连续爬坡
// 解法一:
// 谷峰法
package cn.leetcode;
import java.util.Scanner;
public class leetcode122 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String p = sc.nextLine();
String p1[] = p.split(" ");
int prices[] = new int[p1.length];
for (int i = 0; i < p1.length; i++){
prices[i] = Integer.parseInt(p1[i]);
}
System.out.println(maxProfit(prices));
}
private static int maxProfit(int[] prices) {
int i = 0;
int valley = prices[0];
int peak = prices[0];
int maxprofit = 0;
while (i < prices.length - 1) {
while (i < prices.length - 1 && prices[i] >= prices[i + 1])
i++;
valley = prices[i];
while (i < prices.length - 1 && prices[i] <= prices[i + 1])
i++;
peak = prices[i];
maxprofit += peak - valley;
}
return maxprofit;
}
}
// 解法二:
// 将 谷峰法 变成 连续爬坡问题
// 谷峰法:找到最小值和最大值进行相减
// 连续爬坡:只要后面比前面大就相减,最后所有差值进行相加
// 时间复杂度:O(n),遍历一次。
// 空间复杂度:O(1),需要常量的空间。
class Solution {
public int maxProfit(int[] prices) {
int nums = 0;
for(int i = 1; i < prices.length; i++){
if(prices[i] > prices[i-1]){
nums = nums + prices[i] - prices[i-1];
}
}
return nums;
}
}