LeetCode:LinkedList,Two Pointers,Binary Search

12.Linked List

1.Merge Two Sorted Lists

Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode current,pre,start;
        pre = new ListNode(Integer.MIN_VALUE);
        start = pre;
        while(l1!=null&&l2!=null){
            if(l1.val<l2.val){
                current = new ListNode(l1.val);
                pre.next = current;
                pre = current;
                l1 = l1.next;
            }else{
                current = new ListNode(l2.val);
                pre.next = current;
                pre = current;
                l2 = l2.next;
            }
        }
        while(l1!=null){
            current = new ListNode(l1.val);
            pre.next = current;
            pre = current;
            l1 = l1.next;
        }
        while(l2!=null){
            current = new ListNode(l2.val);
            pre.next = current;
            pre = current;
            l2 = l2.next;
        }
        if(start.next!=null){
            return start.next;
        }else{
            return null;
        }
    }
}

2.Reverse Linked List

Reverse a singly linked list.

Hint:
A linked list can be reversed either iteratively or recursively. Could you implement both?

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */

//success 1
//iterative method
// public class Solution {
//     public ListNode reverseList(ListNode head) {
//         if(head==null){
//             return null;
//         }
//         Stack<Integer> stack = new Stack<>();
//         while(head!=null){
//             stack.add(head.val);
//             head = head.next;
//         }
//         ListNode current,pre,start;
//         pre = new ListNode(Integer.MIN_VALUE);
//         start = pre;
//         while(!stack.isEmpty()){
//             int value = stack.pop();
//             current = new ListNode(value);
//             pre.next = current;
//             pre = current;
//         }
//         if(start.next!=null){
//             return start.next;
//         }else{
//             return null;
//         }
//     }
// }

//success 2
//recursive method
//这里要注意,我起初是定义了一个ListNode newStart来存储起始node,但是不起作用,主要原因是退出调用栈时,如果newStart没有被任何对象引用
//就会在退出时销毁,这样newStart就又回到了Integer.MIN_VALUE值。可以试试将start定义为成员变量,然后在该赋值时赋值为成员变量start,这样程序
//能够正确运行。对比于list.add,因为head添加到了list中,所以不会在推出栈时被销毁。这样也就可以保存最后head信息。
public class Solution {
    //可以试试
    // private ListNode start1;
    public ListNode reverseList(ListNode head) {
        if(head==null){
            return null;
        }
//        ListNode start=new ListNode(Integer.MIN_VALUE);
        List<ListNode> start = new ArrayList<>();
        recursive(head,start);
        return start.get(0);
    }
    ListNode recursive(ListNode head,List newStart){
        if(head.next==null){
//            newStart = head;
            newStart.add(head);
            return head;
        }
        ListNode current = new ListNode(head.val);
        recursive(head.next,newStart).next=current;
        return current;
    }
}

3.Insertion Sort List

Sort a linked list using insertion sort.

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ListNode insertionSortList(ListNode head) {
        //原链表前加头
        ListNode newHead = new ListNode(Integer.MIN_VALUE);
        newHead.next = head;
        ListNode start;
        start = newHead;
        if(head==null){
            return null;
        }
        if(head.next==null){
            return head;
        }
        while(newHead!=null){
            ListNode compare,pre,current,minPre=null,minCur=null,compareNext,minNext;
            compare = newHead;
            int minimum = Integer.MAX_VALUE;
            current = newHead.next;
            pre = newHead;
            //处理边界情况
            if(current==null){
                break;
            }
            while(current!=null){
                if(current.val<=minimum){
                    minimum = current.val;
                    minCur = current;
                    minPre = pre;
                }
                pre = current;
                current = current.next;
            }
            compareNext = compare.next;
            minNext = minCur.next;
            minPre.next = minNext;
            compare.next = minCur;
            //处理边界情况,防止发生链表循环
            if(minCur!=compareNext){
                minCur.next = compareNext;
            }
            newHead = newHead.next;
        }
        return start.next;
    }
}

4.Rotate List

Given a list, rotate the list to the right by k places, where k is non-negative.

For example:
Given 1->2->3->4->5->NULL and k = 2,
return 4->5->1->2->3->NULL.

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */

//success 1
//参考https://discuss.leetcode.com/topic/2861/share-my-java-solution-with-explanation
public class Solution {
    public ListNode rotateRight(ListNode head, int n) {
        if (head==null||head.next==null) return head;
        //原链表添加头
        ListNode dummy=new ListNode(0);
        dummy.next=head;
        ListNode fast=dummy,slow=dummy;

        int i;
        for (i=0;fast.next!=null;i++)//Get the total length 
            fast=fast.next;

        for (int j=i-n%i;j>0;j--) //Get the i-n%i th node
            slow=slow.next;

        //变换
        fast.next=dummy.next; //Do the rotation
        dummy.next=slow.next;
        slow.next=null;

        return dummy.next;
    }
}

注意上述代码中变换部分。

5.Merge k Sorted Lists

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.

