Tuesday, May 17, 2022

611. Valid Triangle Number

 一刷 05/2020

Version #1 Binary Search - Not Optimal

Time O(n^2logn)

Space O(1)

Runtime: 507 ms, faster than 20.28% of Java online submissions for Valid Triangle Number.
Memory Usage: 44.3 MB, less than 18.39% of Java online submissions for Valid Triangle Number.

class Solution {

    public int triangleNumber(int[] nums) {

        // triangle - given side lengths a <= b <= c, if a + b > c then they can make a triangle

        // Brute force O(n^3)

        // Binary search

        //      sort the array

        //      given a and b, find the largest index of c that can make a + b > c

        if (nums == null || nums.length < 3) {

            return 0;

        }

        int cnt = 0;

        Arrays.sort(nums);

        for (int i = 0; i < nums.length; i++) {

            for (int j = i + 1; j < nums.length; j++) {

                cnt += count(nums, i, j);

            }

        }

        return cnt;

    }

    

    private int count(int[] nums, int i, int j) {

        if (i >= j || j + 1 >= nums.length) {

            return 0;

        }

        // find the largest index k that make nums[i] + nums[j] > nums[k]

        // k > j && k < nums.length

        int sum = nums[i] + nums[j];

        int start = j + 1;

        int end = nums.length - 1;

        while (start + 1 < end) {

            int mid = start + (end - start) / 2;

            if (nums[mid] >= sum) {

                end = mid - 1;

            } else {

                start = mid;

            }

        }

        // From j + 1 to k, all nums[k] can form a triangle

        // count = k - (j + 1) + 1 = k - j

        if (nums[end] < sum) {

            return end - j;

        }

        if (nums[start] < sum) {

            return start - j;

        }

        return 0;

    }

}


Version # Two Pointers


Time O(n^2)

Space O(1)

Runtime: 40 ms, faster than 64.01% of Java online submissions for Valid Triangle Number.
Memory Usage: 43.8 MB, less than 52.58% of Java online submissions for Valid Triangle Number.

class Solution {

    public int triangleNumber(int[] nums) {

        // Assuming side lengths a <= b <= c, if a + b > c then they can form a triangle

        // Iterate through nums to select a number c

        // Try to find pairs on its left whose sum is larger than c

        if (nums == null || nums.length < 3) {

            return 0;

        }

        Arrays.sort(nums);

        int cnt = 0;

        // at least needs to be the 3rd number to have two numbers on its left

        for (int i = 2; i < nums.length; i++) {

            cnt += count(nums, i);

        }

        return cnt;

    }

    

    private int count(int[] nums, int targetIndex) {

        int start = 0, end = targetIndex - 1;

        int cnt = 0;

        while (start < end) {

            if (nums[start] + nums[end] > nums[targetIndex]) {

                // for all start < end, nums[start] + nums[end] is larger than nums[targetIndex]

                cnt += end - start;

                end--;

            } else {

                start++;

            }

        }

        return cnt;

    }

}

Monday, May 16, 2022

704. Binary Search

二刷 06/2022

Time O(logN)

Space O(1)

Runtime: 0 ms, faster than 100.00% of Java online submissions for Binary Search.
Memory Usage: 54.5 MB, less than 32.58% of Java online submissions for Binary Search.

class Solution {

    public int search(int[] nums, int target) {

        int start = 0, end = nums.length - 1;

        while (start <= end) {

            int mid = start + (end - start) / 2;

            if (nums[mid] == target) {

                return mid;

            }

            if (nums[mid] < target) {

                start = mid + 1;

            } else {

                end = mid - 1;

            }

        }

        return -1;

    }


一刷 05/2022

Runtime: 0 ms, faster than 100.00% of Java online submissions for Binary Search.
Memory Usage: 42.6 MB, less than 95.81% of Java online submissions for Binary Search.

class Solution {

