LeetCode题——总共174道
一、简单
1. 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
代码:
class Solution {
public int[] twoSum(int[] nums, int target) {
//将需要返回的下边存放在一个数组里面
int[] re=new int[2];
//然后通过遍历数组进行查找怎么可以达到这个这个目标值
for(int i=0;i<nums.length;i++){
for(int j=i+1;j<nums.length;j++){
if(nums[j]==target-nums[i]){
re[0]=i;
re[1]=j;
}
}
}
return re;
}
}
2. 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 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 res=null;//返回结果
boolean up=false;//是否进位
//如果l1和l2有一个不为null,则计算下一位的值
while(l1!=null || l2!=null){
int x=(l1==null?0:l1.val)+(l2==null?0:l2.val)+(up?1:0);
if(up){
up=false;
}
if(x>9){
ListNode temp=new ListNode(x%10);
temp.next=res;
res=temp;
up=true;
}else{
ListNode temp=new ListNode(x);
temp.next=res;
res=temp;
}
//将l1和l2同时往后走一个
l1=l1==null?null:l1.next;
l2=l2==null?null:l2.next;
}
//计算结束以后进行判断是否还有进位,如果进位的这一位为true了,那么整体再进一位
if(up){
//相当于是进行链表的头插
ListNode temp=new ListNode(1);
temp.next=res;
res=temp;
}
//然后将结果进行链表的反转
return reverse(res);
}
//反转链表的方法
public ListNode reverse(ListNode head){
if(head==null||head.next==null){
return head;
}
ListNode reHead=reverse(head.next);
head.next.next=head;
head.next=null;
return reHead;
}
}
3. 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
代码:
class Solution {
public int lengthOfLongestSubstring(String s) {
//分析:首先将字符串转化为字符数组
char[] str =s.toCharArray();
int len =s.length();
if(len==0) return 0;
if(len==1) return 1;
//HashSet<Character> set = new HashSet<>();
int i=0;
int j ,k,max=0;
for(j = 0;j<len;j++){
for(k = i;k<j;k++)
if(str[k]==str[j]){
i = k+1;
break;
}
if(j-i+1 > max)
max = j-i+1;
}
return max;
}
}
4. 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
代码:
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int len1 = nums1.length;
int len2 = nums2.length;
//合并两个数组
int[] arr = new int[nums1.length + nums2.length];
for (int i = 0; i < len1; i++) {
arr[i] = nums1[i];
}
//int index = len1;
for (int i = 0; i < len2; i++) {
arr[len1+i] = nums2[i];
}
//将合并的数组进行排序
Arrays.sort(arr);
if ((arr.length - 1) % 2 == 0) {
return arr[(arr.length - 1) / 2];
} else {
int flag = (arr.length - 1) / 2;
return ((double)arr[flag] + (double)arr[flag + 1]) / 2;
}
}
}
5.给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例 1:
输入: 123
输出: 321
示例 2:
输入: -123
输出: -321
示例 3:
输入: 120
输出: 21
注意:
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
代码:
class Solution {
public int reverse(int x) {
//这道题可以通过将整型转化为字符串型,让你后通过字符串的反转函数进行反转
//(1)首先进行判断这个数是否小于0,然后将所有的数字转化为大于0进行处理
boolean isNegative=x<0;
if(isNegative){
x=-x;
}
//(2)将整型转化为字符串
String s=String.valueOf(x);
//(3)将String类型转化为StringBuilder为了实现后面的字符串拼接
StringBuilder reverseS=new StringBuilder(s);
//因为拼接是拼接到字符串的最后面,所以通过反转以后负号就到了最前面
if(isNegative){
reverseS.append("-");
}
//直接使用字符串反转
reverseS.reverse();
try{
//将字符串转化为整型返回
return Integer.parseInt(reverseS.toString());
}catch(NumberFormatException e){
return 0;
}
}
}
6. 判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例 1:
输入: 121
输出: true
示例 2:
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
代码:
class Solution {
public boolean isPalindrome(int x) {
//首先进行分析负数一定不是回文数
while(x<0){
return false;
}
//将一个整数转化为字符串,然后将这个字符串进行反转,如果反转以后的字符串和原来的相同那个就是回文整数
String s=String.valueOf(x);
//需要注意的是:字符串反转的方法只有StringBuilder里面才有,String里面是没有的
//所以我们需要将String字符串转化为StringBuilder,处理完成以后还需要转化为String
//因为他们进行equals比较的许霆要是同类型
String reverseS=new StringBuilder(s).reverse().toString();
if(reverseS.equals(s)){
return true;
}
return false;
}
}
7. 罗马数字包含以下七种字符: 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:
输入: "IX"
输出: 9
示例 4:
输入: "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:
输入: "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
代码:
class Solution {
public int romanToInt(String s) {
int n = s.length();
int roman_int = 0;
for(int i=0;i<n;i++)
{
switch(s.charAt(i)) {//将字符串转化为字符数组,然后根据下标取得对应的元素
case 'I' : roman_int = roman_int + 1;break;
case 'V' : roman_int = roman_int + 5;break;
case 'X' : roman_int = roman_int + 10;break;
case 'L' : roman_int = roman_int + 50;break;
case 'C' : roman_int = roman_int + 100;break;
case 'D' : roman_int = roman_int + 500;break;
case 'M' : roman_int = roman_int + 1000;break;
default: System.out.println("default");break;
}
if(i!=0)
{
if(((s.charAt(i)=='V')||(s.charAt(i)=='X'))&&(s.charAt(i-1)=='I'))
//因为在前面多加了一次所以在这个地方需要进行减2操作
roman_int = roman_int-1*2;
if(((s.charAt(i)=='L')||(s.charAt(i)=='C'))&&(s.charAt(i-1)=='X'))
roman_int = roman_int-10*2;
if(((s.charAt(i)=='D')||(s.charAt(i)=='M'))&&(s.charAt(i-1)=='C'))
roman_int = roman_int-100*2;
}
}
return roman_int;
}
}
8. 编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""。
示例 1:
输入: ["flower","flow","flight"]
输出: "fl"
示例 2:
输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。
说明:
所有输入只包含小写字母 a-z 。
代码:
class Solution {
public String longestCommonPrefix(String[] strs) {
if(strs.length==1){
return strs[0];
}
StringBuilder str=new StringBuilder();//用来存放最后公共的字符串
if(strs.length>1){
int len=strs[0].length();
for(int i=0;i<len;i++){
//将第一个字符串拆分成字符进行判断
char curr=strs[0].charAt(i);
for(int j=1;j<strs.length;j++){
if(strs[j].length()<=i||strs[j].charAt(i)!=curr){
return str.toString();
}
if(strs[j].charAt(i)==curr&&j==strs.length-1){
str.append(curr);
}
}
}
}
return str.toString();
}
}
9.有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
1. 左括号必须用相同类型的右括号闭合。
2. 左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
代码:
class Solution {
public boolean isValid(String s) {
Stack<Character> stack=new Stack<>();
//接下来对字符串进行分情况判断——
//但是需要注意的是我们传进去的参数是String类型的,但是我们在判断的时候是单个字符进行判断的,所以需要将字符串转化为字符数组
char[] ch=s.toCharArray();
for(int i=0;i<ch.length;i++){
char c=ch[i];
switch (c){
//如果遇到是左括号的情况我们就直接一起处理——入栈
case '(':
case '[':
case '{':
stack.push(c);
break;
//如果是右括号则进行判断
case ')':
case ']':
case '}':
//(1)当出现了右括号,但是栈里面已经没有左括号可以进行匹配了,那么就是无效的字符串
if(stack.empty()){
return false;
}else {
//将当前栈里面栈顶的元素取出来进行比较:这个时候又会出现两种情况匹配|不匹配
char left=stack.peek();
//取出来的括号匹配有三种情况:
if( (left=='('&&c==')')
||(left=='['&&c==']')
||(left=='{'&&c=='}')
){
//一旦满足就将左括号出栈
stack.pop();
}else {
//如果不匹配
return false;
}
}
}
}
//等到这个字符所有的元素都入栈并且判断完成以后还需要进行一步校验——栈是否为空,
// 如果校验完成以后栈里面还有元素,那么说明左括号比右括号多。
if(!stack.empty()){
return false;
}else {
return true;
}
}
}
10.合并两个有序链表
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入: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;
}
ListNode cur1=l1;
ListNode cur2=l2;
ListNode result=null;
ListNode last=null;
while(cur1!=null&&cur2!=null){
if(cur1.val<=cur2.val){
ListNode next =cur1.next;
cur1.next=null;
if(result==null){
result=last=cur1;
}else{
last.next=cur1;
last=cur1;
}
cur1=next;
}else{
ListNode next =cur2.next;
cur2.next=null;
if(result==null){
result=last=cur2;
}else{
last.next=cur2;
last=cur2;
}
cur2=next;
}
}
if(cur2!=null){
last.next=cur2;
}
if(cur1!=null){
last.next=cur1;
}
return result;
}
}
11.删除排序数组中的重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
代码:
class Solution {
public int removeDuplicates(int[] nums) {
int cur1=0;
int cur2=1;
while(cur2<nums.length){
if(nums[cur2]>nums[cur1]){
//但是要往后走一个才移动
cur1++;
//通过判断前后两个数是否相等如果出现相等直接将后面的数移到前面
nums[cur1]=nums[cur2];
}
cur2++;
}
return cur1+1;
}
}
12. 移除元素
给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,1,2,2,3,0,4,2], val = 2,
函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
注意这五个元素可为任意顺序。
你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// 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) {
int k=0;//[0,k)区间非val:即这个k是最后没有val值数组区间上限
for(int i=0;i<nums.length;i++){
//巧妙的使用个数与数组下标之间的关系
if(nums[i]!=val){
//这个表示数组的下标和目前经过删除以后数组里面的个数不相等的时候就将后面的元素往前移动
if(i!=k){
nums[k++]=nums[i];
}else{
//如果是元素下标和元素个数是相等,则直接进行加1操作移动
k++;
}
}
}
return k;
}
}
13.实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1:
输入: haystack = "hello", needle = "ll"
输出: 2
示例 2:
输入: haystack = "aaaaa", needle = "bba"
输出: -1
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
代码:
class Solution {
public int strStr(String haystack, String needle) {
if (needle == null || needle.length() == 0) {
return 0;
}
if (haystack == null || haystack.length() == 0) {
return -1;
}
for (int i = 0; i < haystack.length(); i++) {
if (haystack.charAt(i) == needle.charAt(0)) {
if (haystack.length() - i < needle.length()) { // 如果后面剩余的长度已经小于needle,则直接返回-1
return -1;
} else if (needle.equals(haystack.substring(i, i + needle.length()))){
return i;
}
}
}
return -1;
}
}
14. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5
输出: 2
示例 2:
输入: [1,3,5,6], 2
输出: 1
示例 3:
输入: [1,3,5,6], 7
输出: 4
示例 4:
输入: [1,3,5,6], 0
输出: 0
代码:
class Solution {
public int searchInsert(int[] nums, int target) {
for(int i=0;i<nums.length;i++){
if(nums[i]>=target){
return i;
}
}
//遍历完了整个数组还是没有找到满足条件的值,并且数组里面的所有值都小于目标值target
return nums.length;
}
}
15. 报数
报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下:
1. 1
2. 11
3. 21
4. 1211
5. 111221
1 被读作 "one 1" ("一个一") , 即 11。
11 被读作 "two 1s" ("两个一"), 即 21。
21 被读作 "one 2", "one 1" ("一个二" , "一个一") , 即 1211。
给定一个正整数 n(1 ≤ n ≤ 30),输出报数序列的第 n 项。
注意:整数顺序将表示为一个字符串。
示例 1:
输入: 1
输出: "1"
示例 2:
输入: 4
输出: "1211"
代码:
class Solution {
public String countAndSay(int n) {
if(n == 1) return "1";
if(n == 2) return "11";
String s = "";//用来存放前面一个数
if(n >1 ) {
s = countAndSay(n-1);
}
int temp = 1;//这个temp就是用来计数当前字符有几个
char c = s.charAt(0);
StringBuilder sb = new StringBuilder();
for(int i = 1;i<s.length();i++){
if(c == s.charAt(i)) {
temp++;
}
if(c != s.charAt(i)) {//如果不相等直接进行拼接表示有temp个c
sb.append(temp).append(c);
c = s.charAt(i);
temp = 1;
}
if(i == s.length()-1) {//这个表示整个字符串里面的字符都是相同的
sb.append(temp).append(c);
break;
}
}
return sb.toString();
}
}
16. 最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
代码:
class Solution {
public int maxSubArray(int[] nums) {
//进行分析:只要不出现和为负数我们就一直进行加的操作
if(nums.length==0){
return 0;
}
int cursum=0;
int greatestSum=nums[0];
for(int i=0;i<nums.length;i++){
if(cursum<0){
//如果数组里面的元素全部都是负数则返回第一个数
cursum=nums[i];
}else{
cursum+=nums[i];
}
if(cursum>greatestSum){
greatestSum=cursum;
}
}
return greatestSum;
}
}
17. 最后一个单词的长度
给定一个仅包含大小写字母和空格 ' ' 的字符串,返回其最后一个单词的长度。
如果不存在最后一个单词,请返回 0 。
说明:一个单词是指由字母组成,但不包含任何空格的字符串。
示例:
输入: "Hello World"
输出: 5
代码:
class Solution {
public int lengthOfLastWord(String s) {
if(s.length()==0||s.equals(" ")||s.equals(" ")){
return 0;
}
//首先将这个单词按照空格进行拆分
String[] strs=s.split(" ");
if(strs.length==1){
String curStr=strs[0];
return strs[0].length();
}
//如果不是只有一个元素,那么就取最后一个元素
String lastStr=strs[strs.length-1];
return lastStr.length();
}
}
18.加一
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
示例 2:
输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。
代码:
class Solution {
public int[] plusOne(int[] digits) {
//需要分情况讨论:当后一位不需要进位时/后一位需要进位时
int lastIndex=digits.length-1;
if(digits[lastIndex]<9){
digits[lastIndex]++;
return digits;
}
//否则是需要进位的:需要进位又分为是不是每一位都需要进位
//如果是后面的每一位都需要进位那么这个数组就需要进行扩容,扩大一个位数即可
int[] newNum=new int[lastIndex+2];
int i;
for(i=lastIndex;i>=0&&digits[i]==9;i--){
digits[i]=0;
}
if(i<0){//表示后面的每一位都是9都需要进行进位
newNum[0]=1;
return newNum;
}else{//表示进位到某一位的时候不需要继续进行进位了
digits[i]++;
return digits;
}
}
}
19.二进制求和
给定两个二进制字符串,返回他们的和(用二进制表示)。
输入为非空字符串且只包含数字 1 和 0。
示例 1:
输入: a = "11", b = "1"
输出: "100"
示例 2:
输入: a = "1010", b = "1011"
输出: "10101"
代码:
class Solution {
public String addBinary(String a, String b) {
int i=a.length()-1,j=b.length()-1,jiwei=0,sum=0;
StringBuilder sb=new StringBuilder();
while (i>=0||j>=0){
sum=jiwei;
if (i>=0){
sum+=a.charAt(i--)-'0';
}
if (j>=0){
sum+=b.charAt(j--)-'0';
}
jiwei=sum/2;
sb.insert(0,sum%2);
}
if (jiwei==1)sb.insert(0,1);
return sb.toString();
}
}
20. x的平方根
实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
示例 1:
输入: 4
输出: 2
示例 2:
输入: 8
输出: 2
说明: 8 的平方根是 2.82842...,
由于返回类型是整数,小数部分将被舍去。
代码:
class Solution {
public int mySqrt(int x) {
double temp=Math.sqrt(x);
//再将浮点型转化为整型
int result=(int) temp;
return result;
}
}
21.爬楼梯
假设你正在爬楼梯。需要 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 阶
代码:
class Solution {
public int climbStairs(int n) {
//这道题相当于是斐波那契额数列
if(n==1){
return 1;
}
if(n==2){
return 2;
}
//使用递归算法的时候超出了限制就是用循环
int re1=1;
int re2=2;
int re=0;
for(int i=2;i<n;i++){
re=re1+re2;
re1=re2;
re2=re;
}
return re;
}
}
22.删除排序链表中的重复元素
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 1:
输入: 1->1->2
输出: 1->2
示例 2:
输入: 1->1->2->3->3
输出: 1->2->3
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
/**采用前后指针的方法进行判断:
如果前面的指针指向元素的值不等于后面指针指向元素的值,那么前后指针同时向后走一步;
如果前面指针指向的元素等于后面指针指向的元素,那么后面的指针向后走一步再进行判断,如果不相等
则将当前的值赋值给前面指针的next,如果相等则再向后走一步*/
if(head == null || head.next==null){
return head;
}
ListNode cur1 = head;
ListNode cur2 = head.next;
while (cur2 != null) {
if (cur1.val == cur2.val) {
cur1.next = cur2.next ;
}else {
cur1 = cur1.next ;
}
//无论如何cur2都会向后走一步
cur2 = cur2.next;
}
return head;
}
}
23.合并两个有序数组
给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
代码:
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
//可以采用java中的数组排序算法
if(m==0){
if(n!=0){
for(int i=0;i<n;i++){
nums1[i]=nums2[i];
}
}
}
for(int i=m;i<m+n;i++){
nums1[i]=nums2[i-m];
}
Arrays.sort(nums1);
}
}
24.二叉树的中序遍历
给定一个二叉树,返回它的中序 遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,3,2]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
//二叉树的前序遍历的非递归实现:
//我们用一个栈来存放我们遍历的结点,然后通过查看访问结点的次数就可以知道前序遍历的结果
Stack<TreeNode> s=new Stack<>();
List<Integer> list=new ArrayList<>();
//我们进行遍历时的当前结点
TreeNode cur=root;
//当前栈顶的结点
TreeNode top=null;
while(cur!=null||!s.empty()){
//实现一路向左把结点放进栈里面
while(cur!=null){
s.push(cur);
cur=cur.left;
}
//这个地方表示第二次遇到这个结点
top=s.peek();
list.add(top.val);
s.pop();
cur=top.right;
}
return list;
}
}
25.相同的树
给定两个二叉树,编写一个函数来检验它们是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
示例 1:
输入: 1 1
/ \ / \
2 3 2 3
[1,2,3], [1,2,3]
输出: true
示例 2:
输入: 1 1
/ \
2 2
[1,2], [1,null,2]
输出: false
示例 3:
输入: 1 1
/ \ / \
2 1 1 2
[1,2,1], [1,1,2]
输出: false
代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
/**
分析:我们分析题意可以知道,比较两颗二叉树是否相等总共分为三种情况:
两个都为空树发的时候必然是相等的;
一个为空树一个不为空树是不相等的;
两个都不为空树则分别进行比较两个的左子树和右子树;
*/
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if(p==null && q==null){
return true;
}
if(p!=null && q==null){
return false;
}
if(p==null && q!=null){
return false;
}
//下面用的相当于是前序遍历:根节点就是相等就直接输出了——左子树——右子树
//如果这个地方写相等那么会出现一个问题就是在两棵树里面只要遇到了相同的结点就会返回true
//所以这个地方我们需要判断不相等。
if(p.val!=q.val){
return false;
}
if(isSameTree(p.left,q.left)==false){
return false;
}
if(isSameTree(p.right,q.right)==false){
return false;
}
//如果上面的都不满足,那就是经过比较所有的结点都是相等的,返回true
return true;
}
}
26.对称二叉树
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \
2 2
\ \
3 3
代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
/**
*对称二叉树:我们最重要的就是要将它分开成两棵树进行判断,对于根节点我们单独考虑。
如果一棵树只有一个根节点,那么这棵树一定是二叉树。否则我们将这棵树的左右子树进行镜像比较;
*/
class Solution {
/*
我们首先编写一个函数来实现镜像比较
*/
public boolean isMirrorTree(TreeNode p,TreeNode q){
if(p==null&&q==null){
return true;
}
//这个地方必须包含所有为空的情况,不然下面的引用就会出现空指针异常
//这两种写法是等价的:if((p!=null&&q==null)||(p==null&&q!=null))<==>(p==null||q==null)
if((p!=null&&q==null)||(p==null&&q!=null)){
return false;
}
return (p.val==q.val&&
isMirrorTree(p.left,q.right)&&
isMirrorTree(p.right,q.left));
}
public boolean isSymmetric(TreeNode root) {
//下面其实就是使用前序遍历实现对称二叉树的判断
if(root==null){
return true;
}
//对于对称二叉树我们的终止条件需要对一个结点单独考虑,一个结点的就是对称二叉树
if(root!=null&&root.left==null&&root.right==null){
return true;
}
return isMirrorTree(root.left,root.right);
}
}
27.二叉树的层序遍历
给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。
例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]
代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
//这个层序遍历返回的结果是一个List集合,但是这个结合比较特殊,因为最后需要的是一个二维数组
//就是在List集合里面存放的还是List集合。
//(1)根节点为空
if(root==null){
//返回一个空的顺序表
return new ArrayList<>();
}
//我们需要一个队列来实现中间的转化关系:就是将一颗二叉树首先放进一个队列里面,然后取出来放进集合里面
Queue<TreeNode> queue=new LinkedList<>();
//再定义一个集合来存放返回的结果
List<List<Integer>> result=new ArrayList<>();
//根节点不为空的情况
queue.add(root);
while (!queue.isEmpty()){
//用于计数队列里面还有几个数
int count= queue.size();
List<Integer> list=new ArrayList<>();
while (count>0){
//(1)将根节点放进集合里面了
TreeNode top=queue.peek();
queue.poll();
list.add(top.val);
//再判断它的左子树
if(top.left!=null){
queue.add(top.left);
}
//再判断右子树
if(top.right!=null){
queue.add(top.right);
}
count--;
}
//如果跳出了上面的循环以后就结束了遍历整个二叉树,此时将这个集合放进一个集合里面
//这个result就是一个二位的数组——二维顺序表
result.add(list);
}
return result;
}
}
28.二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root==null){
return 0;
}
int leftMax=maxDepth(root.left);
int rightMax=maxDepth(root.right);
return leftMax>rightMax?leftMax+1:rightMax+1;
}
}
29.从前序与中序遍历序列构造二叉树
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode build(int[] preorder,int[] inorder,int startPre,int endPre,int startIn,int endIn){
if(startPre>endPre){
return null;
}
if(startPre==endPre){
return new TreeNode(preorder[startPre]);
}
int rootval=preorder[startPre];
TreeNode root=new TreeNode(rootval);
//在中序遍历里面找到属于二叉树的左子树和右子树的结点
for(int i=startIn;i<=endIn;i++){
if(inorder[i]==rootval){
root.left=build(preorder,inorder,startPre+1,startPre+i-startIn,startIn,i-1);
root.right=build(preorder,inorder,startPre+i-startIn+1,endPre,i+1,endIn);
break;
}
}
return root;
}
public TreeNode buildTree(int[] preorder, int[] inorder) {
return build(preorder,inorder,0,preorder.length-1,0,inorder.length-1);
}
}
30.从中序与后序遍历序列构造二叉树
根据一棵树的中序遍历与后序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
return realTree(inorder,0,inorder.length,postorder,0,postorder.length);
}
public TreeNode realTree(int[] inorder,int a,int b,int[] postorder,int c,int d){
if(a==b || c==d)
return null;
TreeNode node=new TreeNode(postorder[d-1]);
for(int i=a;i<b;i++){
if(inorder[i]==postorder[d-1]){
node.left=realTree(inorder,a,i,postorder,c,i+c-a);
node.right=realTree(inorder,i+1,b,postorder,i+c-a,d-1);
break;
}
}
return node;
}
}
31.平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回 true 。
示例 2:
给定二叉树 [1,2,2,3,3,null,null,4,4]
1
/ \
2 2
/ \
3 3
/ \
4 4
返回 false 。
代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
/**
我们首先需要定义一个方法实现求解二叉树的高度
*/
public int maxDepth(TreeNode root) {
if(root==null){
return 0;
}
int leftMax=maxDepth(root.left);
int rightMax=maxDepth(root.right);
return leftMax>rightMax?leftMax+1:rightMax+1;
}
// public int getHight(TreeNode root){
// if(root==null){
// return 0;
// }
// int leftHight=getHight(root.left);
// int rightHight=getHight(root.right);
// return leftHight>rightHight?leftHight+1:rightHiht+1;
// }
public boolean isBalanced(TreeNode root) {
if(root==null){
return true;
}
//如果左子树不为平衡二叉树则整棵树一定不是平衡二叉树
boolean leftBanlance=isBalanced(root.left);
if(!leftBanlance){
return false;
}
//如果右子树不是平衡二叉树那么这棵树也一定不是平衡二叉树
boolean rightBanlance=isBalanced(root.right);
if(!rightBanlance){
return false;
}
int leftHight=maxDepth(root.left);
int rightHight=maxDepth(root.right);
int abslute=leftHight-rightHight;
// if(abslute<0){
// abslute=abslute*(-1);
// }
if(abslute>=-1&&abslute<=1){
return true;
}
else{
return false;
}
}
}
32.二叉树的层序遍历
给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其自底向上的层次遍历为:
[
[15,7],
[9,20],
[3]
]
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
if(root==null){
return new ArrayList();
}
//这个集合是用来存放所有的数组
List<List<Integer>> ans=new ArrayList<>();
Queue<TreeNode> queue=new LinkedList<>();
queue.add(root);
//这个循环不为空表示一层里面的所有结点
while(!queue.isEmpty()){
//一层里面结点的总个数
int count=queue.size();
List<Integer> list=new ArrayList<>();
while(count>0){
TreeNode node=queue.poll();
list.add(node.val);
if(node.left!=null){
//同时将下一层的结点添加进队列里面
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
count--;
}
//添加0表示这个元素是逆序存放的
ans.add(0,list);
}
return ans;
}
}
33.将有序数组转化为二叉搜索树
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定有序数组: [-10,-3,0,5,9],
一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
// 左右等分建立左右子树,中间节点作为子树根节点,递归该过程
return nums == null ? null : buildTree(nums, 0, nums.length - 1);
}
public TreeNode buildTree(int[] nums, int l, int r) {
if (l > r) {
return null;
}
int m = l + (r - l) / 2;
//中序遍历
TreeNode root = new TreeNode(nums[m]);
root.left = buildTree(nums, l, m - 1);
root.right = buildTree(nums, m + 1, r);
return root;
}
}
34.二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最小深度 2.
代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int minDepth(TreeNode root) {
//这个题通过前序遍历的方式来实现
if(root==null){
return 0;
}
int count=0;
Queue<TreeNode> q=new LinkedList<>();
q.add(root);
while(!q.isEmpty()){
int n=q.size();
count++;
for(int i=0;i<n;i++){
TreeNode cur=q.poll();
if(cur.left==null&&cur.right==null) return count;
if(cur.left!=null)q.add(cur.left);
if(cur.right!=null)q.add(cur.right);
}
}
return count;
}
}
35.路径总和
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
//对于二叉树这类的题都是这样的,先从根节点开始判断是否满足条件,在进行判断左右子树是否满足
public boolean hasPathSum(TreeNode root, int sum) {
if(root==null){
return false;
}
if(root.left==null&&root.right==null){
return sum-root.val==0;
}
return hasPathSum(root.left,sum-root.val) || hasPathSum(root.right,sum-root.val);
}
}
36.杨辉三角形
给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。
示例:
输入: 5
输出:
[
[1],
[1,1],
[1,2,1],
[1,3,3,1],
[1,4,6,4,1]
]
代码:
class Solution {
public List<List<Integer>> generate(int numRows) {
if(numRows==0)return new ArrayList<>();
//这个表示每一层的数字
List<List<Integer>> list1=new ArrayList<>();
//这个里面存放的是每一层里面的数字
List<Integer> list2=new ArrayList<>();
//将第一层里面的数字添加到集合面
list2.add(1);
list1.add(list2);
if(numRows==1){//表示只需要杨辉三角形的第一层
return list1;
}
//如果大于1层
generate1(list1,2,numRows);
return list1;
}
public void generate1(List<List<Integer>> list,int n,int numRow){
//n表示i总共需要多少行,numRow表示想在所在行
if(n==numRow+1) return;
List<Integer> arr=new ArrayList<>();
arr.add(1);
for(int i=1;i<n-1;i++){
int num=list.get(n-2).get(i-1)+list.get(n-2).get(i);
arr.add(num);
}
arr.add(1);
list.add(arr);
generate1(list,++n,numRow);
}
}
37.杨辉三角形II
给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。
在杨辉三角中,每个数是它左上方和右上方的数的和。
示例:
输入: 3
输出: [1,3,3,1]
代码:
class Solution {
public List<Integer> getRow(int rowIndex) {
List<Integer> res=new ArrayList<>(rowIndex+1);
long cur=1;
for(int i=0;i<=rowIndex;i++){
res.add((int) cur);
cur=cur*(rowIndex-i)/(i+1);
}
return res;
}
}
38.买股票的最佳时机
给定一个数组,它的第 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 {
//1.记录今天之前买入的最小值
//2.计算今天之前最小值买入,今天卖出的获利,也就是今天卖出的最大获利
//3.比较每天的最大获利,取最大值即可
public int maxProfit(int[] prices) {
if(prices.length<=1){
return 0;
}
int min = prices[0],max=0;
for(int i=1;i<prices.length;i++){//一次遍历
max=Math.max(max,prices[i]-min);
min=Math.min(min,prices[i]);
}
return max;
}
}
39.买股票的最佳时机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。
代码:
class Solution {
public int maxProfit(int[] prices) {
if(prices.length<=1) return 0;
//不管我们有多少种买法,都是后面的价钱比前面的价钱高的时候我们就卖出
int max=0;
for(int i=1;i<prices.length;i++){
if(prices[i]>prices[i-1]){
max+=(prices[i]-prices[i-1]);
}
}
return max;
}
}
40.验证回文串
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: "A man, a plan, a canal: Panama"
输出: true
示例 2:
输入: "race a car"
输出: false
代码:
class Solution {
public boolean isPalindrome(String s) {
//将这个字符串转化为StringBuilder的类型,然后通过他的逆置方法逆置以后进行判断是否相等
if(s==null){
return true;
}
s=s.toLowerCase();//转为小写,不区分大小写
int l=s.length();
//将String转化为StringBuilder
StringBuilder str=new StringBuilder(l);
for(char c:s.toCharArray()){
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')) {
str.append(c);
}
}
return str.toString().equals(str.reverse().toString());
}
}
41.只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
代码:
class Solution {
// //1.方法1
// public int singleNumber(int[] nums) {
// //这个题的思路是首先对它进行排序,然后判断这个排序数组里面出现一次的数字
// //数组排序
// Arrays.sort(nums);
// for(int i=0;i<nums.length-1;i+=2){
// if(nums[i]==nums[i+1]){
// continue;
// }
// return nums[i];
// }
// return nums[nums.length-1];
// }
//方法2:异或的方法
public int singleNumber(int[] nums){
int result=0;
for(int i=0;i<nums.length;i++){
result^=nums[i];
}
return result;
}
}
42.环形链表
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
代码:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
//通过HashMap来存放这个遍历的链表的值
Set<ListNode> node=new HashSet<>();
while(head!=null){
//如果在容器中出现了重复的元素那个就说明这个链表是循环的,但是如果遍历完了还是没有找到相等
//的元素,那么就说明没有相等的元素
if(node.contains(head))
return true;
else
node.add(head);
head=head.next;
}
return false;
}
// //2.快慢指针的方法
// public boolean hasCycle(ListNode head){
// if(head==null||head.next==null||head.next.next==null){
// return false;
// }
// ListNode slow=head.next;
// ListNode fast=slow.next;
// while(fast!=slow){
// if(fast.next==null||fast.next.next==null){
// return false;
// }
// slow=slow.next;
// fast=fast.next.next;
// }
// return true;
// }
}
43.最小栈
设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。
• push(x) -- 将元素 x 推入栈中。
• pop() -- 删除栈顶的元素。
• top() -- 获取栈顶元素。
• getMin() -- 检索栈中的最小元素。
示例:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
代码:
class MinStack {
/** initialize your data structure here. */
public MinStack() {
}
/**
对于这个最小栈的实现我们采用两个栈来实现,我们通过一次遍历来实现也就是时间复杂度为O(1)
但是我们的空间复杂度就不能是O(1)了,相当于是用时间来换空间。
对于这两个栈,一个栈里面存放我们入栈的元素,一个栈里面存放我们的最小元素
*/
//s1存放元素
Stack<Integer> s1=new Stack<>();
//s2存放最小元素
Stack<Integer> s2=new Stack<>();
public void push(int x) {
//对于s1的进栈没有变化就是正常进栈
s1.push(x);
//元素但是对于s2的进栈就需要考虑到比较每次进展元素的大小,始终保证是最小的
if(s2.empty()){
s2.push(x);
}else{
if(x<s2.peek()){
s2.push(x);
}else{
s2.push(s2.peek());
}
}
}
public void pop() {
//对于出栈就是两个栈都同时出栈就可以了
s1.pop();
s2.pop();
}
public int top() {
//查看栈顶元素
int v=s1.peek();
return v;
}
public int getMin() {
//获取最小的元素就是直接在s2中得到栈顶的元素就是最小的
int v=s2.peek();
return v;
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
44.相交链表
编写一个程序,找到两个单链表相交的起始节点。
如下面的两个链表:
在节点 c1 开始相交。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
示例 2:
输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
示例 3:
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。
注意:
• 如果两个链表没有交点,返回 null.
• 在返回结果后,两个链表仍须保持原有的结构。
• 可假定整个链表结构中没有循环。
• 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
int len1=getLength(headA);
int len2=getLength(headB);
if(len1<len2){
int n=len2-len1;
for(int i=0;i<n;i++){
headB=headB.next;
}
}else{
int n=len1-len2;
for(int i=0;i<n;i++){
headA=headA.next;
}
}
//否则一起走
while(headA!=headB){
headA=headA.next;
headB=headB.next;
}
//否则就是找到了相等的结点
return headB;
}
public static int getLength(ListNode tmp) {
int cnt = 0;
while (tmp != null) {
cnt++;
tmp = tmp.next;
}
return cnt;
}
}
45.阶乘后的0
给定一个整数 n,返回 n! 结果尾数中零的数量。
示例 1:
输入: 3
输出: 0
解释: 3! = 6, 尾数中没有零。
示例 2:
输入: 5
输出: 1
解释: 5! = 120, 尾数中有 1 个零.
说明: 你算法的时间复杂度应为 O(log n) 。
分析:
首先题目的意思是末尾有几个0
比如6! = 【1* 2* 3* 4* 5* 6】
其中只有2*5末尾才有0,所以就可以抛去其他数据 专门看2 5 以及其倍数 毕竟 4 * 25末尾也是0
比如10! = 【2*4*5*6*8*10】
其中 4能拆成2*2 10能拆成2*5
所以10! = 【2*(2*2)*5*(2*3)*(2*2*2)*(2*5)】
一个2和一个5配对 就产生一个0 所以10!末尾2个0
转头一想 2肯定比5多 所以只数5的个数就行了
假若N=31 31里能凑10的5为[5, 2*5, 3*5, 4*5, 25, 6*5] 其中 25还能拆为 5**2
所以 里面的5的个数为 int(31/(5**1)) + int(31/(5**2))
所以 只要先找个一个 5**x < n 的x的最大数 然后按上面循环加起来
代码:
import java.util.StringJoiner;
class Solution {
public int trailingZeroes(int n) {
if(n<5){
return 0;
}
return n/5+trailingZeroes(n/5);
}
}
46.两数之和-输入有序数组
给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
• 返回的下标值(index1 和 index2)不是从零开始的。
• 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:
输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
代码:
class Solution {
public int[] twoSum(int[] numbers, int target) {
int[] result=new int[2];
int small_index=0;
int big_index=numbers.length-1;
while(small_index<big_index){
if(numbers[small_index]+numbers[big_index]==target){
result[0]=small_index+1;
result[1]=big_index+1;
break;
}
if(numbers[small_index]+numbers[big_index]>target){
big_index--;
}
if(numbers[small_index]+numbers[big_index]<target){
small_index++;
}
}
return result;
}
}
47.Excel表列名称
给定一个正整数,返回它在 Excel 表中相对应的列名称。
例如,
1 -> A
2 -> B
3 -> C
...
26 -> Z
27 -> AA
28 -> AB
...
示例 1:
输入: 1
输出: "A"
示例 2:
输入: 28
输出: "AB"
示例 3:
输入: 701
输出: "ZY"
代码:
class Solution {
public String convertToTitle(int n) {
if(n<=0){
return "";
}
//首先A到Z和数字之间的转换是A=30
StringBuilder sb=new StringBuilder();
while(n>0){
n--;
sb.append((char)(n%26+'A'));
n=n/26;
}
return sb.reverse().toString();
}
}
48.Excel表列序号
给定一个Excel表格中的列名称,返回其相应的列序号。
例如,
A -> 1
B -> 2
C -> 3
...
Z -> 26
AA -> 27
AB -> 28
...
示例 1:
输入: "A"
输出: 1
示例 2:
输入: "AB"
输出: 28
示例 3:
输入: "ZY"
输出: 701
代码:
import static java.lang.Math.pow;
class Solution {
//方法1:
// public int titleToNumber(String s) {
// if(s==null){
// return 0;
// }
// int len=s.length();
// int result=0;
// for(int i=0;i<len;i++){
// int temp=s.charAt(i)-'A'+1;
// result=result*26+temp;
// }
// return result;
// }
//方法2:
public int titleToNumber(String s) {
if(s==null){
return 0;
}
//将字符串转化为单个字符然后再将单个字符转化为整型
char[] ch=s.toCharArray();
int len=ch.length;
int[] re=new int[len];
int result=0;
for(int i=0;i<len;i++){
int temp=(int)ch[i];
re[i]=temp-'A'+1;
result=result*26+re[i];
}
return result;
}
}
49.求众数
给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数。
示例 1:
输入: [3,2,3]
输出: 3
示例 2:
输入: [2,2,1,1,1,2,2]
输出: 2
代码:
class Solution {
public int majorityElement(int[] nums) {
int count=1;
int maj=nums[0];
for(int i=1;i<nums.length;i++){
if(nums[i]==maj){
count++;
}else{
count--;
if(count==0){
maj=nums[i+1];
}
}
}
return maj;
}
}
二、中等
50.字符串转换整数
请你来实现一个 atoi 函数,使其能将字符串转换成整数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,qing返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
示例 1:
输入: "42"
输出: 42
示例 2:
输入: " -42"
输出: -42
解释: 第一个非空白字符为 '-', 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:
输入: "4193 with words"
输出: 4193
解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。
示例 4:
输入: "words and 987"
输出: 0
解释: 第一个非空字符是 'w', 但它不是数字或正、负号。
因此无法执行有效的转换。
示例 5:
输入: "-91283472332"
输出: -2147483648
解释: 数字 "-91283472332" 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231) 。
代码:
import java.math.BigInteger;
class Solution {
public int myAtoi(String str) {
//首先判断这个字符串是否有效或者是否为空
String res="";
str=str.trim();//去掉前后的空字符串
if(str.length()==0 || str==null)
return 0;
char c=str.charAt(0);
int i=1;//字符串元素对应下标,从1开始
//第一位单独处理,因为有符号的涉及
if(c!='-' && c!='+' && (c<'0'||c>'9'))
return 0;
//否则就是第一位已经为有效的字符串
res=res+c;
while(i<str.length()&&str.charAt(i)>='0'&&str.charAt(i)<='9'){
res=res+str.charAt(i);
i++;
}
//如果这个字符串只有正负号那么也是返回0
if(res.equals("-")||res.equals("+"))
return 0;
BigInteger b = new BigInteger(res);
if(c == '-'){
if( b.compareTo(new BigInteger("-2147483648")) == -1) return Integer.MIN_VALUE;
}else{
if(b.compareTo(new BigInteger("2147483647")) == 1) return Integer.MAX_VALUE;
}
return Integer.valueOf(res);
}
}
51.盛最多水的容器
给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例:
输入: [1,8,6,2,5,4,8,3,7]
输出: 49
代码:
import java.lang.Math;
class Solution {
public int maxArea(int[] height) {
//首先我们需要进行判断这个数组的长度是否大于1
if(height.length<=1){
return 0;
}
//否则我们分别从两端开始进行遍历
int max=0;
int low=0;
int high=height.length-1;
while(low<high){
if(height[low]<=height[high]){
max=Math.max(max,height[low]*(high-low));
low++;
}else{
max=Math.max(max,height[high]*(high-low));
high--;
}
}
return max;
}
}
52.整数转罗马数字
罗马数字包含以下七种字符: 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:
输入: 3
输出: "III"
示例 2:
输入: 4
输出: "IV"
示例 3:
输入: 9
输出: "IX"
示例 4:
输入: 58
输出: "LVIII"
解释: L = 50, V = 5, III = 3.
示例 5:
输入: 1994
输出: "MCMXCIV"
解释: M = 1000, CM = 900, XC = 90, IV = 4.
代码:
class Solution {
public String intToRoman(int num) {
//需要注意的是:罗马数字最大不超过3999,做长的罗马数字对应的是3888
int a=num/1000;//千位
int b=num/100%10;//百位
int c=num/10%10;//十位
int d=num%10;//个位
//对于个十百千不同的位数所有可能出现的情况都分别存放到一个数组里面
String[] qian={"","M","MM","MMM"};
String[] bai={"","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"};
String[] shi={"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"};
String[] ge={"","I","II","III","IV","V","VI","VII","VIII","IX"};
//存放结果
StringBuilder str=new StringBuilder();
str.append(qian[a]);
str.append(bai[b]);
str.append(shi[c]);
str.append(ge[d]);
return str.toString();
}
}
53.三数之和
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
代码:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> list=new ArrayList<>();
Arrays.sort(nums);
//a+b+c=0——>a+b=-c
for(int i=0;i<nums.length-2;i++){
if(i==0 ||(i>0 && nums[i]!=nums[i-1])){
int c=0-nums[i];//将它作为基准,从头到尾的遍历
int l=i+1;
int r=nums.length-1;
while(l<r){
if(nums[l]+nums[r]==c){
List<Integer> list1=new ArrayList<>();//为什么这个集合只能在这个地方定义?
list1.add(nums[i]);
list1.add(nums[l]);
list1.add(nums[r]);
list.add(list1);
l++;
r--;
while(nums[l]==nums[l-1]&&l<r){
l++;
}
while(nums[r]==nums[r+1]&&l<r){
r--;
}
}
else if(nums[l]+nums[r]<c&&l<r){
l++;
continue;
}
else{
r--;
continue;
}
}
}
}
return list;
}
}
54.最接近的三数之和
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).
代码:
class Solution {
public int threeSumClosest(int[] nums, int target) {
//首先将数组进行排序,然后遍历数组看是否有数等于这个目标值
Arrays.sort(nums);
int sum=nums[0]+nums[1]+nums[2];//先选一个基准值,然后将其他三数之和和他进行对比
for(int i=0;i<nums.length-2;i++){
int l=i+1;
int r=nums.length-1;
while(l<r){
int threeSum=nums[i]+nums[l]+nums[r];
if(Math.abs(threeSum-target)<Math.abs(sum-target)){
sum=threeSum;
}
if(threeSum>target){
r--;
}
else if(threeSum<target){
l++;
}else{
return target;
}
}
}
return sum;
}
}
55.四数之和
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
代码:
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {//将复杂问题简单化——转化为两个数字之和
List<List<Integer>> list=new ArrayList<>();
Arrays.sort(nums);
for(int i=0;i<nums.length-3;i++){
if(i==0||(i>0&&nums[i]!=nums[i-1])){//进到循环里面肯定就是没有重复的
for(int j=i+1;j<nums.length-2;j++){
if(j==i+1||j>i+1&&nums[j]!=nums[j-1]){
int l=j+1;
int r=nums.length-1;
int sum=target-nums[i]-nums[j];//还是转化为两数之和
while(l<r){
if(nums[r]+nums[l]==sum){
List<Integer> list1=new ArrayList<>();
list1.add(nums[i]);
list1.add(nums[j]);
list1.add(nums[l]);
list1.add(nums[r]);
list.add(list1);
while(l<r&&nums[l]==nums[l+1]){
l++;
}
while(l<r&&nums[r]==nums[r-1]){
r--;
}
l++;
r--;
}
else if(nums[l]+nums[r]<sum){
while(l<r&&nums[l]==nums[l+1]){
l++;
}
l++;
}
else if(nums[l]+nums[r]>sum){
while(l<r&&nums[r]==nums[r-1]){
r--;
}
r--;
}
}
}
}
}
}
return list;
}
}
56.电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例:
输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
代码:
class Solution {
List<String> list=new ArrayList<>();
String[] button=new String[]{"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public List<String> letterCombinations(String digits) {
if(digits==null || digits.length()==0)
return list;
letterCombinations(digits,0,new String());
return list;
}
public void letterCombinations(String digits,int index,String temp){
if(index==digits.length()){
list.add(temp);
return;
}
int position=digits.charAt(index)-'0';
String str=button[position];
for(int i=0;i<str.length();i++){//当第一位为一个数的时候另外一位一直遍历
letterCombinations(digits,index+1,temp+str.charAt(i));
}
}
}
57.删除倒数第n个结点
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
代码:
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
//使用两个结点,当first走到头的时候,second即是倒数第n+1个结点
ListNode first=head;
ListNode second=head;
//先让second先走几步
while(n-->0){
second=second.next;
}
if(second==null){
return head.next;
}
while(second.next!=null){
second=second.next;
first=first.next;
}
//如果跳出循环表示第二个结点已经走到末尾了:删除倒数第n个结点
first.next=first.next.next;
return head;
}
}
58.括号生成
给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
例如,给出 n = 3,生成结果为:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
代码:
class Solution {
List<String> list=new ArrayList<>();
public List<String> generateParenthesis(int n) {
recurison(list,n,n,"");
return list;
}
//使用递归调用:分别统计左括号和右括号的数量
public void recurison(List<String> res,int left,int right,String s){
if(left>right){
return;
}
if(left==0&&right==0){
list.add(s);
}
if(left>0){
recurison(res,left-1,right,s+"(");
}
if(right>0){
recurison(res,left,right-1,s+")");
}
}
}
59.合并k个链表——分治算法
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
代码:
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
//将一个复杂的问题简单化:将k个链表通过分治算法将他们拆分成两个链表的合并
if(lists.length==0){
return null;
}
if(lists.length==1){
return lists[0];
}
if(lists.length==2){
return mergeTwoLists(lists[0],lists[1]);
}
int mid=lists.length/2;
ListNode[] l1=new ListNode[mid];
for(int i=0;i<mid;i++){
l1[i]=lists[i];
}
ListNode[] l2=new ListNode[lists.length-mid];
for(int i=mid,j=0;j<l2.length;i++,j++){
l2[j]=lists[i];
}
return mergeTwoLists(mergeKLists(l1),mergeKLists(l2));
}
public ListNode mergeTwoLists(ListNode l1,ListNode l2){
if(l1==null) return l2;
if(l2==null) return l1;
ListNode head=null;
if(l1.val<=l2.val){
head=l1;
head.next=mergeTwoLists(l1.next,l2);
}else{
head=l2;
head.next=mergeTwoLists(l1,l2.next);
}
return head;
}
}
60.两两交换链表结点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
给定 1->2->3->4, 你应该返回 2->1->4->3.
代码:
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;
}
}
61.两数相除
给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。
返回被除数 dividend 除以除数 divisor 得到的商。
示例 1:
输入: dividend = 10, divisor = 3
输出: 3
示例 2:
输入: dividend = 7, divisor = -3
输出: -2
说明:
• 被除数和除数均为 32 位有符号整数。
• 除数不为 0。
• 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。本题中,如果除法结果溢出,则返回 231 − 1。
代码:
/**
解题思路:
要求商,首先想到的是减法,看可以被减多少次商就是多少,但是这样的效率很低,所以使用移位算法
左移1相当于乘以2,右移一位相当于除以2
我们可以把一个dividend(被除数)先除以2^n,n开始为31,不断减小n去试探,当满足dividend/2^n>=divisor时,表示我们找到了一个足够大的数,(这个数 X divisor)是不大于dividend的,所以我们就减去2^n个divisor,依此类推。
*/
class Solution {
public int divide(int dividend, int divisor) {
if(dividend==0){
return 0;
}
if(dividend==Integer.MIN_VALUE&&divisor==-1){
return Integer.MAX_VALUE;
}
//判断符号是否相异
boolean negative=(dividend^divisor)<0;
long d1=Math.abs((long)dividend);
long d2=Math.abs((long)divisor);
int count=0;
for(int i=31;i>=0;i--){
if((d1>>i)>=d2){//找出足够大的数2^n*divisor
count+=1<<i;//将结果加上2^n
d1-=d2<<i;//将被除数减去2^n*divisor
}
}
return negative?-count:count;
}
}
62.下一个排列
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
思路:
(1) 从数组最后一个元素开始扫描,寻找到nums[i]>nums[i-1]的第一个i值;
(2) 如果得到i值大于等于1,说明数组存在下一个排列;
1) 我们还是从数组最后一个元素开始扫描,寻找到nums[j]>nums[i-1]的第一个值。因为nums[i]>nums[i-1],所以我们的j值一定大于等于i的;
2) 交换索引为i-1和索引为j的元素的值;
3) 此时索引i及以后的排列是一个降序排列,将其变为升序排列;
(3)如果得到的i值小于1,说明数组不存在下一个排列,倒序输出数组即可;
代码:
class Solution {
public void nextPermutation(int[] nums) {
int i=nums.length-1;
for(;i>=1;i--){
if(nums[i]>nums[i-1]){
break;
}
}
if(i>=1){
int j=nums.length-1;
for(;j>=i;j--){
if(nums[j]>nums[i-1]){
break;
}
}
swap(i-1,j,nums);
reverse(nums,i);
}else{
reverse(nums,0);
}
}
public void reverse(int[] nums,int index){
int i=index;
int j=nums.length-1;
while(i<j){
swap(i,j,nums);
i++;
j--;
}
}
public void swap(int a,int b,int nums[]){
int t=nums[a];
nums[a]=nums[b];
nums[b]=t;
}
}
63.搜索旋转排序数组
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
分析:
将数组从中间进行一分为二,这样的话有一边的数组一定是有序的,另外一边的数组是无序的,无序的数组再次进行刚才的动作,直到找到目标值。
代码:
class Solution {
public int search(int[] nums, int target) {
return search(nums,0,nums.length-1,target);
}
public int search(int[] nums,int low,int high,int target){
if(low>high){
return -1;
}
int mid=(high+low)/2;
if(nums[mid]==target){
return mid;
}
//中间比最右边位置的数小,表示右边有序
if(nums[mid]<nums[high]){
if(nums[mid]<target&&target<=nums[high]){
return search(nums,mid+1,high,target);
}else{
return search(nums,low,mid-1,target);
}
}else{//否则是中间比最右边的数大,表示左边有序
if(nums[low]<=target&&target<nums[mid]){
return search(nums,low,mid-1,target);
}else{
return search(nums,mid+1,high,target);
}
}
}
}
64.在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
代码:
class Solution {
public int[] searchRange(int[] nums, int target) {
//二分查找
if(nums.length==1){
if(nums[0]==target){
return new int[]{0,0};
}else{
return new int[]{-1,-1};
}
}
int left=0;
int right=nums.length-1;
int flag=Integer.MIN_VALUE;
while(left<=right){
int mid=(right+left)/2;
if(nums[mid]>target){
right=mid-1;
}else if(nums[mid]<target){
left=mid+1;
}else{
//找到了就终止循环
flag=mid;
break;
}
}
if(flag==Integer.MIN_VALUE){
return new int[]{-1,-1};
}
//通过下面的方法,对于数组的下标不会出现越界的现象
int i=flag;
int j=flag;
for(;i>=0;i--){
if(nums[i]!=nums[flag]) break;
}
for(;j<=nums.length-1;j++){
if(nums[j]!=nums[flag]) break;
}
return new int[]{i+1,j-1};
}
}
65.组合总和
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
• 所有数字(包括 target)都是正整数。
• 解集不能包含重复的组合。
示例 1:
输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例 2:
输入: candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
使用回溯算法——代码:
class Solution {
//回溯算法
List<List<Integer>> res;
public List<List<Integer>> combinationSum(int[] candidates, int target) {
res=new ArrayList<>();
getCombinationSum(candidates,target,0,0,new ArrayList<>());
return res;
}
public void getCombinationSum(int[] candidates,int target,int start,int sum,List<Integer> list){
if(sum==target){
res.add(new ArrayList<>(list));
}
for(int i=start;i<candidates.length;i++){
if(sum+candidates[i]<=target){
list.add(candidates[i]);
//如果可以就直接进行下一个值的寻找
getCombinationSum(candidates,target,i,sum+candidates[i],list);
//如果不可以则直接返回到上一种情况,再次寻找
list.remove(list.size()-1);
}
}
}
}
66.组合总和II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
• 所有数字(包括目标数)都是正整数。
• 解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]
代码:
class Solution {//排序去重 回溯剪枝
List<List<Integer>> res=new ArrayList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
//这个题目和前一道题目的区别是不能重复使用
Arrays.sort(candidates);
getCombination(candidates,0,0,target,new ArrayList<>());
return res;
}
public void getCombination(int[] nums,int start,int sum,int target,List<Integer> list){
if(sum==target){
res.add(new ArrayList<>(list));
//return;
}
//定义一个前一次出现的数字
int removed=-1;
//因为在回溯算法里面它是将所有的可能都遍历一遍,所以我们就通过定义一个前一次出现的数字来排出相同的这种可能
for(int i=start;i<nums.length;i++){
if(nums[i]!=removed&&sum+nums[i]<=target){//不是前一次出现的数字
list.add(nums[i]);
getCombination(nums,i+1,sum+nums[i],target,list);
list.remove(list.size()-1);
removed=nums[i];
}
}
}
}
67.字符串相乘
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
示例 1:
输入: num1 = "2", num2 = "3"
输出: "6"
示例 2:
输入: num1 = "123", num2 = "456"
输出: "56088"
说明:
1. num1 和 num2 的长度小于110。
2. num1 和 num2 只包含数字 0-9。
3. num1 和 num2 均不以零开头,除非是数字 0 本身。
4. 不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。
代码:
class Solution {
public String multiply(String num1, String num2) {
/**
方法一:
*/
// if ("0".equals(num1) || "0".equals(num2)) {
// return "0";
// }
// //这道题的算法思想特别的巧妙
// int[] res = new int[num1.length() + num2.length() - 1];
// // 正反原理都是一样的,在纸上多算几次就明白了,正算好处理
// for (int i = 0; i < num1.length(); i++) {
// for (int j = 0; j < num2.length(); j++) {
// res[i + j] += (num1.charAt(i) - '0') * (num2.charAt(j) - '0');
// }
// }
// for (int i = res.length - 1; i > 0; i--) {
// if (res[i] > 9) {
// res[i - 1] += res[i] / 10;
// res[i] %= 10;
// }
// }
// StringBuilder sb = new StringBuilder();
// Arrays.stream(res).forEach(sb::append);
// return sb.toString();
/*
*方法二:
*/
/**
num1的第i位(高位从0开始)和num2的第j位相乘的结果在乘积中的位置是[i+j, i+j+1]
例: 123 * 45, 123的第1位 2 和45的第0位 4 乘积 08 存放在结果的第[1, 2]位中
index: 0 1 2 3 4
1 2 3
* 4 5
---------
1 5
1 0
0 5
---------
0 6 1 5
1 2
0 8
0 4
---------
0 5 5 3 5
这样我们就可以单独都对每一位进行相乘计算把结果存入相应的index中
**/
int n1 = num1.length()-1;
int n2 = num2.length()-1;
if(n1 < 0 || n2 < 0) return "";
int[] mul = new int[n1+n2+2];
for(int i = n1; i >= 0; --i) {
for(int j = n2; j >= 0; --j) {
int bitmul = (num1.charAt(i)-'0') * (num2.charAt(j)-'0');
bitmul += mul[i+j+1]; // 先加低位判断是否有新的进位
mul[i+j] += bitmul / 10;
mul[i+j+1] = bitmul % 10;
}
}
StringBuilder sb = new StringBuilder();
int i = 0;
// 去掉前导0
while(i < mul.length-1 && mul[i] == 0)
i++;
for(; i < mul.length; ++i)
sb.append(mul[i]);
return sb.toString();
}
}
68.全排列
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
代码:
class Solution {
List<List<Integer>> res;
public List<List<Integer>> permute(int[] nums) {
//回溯算法,遍历所有可能的结果
res=new ArrayList<>();
int[] visited=new int[nums.length];//数组里面的元素每一个对应一个标志位:是否有使用过这个元素
Arrays.sort(nums);
getPermute(nums,new ArrayList<>(),visited);
return res;
}
public void getPermute(int[] nums,List<Integer> list,int[] visited){
//递归终止条件
if(list.size()==nums.length){
res.add(new ArrayList<>(list));
return;
}
for(int i=0;i<nums.length;i++){
if(visited[i]==1)continue;//如果这个值为1表示上一次使用过了,就不再进行使用
visited[i]=1;
list.add(nums[i]);
getPermute(nums,list,visited);
visited[i]=0;//当没有进行回溯的时候表示这个值并没有被使用,所以就将它置为0
list.remove(list.size()-1);
}
}
}
69.全排列II
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
代码:
class Solution {
List<List<Integer>> res;
List<Integer> list;
boolean[] visited;
//有重复数字的全排列:我们需要做的就是将每次排列中相同的去掉
public List<List<Integer>> permuteUnique(int[] nums) {
visited=new boolean[nums.length];
res=new ArrayList<>();
list=new ArrayList<>();
Arrays.fill(visited,true);//给数组赋值:前提是赋的值全部是一样的
Arrays.sort(nums);
int n=nums.length;
getPermuteUnique(nums,n);
return res; }
public void getPermuteUnique(int[] nums,int n){
if(list.size()==n){
res.add(new ArrayList<>(list));
return;
}
int mark=Integer.MAX_VALUE;//记录该层目录的元素
for(int i=0;i<n;i++){
if(mark==nums[i]) continue;//如果等于表示前一次
if(visited[i]){//
mark=nums[i];
list.add(nums[i]);
visited[i]=false;
getPermuteUnique(nums,n);
visited[i]=true;
list.remove(list.size()-1);
}
}
}
}
70.旋转图像
给定一个 n × n 的二维矩阵表示一个图像。
将图像顺时针旋转 90 度。
说明:
你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。
示例 1:
给定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋转输入矩阵,使其变为:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
示例 2:
给定 matrix =
[
[ 5, 1, 9,11],
[ 2, 4, 8,10],
[13, 3, 6, 7],
[15,14,12,16]
],
原地旋转输入矩阵,使其变为:
[
[15,13, 2, 5],
[14, 3, 4, 1],
[12, 6, 8, 9],
[16, 7,10,11]
]
代码:
class Solution {
public void rotate(int[][] matrix) {
//首先是对于3 x 3的进行分析得到变换的关系,然后对n x n进行递归变换
rotate(matrix,0,0,matrix[0].length-1,matrix[0].length-1);
}
public void rotate(int[][] matrix,int startX,int startY,int endX,int endY){
//递归终止条件
if(startX>=endX){
return;
}
//具体关系如下
for(int i=startX;i<endX;i++){
int step=i-startX;//表示第几层的交换
int temp=matrix[startY][i];
matrix[startY][i]=matrix[endY-step][startX];
matrix[endY-step][startX]=matrix[endY][endX-step];
matrix[endY][endX-step]=matrix[startY+step][endX];
matrix[startY+step][endX]=temp;
}
rotate(matrix,startX+1,startY+1,endX-1,endY-1);
}
}
71.字母异位词分组
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
说明:
*所有输入均为小写字母。
*不考虑答案输出的顺序。
代码:
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
//使用HashMap来实现快速的查找
List<List<String>> res=new ArrayList<>();
HashMap<String,List<String>> map=new HashMap<>();
if(strs==null||strs.length==0){
return res;
}
for(int i=0;i<strs.length;i++){
char[] cur=strs[i].toCharArray();
Arrays.sort(cur);
//将排序后的字符串作为HashMap的key
String key=String.valueOf(cur);
if(!map.containsKey(key)){//首先判断是否包含这个key,如果没有,那么直接开辟一个一维的集合存放异位词
map.put(key,new ArrayList<>());
}
//如果相等,则直接将这个词添加到该key对应的集合里面
map.get(key).add(strs[i]);
}
return new ArrayList<>(map.values());
}
}
72.Pow(x,n)
实现 pow(x, n) ,即计算 x 的 n 次幂函数。
示例 1:
输入: 2.00000, 10
输出: 1024.00000
示例 2:
输入: 2.10000, 3
输出: 9.26100
示例 3:
输入: 2.00000, -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.25
说明:
• -100.0 < x < 100.0
• n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。
分析:采用的是折半计算方法:将n进行对半的缩小,n一定会缩小到0,等到n缩小到0以后我们知道任何一个数的0次方等于1,再往回乘,如果这个数是偶数直接将上次得到的值进行平方即可得到结果,但是如果是奇数的话这个结果还需要再乘以一个x;还需要注意对于n为正负结果是不同的。
代码:
class Solution {
public double myPow(double x, int n) {
double res=1.0;
for(int i=n;i!=0;i=i/2){
if(i%2!=0){
res=res*x;
}
x=x*x;
}
return n>0?res:1/res;
}
}
73.螺旋矩阵
给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。
示例 1:
输入:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
输出: [1,2,3,6,9,8,7,4,5]
示例 2:
输入:
[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
]
输出: [1,2,3,4,8,12,11,10,9,5,6,7]
代码:
class Solution {
List<Integer> list ;
public List<Integer> spiralOrder(int[][] matrix) {
//这道题和前面的顺时针旋转矩阵的类似的
list = new ArrayList<>();
if (matrix.length == 0 || matrix[0].length == 0) {
return list;
}
int y1 = 0, y2 = matrix.length - 1;
int x1 = 0,x2 = matrix[0].length - 1;
while (y1 <= y2 && x1 <= x2) {
//先是行循环
for (int i = x1; i <= x2; i++) {
list.add(matrix[y1][i]);
}
//这个是扣边界 以下三个跟这个类似的if语句都是扣边界,让x,y不要出去这个边界。
if (y1 == y2) break;
y1 ++;
//列循环
for (int j = y1; j <= y2; j++) {
list.add(matrix[j][x2]);
}
if (x1 == x2) break;
x2--;
//然后再是行循环
for (int i = x2; i >= x1; i--) {
list.add(matrix[y2][i]);
}
if (y1 == y2) break;
y2--;
//最后列循环
for (int j = y2; j >= y1; j--) {
list.add(matrix[j][x1]);
}
if (x1 == x2) break;
x1++;
}
return list;
}
}
74.跳跃游戏
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
示例 1:
输入: [2,3,1,1,4]
输出: true
解释: 从位置 0 到 1 跳 1 步, 然后跳 3 步到达最后一个位置。
示例 2:
输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。
方法 3:自底向上的动态规划
底向上和自顶向下动态规划的区别就是消除了回溯,在实际使用中,自底向下的方法有更好的时间效率因为我们不再需要栈空间,可以节省很多缓存开销。更重要的事,这可以让之后更有优化的空间。回溯通常是通过反转动态规划的步骤来实现的。
这是由于我们每次只会向右跳动,意味着如果我们从右边开始动态规划,每次查询右边节点的信息,都是已经计算过了的,不再需要额外的递归开销,因为我们每次在 memo 表中都可以找到结果。
Java
enum Index {
GOOD, BAD, UNKNOWN
}
public class Solution {
public boolean canJump(int[] nums) {
Index[] memo = new Index[nums.length];
for (int i = 0; i < memo.length; i++) {
memo[i] = Index.UNKNOWN;
}
memo[memo.length - 1] = Index.GOOD;
for (int i = nums.length - 2; i >= 0; i--) {
int furthestJump = Math.min(i + nums[i], nums.length - 1);
for (int j = i + 1; j <= furthestJump; j++) {
if (memo[j] == Index.GOOD) {
memo[i] = Index.GOOD;
break;
}
}
}
return memo[0] == Index.GOOD;
}
}
复杂度分析
时间复杂度:O(n^2)O(n 2 ),数组中的每个元素,假设为 i,需要搜索右边相邻的 nums[i] 个元素查找是否有 GOOD 的坐标。 nums[i] 最多为 n,n 是 nums 数组的大小。
空间复杂度:O(n)O(n),记忆表的存储开销。
方法 4:贪心
当我们把代码改成自底向上的模式,我们会有一个重要的发现,从某个位置出发,我们只需要找到第一个标记为 GOOD 的坐标(由跳出循环的条件可得),也就是说找到最左边的那个坐标。如果我们用一个单独的变量来记录最左边的 GOOD 位置,我们就可以避免搜索整个数组,进而可以省略整个 memo 数组。
从右向左迭代,对于每个节点我们检查是否存在一步跳跃可以到达 GOOD 的位置(currPosition + nums[currPosition] >= leftmostGoodIndex)。如果可以到达,当前位置也标记为 GOOD ,同时,这个位置将成为新的最左边的 GOOD 位置,一直重复到数组的开头,如果第一个坐标标记为 GOOD 意味着可以从第一个位置跳到最后的位置。
模拟一下这个操作,对于输入数组 nums = [9, 4, 2, 1, 0, 2, 0],我们用 G 表示 GOOD,用 B 表示 BAD 和 U 表示 UNKNOWN。我们需要考虑所有从 0 出发的情况并判断坐标 0 是否是好坐标。由于坐标 1 是 GOOD,我们可以从 0 跳到 1 并且 1 最终可以跳到坐标 6,所以尽管 nums[0] 可以直接跳到最后的位置,我们只需要一种方案就可以知道结果。
Index 0 1 2 3 4 5 6
nums 9 4 2 1 0 2 0
memo U G B B B G G
Java
public class Solution {
public boolean canJump(int[] nums) {
int lastPos = nums.length - 1;
for (int i = nums.length - 1; i >= 0; i--) {
if (i + nums[i] >= lastPos) {
lastPos = i;
}
}
return lastPos == 0;
}
}
复杂度分析
时间复杂度:O(n)O(n),只需要访问 nums 数组一遍,共 nn 个位置,nn 是 nums 数组的长度。
空间复杂度:O(1)O(1),不需要额外的空间开销。
75.合并区间
给出一个区间的集合,请合并所有重叠的区间。
示例 1:
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入: [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
代码:
class Solution {
public int[][] merge(int[][] intervals) {
if(intervals == null || intervals.length < 2)
return intervals;
Arrays.sort(intervals, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0];
}
});
ArrayList<int[]> res = new ArrayList<>();
int len = intervals.length;
for(int i = 0 ; i < len ; i ++) {
int[] temp = new int[2];
temp[0] = intervals[i][0];
temp[1] = intervals[i][1];
while(i < len - 1 &&temp[1] >= intervals[i + 1][0]) {//寻找本区间的可合并区间,有的话就合并
temp[1] = Math.max(temp[1],intervals[i + 1][1]);
i++;//标记到下一个待确认区间
}
res.add(temp);
}
int[][] arr = new int[res.size()][2];
res.toArray(arr);//转化为数组
return arr;
}
}
【LeetCode】Java实现
最新推荐文章于 2022-12-29 23:52:08 发布