public class Solution {
    //success 1
    //利用优先队列来存储lists中的node,每次返回val最小的node,同时将该node.next加入优先队列
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists==null||lists.length==0) return null;

        //pop返回最小数的优先队列
        PriorityQueue<ListNode> queue= new PriorityQueue<ListNode>(lists.length,new Comparator<ListNode>(){
            @Override
            public int compare(ListNode o1,ListNode o2){
                if (o1.val<o2.val)
                    return -1;
                else if (o1.val==o2.val)
                    return 0;
                else 
                    return 1;
            }
        });

        ListNode dummy = new ListNode(0);
        ListNode tail=dummy;

        for(int i=0;i<lists.length;i++){
            if (lists[i]!=null)
                queue.add(lists[i]);
        }
        while (!queue.isEmpty()){
            tail.next=queue.poll();
            tail=tail.next;
            if (tail.next!=null)
                queue.add(tail.next);
        }
        return dummy.next;
    }
}

默认情况下,如果优先队列中存储的元素为Integer,它返回的顺序为优先返回最小的。Integer中compare方法的实现:

public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }

那么,如果我们要返回ListNode中存储val最小的元素,只需要像Integer一样重写之。

注意:在优先队列的定义中,优先返回的是具有最大优先级的元素,那么也就是说,对于Integer来说,优先级顺序为小于,等于,大于(即-1,0,1,至于这些小于,等于,大于怎么定义,就是另一回事了。你可以用实际上大于的情况来返回-1,这样的话,重写之后的Integer将按照从大到小的顺序输出)。

思路二:我们也可以利用分治的思想实现之:

//success 2
    //利用分治法的思想
    public ListNode mergeKLists(ListNode[] lists){
    return partion(lists,0,lists.length-1);
}

    public ListNode partion(ListNode[] lists,int s,int e){
        if(s==e)  return lists[s];
        if(s<e){
            int q=(s+e)/2;
            ListNode l1=partion(lists,s,q);
            ListNode l2=partion(lists,q+1,e);
            return merge(l1,l2);
        }else
            return null;
    }

    //This function is from Merge Two Sorted Lists.
    //注意该merge方法的实现,很简洁
    public static ListNode merge(ListNode l1,ListNode l2){
        if(l1==null) return l2;
        if(l2==null) return l1;
        if(l1.val<l2.val){
            l1.next=merge(l1.next,l2);
            return l1;
        }else{
            l2.next=merge(l1,l2.next);
            return l2;
        }
    }
}

13.Two Pointers

1.Valid Palindrome

Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases.

For example,
“A man, a plan, a canal: Panama” is a palindrome.
“race a car” is not a palindrome.

Note:
Have you consider that the string might be empty? This is a good question to ask during an interview.

For the purpose of this problem, we define empty string as valid palindrome.

//success 1
//需要预处理,比较容易想到,success 2更简洁
// public class Solution {
//     public boolean isPalindrome(String s) {
//         if(s.isEmpty()){
//             return true;
//         }
//         //pre process
//         StringBuilder sb = new StringBuilder("");
//         for(int i=0;i<s.length();i++){
//             //大写与小写之间还有间隔
//             if((s.charAt(i)>='A'&&s.charAt(i)<='Z')||(s.charAt(i)>='a'&&s.charAt(i)<='z')||(s.charAt(i)>='0'&&s.charAt(i)<='9')){
//                 sb.append(String.valueOf(s.charAt(i)).toLowerCase());
//             }
//         }
//         String string = sb.toString();
//         if(string.length()==0||string.length()==1){
//             return true;
//         }
//         for(int i=0;i<string.length()/2+1;i++){
//             if(string.charAt(i)!=string.charAt(string.length()-1-i)){
//                 return false;
//             }
//         }
//         return true;
//     }
// }

//success 2
//two pointers
public class Solution {
    public boolean isPalindrome(String s) {
        if (s.isEmpty()) {
            return true;
        }
        int head = 0, tail = s.length() - 1;
        char cHead, cTail;
        while(head <= tail) {
            cHead = s.charAt(head);
            cTail = s.charAt(tail);
            if (!Character.isLetterOrDigit(cHead)) {
                head++;
            } else if(!Character.isLetterOrDigit(cTail)) {
                tail--;
            } else {
                if (Character.toLowerCase(cHead) != Character.toLowerCase(cTail)) {
                    return false;
                }
                head++;
                tail--;
            }
        }

        return true;
    }
}

2.Intersection of Two Arrays

Given two arrays, write a function to compute their intersection.

Example:
Given nums1 = [1, 2, 2, 1], nums2 = [2, 2], return [2].

Note:
Each element in the result must be unique.
The result can be in any order.

参考:Three Java Solutions,代码如下:

//success 1
//Use two hash sets
//Time complexity: O(n)
public class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        Set<Integer> set = new HashSet<>();
        Set<Integer> intersect = new HashSet<>();
        for (int i = 0; i < nums1.length; i++) {
            set.add(nums1[i]);
        }
        for (int i = 0; i < nums2.length; i++) {
            if (set.contains(nums2[i])) {
                intersect.add(nums2[i]);
            }
        }
        //转换为array
        int[] result = new int[intersect.size()];
        int i = 0;
        for (Integer num : intersect) {
            result[i++] = num;
        }
        return result;
    }
}

