twoSum Algorithm : How to improve this?

前端 未结 22 2100
礼貌的吻别
礼貌的吻别 2021-02-06 01:38

I felt like doing an algorithm and found this problem on leetcode

Given an array of integers, find two numbers such that they add up to a specific target num

相关标签:
22条回答
  • 2021-02-06 02:00

    Below you can find a solution in which the two numbers could be found in O(n log n) time:

    1- Sort the numbers in ascending (or descending) order             // O(n log n)
    
    2- Compute diff = target - item for each item                      // O(n) 
    
    3- For each calculated diff, look up the calculated value in the sorted items 
       using the Binary search algorithm                               // O(n log n) 
    

    A complete, working implementation in Java:

    import java.util.ArrayList;
    
    public class NumbersFinder {
    
        class Item{
            private int value;
            private int index;
    
            public Item(int value, int index){
                this.value = value;
                this.index = index;
            }
    
            public int getValue(){
                return value;
            }
    
            public int getIndex(){
                return index;
            }
        }
    
        public ArrayList<Item> find(int[] values, int target){      
            ArrayList<Item> items = new ArrayList<Item>();
            for(int i = 0; i < values.length; i++)
                items.add(new Item(values[i], i));
    
            items = quicksort(items);
            ArrayList<Integer> diffs = computeDiffs(items, target);
    
            Item item1 = null;
            Item item2 = null;
    
            boolean found = false;
    
            for(int i = 0; i < diffs.get(i) && !found; i++){
                item1 = items.get(i);
                item2 = searchSortedItems(items, diffs.get(i), 0, items.size());
                found = item2 != null;
            }
            if(found){
                ArrayList<Item> result = new ArrayList<Item>();
                result.add(item1);
                result.add(item2);
                return result;
            }
            else
                return null;
        }
    
        // find "value" in the sorted array of "items" using Binary search in O(log n)
        private Item searchSortedItems(ArrayList<Item> items, Integer value, int lower, int upper) {
            if(lower > upper)
                return null;
            int middle = (lower + upper)/2;
            Item middleItem = items.get(middle);
            if(middleItem.getValue() == value)
                return middleItem;
            else if(middleItem.getValue() < value)
                return searchSortedItems(items, value, middle+1, upper);
            else
                return searchSortedItems(items, value, lower, middle-1);
        }
    
        // Simply calculates difference between the target value and each item in the array; O(n)
        private ArrayList<Integer> computeDiffs(ArrayList<Item> items, int target) {
            ArrayList<Integer> diffs = new ArrayList<Integer>();
            for(int i = 0; i < items.size(); i++)
                diffs.add(target - items.get(i).getValue());
            return diffs;
        }
    
        // Sorts items using QuickSort algorithm in O(n Log n)
        private ArrayList<Item> quicksort(ArrayList<Item> items) {
            if (items.size() <= 1)
                return items;
            int pivot = items.size() / 2;
            ArrayList<Item> lesser = new ArrayList<Item>();
            ArrayList<Item> greater = new ArrayList<Item>();
            int sameAsPivot = 0;
            for (Item item : items) {
                if (item.getValue() > items.get(pivot).getValue())
                    greater.add(item);
                else if (item.getValue() < items.get(pivot).getValue())
                    lesser.add(item);
                else
                    sameAsPivot++;
            }
            lesser = quicksort(lesser);
            for (int i = 0; i < sameAsPivot; i++)
                lesser.add(items.get(pivot));
            greater = quicksort(greater);
            ArrayList<Item> sorted = new ArrayList<Item>();
            for (Item item : lesser)
                sorted.add(item);
            for (Item item: greater)
                sorted.add(item);
            return sorted;
        }
    
    
        public static void main(String[] args){
            int[] s = {150,24,79,50,88,345,3};
            int value = 200;
    
            NumbersFinder finder = new NumbersFinder();
            ArrayList<Item> numbers = finder.find(s, value);
    
            if(numbers != null){
                System.out.println("First Number Found = " + numbers.get(0).getValue() + " ; Index = " + + numbers.get(0).getIndex());
                System.out.println("Second Number Found = " + numbers.get(1).getValue() + " ; Index = " + + numbers.get(1).getIndex());
            }
            else{
                System.out.println("No such two numbers found in the array!");
            }
        }
    }
    

    Output:

    First Number Found = 50 ; Index = 3
    Second Number Found = 150 ; Index = 0
    
    0 讨论(0)
  • 2021-02-06 02:01

    O(n log n) time, O(1) memory (not counting the list):

    1. First, sort the list. This should take O(n log n) time, as most sort functions do.

    2. Iterate through the list, which should take O(n) time in the outer loop. At this point you can do a binary search for the closest matching integer in a sorted sublist, which should take O(log n) time. This stage should wind up taking O(n log n) total.

    Edit: Check out Max's answer below. It's still O(n log n) time and O(1) memory, but he avoids the binary searches by walking a pointer from each end of the list.

    O(n) time, O(n) memory:

    Build a hash table, which should have O(1) insertion and O(1) contains. Then, in a O(n) outer loop, for each number i, check if total - i is in the hash table. If not, add it; if so, then you've got your two numbers.

    Either way, you would need an additional scan through the array to get the indices, but that's no problem--it only takes O(n) time. If you wanted to avoid it you could keep the original index in the sorted list or hash table as needed, but that has a memory footprint instead of a time footprint.

    0 讨论(0)
  • 2021-02-06 02:01

    I would approach it this way:

    1. Order your array from smaller to lower value

    2. Loop over your array the way you have it but exit the loop early whenever

      target <(numbers[i]+numbers[j])

    3. Once you have the value of your two elements such that n[0] + n[1] = target, look back at the original array to find their index.

    0 讨论(0)
  • 2021-02-06 02:03

    Here is my cpp solution with O(nlog(n)):

    vector<int> two_sum(vector<int> &numbers, int target) {
        vector<int> result;
        vector<int> numbers_dup = vector<int>(numbers);
    
        sort(numbers_dup.begin(), numbers_dup.end());
    
        int left = 0, right = numbers_dup.size() - 1;
        while(left <= right) {
            int sum = numbers_dup[left] + numbers_dup[right];
    
            if(sum == target) {
                //find the idex of numbers_dup[left] and numbers_dup[right]
                for(int i = 0; i < numbers.size(); i++) {
                    if(numbers[i] == numbers_dup[left] || numbers[i] == numbers_dup[right]) {
                        result.push_back(i);
                    }
                    if(result.size() == 2) {
                        return result;
                    }
                }
            }
            else if(sum > target) {
                right--;
            }
            else {
                left++;
            }
        }
    }
    

    Check out my blog for the detailed explanation. https://algorithm.pingzhang.io/Array/two_sum_problem.html

    0 讨论(0)
  • 2021-02-06 02:10

    Can provide an O(n) time solution, but his memory cannot be O(1)

    First you declare a key-value pair , where key is the value in the array of integers and value is the index in the array of integers. And, you need to declare a hash table to hold the key-value pairs. Then you need to iterate through the entire array of integers, if the value of this array (=sum - array[i]) is in the hash table, congratulations you found it, if not, it is stored in the hash table.

    So the entire time spent is the time to insert and query the hash table. Memory is the size of the hash table.

    My English is not very good, I hope I can help you.

    public static void main(String args[]) {
        int[] array = {150,24,79,50,88,345,3};
        int sum = 200;
    
    
        Map<Integer,Integer> table = new HashMap<>();
        StringBuilder builder = new StringBuilder();
        for(int i=0;i<array.length;i++){
            int key = sum - array[i];
            if(table.containsKey(key)){
                builder.append("index1=").append(table.get(key)).
                        append(" index2=").append(i);
            }else{
                table.put(array[i],i);
            }
        }
        System.out.println(builder);
    }
    

    0 讨论(0)
  • 2021-02-06 02:11

    Sort the array. Make two pointers point at first and last (x and X). Run this in a loop:

    if      (a[X]+a[x] >  N) then X-- 
    else if (a[X]+a[x] <  N) then x++
    else if (a[X]+a[x] == N) then found.
    
    if (x > X) then no numbers exist.
    

    O(nlogn) time, O(1) memory

    0 讨论(0)
提交回复
热议问题