bj_LeetCode刷题笔记

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;
    }
}

LeetCode123. 买卖股票的最佳时机III

两次买入卖出

先看一个例子:

我手上有10元
第一次买入花了2元, 利润为 -2 元
第一次卖出,买了4元,利润为 4-2 = 2 元
第二次买入,花了3元,利润为 2-3 = -1 元
第二次卖出,卖了5元,利润为 -1 + 5 = 4 元

所以下面我们就定义了4个变量与上述4个过程对应:

第一次买入后的状态: minPrice1: 最低价格,要最小
第一次卖出后的状态: maxProfit1: 第一次交易最大利润,要最大
第二次买入后的状态: maxProfitAtferBuy: 第二次买入后的最大剩余利润,要最大
第二次卖出后的状态: maxProfit2: 当天能获得最大最大利润,要最大


即,算出第一次购买最低价,接着 买入就扣钱,卖出就加钱
    除了第一次要比较购入最小值,其他参数更新都是和上次比较,取最大值保留

  public int maxProfit(int[] prices) {
        
        int minPrice1 = Integer.MAX_VALUE;         
        int maxProfit1 = 0;                        
        int maxProfitAfterBuy = Integer.MIN_VALUE; 
        int maxProfit2 = 0;                        
        for(int price : prices) {
            // 1.第一次最小购买价格
            minPrice1  = Math.min(minPrice1,  price);

            // 2.第一次卖出的最大利润
            maxProfit1 = Math.max(maxProfit1, price - minPrice1);

            // 3.第二次购买后的剩余净利润
            maxProfitAfterBuy  = Math.max(maxProfitAfterBuy,  maxProfit1 - price );

            // 4.第二次卖出后,总共获得的最大利润(第3步的净利润 + 第4步卖出的股票钱)
            maxProfit2 = Math.max(maxProfit2, price + maxProfitAfterBuy);
        }
        return maxProfit2;
    }

LeetCode371. 两数之和(位运算)

class Solution {
    public int getSum(int a, int b) {
    	int c=0,d=0;  
        while ((a&b)!=0){    //进位为0时结束
            c=a^b;    //c为进位,d为不进位求和
            d=(a&b)<<1;
            a=c;
            b=d;
        }
        return a|b;  //总值=进位值+不进位求和的值
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值