//success 2
//Sort both arrays, use two pointers
//Time complexity: O(nlogn)
public class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        Set<Integer> set = new HashSet<>();
        //预排序
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        int i = 0;
        int j = 0;
        while (i < nums1.length && j < nums2.length) {
            if (nums1[i] < nums2[j]) {
                i++;
            } else if (nums1[i] > nums2[j]) {
                j++;
            } else {
                set.add(nums1[i]);
                i++;
                j++;
            }
        }
        int[] result = new int[set.size()];
        int k = 0;
        for (Integer num : set) {
            result[k++] = num;
        }
        return result;
    }
}

//success 3
//Binary search
//Time complexity: O(nlogn)
public class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        Set<Integer> set = new HashSet<>();
        Arrays.sort(nums2);
        for (Integer num : nums1) {
            if (binarySearch(nums2, num)) {
                set.add(num);
            }
        }
        int i = 0;
        int[] result = new int[set.size()];
        for (Integer num : set) {
            result[i++] = num;
        }
        return result;
    }

    public boolean binarySearch(int[] nums, int target) {
        int low = 0;
        int high = nums.length - 1;
        while (low <= high) {
            int mid = low + (high - low) / 2;
            if (nums[mid] == target) {
                return true;
            }
            if (nums[mid] > target) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return false;
    }
}

3.Intersection of Two Arrays II

题意

实现该题意并不难,我们回答它的follow-up questions:

先回顾下上一节Intersection of Two Arrays中,我们使用的三种方法,1.Use two hash sets,时间复杂度O(n),2.Sort both arrays, use two pointers,时间复杂度O(nlogn),3.Binary search,时间复杂度O(nlogn)。

1.What if the given array is already sorted? How would you optimize your algorithm?

solution:如果已经排好序,那么我们可以使用方法2,two pointers。

2.What if nums1’s size is small compared to nums2’s size? Which algorithm is better?

solution:如果一方特别小,那么可以将另一方作为被比较对象,这样在遍历时,我们的次数会很小。

3.What if elements of nums2 are stored on disk, and the memory is limited such that you cannot load all elements into the memory at once?

solution(参考Solution to 3rd follow-up question):

1.If only nums2 cannot fit in memory, put all elements of nums1 into a HashMap, read chunks of array that fit into the memory, and record the intersections.如果只是num2太大,那么将num1映射成hashmap,然后分片读取num2。

2.If both nums1 and nums2 are so huge that neither fit into the memory, sort them individually (external sort), then read 2 elements from each array at a time in memory, record intersections.如果都太大,那么分别使用外部排序进行排序,每次分别将num1和num2的一个元素(或者合适size的元素)放入内存进行比较。

4.Merge Sorted Array

Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array.

Note:
You may assume that nums1 has enough space (size that is greater or equal to m + n) to hold additional elements from nums2. The number of elements initialized in nums1 and nums2 are m and n respectively.

很简单,直接写:

public class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        //因为num1的size很大,我们可以考虑直接在num1中原地排序
        //trick:如果从index 0开始,1.我们不好判断num1真实有值的情况什么时候结束
        //2.而且可能对真实值造成了破坏。我们考虑从后向前来赋值!
        int end = m+n-1;
        int i = m-1;
        int j = n-1;
        while(i>=0&&j>=0){
            if(nums1[i]>nums2[j]){
                nums1[end] = nums1[i];
                end--;
                i--;
            }else{
                nums1[end] = nums2[j];
                end--;
                j--;
            }
        }
        //只需要对num2不为空进行处理
        while(j>=0){
            nums1[end] = nums2[j];
            end--;
            j--;
        }
    }
}

5.Longest Substring Without Repeating Characters

Given a string, find the length of the longest substring without repeating characters.

Examples:

Given “abcabcbb”, the answer is “abc”, which the length is 3.

Given “bbbbb”, the answer is “b”, with the length of 1.

Given “pwwkew”, the answer is “wke”, with the length of 3. Note that the answer must be a substring, “pwke” is a subsequence and not a substring.

解答:

//fail 1
//timeOut
// public class Solution {
//     public int lengthOfLongestSubstring(String s) {
//         if(s.isEmpty()||s.length()==0){
//             return 0;
//         }
//         int length = s.length();
//         int i=0,j=0;
//         int max = Integer.MIN_VALUE,len=0;
//         Set<String> set = new HashSet<>();
//         while(i<=j&&j<length){
//             //不重复
//             if(!set.contains(String.valueOf(s.charAt(j)))){
//                 set.add(String.valueOf(s.charAt(j)));
//                 j++;
//                 len++;
//             }else{
//                 //从i+1继续
//                 i++;
//                 j=i;
//                 set.clear();
//                 max = Math.max(max,len);
//                 len=0;
//             }
//         }
//         return Math.max(max,len);
//     }
// }

//success 2
//方法1中如果是重复情况,那么就从i的下一个位置重新开始,可以对这里进行优化
//通过记录每个字母最后一次出现的index,来优化,注意这里的i和j的含义跟
//方法1相反
public class Solution{
    public int lengthOfLongestSubstring(String s) {
        if (s.length()==0) return 0;
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        int max=0;
        for (int i=0, j=0; i<s.length(); ++i){
            if (map.containsKey(s.charAt(i))){
                j = Math.max(j,map.get(s.charAt(i))+1);
            }
            map.put(s.charAt(i),i);
            max = Math.max(max,i-j+1);
        }
        return max;
    }
}