    public int search(int[] nums, int target) {

        if (nums == null || nums.length == 0) {

            return -1;

        }

        int start = 0, end = nums.length - 1;

        // Stop iteration when they are next to each other

        while (start + 1 < end) {

            int mid = start + (end - start) / 2;

            if (nums[mid] == target) {

                return mid;

            }

            if (nums[mid] < target) {

                start = mid + 1;

            } else {

                end = mid - 1;

            }

        }

        if (nums[end] == target) {

            return end;

        }

        if (nums[start] == target) {

            return start;

        }

        return -1;

    }

}

Friday, May 13, 2022

912. Sort an Array

 一刷

Version #1 Quick sort 

这个写的不好,看下边一个版本

有两个问题

1.不应该选取nums[start] 作为pivot,因为如果原有的nums是descending or ascending order, the time efficiency will be reduced

2.left++和right--的条件应该是大于或者小于

Runtime: 952 ms, faster than 16.06% of Java online submissions for Sort an Array.
Memory Usage: 72.9 MB, less than 17.00% of Java online submissions for Sort an Array.

Version #1 Quick Sort

class Solution {

    public int[] sortArray(int[] nums) {

        // Quick sort

        // Select the 1st element in the array, devide the array by comparing each element with the seleted element

        // start         end

        // nums[i] <= p    nums[i] >= p

        return sortArrayHelper(nums, 0, nums.length - 1);

    }

    //  s             e  r l

    // [0,1,1,1,1,1,1,1,1,6,5]

    private int[] sortArrayHelper(int[] nums, int start, int end) {

        if (start >= end) {

            return nums;

        }

        //                      lr

        // [(1),1,1,1,1,1,1,1,0,6,5]

        int left = start + 1, right = end;

        int p = nums[start];

        while (left <= right) {

            // left is the first character that's larger than p

            while (left <= right && nums[left] <= p) {

                left++;

            }

            while (left <= right && nums[right] >= p) {

                right--;

            }

            if (left <= right) {

                swap(nums, left, right);

                left++;

                right--;

            }

        }

        swap(nums, start, left - 1);

        sortArrayHelper(nums, start, left - 2);

        sortArrayHelper(nums, left, end);

        return nums;

    }

    

    private void swap(int[] nums, int i, int j) {

        int temp = nums[i];

        nums[i] = nums[j];

        nums[j] = temp;

    }

}


Version #1 Quick sort

Optimized version

Using nums[(start + end) / 2] as the pivot

This will improve the speed if the nums are in ascending or descending order

Runtime: 10 ms, faster than 88.81% of Java online submissions for Sort an Array.
Memory Usage: 50.8 MB, less than 97.00% of Java online submissions for Sort an Array.

class Solution {

    public int[] sortArray(int[] nums) {

        // Quick sort

        // Shuffle the array (optional)

        // Partition so that, for some j

        // - entry a[j] is in place

        // - no larger entry to the left of j

        // - no smaller entry to the right of j

        // Sort each piece recursively

        if (nums == null) {

            return null;

        }

        quickSort(nums, 0, nums.length - 1);

        return nums;

    }

    

    // Sort given array from index start to index end

    private void quickSort(int[] nums, int start, int end) {

        if (start >= end) {

            return;

        }

        int pivot = nums[start + (end - start) / 2];

        // partition the subarray between start and end

        // so that all nums <= pivot are on the left, all nums >= pivot are on the right

        int left = start, right = end;

        while (left <= right) {

            while (left <= right && nums[left] < pivot) {

                left++;

            }

            while (left <= right && nums[right] > pivot) {

                right--;

            }

            if (left <= right) {

                // swap nums[left] and nums[right]

                int temp = nums[left];

                nums[left] = nums[right];

                nums[right] = temp;

                left++;

                right--;

            }

        }

        // All nums to the left side of pointer l are smaller than pivot

        // All nums to the right side of pointer r are larger than pivot

        //       l

        //   r

        // 0 1 2 3

        quickSort(nums, start, right);

        quickSort(nums, left, end);

    }

}


Version #2 Merge Sort

每次都new了一个新的result数组,浪费了空间,同时time wasted during the construction of new temporary arrays

下面写了optimized solution

Runtime: 19 ms, faster than 52.47% of Java online submissions for Sort an Array.
Memory Usage: 72.8 MB, less than 17.01% of Java online submissions for Sort an Array.

class Solution {

