码迷,mamicode.com
首页 > Windows程序 > 详细

LeetCode-1TwoSum(C#)

时间:2016-08-02 06:32:16      阅读:764      评论:0      收藏:0      [点我收藏+]

标签:

# 地址

Q: https://leetcode.com/problems/two-sum/

A: https://github.com/mofadeyunduo/LeetCode/tree/master/1TwoSum

(希望各位多多支持本人刚刚建立的GitHub和博客,谢谢,有问题可以联系QQ:609092186或者留言,我尽快回复)

 

# 题目

1. Two Sum

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution.

Example:

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

UPDATE (2016/2/13):
The return format had been changed to zero-based indices. Please read the above updated description carefully.

 

# 思路

无论三七二十一,上来先看看能不能暴力破解

        // brute force: time O(n^2) space O(1) result: 632ms
        public int[] TwoSum(int[] nums, int target)
        {
            for (int i = 0; i < nums.Length; i++)
                for (int j = i + 1; j < nums.Length; j++)
                    if (nums[i] + nums[j] == target) return new int[] { i, j };
            throw new Exception("no answer");
        }
        

对整个数组的两个for循环注定了时间复杂度为O(n^2),测试出来的结果为623ms。

 

这个时候可以仔细思考一下慢在哪里了。假设nums = {1,7,4},target = 5,显然answer = {0, 2}。用暴力破解方法的时候,计算了1 + 7, 1 + 4, 7 + 4。这里面和结果无关的计算有1 + 7和 7 + 4。1 + 7 比 5(target)大,我们需要一个比7更小的数字,但我们也不希望太小。这一系列行为,就让人很容易就想到利用排序去寻找。

我们可以通过排序来寻找比7更小且离他最近的数字,在这里是4,很巧,配对成功了。如果不成功的话,有可能这两个数字之和还是比target大,那就再找一个除了4,离7接近的,继续配对。也有可能比target小,那说明对于1,我们找不到这样一个配对组合(请注意这里利用的是有序数组)。

        // sort first, get left part and right part tricky, find left index and right index
        // time: O(nlgn) space: O(n) 500ms
        // reference: https://github.com/yangliguang yangliguang 
        public int[] TwoSum(int[] nums, int target)
        {
            int[] copy = new int[nums.Length];
            Array.Copy(nums, copy, nums.Length); 
            Array.Sort(copy); // O(nlgn) 
            int start = 0, end = copy.Length - 1, left = 0, right = 0;

            while (start < end) // O(n)
            {
                if (copy[start] + copy[end] == target)
                {
                    left = copy[start];
                    right = copy[end];
                    break;
                }
                else if (copy[start] + copy[end] < target)
                {
                    start++;  // very tricky
                }
                else
                {
                    end--; // very tricky
                }
            }

            start = end = -1;
            for (int i = 0; i < nums.Length; i++)
            {
                if (nums[i] == left)
                {
                    start = i;
                    break;
                }
            }

            for (int i = nums.Length - 1; i >= 0; i--)
            {
                if (nums[i] == right)
                {
                    end = i;
                    break;
                }
            }
            if (start > end) return new int[] { end, start };
            else return new int[] { start, end };
        }

可以看到时间复杂度为O(nlgn),还是比较理想的复杂度。具体测试出来的时间也是比较理想的,500ms(与下面的O(n)时间只差了4ms,原因可能是用HashTable实现,在时间复杂度方面,常数因子比较大)。

 

这个时候我们会想,能不能找到一个O(n)的算法?有。对于这个例子,nums = {1,7,4},target = 5,我们一般人会这么做:嗯...先拿出1,target是5啊,那我只要找到4就可以啦!其实可以进一步想,我怎么去找4呢?如果可以按照某种规则,看一看4存不存在就好了!

这种规则可以有吗?可以!就是桶(bucket)。这里正好是数字,直接放进数组对应下标的桶。这个例子中,我们先读取到1,让数组对应下标为1增加1,表明桶1里有1个1(似乎有点绕)。

我们每次读取一个元素,计算(target-该元素)的值,然后查看对应的桶,如果桶里有东西,恭喜你,找到答案了。没有的话,先把元素存入桶中,等待后面的元素配对(只有一个幸运儿可以配对成功)。

实际上,上面的思路我们还需要修改一下。别灰心。第一点就是,因为这个题目是要求下标的,而我们把元素放进桶的时候,并没有附带下标。这个时候可以用一种数组结构来附带下标,那就是映射(俗称键值对)(Map)。第二点就是,如果元素大小的范围比较大,比如nums= {1, 100000000},元素范围[1, 100000000],这个时候直接放进数组对应下标的桶就不好用了(int[]的长度到不了那么大)。所以这种放进数组的方式需要改变。显然,有关“放入”问题,Hash函数可以很好的帮我们做到这一点

综合上述,我们选用Hashtable来完美的完成此题。

 

# 解决(Hashtable)

        // right hash: time O(n) (degenerate when collison occured) ususlly O(1) (insert and search) space: O(n) result: 496ms
        public int[] TwoSum(int[] nums, int target)
        {
            int[] result = new int[2];
            System.Collections.Hashtable table = new System.Collections.Hashtable(nums.Length);  // use empty constructor, result: 544 ms
            for (int i = 0; i < nums.Length; i++)
            {
                int another = target - nums[i];
                if (table.ContainsKey(another))
                {
                    int anotherIndex = (int)table[another];
                    result[0] = anotherIndex > i ? i : anotherIndex;
                    result[1] = anotherIndex < i ? i : anotherIndex;
                    return result;
                }
                else
                {
                    if (!table.Contains(nums[i])) table.Add(nums[i], i);
                }
            }

            throw new Exception("no answer");
        }

时间复杂度O(n),空间复杂度O(n),运行时间496ms。

 

# 题外话

这也是一段用Hashtable思想写的一段代码,我把它标记为bad hash,各位可以仔细推敲一下这里的问题(参考一下下面的测试用例)。

        // bad hash: ignore duplication
        public int[] TwoSum(int[] nums, int target)
        {
            System.Collections.Hashtable table = new System.Collections.Hashtable(nums.Length);
            for (int i = 0; i < nums.Length; i++)
                 table.Add(nums[i], i); // key can have duplications, so hash like this is a bad idea

            for (int i = 0; i < table.Count; i++)
            {
                int another = target - nums[i];
                if (table.ContainsKey(another) && another != nums[i]) return new int[] { i, (int)table[another] };
            }
            throw new Exception("no answer");
        }

 

# 测试用例

        static void Main(string[] args)
        {
            _1TwoSum solution = new _1TwoSum();

            Debug.Assert(Test.match(solution.TwoSum(new int[] { 3, 2, 4 }, 6), new int[] { 1, 2 }), "wrong 1");
            Debug.Assert(Test.match(solution.TwoSum(new int[] { 5, 3, 2, 4 }, 6), new int[] { 2, 3 }), "wrong 2");
            Debug.Assert(Test.match(solution.TwoSum(new int[] { 2, 4 }, 6), new int[] { 0, 1 }), "wrong 3");
            Debug.Assert(Test.match(solution.TwoSum(new int[] { 0, 4, 3, 0 }, 0), new int[] { 0, 3 }), "wrong 4"); // duplication is result
            int[] test = { 230, 863, 916, 585, 981, 404, 316, 785, 88, 12, 70, 435, 384, 778, 887, 755, 740, 337, 86, 92, 325, 422, 815, 650, 920, 125, 277, 336, 221, 847, 168, 23, 677, 61, 400, 136, 874, 363, 394, 199, 863, 997, 794, 587, 124, 321, 212, 957, 764, 173, 314, 422, 927, 783, 930, 282, 306, 506, 44, 926, 691, 568, 68, 730, 933, 737, 531, 180, 414, 751, 28, 546, 60, 371, 493, 370, 527, 387, 43, 541, 13, 457, 328, 227, 652, 365, 430, 803, 59, 858, 538, 427, 583, 368, 375, 173, 809, 896, 370, 789 };
            int target = 542;
            Debug.Assert(Test.match(solution.TwoSum(test, target), new int[] { 28, 45 }), "wrong 5"); // duplication isn‘t result
            Debug.Assert(Test.match(solution.TwoSum(new int[] { 150, 24, 79, 50, 88, 345, 3 }, 200), new int[] { 0, 3 }), "wrong 6");          
        }
    class Test
    {
        public static bool match(Array result, Array expect)
        {
            Debug.Assert(result.Length == expect.Length, "result‘s length isn‘t equal to expect‘s length");

            for (int i = 0; i < result.Length; i++)
                if (!result.GetValue(i).Equals(expect.GetValue(i))) return false;
            return true;
        }
    }

 

LeetCode-1TwoSum(C#)

标签:

原文地址:http://www.cnblogs.com/Piers/p/5727852.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!