力扣242、349、202、1、24、19、160、142-java刷题笔记

  一、242. 有效的字母异位词

 1.1题目

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

示例 1:

输入: s = "anagram", t = "nagaram"
输出: true

示例 2:

输入: s = "rat", t = "car"
输出: false

1.2思路分析

方法1:传统方法

将字符串转换为字符数组,然后分别对两个数组进行排序,然后在调用equals方法进行比较;

方法2:哈希表

因为题目的需求是找"是否存在"的问题,因此可以考虑使用哈希表,对两个字符串中的字符作为数组的下表,其出现的频次作为数组的值,然后根据出现的频次就可以判断两个字符串是否相等。

1.3代码实现

 方法1暴力求解

class Solution {
    public boolean isAnagram(String s, String t) {
        // 注意字符串数组在获取数组长度时调用length()
        int length1 = s.length();
        int length2 = t.length();
        if(length1 != length2){
            return false;
        }
        // 调用toCharArray()方法将字符串转换为字符数组
        char[] s1 = s.toCharArray();
        char[] s2 = t.toCharArray();
        // 调用Array.sort()方法对数组进行排序
        Arrays.sort(s1);
        Arrays.sort(s2);
        // 使用Arrays.equals()方法对数组中的值进行比较
        return  Arrays.equals(s1,s2);
    }
}


方法2哈希表

class Solution {
    public boolean isAnagram(String s, String t) {
        // 使用哈希表对字符串中的每个字符出现的频次进行统计
        // 定义一个新数组
        if (s.length() != t.length()) {
            return false;
        }
        // 因为result中每个值存的是对应的字符出现的频次因此,其长度定义为26
        int[] result = new int[26];
        // 对字符串中的字符出现的频次进行统计
        for (int i = 0; i < s.length(); i++) {
            result[s.charAt(i) - 'a']++;
        }
        for (int i = 0; i < s.length(); i++) {
            result[t.charAt(i) - 'a']--;
        }
        for (int i = 0; i < 26; i++) {
            if (result[i] != 0) {
                return false;
            }
        }
        return true;
    }
}

二、349. 两个数组的交集

 2.1题目

给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。

示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的

 2.2思路分析

该题也可以转化为是否存在问题,即当两个数组中存在相同值时,此时则可以理解为两个数组存在交集;但是输出中要求不能出现冗余,因此考虑使用HashSet。

2.3代码实现

首先判断这两个数组中是否有值,若没有值,直接返回一个空数组,然后定义两个集合,一个用于存放待判断的数组,另一个存放最后的结果,最后再将集合转化为数组返回。

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        // 判断nums1与nums2中是否有值
        if(nums1.length == 0 ||nums2.length== 0 || nums2 ==null ||nums1 == null){
            return new int[0];
        }
        // 因为数组中有重复的值,因此我们使用 HashSet对数组去重
       HashSet  set1 =  new HashSet<>();
    //    定义一个结果hashset
       HashSet result = new HashSet<>();
       for(int i:nums1){
        //    将nums1中的数值添加到set1中并去重
           set1.add(i);
       }
       for(int i:nums2){
           if(set1.contains(i)){
               result.add(i);
           }
       }
    //    int [] arr = new int[result.]
    return result.stream().mapToInt(x->x).toArray();
    }
}

三、202. 快乐数