    public int[] sortArray(int[] nums) {

        // Merge sort

        // Divide nums array from the middle, sort each subarray separately

        // Merge two arrays together

        if (nums == null) {

            return null;

        }

        return mergeSort(nums, 0, nums.length - 1);

    }

    

    private int[] mergeSort(int[] nums, int start, int end) {

        // exit criteria

        if (start > end) {

            return new int[0];

        }

        if (start == end) {

            return new int[]{nums[start]};

        }

        int[] leftNums = mergeSort(nums, start, (start + end) / 2);

        int[] rightNums = mergeSort(nums, (start + end) / 2 + 1, end);

        return merge(leftNums, rightNums);

    }

    

    private int[] merge(int[] nums1, int[] nums2) {

        int[] result = new int[nums1.length + nums2.length];

        int p1 = 0, p2 = 0, pr = 0;

        while (p1 < nums1.length && p2 < nums2.length) {

            if (nums1[p1] <= nums2[p2]) {

                result[pr] = nums1[p1];

                p1++;

                pr++;

            } else {

                result[pr] = nums2[p2];

                p2++;

                pr++;

            }

        }

        while (p1 < nums1.length) {

            result[pr] = nums1[p1];

            pr++;

            p1++;

        }

        while (p2 < nums2.length) {

            result[pr] = nums2[p2];

            pr++;

            p2++;

        }

        return result;

    }

}


Version #2 Merge Sort [Optimized]

To preserve stability, if two keys are equal, we always need to first choose from the left subarray

Runtime: 10 ms, faster than 88.81% of Java online submissions for Sort an Array.
Memory Usage: 51.2 MB, less than 91.77% of Java online submissions for Sort an Array.

class Solution {

    public int[] sortArray(int[] nums) {

        // Merge sort

        // Divide array into two halves

        // Recursively sort each half

        // Merge two halves

        if (nums == null) {

            return null;

        }

        // Create a auxiliary array to temporarily store the subarray

        int[] aux = new int[nums.length];

        quickSort(nums, 0, nums.length - 1, aux);

        return nums;

    }

    

    // quickSort - sort array from index start to index end

    private void quickSort(int[] nums, int start, int end, int[] aux) {

        // base case

        if (start >= end) { // stop when there's less than or equal to one element

            return;

        }

        int mid = start + (end - start) / 2;

        quickSort(nums, start, mid, aux);

        quickSort(nums, mid + 1, end, aux);

        // Optimize - if the largest element in the first half is smaller than the smallest element in the second half, no need to merge since the array is already sorted

        if (nums[mid] < nums[mid + 1]) {

            return;

        }

        merge(nums, start, mid, end, aux);

    }

    

    // merge - merge subarray [start, mid] with subarray [mid + 1, end]

    // Guarantee that the two subarrays are ordered

    private void merge(int[]nums, int start, int mid, int end, int[] aux) {

        // Firstly copy the original array to the auxiliary array

        for (int i = start; i <= end; i++) {

            aux[i] = nums[i];

        }

        // keep 3 pointers, pFirstHalf, pSecondHalf, pResult

        int pFirstHalf = start, pSecondHalf = mid + 1, pResult = start;

        while (pFirstHalf <= mid && pSecondHalf <= end) {

            // If two elements are equal, choose from the firsthalf

            if (aux[pFirstHalf] <= aux[pSecondHalf]) {

                nums[pResult] = aux[pFirstHalf];

                pFirstHalf++;

            } else {

                nums[pResult] = aux[pSecondHalf];

                pSecondHalf++;

            }

            pResult++;

        }

        while (pFirstHalf <= mid) {

            nums[pResult++] = aux[pFirstHalf++];

        }

        while (pSecondHalf <= end) {

            nums[pResult++] = aux[pSecondHalf++];

        }

    }

}

Thursday, May 12, 2022

680. Valid Palindrome II