如果仔细体会的话,方法二运用了DP的思想,字符串s在[0,i]区间上的最大不重复子串长度dp[i]=

1.若在[0,i-1]区间上存在s[i],等于i-m+1(m为s[i]最后出现位置,所以才要维护map啊)
2.若不存在s[i],那么等于dp[i-1]+1,并把自身信息加入到map中

参考:11-line simple Java solution, O(n) with explanation

6.Minimum Size Subarray Sum

Given an array of n positive integers and a positive integer s, find the minimal length of a subarray of which the sum ≥ s. If there isn’t one, return 0 instead.

For example, given the array [2,3,1,2,4,3] and s = 7,
the subarray [4,3] has the minimal length under the problem constraint.

//success 1
//运用DP的思想,map中存入index,start(小于但最接近s的index),num(start到index之和)
public class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        int length = nums.length;
        if(length==0){
            return 0;
        }
        //注意该初始化的写法,实际后面的<Integer,Map<Integer, Integer>>可以不写
        Map<Integer,Map<Integer,Integer>> dp = new HashMap<Integer,Map<Integer, Integer>>(length+1);
        Map<Integer,Integer> m = new HashMap<Integer,Integer>();
        //两个map都put
        m.put(0, 0);
        dp.put(0, m);
        int min=Integer.MAX_VALUE;
        for (int i = 0; i < length; i++) {
            if(nums[i]>=s){
                return 1;
            }else{
                Map<Integer,Integer> mapPreI = dp.get(i);
                int start=0,num = 0;
                for (Integer key:mapPreI.keySet()) {
                    num = mapPreI.get(key);
                    start = key;
                }
                while(num+nums[i]>=s){
                    num = num-nums[start];
                    start++;
                    min = Math.min(min,i-start+2);
                }
                //更新dp[i]
                Map<Integer,Integer> dpI = new HashMap<Integer,Integer>();
                dpI.put(start, num+nums[i]);
                dp.put(i+1, dpI);
            }
        }
        return min==Integer.MAX_VALUE?0:min;
    }
}

一个更简洁的版本:

//success 2
//跟我的思想一样,但很简洁啊,话说这种变量少(指需要自己维护的变量,如success 1中的index,start,num就分别对应于success 2中的j,i,sum)
//而且要求连续的题目(一般为substring问题),确实用dp存储太耗费时间空间了
//但!!!DP的思想是非常重要的!
public class Solution{
    public int minSubArrayLen(int s, int[] a) {
  if (a == null || a.length == 0)
    return 0;

  int i = 0, j = 0, sum = 0, min = Integer.MAX_VALUE;

  while (j < a.length) {
    sum += a[j++];

    while (sum >= s) {
      min = Math.min(min, j - i);
      sum -= a[i++];
    }
  }

  return min == Integer.MAX_VALUE ? 0 : min;
}
}

7.Linked List Cycle II

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Note: Do not modify the linked list.

Follow up:
Can you solve it without using extra space?

参考:
1.Java O(1) space solution with detailed explanation.
2.Concise JAVA solution based on slow fast pointers

两种方法都是运用two pointers思想来解决的。

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

8.Find the Duplicate Number

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.

Note:
You must not modify the array (assume the array is read only).
You must use only constant, O(1) extra space.
Your runtime complexity should be less than O(n2).
There is only one duplicate number in the array, but it could be repeated more than once.

跟上述7类似,我们也可以利用寻找linkedlist中环的起点中two pointers的思想来处理重复number。可以将数组看成linkedlist嘛,如果出现相等元素,那么就意味着遍历到了相同的node,也即是出现了环!

为什么我们能将该数组看成环呢?因为数组有n+1个元素,而每个元素都在1到n之间(正好在index范围内,我想题意也是有意为之。不然不能看成链表的话,很难做啊)。代码如下:

public class Solution {
    public int findDuplicate(int[] nums) {
        int fast=nums[0],slow=nums[0];
        while(true){
            fast = nums[nums[fast]];
            slow = nums[slow];
            if(fast==slow){
                int slow2 = nums[0];
                while(slow2!=slow){
                    slow = nums[slow];
                    slow2 = nums[slow2];
                }
                return slow;
            }
        }
    }
}

跟上述7的思想非常类似,说一模一样也不为过!

1.Arranging Coins

题意

方法二参考:Java O(1) Solution - Math Problem

//success 1
//直接写,很朴素
// public class Solution {
//     public int arrangeCoins(int n) {
//         int counter = 1;
//         while(n>=counter){
//             n-=counter;
//             counter++;
//         }
//         return counter-1;
//     }
// }

//success 2
//运用公式,也很朴素
public class Solution {
    public int arrangeCoins(int n) {
    //注意(long)n将n从int转换为long
        return (int)((-1 + Math.sqrt(1 + 8 * (long)n)) / 2);
    }
}

Note that 8.0 * n is very important because it will cause Java to implicitly autoboxed the intermediate result into double data type. The code will not work if it is simply 8 * n. Alternatively, an explicit casting can be done 8 * (long) n).

2.Find Right Interval

题意

思路一:直接无脑比较

/**
 * Definition for an interval.
 * public class Interval {
 *     int start;
 *     int end;
 *     Interval() { start = 0; end = 0; }
 *     Interval(int s, int e) { start = s; end = e; }
 * }
 */
 //fail 1
 //timeOut,思想很朴素,时间复杂度为n的平方