3.1题目

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

    如果 n 是 快乐数 就返回 true ;不是,则返回 false 。

    示例 1:

    输入:n = 19
    输出:true
    解释:
    12 + 92 = 82
    82 + 22 = 68
    62 + 82 = 100
    12 + 02 + 02 = 1
    

    示例 2:

    输入:n = 2
    输出:false

    3.2思路分析

    该题的难点在于如何判断是否进入死循环,那么什么时候迭代过程会进入死循环呢,即当当前的数值在以往的迭代过程中出现过,因此该题同样可以考虑使用HashSet。

    3.3代码实现

    首先定义一个求和的函数;然后再根据条件迭代即可,注意:(n % 10)*(n % 10)取模的平方时需要对其带括号。

    class Solution {
        public boolean isHappy(int n) {
            // 先判断该数是不是1,若是1则直接返回
            if(n == 1){
                return true;
            }
            // 需要判断该数是否为无限循环即存在过问题,因此可以定义一个哈希表
            // 返回值是无须的因此可以使用unordered_set set(c++) 或直接使用set
             HashSet set1 =new HashSet <>();
            //循环的条件为 判断值是否存在于hashset中且 n !=1
            while(n != 1 && !set1.contains(n)){
                set1.add(n);
                n = addsum(n);
            }
            return n == 1;
        }
        // 对n的各个位置进行平方求和
        public int addsum(int n){
            int temp = 0;
            while(n > 0){
                // int res = n % 10;
                temp += (n % 10)*(n % 10);
                n = n/10;
            }
        return temp;
        }
    }

       四、1. 两数之和

     4.1题目

    给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

    你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

    你可以按任意顺序返回答案。

    示例 1:

    输入:nums = [2,7,11,15], target = 9
    输出:[0,1]
    解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
    

    示例 2:

    输入:nums = [3,2,4], target = 6
    输出:[1,2]
    

    示例 3:

    输入:nums = [3,3], target = 6
    输出:[0,1]

    4.2思路分析

    方法1:暴力求解

    使用两次for循环来遍历求解

    方法2:哈希表

    我们可以将题目转化为寻找target-nums[1]的值是否在nums中存在的问题,又因为题目最后的返回值需要满足条件的下标,因此可以考虑使用HashMap,我们将数组中的值作为HashMap中的key,值的下标作为HashMap中的value。

    4.3代码实现

     方法1暴力求解

    class Solution {
        public int[] twoSum(int[] nums, int target) {
            int temp = 0;
            // int [] k = new int[2] ;
            for(int i = 0; i 


    方法2哈希表

    class Solution {
        public int[] twoSum(int[] nums, int target) {
            // 因为hashmap是键值对所以其泛型的返回值由两个,并且俩个的返回类型都是Integer
          HashMap HashMap =  new HashMap<>();
        // 定义一个新数组用于存放返回的结果、
        int [] result = new int[2];
        //   该题属于值是否存在问题即判断目标值与数组值之差是否在数组中,若在将该值添加到
        // HashMap中,其中值的大小为key,其下表为vale
        // 定义一个变量
          for(int i = 0 ; i < nums.length; i++){
              if(HashMap.containsKey((target - nums[i]))){
                  result[0] = i;
                //   使用get方法获得 key对应的value
                  result[1] = HashMap.get((target - nums[i]));
                //   如果找到了就退出
                  break;
              }
            //   如果没有找到就将该数使用put将其放置到HashMap中
            // nums[i]表示key,i表示其具体的值
            HashMap.put(nums[i],i);
          }
          return result;
        }
    }

    五、24. 两两交换链表中的节点

     5.1题目

    给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

    示例 1:

    输入:head = [1,2,3,4]
    输出:[2,1,4,3]
    

    示例 2:

    输入:head = []
    输出:[]
    

    示例 3:

    输入:head = [1]
    输出:[1]

     5.2思路分析

    定义变量对链表中的节点进行交换,该题的难点在于理清楚链表的下一个节点的具体指向。

    交换后:

    5.3代码实现

    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode() {}
     *     ListNode(int val) { this.val = val; }
     *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
     * }
     */
    class Solution {
        public ListNode swapPairs(ListNode head) {
            ListNode dummpy = new ListNode (-1, head);
            ListNode cur = dummpy;
            // 定义一个中间节点用于链表交换
            ListNode temp1;
            ListNode temp2;
            ListNode temp3;//用于存放交换完后连接的下一个节点
            while(cur.next != null && cur.next.next != null){
                temp1 = cur.next;
                temp2 = cur.next.next;
                temp3 = cur.next.next.next;
                cur.next = temp2;
                temp2.next = temp1;
                temp1.next = temp3;
                cur = cur.next.next;
            }
            return dummpy.next;
        }
    }

    六、19. 删除链表的倒数第 N 个结点

    6.1题目

    给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

    示例 1:

    输入:head = [1,2,3,4,5], n = 2
    输出:[1,2,3,5]
    

    示例 2:

    输入:head = [1], n = 1
    输出:[]
    

    示例 3:

    输入:head = [1,2], n = 1
    输出:[1]
    

    6.2思路分析

    定义快慢指针,让快指针先走n+1步,慢指针再走,这样就可以保证慢指针最后停留到待删除指针的前一个指针。

    6.3代码实现

    可以定义虚拟头指针来简化判断过程

    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode() {}
     *     ListNode(int val) { this.val = val; }
     *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
     * }
     */
    class Solution {
        public ListNode removeNthFromEnd(ListNode head, int n) {
            ListNode dummpy  = new ListNode(-1,head);
            // 定义虚拟头节点来处理删除头节点的情况
            // 定义快慢指针求解
            ListNode fast = dummpy;
            ListNode slow = dummpy;
    // 快指针走n+1步
            for(int i = 0; i<=n ; i++){
             fast = fast.next;
            }
            // 当快指针不为空
            while(fast != null){
            fast = fast.next;
            slow = slow.next;
            }
            slow.next = slow.next.next;
            return dummpy.next;
        }
    }

    七、160. 相交链表

     7.1题目

    给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

    图示两个链表在节点 c1 开始相交:

    题目数据 保证 整个链式结构中不存在环。

    注意,函数返回结果后,链表必须 保持其原始结构 。

    自定义评测:

    评测系统 的输入如下(你设计的程序 不适用 此输入):

    • intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
    • listA - 第一个链表
    • listB - 第二个链表
    • skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
    • skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数

      评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。

      示例 1:

      输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
      输出:Intersected at '8'
      解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
      从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
      在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
      — 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。
      

       

      示例 2:

      输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
      输出:Intersected at '2'
      解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
      从各自的表头开始算起,链表 A 为 [1,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 。

      7.2思路分析

      该题需注意当两个链表相交时需满足两个指针完全相同,而不是值相同。

      7.3代码实现

      首先求出两个链表各自的长度,然后求出两个链表长度的差值,并将较长链表的指针移动到与较短链表的指针末尾对其位置。

      /**
       * 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) {
              // 第一步求链表A和链表B的长度
              ListNode cur1 = headA;
              ListNode cur2 = headB;
              int lenA = 0;
              int LenB = 0;
              int gap = 0;
              while(cur1 != null){
                  cur1 = cur1.next;
                  lenA ++;
              }
              while(cur2 !=null){
                  cur2 = cur2.next;
                  LenB++;
              }
              cur1 = headA;
              cur2 = headB;
              ListNode temp;
              int tempLen = 0;
              gap = lenA > LenB ? (lenA-LenB):(LenB-lenA);
              // int tempgap = gap;
              // 确保A为较长链表
              if(LenB > lenA){
                  temp = cur2;
                  cur2 = cur1;
                  cur1 = temp;
                  tempLen = LenB;
                  LenB = lenA;
                  lenA = tempLen;
              } 
              while(gap>0){
                  cur1 = cur1.next;
                  gap--;
              }
              while(cur1!=null && cur2 != null){
                  if(cur1 == cur2){
                      return cur1;
                  }
                  cur1 = cur1.next;
                  cur2 = cur2.next;
              }
            return null;
          }
      }

      八、142. 环形链表 II

       8.1题目

      给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

      如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

      不允许修改 链表。

      示例 1:

      输入:head = [3,2,0,-4], pos = 1
      输出:返回索引为 1 的链表节点
      解释:链表中有一个环,其尾部连接到第二个节点。
      

      示例 2:

      输入:head = [1,2], pos = 0
      输出:返回索引为 0 的链表节点
      解释:链表中有一个环,其尾部连接到第一个节点。
      

      示例 3:

      输入:head = [1], pos = -1
      输出:返回 null
      解释:链表中没有环。

       8.2思路分析

      第一步:判断链表中是否有环

      定义快慢指针,让快指针以比慢指针快2倍的速度移动,若快指针可以和慢指针相遇则证明该链表有环。

      第二步:找到初始相遇的节点。

      假设头节点到初始相遇节点的长度为x,环入口节点到相遇节点的长度为y,相遇节点到换入口的长度为z。

      则我们根据快慢指针的移动速度可以列入下表达式:2(x+y)=n(y+z)+x+y

      进一步化简得x=(n-1)(y+z)+z;

      (n-1)(y+z),我们取个极限情况,即n=1时,此时x=z

      因此我们可以进一步定义两个指针,一个指针由头指针开始向后运动,另一个指针由相遇位置开始移动,当两个新定义得指针再次相遇时,此时得相遇点则为快慢指针得初始相遇点。

      8.3代码实现

      /**
       * Definition for singly-linked list.
       * class ListNode {
       *     int val;
       *     ListNode next;
       *     ListNode(int x) {
       *         val = x;
       *         next = null;
       *     }
       * }
       */
      public class Solution {
          public ListNode detectCycle(ListNode head) {
              // 使用快慢指针,快指针以2倍速的方式取去追赶慢指针,当快指针等于慢指针时
              // 说明此时有环
              ListNode fast = head;
              ListNode slow = head;
              // 因为快指针一次走两步因此需要判断fast.next是否为空,若不为空则继续循环
              while(fast != null && fast.next != null){
                  slow = slow.next;
                  fast = fast.next.next;
                  if(slow == fast){
                    ListNode index1 = head;
                    ListNode index2 = fast;
                    while(index1 != index2){
                        index1 = index1.next;
                        index2 = index2.next;
                    }
                    return index1;
                }
              }
             return null; 
          }
      }