热题 HOT 100(1-10)

环形链表

1.给定一个链表,判断链表中是否有环。

将快指针的移动速度设置为慢指针的两倍,将快慢指针同时遍历链表,若此链表存在环的时候,遍历过程中必然存在快慢指针指向同一个元素的时候。

且此时快慢指针相遇点到入环点的距离,等于头结点到入环点的距离
设相遇时slow走的路程为S1,fast走的路程为S2,设相遇点为p;起点为s,入环点为e;
有S2=2*S1。 S1= sp, S2=sp+pp(fast绕了一圈)
所以有sp=pp,sp=se+ep,pp = pe+ep,所以se = pe这个等式很关键

/**
 * 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) {
        if(head==null||head.next==null){
            return false;
        }
        ListNode slow=head,fast=slow;
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
            if(slow==fast){
                return true;
            }
        }
        return false;
    }
}

2.给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

用到java中内置contains方法和startsWith() 方法
作用:是判断是否存在包含关系的,
比如说集合a =[1,2,3,4],b=1,那么a就包含b;
contains返回的是布尔类型true 和false,包含的话就返回true,不包含的话就返回false
startsWith() 方法用于检测字符串是否以指定的前缀开
public boolean startsWith(String prefix, int toffset)
prefix -- 前缀。
toffset -- 字符串中开始查找的位置。

BFS剪枝方法

package leetcode;

import java.util.*;

public class test {
        String str;
        static Set cache = new HashSet();;
        List list;
        public static void main(String[] args){
         List name = new ArrayList();
         test aa = new test();
         name.add("leet");
         name.add("coe");
         name.add("code");
         name.add("lee");
         name.add("tecode");
        System.out.println(aa.wordBreak("leetecodecoe",name));
        Iterator it = cache.iterator();  
        while (it.hasNext()) {  
        Integer str = it.next();  
          System.out.println(str);  
        }  
    }
     boolean wordBreak(String s, List wordDict) {
        str = s;
        cache=new HashSet();
        list=wordDict;
        return wordBreak(0);
    }
    boolean wordBreak(int d){
        System.out.println("d的值为:"+d);
        if(d == str.length())return true;
        if(cache.contains(d))return false;//无需再次重复计算,因为以这个点开头已经判断不能了,剪枝
        for(String word : list){
            if(str.startsWith(word,d)){//从d位置开始查找,是否以指定的word开头
                if(wordBreak(d+word.length())){
                    return true;
                }
                cache.add(d+word.length());//已经为false的 就是从这个点开始找 不存在以这个点开头且满足的单词 不满足的点
            }
        }
        return false;
    }
     List name = new ArrayList();
}
/*
d的值为:0
d的值为:4
d的值为:3
d的值为:9
d的值为:12
true
4
*/

3.给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

异或,不是对十进制异或,而是与之对应的二进制异或得出结果,如0000 ^ 0001,异或规则是:不同则为1,相同则为0,可以发现,0 ^ 任何数就等于该数本身,如果数组为{1,2,2},如1 ^ 2,就等于0001 ^ 0010,异或之后则为0011,如果再 ^ 2,就是遇见了第二个2,则为0011^0010,可得0001,就是剩下的数1,简单来说从0开始时,异或一个数相当于加上这个数,再异或这个数时,相当于减掉这个数,最后剩下的就是唯一存在的数了

学习java8的Stream(流)https://www.jianshu.com/p/314e284652d2
class Solution {
    public int singleNumber(int[] nums) {
         return Arrays.stream(nums).reduce((x, y) -> x ^ y).getAsInt();
    }
}

题目变形
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

将每个数想象成32位的二进制,对于每一位的二进制的1和0累加起来必然是3N或者3N+1, 为3N代表目标值在这一位没贡献,3N+1代表目标值在这一位有贡献(改位等于=1),然后将所有有贡献的位|(或运算)起来就是结果。这样做的好处是如果题目改成K个一样,只需要把代码改成cnt%k,很通用

