posted in leetcode  数组  双指针 

问题

给定两个数组,编写一个函数来计算它们的交集。
示例 1:

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

示例 2:

输入: nums1 = \([4,9,5]\), nums2 = \([9,4,9,8,4]\)
输出: [4,9]

说明:

  • 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
  • 我们可以不考虑输出结果的顺序。

进阶:

  • 如果给定的数组已经排好序呢?你将如何优化你的算法?
  • 如果 nums1 的大小比 nums2 小很多,哪种方法更优?
  • 如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?

解法

问题比较简单。可以遍历两个数组,获得数字出现的频次,然后对比两个频次即可。或者可以直接排序两个数组,然后使用两个指针分别遍历数组即可。

解法

频次法

class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        if(nums1.length > nums2.length) {
            return intersect(nums2, nums1);
        }
        //这里使用hashmap,因为数字是不定长的。
        Map<Integer, Integer> map1 = new HashMap<>(nums1.length);
        for (Integer i: nums1) {
            Integer count = map1.get(i);
            if (count == null) {
                map1.put(i, 1);
            } else {
                map1.put(i, count+1);
            }
        }
        Map<Integer, Integer> map2 = new HashMap<>(nums2.length);
        for (Integer i: nums2) {
            Integer count = map2.get(i);
            if (count == null) {
                map2.put(i, 1);
            } else {
                map2.put(i, count+1);
            }
        }
        Iterator iter = map1.entrySet().iterator(); 
        List<Integer> result = new LinkedList();
        //遍历两个频次map即可,这里使用小的那个map遍历。可以减少查询次数
        while(iter.hasNext()) {
            Map.Entry<Integer, Integer> entry = (Map.Entry) iter.next(); 
            Integer key = entry.getKey(); 
            Integer val1 = entry.getValue(); 
            Integer val2 = map2.get(key);
            if(val2 != null) {
                int count = Math.min(val1, val2);
                while (count-- > 0) {
                    result.add(key);
                }
            }
        }
        
        //最终生成结果,因为没有用从List到int[]的方便转换方法。
        int[] res = new int[result.size()];
        for(int m=0;m<res.length;m++)
        {
            res[m]=result.get(m);
        }
        return res;
    }
}

排序遍历法

class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        if(nums1.length > nums2.length) {
            return intersect(nums2, nums1);
        }
        List<Integer> result = new LinkedList();
        
        //排序两个数组
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        //遍历两个数组
        for (int i = 0,j=0; i<nums1.length && j<nums2.length;) {
            //相等则加入result
            if (nums1[i] == nums2[j]) {
                result.add(nums1[i]);
                i++;
                j++;
            } else if (nums1[i] < nums2[j]){
                // 这里注意只有其中一个往前推进。
                i++;
            } else {
                j++;
            }
        }
        
        int[] res = new int[result.size()];
        for(int m=0;m<res.length;m++)
        {
            res[m]=result.get(m);
        }
        return res;
    }
}
posted in leetcode  链表  双指针 

问题

编写一个程序,找到两个单链表相交的起始节点。
如下面的两个链表:

在节点\( 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。

注意:

  1. 如果两个链表没有交点,返回 null.
  2. 在返回结果后,两个链表仍须保持原有的结构。
  3. 可假定整个链表结构中没有循环。
  4. 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

解法

注意到这样一个事实,如果两个链表相交,那么在相交节点前面的两个分叉上,正好相差这两个链表长度之差的元素个数。因此,首先遍历两个链表,得到链表长度,然后长度更长的链表先往前走长度之差的步数,然后后面两个链表向前推进到相等即为结果。

代码

java

/**
 * 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 l1 = 0;
        ListNode h1 = headA;
        while (h1 != null) {
            h1 = h1.next;
            l1++;
        }
        int l2 = 0;
        ListNode h2 = headB;
        while (h2 != null) {
            h2 = h2.next;
            l2++;
        }
        
        //长度更长的那个需要先走几步,直到两个长度相等时才同时前进。
        h1 = headA;
        h2 = headB;
        int dif = l1-l2;
        if (dif > 0) {
            while(dif-- >0) {
                h1 = h1.next;
            }
        } else if (dif < 0) {
            while(dif++ < 0) {
                h2 = h2.next;
            }
        }
        int len = Math.min(l1, l2);
        while (h1 != h2){
            h1 = h1.next;
            h2 = h2.next;
        }
        return h1;
    }
}