public class Solution {
    public int[] findRightInterval(Interval[] intervals) {
        int n = intervals.length;
        int[] result=new int[n];
        for(int i=0;i<n;i++){
            int num=-1;
            int min=Integer.MAX_VALUE;
            for(int j=0;j<n;j++){
                if(i!=j){
                    if(intervals[j].start>=intervals[i].end){
                        if(intervals[j].start<min){
                            min = intervals[j].start;
                            num=j;
                        }
                    }
                }
            }
            result[i] = num;
        }
        return result;
    }
}

思路二:参考Java clear O(n logn) solution based on TreeMap,代码如下:

//success 2
//利用treemap
public class Solution {
    public int[] findRightInterval(Interval[] intervals) {
        int[] result = new int[intervals.length];
        java.util.NavigableMap<Integer, Integer> intervalMap = new TreeMap<>();

        for (int i = 0; i < intervals.length; ++i) {
            intervalMap.put(intervals[i].start, i);
        }

        for (int i = 0; i < intervals.length; ++i) {
            Map.Entry<Integer, Integer> entry = intervalMap.ceilingEntry(intervals[i].end);
            result[i] = (entry != null) ? entry.getValue() : -1;
        }

        return result;
    }
}

补充关于Map,sortedMap,NavigableMap,TreeMap的知识

在idea ide下打开这些类,看看对应关系,以及有哪些方法。(左下角点击structure查看类下所有方法)

1.Map:

public interface Map<K,V> {}

//An object that maps keys to values.
//A map cannot contain duplicate keys;
//each key can map to at most one value.
//代替jdk1.0就出现的Dictionary抽象类,目前Dictionary已经废弃
//The Map interface provides three collection views, which
//allow a map's contents to be viewed as 
//1.a set of keys, 
//2.collection of values,
//3.or set of key-value mappings.

//解除map中该key与value的对应关系,并返回该解除了对应关系的value。
//如果map中没有该key,返回null。注意,在允许value=null的map中,
//返回null理所当然地并不意味着没有该key,也可能是value本身就为null。
V remove(Object key);

//复制一个map中的所有对应关系到本map中,相当于将源map中key,value
//全部取出(get方法),然后全部复制存储到本map中。
//注意:
//The behavior of this operation is undefined if the
//specified map is modified while the operation is 
//in progress.
//试着翻译:在该操作执行过程中,如果源map有改动,那么结果未知。
void putAll(Map<? extends K, ? extends V> m);

//三种不同视角遍历:
Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();

//遍历得到key,value的示例:
for (Map.Entry<String, String> entry : map.entrySet())
{
    System.out.println(entry.getKey() + "/" + entry.getValue());
}

//在map类还定义了Entry内部类,上面的key,value视角就用到了哈
interface Entry<K,V> {}
//每个entry代表了一个对应关系,可以得到该entry中的key和value
//如该Entry中的K getKey();和V getValue();

2.SortedMap:

public interface SortedMap<K,V> extends Map<K,V> {}

//从名称可以看出,是有顺序的map
//(这种顺序是针对于key来说的,不是value)
//如果在创建时指定了Comparator,
//那么返回的顺序由该Comparator定义,否则按照正常的自然的顺序返回。
//在遍历map的三种方式中,这种顺序都能够体现。

//return the comparator used to order the keys in thismap,
//or null if this map uses the natural ordering of itskeys
Comparator<? super K> comparator();

//返回fromKey到toKey之间的subMap
SortedMap<K,V> subMap(K fromKey, K toKey);

//high endpoint (exclusive) of the keys in the returned
//map,相当于subMap(最小的key,toKey)
SortedMap<K,V> headMap(K toKey);

//low endpoint (inclusive) of the keys in the returned map
//相当于subMap(fromkey,最大的key+1)
SortedMap<K,V> tailMap(K fromKey);

//Returns the first (lowest) key currently in this map.
//第一个当然时最low的啦
K firstKey();

//同理
K lastKey();

3.NavigableMap:

public interface NavigableMap<K,V> extends SortedMap<K,V> {}

//A SortedMap extended with navigation methods returning
//the closest matches for given search targets. 
//Methods lowerEntry,floorEntry,ceilingEntry,and
//higherEntry return Map.Entry objects associated with
//keys respectively less than, less than or equal,greater
//than or equal, and greater than a given key, returning
//null if there is no such key.  Similarly, methods
//lowerKey, floorKey, ceilingKey, and higherKey return
//only the associated keys. All of these methods are
//designed for locating, not traversing entries.
//第一句话告诉我们NavigableMap其实一点都不特别,他只是
//具有locat功能的SortedMap而已;最后一句话告诉我们,
//上述这些方法只是用来定位,并不是用来遍历的,这很显然嘛。

//返回所有小于key的keys中最大的key的entry,即closest matches
Map.Entry<K,V> lowerEntry(K key);
//其他相似的方法都同理

//Returns a key-value mapping associated with the least
//key in this map, or null if the map is empty.
Map.Entry<K,V> firstEntry();

//Removes and returns a key-value mapping associated with
//the least key in this map, or null if the map is empty.
Map.Entry<K,V> pollFirstEntry();