class Solution {
    public int singleNumber(int[] nums) {
        int sum = 0;
        for(int i = 0;i < 32 ;i++){
            int mask = 1 << i;
            int cnt = 0;
            for(int j=0;j

再变形题目
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

看注释

class Solution {
    public int[] singleNumber(int[] nums) {
        //将所有元素异或,得到的结果为出现一次的A^B
        //其他出现两次的数在异或中抵消为0了
        int xorResult = 0;
        for(int i :nums){
            xorResult ^=i;
        }
        //从后往前找出第一位为1的位置,那就是他俩的区别了
        //tempRes为A^B的结果,通过右移diffNum的方式和它进行与操作,得到tempRes第一位为1的位置
        int diffNum = 1;
        while ((xorResult & diffNum) == 0) {
            diffNum <<= 1;
        }
        //根据元素种类进行分类
        //通过最低位为1的那个位进行划分,如
//            01 1 1 ^
//            00 0 1 
 //           01 1 0     
        //其中结果中最低位的1和diffNum 进行 &操作可以区分这两个数,其余数都属偶数个不用管,与运算为1表示这位数在这个位置是1,与另外一个数是有区别的(=0) 然后用异或还原成原来的数 因为偶数个异或为0 ,且和0异或该数不变
        int resultA = 0, resultB = 0;
        for(int j : nums){
            if((diffNum & j) == 0){
                resultA ^= j;
            }else{
                resultB ^= j;
            }
        }
        return new int[]{resultA,resultB};
    }
}

4.给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

哈希映射
这道题本身如果通过暴力遍历的话也是很容易解决的,时间复杂度在 O(n2)
由于哈希查找的时间复杂度为 O(1),所以可以利用哈希容器 map 降低时间复杂度
1.遍历数组 nums,i 为当前下标,每个值都判断map中是否存在 target-nums[i]的 key 值
2.如果存在则找到了两个值,如果不存在则将当前的(nums[i],i)存入 map 中,继续遍历直到找到为止
3.如果最终都没有结果则抛出异常
时间复杂度:O(n)
注意,该目标元素不能是 nums[i]nums[i] 本身!

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map map = new HashMap<>();
        for(int i = 0; i< nums.length; i++) {
            if(map.containsKey(target - nums[i])) {
                return new int[] {map.get(target-nums[i]),i};
            }
            map.put(nums[i], i);
        }
        throw new IllegalArgumentException("No two sum solution");
    }
}
map的put方法的时间复杂度为O(1),get方法的时间复杂度为O(1)~O(n)。
那么containKey()方法的时间复杂度呢,其实和get方法的时间复杂度是一样的,也是O(1)~O(n)

5.给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 1位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

思路

  • 将两个链表看成是相同长度的进行遍历,如果一个链表较短则在前面补 0,比如 987 + 23 = 987 + 023 = 1010
  • 每一位计算的同时需要考虑上一位的进位问题,而当前位计算结束后同样需要更新进位值
  • 如果两个链表全部遍历完毕后,进位值为 1,则在新链表最前方添加节点 1
  • 小技巧:对于链表问题,返回结果为头结点时,通常需要先初始化一个预先指针 pre,该指针的下一个节点指向真正的头结点head。使用预先指针的目的在于链表初始化时无可用节点值,而且链表构造过程需要指针移动,进而会导致头指针丢失,无法返回结果。
/**
 * 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 pre = new ListNode(0);
        ListNode cur = pre;
        //进位值
        int carry = 0;
        while(l1 != null || l2 != null) {
            int x = l1 == null ? 0 : l1.val;//用0补全
            int y = l2 == null ? 0 : l2.val;
            int sum = x + y + carry;
            
            carry = sum / 10;
            sum = sum % 10;
            cur.next = new ListNode(sum);

            cur = cur.next;
            if(l1 != null)
                l1 = l1.next;
            if(l2 != null)
                l2 = l2.next;
        }
        if(carry == 1) {
            cur.next = new ListNode(carry);
        }
        return pre.next;//真正的头节点为pre.next
    }
}

6.给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

这道题主要用到思路是
滑动窗口.什么是滑动窗口?其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为abc满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!如何移动?我们只要把队列的左边的元素移出就行了,直到满足题目要求!
一直维持这样的队列,找出队列出现最长的长度时候,就是解!
时间复杂度:O(n)

我们将字符存储在当前窗口 [i, j)(最初 j = i)中。 然后我们向右侧滑动索引 j,如果它不在窗口中,我们会继续滑动 j。直到 s[j] 已经存在于 窗口中。此时,我们找到的没有重复字符的最长子字符串将会以索引 i 开头。如果我们对所有的 i 这样做,就可以得到答案。

package leetcode;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Solution {
    
    public static int lengthOfLongestSubstring(String s) {
        int n = s.length(), ans = 0;
        Map map = new HashMap<>(); // 当前字符索引
        // 尝试扩大范围[i,j]
        for (int j = 0, i = 0; j < n; j++) {
            if (map.containsKey(s.charAt(j))) {//判断是否存在某个键
                // s[j] 在 [i, j) 范围内有与 j ′重复的字符,我们不需要逐渐增加 i 。 我们可以直接跳过 [i,j']范围内的所有元素,并将 i 变为 j' + 1。
                i = Math.max(map.get(s.charAt(j)), i);//得到这个键的值与i比较
            }
            ans = Math.max(ans, j - i + 1);
            map.put(s.charAt(j), j + 1);//存入键值 字符和它的位置加1 因为字符位置从0开始
        }
        return ans;
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String str = scanner.next();
        System.out.println(lengthOfLongestSubstring(str));
    }
}

7.给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。

中位数被用来:将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。
思路查看

package leetcode;
public class Solution {
    public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n=nums1.length;
        int m=nums2.length;
        if(n>m) {
            return findMedianSortedArrays(nums2,nums1);
        }
        int Rmin1 = 0,Lmax1 = 0,Rmin2 = 0,Lmax2 = 0,c1,c2,l=0,r=2*n;
        //r=2*n,数组1是的长度2n+1,但是二分法是二分下标,r表示最大下标不是长度,hi=2n+1-1 =2n.
        while(l<=r) {//二分
            c1=(l+r)/2;
            c2=(n+m)-c1;
            Lmax1=(c1==0)?Integer.MIN_VALUE:nums1[(c1-1)/2];
            Rmin1=(c1==2*n)?Integer.MAX_VALUE:nums1[c1/2];
            Lmax2=(c2==0)?Integer.MIN_VALUE:nums2[(c2-1)/2];
            Rmin2=(c2==2*m)?Integer.MAX_VALUE:nums2[c2/2];
            if(Lmax1>Rmin2) {
                r=c1-1;
            }else if(Lmax2>Rmin1) {
                l=c1+1;
            }else {
                break;
            }
        }
        return (Math.min(Rmin1, Rmin2)+Math.max(Lmax1,Lmax2))/2.0;
    }
    public static void main(String[] args) {
        int[] a1= {2,3};
        int[] a2= {4,7};
        System.out.println(findMedianSortedArrays(a1, a2));
    }
}

8.给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

在 O(n^2)的时间内解决这个问题。
我们观察到回文中心的两侧互为镜像。因此,回文可以从它的中心展开,并且只有2n - 1个这样的中心。
你可能会问,为什么会是 2n - 1个,而不是 n 个中心?原因在于所含字母数为偶数的回文的中心可以处于两字母之间(例如 abba 的中心在两个 b 之间)。

package leetcode;
public class Solution {
    public static String longestPalindrome(String s) {
        if(s==null||s.length()==0) {
            return "";
        }
        int start = 0,end=0;
        for(int i=0,l=s.length();iend-start) {//这个就举例一下abasubbaabb
                end=i+len/2;
                start=i-(len-1)/2;
            }
        }
        return s.substring(start, end+1);
    }
    public static int expandAroundCenter(String s, int left, int right) {
        int L=left,R=right;
        while(L>=0&&R

9.垂直的两条线段将会与坐标轴构成一个矩形区域,较短线段的长度将会作为矩形区域的宽度,两线间距将会作为矩形区域的长度,而我们必须最大化该矩形区域的面积。

图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49(7*7)。

双指针法,头尾俩个指针,每次向内移动长度较短的,来达到获取最大面积

public class Solution {
    public int maxArea(int[] height) {
        int maxarea = 0, l = 0, r = height.length - 1;
        while (l < r) {
            maxarea = Math.max(maxarea, Math.min(height[l], height[r]) * (r - l));
            if (height[l] < height[r])
                l++;
            else
                r--;
        }
        return maxarea;
    }
}

10.给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。注意:答案中不可以包含重复的三元组。

排序+双指针
1.首先对数组进行排序,排序后固定一个数 nums[i],再使用左右指针指向 nums[i]后面部分的两端,数字分别为 nums[L]nums[R]],计算三个数的和 sum 判断是否满足为 0,满足则添加进结果集
2.如果 nums[i]大于 00,则三数之和必然无法等于 0,结束循环
3.如果 nums[i] == nums[i-1],则说明该数字重复,会导致结果重复,所以应该跳过
4.当 sum == 0 时,nums[L] == nums[L+1]则会导致结果重复,应该跳过,L++
5.当 sum == 0 时,nums[R] == nums[R−1] 则会导致结果重复,应该跳过,R--
时间复杂度:O(n^2),n 为数组长度

package leetcode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Solution {
    public static List> threeSum(int[] nums)  {
        List> result = new ArrayList>();
        if(nums==null||nums.length<3) {
            return result;
        }
        Arrays.sort(nums);//排序
        for(int i=0,l=nums.length;i0)break;
            if(i!=0) {
                if(nums[i]==nums[i-1])continue;
            }
            int L=i+1,R=l-1;
            while(L

文章参考
https://leetcode-cn.com/problemset/hot-100/

你可能感兴趣的:(热题 HOT 100(1-10))