 一刷 2020/05


Runtime: 10 ms, faster than 58.06% of Java online submissions for Valid Palindrome II.
Memory Usage: 54.7 MB, less than 42.39% of Java online submissions for Valid Palindrome II.

class Solution {

    public boolean validPalindrome(String s) {

        // Use two pointers scan towards each other

        // If the pointed characters are not equal, check if there's any remaining quota left to delete character

        // If yes, try to move each pointer to skip one character and check the remaining string

        if (s == null || s.equals("")) {

            return true;

        }

        int left = 0, right = s.length() - 1;

        int delete = 1;

        return validPalinHelper(s, left, right, delete);

    }

    

    private boolean validPalinHelper(String s, int left, int right, int delete) {

        while (left < right) {

            if (s.charAt(left) == s.charAt(right)) {

                left++;

                right--;

                continue;

            }

            if (delete <= 0) {

                return false;

            }

            return validPalinHelper(s, left  + 1, right, delete - 1) || validPalinHelper(s, left, right - 1, delete - 1);

        }

        return true;

    }

}

Monday, August 9, 2021

415. Add Strings

需要改进的地方是求carry

应该用

int value = (x1 + x2 + carry) % 10;

            carry = (x1 + x2 + carry) / 10;


 class Solution {

    public String addStrings(String num1, String num2) {

        // Assuming num1 is longer.

        if (num1.length() < num2.length()) {

            Solution s = new Solution();

            return s.addStrings(num2, num1);

        }

        StringBuilder sb = new StringBuilder();

        int l1 = num1.length(), l2 = num2.length();

        int carry = 0;

        for (int i = 0; i < l1; i++) {

            int v1 = num1.charAt(l1 - 1 - i) - '0';

            int v2 = i >= l2 ? 0: num2.charAt(l2 - 1 - i) - '0';

            int sum = v1 + v2 + carry;

            if (sum >= 10) {

                carry = 1;

                sum -=10;

            } else {

                carry = 0;

            }

            sb.append((char)('0' + sum));

        }

        if (carry != 0) {

            sb.append('1');

        }

        return sb.reverse().toString();

    }

    

}

Monday, March 15, 2021

953. Verifying an Alien Dictionary

 二刷 07/2022

Version #2 HashMap

Time O(MN)

Space O(26) ~ O(1)

Runtime: 2 ms, faster than 36.78% of Java online submissions for Verifying an Alien Dictionary.
Memory Usage: 43.1 MB, less than 9.85% of Java online submissions for Verifying an Alien Dictionary.

class Solution {

    public boolean isAlienSorted(String[] words, String order) {

        Map<Character, Integer> map = new HashMap<>();

        for (int i = 0; i < order.length(); i++) {

            map.put(order.charAt(i), i);

        }

        for (int i = 1; i < words.length; i++) {

            if (!smaller(words[i - 1], words[i], map)) {

                return false;

            }

        }

        return true;

    }

    

    private boolean smaller(String a, String b, Map<Character, Integer> map) {

        int pa = 0, pb = 0;

        while (pa < a.length() && pb < b.length()) {

            char ca = a.charAt(pa);

            char cb = b.charAt(pb);

            if (ca == cb) {

                pa++;

                pb++;

                continue;

            }

            if (map.get(ca) < map.get(cb)) {

                return true;

            } else {

                return false;

            }

        }

        return a.length() <= b.length();

    }

}


一刷

Version #1 Array as HashMap

Runtime: 0 ms, faster than 100.00% of Java online submissions for Verifying an Alien Dictionary.

Memory Usage: 37.3 MB, less than 94.58% of Java online submissions for Verifying an Alien Dictionary. 


一开始想复杂了

其实只要比较相邻的word就可以了, 因为smaller than是transitive的

if a < b && b < c we can infer that a < c

Time O(word count * avg word length)

Space O(1) 


class Solution {