//Returns a reverse order view of the mappings contained
//in this map.The descending map is backed by this map,
//so changes to the map are reflected 
//in the descending map, and vice-versa.相互会影响
NavigableMap<K,V> descendingMap();

//Returns a NavigableSet view of the keys contained in
//this map.
//The set's iterator returns the keys in ascending order.
NavigableSet<K> navigableKeySet();

//同理,但顺序相反
NavigableSet<K> descendingKeySet();

4.TreeMap:

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{}

//红黑树实现之
//A Red-Black tree based NavigableMap implementation.
//The map is sorted according to the Comparable 
//natural ordering of its keys, 
//or by a Comparator provided at map creation time,
//depending on which constructor is used.

//时间复杂度
//This implementation provides guaranteed log(n) time
//cost for the containsKey, get, put and remove operations.

//Copies all of the mappings from the specified map to
//this map.These mappings replace any mappings that this
//map had for any of the keys currently in the specified
//map.
public void putAll(Map<? extends K, ? extends V> map) {}

//Returns a shallow copy of this TreeMap instance. 
//(The keys and values themselves are not cloned.)
public Object clone() {}

再补充关于HashMap,HashTable的知识

1.HashMap:

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {}

//可以看出其与HashTable的关系
//Hash table based implementation of the Map interface. 
//This implementation provides all of the optional map
//operations, and permits(允许) 
//null values and the null  key.  
//(The HashMap class is roughly equivalent to Hashtable,
//except that it is unsynchronized and permits nulls.)
//从这里可以看出他们的主要差别!  
//This class makes no guarantees as to the order of 
//the map; in particular, it does not guarantee that 
//the order will remain constant over time.
//也不保证order不随时间变化

//只要hash函数分散的好,那么该map的基本操作,如get,put都将只花费常数时间。遍历花费的时间比较复杂,记住下面的tip
//Thus, it's very important not to set the 
//initial capacity too high (or the load factor too low)
//if iteration performance is important.

//HashMap原理,跟HashTable原理肯定一样啊
//An instance of HashMap has two parameters that 
//affect its performance: 
//1.initial capacity
//2.load factor.  
//The capacity is the number of buckets in the 
//hash table, and the initial capacity is simply 
//the capacity at the time the hash table is created.
//The load factor is a measure of how full the 
//hash table is allowed to get before its capacity 
//is automatically increased.  When the number of 
//entries in the hash table exceeds the product of 
//the load factor and the current capacity, 
//the hash table is rehashed (that is, internal 
//data structures are rebuilt) so that the hash table 
//has approximately twice the number of buckets.

//As a general rule, the default load factor 
//(.75)(默认情况是.75) offers a good tradeoff between 
//time and space costs.  
//Higher values decrease the space overhead but
//increasethe lookup cost

//If many mappings are to be stored in a 
//HashMap instance, creating it with a sufficiently 
//large capacity will allow the mappings to be stored
//more efficiently than letting it perform 
//automatic rehashing as needed to grow the table.
//如果要存入很多,可以在开始时指定较大的容量

2.Hashtable

public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {}
//跟HashMap真的差不多啊

关于这两者的比较:
1.HashMap和Hashtable的区别
2.Differences between HashMap and Hashtable?

Hashtable和HashMap有几个主要的不同:线程安全以及速度。仅在你需要完全的线程安全的时候使用Hashtable,而如果你使用Java 5或以上的话,请使用ConcurrentHashMap吧。
这样看的话,Hashtable还是有点过时了。

3.Sqrt(x)

Implement int sqrt(int x).

Compute and return the square root of x.

//success 1
//二分法
public class Solution{
    public int mySqrt(int x) {
    if (x == 0)
        return 0;
    int left = 1, right = Integer.MAX_VALUE;
    while (true) {
        int mid = left + (right - left)/2;
        if (mid > x/mid) {
            right = mid - 1;
        } else {
             //如果不能整开方,取近似值
            if (mid + 1 > x/(mid + 1))
                return mid;
            left = mid + 1;
        }
    }
}
}

//success 2
//牛顿法
// public class Solution{
//     public int mySqrt(int x){
//         long r = x;
//         while (r*r > x)
//             r = (r + x/r) / 2;
//         return (int) r;
//     }
// }

4.Kth Smallest Element in a Sorted Matrix

Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth smallest element in the matrix.

Note that it is the kth smallest element in the sorted order, not the kth distinct element.

Example:

matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,

return 13.

Note:
You may assume k is always valid, 1 ≤ k ≤ n2.

跟Divide and Conquer部分的Search a 2D Matrix II题目类似。

//fail 1
//思路完全错了,并不是最小row和column之间取得所有最小的数
//跟原来的Search a 2D Matrix II这个类似的题犯错的思路一样
public class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        int m = matrix.length;
        int n = matrix[0].length;
        int row=0,column=0,rowI=0,columnI=0;
        int preI=0,preJ=0;
        int count=k+1;
        while(count>0&&row<m&&column<n&&rowI<n&&columnI<m){
            if(matrix[row][rowI]<=matrix[columnI][column]){
                preI = row;
                preJ = rowI;
                if(rowI<n-1){
                    rowI++;
                }else{
                    row++;
                    rowI=column+1;
                }
            }else{
                preI = columnI;
                preJ = column;
                if(columnI<m-1){
                    columnI++;
                }else{
                    column++;
                    columnI=row+1;
                }
            }
            count--;
        }
        if(count>0){
            if(row==m||rowI==n){
                return matrix[columnI+count-1][column];
            }else{
                return matrix[row][rowI+count-1];
            }
        }
        return matrix[preI][preJ];
    }
}

参考:细语呢喃同学的解答:
1.如果行和列都无序怎么解
2.如果行列只有一个有序怎么解
3.如果都有序(即本题)怎么解
都写的非常好!

//success 1
//利用了行列都有序的特点
//思想演进:
//思想1.在最小数和最大数之间每个数每个数试(穷举法)(第一步:选数),在matrix中找出小于等于它的元素的个数(第二步:找出该数顺序),
//如果正好为k,那么找到啦。
//针对第一步,优化思想就是利用二分法,相比于穷举法可以更快的找出这个数来
//针对第二步,优化的思想实际上跟Search a 2D Matrix II很相似,更快的找出该数顺序
//这样两种优化方法相结合,完美!
// public class Solution {
//     public int kthSmallest(int[][] matrix, int k) {
//      int n = matrix.length;
//      //初始L为最小数,初始R为最大数
//      int L = matrix[0][0], R = matrix[n - 1][n - 1];
//      while (L < R) {
//          int mid = L + ((R - L) >> 1);
//          int temp = search_lower_than_mid(matrix, n, mid);
//          if (temp < k) L = mid + 1;
//          else R = mid;
//      }
//      return L;
//  }

//  //在matrix中,寻找小于等于mid的元素的个数
//  private int search_lower_than_mid(int[][] matrix,int n,int x) {
//      int i = n - 1, j = 0, cnt = 0;
//      while (i >= 0 && j < n) {
//          if (matrix[i][j] <= x) {
//              j++;
//              cnt += i + 1;
//          }
//          else i--;
//      }
//      return cnt;
//  }
// }

//fail 2
//值得注意的是枚举答案应该用下界,因为猜想的解不一定在数组中,不断的收缩直到找到在数组中的元素为止。
//如下演示,看起来思想相同,但因为找的数不一定在数组中,所以会运行失败
// public class Solution {
//     public int kthSmallest(int[][] matrix, int k) {
//      int n = matrix.length;
//      //初始L为最小数,初始R为最大数
//      int L = matrix[0][0], R = matrix[n - 1][n - 1];
//      while (L <= R) {
//          int mid = L + ((R - L) >> 1);
//          int temp = search_lower_than_mid(matrix, n, mid);
//          if(temp<k){
//              L = mid+1;
//          }else if(temp>k){
//              R = mid-1;
//          }else{
//              return mid;
//          }
//      }
//      return L;
//  }

//  //在matrix中,寻找小于等于mid的元素的个数
//  private int search_lower_than_mid(int[][] matrix,int n,int x) {
//      int i = n - 1, j = 0, cnt = 0;
//      while (i >= 0 && j < n) {
//          if (matrix[i][j] <= x) {
//              j++;
//              cnt += i + 1;
//          }
//          else i--;
//      }
//      return cnt;
//  }
// }

//fail 3
//timeOut,但算法应该没问题
//直接利用穷举法,即未经过优化1
public class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        int n = matrix.length;
        //初始L为最小数,初始R为最大数
        int L = matrix[0][0], R = matrix[n - 1][n - 1];
        int s = L;
        for(s = L;s<=R;s++){
            if(search_lower_than_mid(matrix, n, s)>=k){
                return s;
            }
        }
        return s;
    }

    //在matrix中,寻找小于等于mid的元素的个数
    private int search_lower_than_mid(int[][] matrix,int n,int x) {
        int i = n - 1, j = 0, cnt = 0;
        while (i >= 0 && j < n) {
            if (matrix[i][j] <= x) {
                j++;
                cnt += i + 1;
            }
            else i--;
        }
        return cnt;
    }
}

5.Search Insert Position

Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.

You may assume no duplicates in the array.

Here are few examples.
[1,3,5,6], 5 → 2
[1,3,5,6], 2 → 1
[1,3,5,6], 7 → 4
[1,3,5,6], 0 → 0

关于在一个有序数组中二分查找,按下面代码流程,无论哪种情况都会退化到low==high的情况(二分查找本来就应该很简洁啊!记住这二分法的简洁套路啊!):

1    int[] data;
2    int size;
3
4    public boolean binarySearch(int key) 
5    {
6         int low = 0;
7         int high = size - 1;
8          
9         while(high >= low) {
10             int middle = (low + high) / 2;
11             if(data[middle] == key) {
12                 return true;
13             }
14             if(data[middle] < key) {
15                 low = middle + 1;
16             }
17             if(data[middle] > key) {
18                 high = middle - 1;
19             }
20        }
21        return false;
22   }

该题还多了一个如果没有找到返回插入位置的功能,实际上就是将上述return false的替换掉而已(实际上就是返回low的位置!)。代码如下:

public class Solution{
    public int searchInsert(int[] A, int target) {
        int low = 0, high = A.length-1;
        while(low<=high){
            int mid = (low+high)/2;
            if(A[mid] == target) return mid;
            else if(A[mid] > target) high = mid-1;
            else low = mid+1;
        }
        return low;
    }
}