    public boolean isAlienSorted(String[] words, String order) {

        char[] map = new char[26];

        for (int i = 0; i < order.length(); i++) {

            map[order.charAt(i) - 'a'] = (char)('a' + i); 

        }

        String prev = null;

        for (String word : words) {

            if (prev != null && !smaller(prev, word, map)) {

                return false;

            }

            prev = word;

        }

        return true;

    }

    

    private boolean smaller(String a, String b, char[] map) {

        int i = 0;

        for (i = 0; i < a.length() && i < b.length(); i++) {

            char ca = map[a.charAt(i) - 'a'];

            char cb = map[b.charAt(i) - 'a'];

            if (ca != cb) {

               return ca < cb;

            }

            // continue comparing if ca == cb

        }

        // apple

        // app

        if (i < a.length()) {

            return false;

        }

       // Can be optimized to: return a.length() <= b.length()

        return true;

    }

}

Saturday, March 13, 2021

621. Task Scheduler

Version #1 

好久没写java了,自己磕磕绊绊写了一版

用到了hashmap,没有利用tasks[i] is upper-case English letter这个条件

Time O(n)

Space O(1)

class Solution {

    public int leastInterval(char[] tasks, int n) {

        // A -> x -> x -> A -> x -> x -> A

        // (count(max chars) - 1) * (n + 1) + #chars that have max count

        if (n == 0) {

            return tasks.length;

        }

        int maxCount = 0;

        Map<Character, Integer> map = new HashMap<>();

        for (int i = 0; i < tasks.length; i++) {

            int cnt = map.getOrDefault(tasks[i], 0) + 1;

            maxCount = Math.max(maxCount, cnt);

            map.put(tasks[i], cnt);

        }

        int maxTasks = 0;

        for (Integer cnt : map.values()) {

            if (maxCount == cnt) {

                maxTasks++;

            }

        }

        return Math.max((maxCount - 1) * (n + 1) + maxTasks, tasks.length);

    }

}

Version #2 Optimized solution

Time O(n)

Space O(1)

这个testcase两次都错了

["A","A","A","B","B","B", "C","C","C", "D", "D", "E"]

2

class Solution {

    public int leastInterval(char[] tasks, int n) {

        // calculate the characters with max count

        int[] counts = new int[26];

        int maxCount = 0;

        int maxCountCharNum = 0;

        for (char task : tasks) {

            int i = task - 'A';

            counts[i]++;

            if (counts[i] > maxCount) {

                maxCountCharNum = 1;

                maxCount = counts[i];

            } else if (counts[i] == maxCount) {

                maxCountCharNum++;

            }

        }

        // n = 2

        // maxCountCharNum

        // (A B C) _divider_ (A B C) _ (A B C)

        // maxChars need (maxCount - 1) dividers to be seaparated

        // divider length = n - maxCountCharNum + 1

        // e.g. n = 2, maxCountCharNum = 1

        // A _ _ A _ _  divider = 2-1+1=2

        // if divider length <= 0 it means we don't need idle

        // e.g. n = 1, maxCountCharNum = 2

        // A B A B

        // so divider length = Math.max(0, n - maxCountCharNum + 1)

        // left chars = total chars - maxCountCharNum * maxCount

        // white spaces = divider length * (maxCount - 1) - left chars

        int idleCount = Math.max(0, n - maxCountCharNum + 1) * (maxCount - 1) - (tasks.length - maxCountCharNum * maxCount);

        return tasks.length + Math.max(0, idleCount);

    }

}