6.Divide Two Integers

Divide two integers without using multiplication, division and mod operator.

If it is overflow, return MAX_INT.

代码如下:

public class Solution{
    public int divide(int dividend, int divisor) {
    //Reduce the problem to positive long integer to make it easier.
    //Use long to avoid integer overflow cases.
    int sign = 1;
    if ((dividend > 0 && divisor < 0) || (dividend < 0 && divisor > 0))
        sign = -1;
    long ldividend = Math.abs((long) dividend);
    long ldivisor = Math.abs((long) divisor);

    //Take care the edge cases.
    if (ldivisor == 0) return Integer.MAX_VALUE;
    if ((ldividend == 0) || (ldividend < ldivisor)) return 0;

    long lans = ldivide(ldividend, ldivisor);

    int ans;
    if (lans > Integer.MAX_VALUE){ //Handle overflow.
        ans = (sign == 1)? Integer.MAX_VALUE : Integer.MIN_VALUE;
    } else {
        ans = (int) (sign * lans);
    }
    return ans;
}

private long ldivide(long ldividend, long ldivisor) {
    // Recursion exit condition
    if (ldividend < ldivisor) return 0;

    //  Find the largest multiple so that (divisor * multiple <= dividend), 
    //  whereas we are moving with stride 1, 2, 4, 8, 16...2^n for performance reason.
    //  Think this as a binary search.
    long sum = ldivisor;
    long multiple = 1;
    while ((sum+sum) <= ldividend) {
        sum += sum;
        multiple += multiple;
    }
    //Look for additional value for the multiple from the reminder (dividend - sum) recursively.
    return multiple + ldivide(ldividend - sum, ldivisor);
}
}

7.Max Sum of Rectangle No Larger Than K

返回子matrix和不超过k的最大和。

题意

参考:Maximum Sum Rectangular Submatrix in Matrix该视频代码,视频中方法适用于matrix中至少有一个为正数的情况,这个算法巧妙在把二维数组按行或列拆成多个一维数组,然后利用一维数组的累加和来找符合要求的数字,也就是将二维的情况退化成多个一维,然后求解。我以为思想相同,只需要改写存入maxSum的条件即可,但经过了如下改写,运行却是错误的。说明,两个题目的内涵不同,不能通过简单地改写来实现。以下为改写代码:

//改写1
//在视频代码的if(kadaneResult.maxSum > result.maxSum){后添加
if(kadaneResult.maxSum<=target){//最大不超过的数
                        System.out.println("test");
                        test = Math.max(test,kadaneResult.maxSum);
                    }
 //改写2
 在视频代码的if(kadaneResult.maxSum > result.maxSum)判断中
 //添加&&kadaneResult.maxSum<=target也不对

弃用之,寻找其他方法!

改变策略,不在上述maxSum时才判断是否小于等于target,而是改写原来的Maximum Subarray算法为Maximum Subarray no larger than k。

参考:

1.Quora-思想并附上CPP实现
2.largest sum of contiguous subarray No Larger than k-java实现
3.细语呢喃
4.Java Binary Search solution

原算法:

public int maxSubArray(int[] nums) {
            int count=0,largest=Integer.MIN_VALUE;
            for (int i = 0; i < nums.length; i++) {
                count+=nums[i];
                if(count>largest){
                    largest=count;
                }
                if(count<0){
                    count=0;
                }
            }
            return largest;
        }

改写后(小于等于k):

public int maxSumSubArray2(int[] a , int k){

        int max = Integer.MIN_VALUE;
        int sumj = 0;
        TreeSet<Integer> s = new TreeSet();
        s.add(0);

        for(int i=0;i<a.length;i++){
            int t = sumj + a[i];
            sumj = t;
            Integer gap = s.ceiling(sumj - k);
            if(gap != null) max = Math.max(max, sumj - gap);
            s.add(t);
        }

        return max;
    }

最终本题的Solution:

//success 1
public class Solution {
    public int maxSumSubmatrix(int input[][],int target){
        int rows = input.length;
        int cols = input[0].length;
        int temp[] = new int[rows];
        int maxSum = Integer.MIN_VALUE;
        for(int left = 0; left < cols ; left++){
            for(int i=0; i < rows; i++){
                temp[i] = 0;
            }
            for(int right = left; right < cols; right++){
                for(int i=0; i < rows; i++){
                    temp[i] += input[i][right];
                }
                int max = maxSumSubArray2(temp,target);
                maxSum = Math.max(max,maxSum);
            }
        }
        return maxSum;
    }

    //Maximum Subarray no larger than k
    public int maxSumSubArray2(int[] a , int k){
        int max = Integer.MIN_VALUE;
        int sumj = 0;
        TreeSet<Integer> s = new TreeSet();
        s.add(0);
        for(int i=0;i<a.length;i++){
            int t = sumj + a[i];
            sumj = t;
            //Returns the least element in this set greater than or equal to
            //the given element, or null if there is no such element.
            //E ceiling(E e);
            //对于当前的和为sum,我们只需要找到一个最小的数x,使得 sum – k <=x,这样可以保证sum – x <=k。
            Integer gap = s.ceiling(sumj - k);
            if(gap != null) max = Math.max(max, sumj - gap);
            s.add(t);
        }
        return max;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值