Showing posts with label hash table. Show all posts
Showing posts with label hash table. Show all posts

Thursday, November 27, 2014

[LeetCode] Max Points on a Line

Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.

思路:

解这个平面几何题有3个要点:

1. 如何判断共线?
两点成一直线,所以两点没有共线不共线之说。对于点p1(x1, y1),p2(x2, y2),p3(x3, y3)来说,共线的条件是p1-p2连线的斜率与p1-p3连线的斜率相同,即
(y2-y1)/(x2-x1) = (y3-y1)/(x3-x1)
所以对共线的n点,其中任意两点连线的斜率相同。

2. 如何判断最多的共线点?
对于每个点p出发,计算该点到所有其他点qi的斜率,对每个斜率统计有多少个点符合。其中最多的个数加1(出发点本身)即为最多的共线点。

3. 特殊情况
当x1 = x2,y1!=y2时,为垂直连线。计算斜率时分母为0会出错。
当x1 = x2,y1 = y2时,两点重合。则(x2, y2)和所有(x1, y1)的连线共线。


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public:
    int maxPoints(vector<Point> &points) {
        int maxPts = 0;
        for(int i=0; i<points.size(); i++) {
            int nMax = 0, nSame = 0, nInf = 0;
            unordered_map<float,int> comSlopes;
            
            for(int j=i+1; j<points.size(); j++) {
                if(points[j].x==points[i].x) {
                    if(points[j].y==points[i].y)
                        nSame++;
                    else
                        nInf++;
                    continue;
                }
                float slope = (float)(points[j].y-points[i].y)/(float)(points[j].x-points[i].x);
                comSlopes[slope]++;
                nMax = max(nMax, comSlopes[slope]);
            }
            
            nMax = max(nMax, nInf)+nSame+1;
            maxPts = max(maxPts,nMax);
        }
        return maxPts;
    }
};

[LeetCode] Word Ladder I, II

Word Ladder I

Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that:
  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the dictionary
For example,
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
Note:
  • Return 0 if there is no such transformation sequence.
  • All words have the same length.
  • All words contain only lowercase alphabetic characters.


Word Ladder II

Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:
  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the dictionary
For example,
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
Return
  [
    ["hit","hot","dot","dog","cog"],
    ["hit","hot","lot","log","cog"]
  ]
Note:
  • All words have the same length.
  • All words contain only lowercase alphabetic characters.


思路:

LeetCode中为数不多的考图的难题。尽管题目看上去像字符串匹配题,但从“shortest transformation sequence from start to end”还是能透露出一点图论中最短路径题的味道。如何转化?

1. 将每个单词看成图的一个节点。
2. 当单词s1改变一个字符可以变成存在于字典的单词s2时,则s1与s2之间有连接。
3. 给定s1和s2,问题I转化成了求在图中从s1->s2的最短路径长度。而问题II转化为了求所有s1->s2的最短路径。

无论是求最短路径长度还是求所有最短路径,都是用BFS。在BFS中有三个关键步骤需要实现:

1. 如何找到与当前节点相邻的所有节点。
这里可以有两个策略:
(1) 遍历整个字典,将其中每个单词与当前单词比较,判断是否只差一个字符。复杂度为:n*w,n为字典中的单词数量,w为单词长度。
(2) 遍历当前单词的每个字符x,将其改变成a~z中除x外的任意一个,形成一个新的单词,在字典中判断是否存在。复杂度为:26*w,w为单词长度。
这里可以和面试官讨论两种策略的取舍。对于通常的英语单词来说,长度大多小于100,而字典中的单词数则往往是成千上万,所以策略2相对较优。

2. 如何标记一个节点已经被访问过,以避免重复访问。
可以将访问过的单词从字典中删除。

3. 一旦BFS找到目标单词,如何backtracking找回路径?



Word Ladder I

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Solution {
public:
    int ladderLength(string start, string end, unordered_set<string> &dict) {
        dict.insert(end);
        queue<pair<string,int>> q;
        q.push(make_pair(start,1));
        while(!q.empty()) {
            string s = q.front().first;
            int len = q.front().second;
            if(s==end) return len;
            q.pop();
            vector<string> neighbors = findNeighbors(s, dict);
            for(int i=0; i<neighbors.size(); i++) 
                q.push(make_pair(neighbors[i],len+1));
        }
        return 0;
    }
    
    vector<string> findNeighbors(string s, unordered_set<string> &dict) {
        vector<string> ret;
        for(int i=0; i<s.size(); i++) {
            char c = s[i];
            for(int j=0; j<26; j++) {
                if(c=='a'+j) continue;
                s[i] = 'a'+j;
                if(dict.count(s)) {
                    ret.push_back(s);    
                    dict.erase(s);    
                }
            }
            s[i] = c;
        }
        return ret;
    }
};


Word Ladder II


Wednesday, November 26, 2014

[LeetCode] LRU Cache

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.
get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

思路:

LRU cache数据结构的核心就是当存储空间满了,而有新的item要插入时,要先丢弃最早更新的item。这里的数据结构需要符合以下条件:

1. 要能快速找到最早更新的item。这里需要将item以更新时间顺序排序。
可选的有:queue,heap,linked list

2. 要能快速访问指定item,并且访问以后要更新它的时间顺序。
对于更新时间顺序这个操作,queue和heap要做到就很困难了。所以这点最佳的是linked list。但linked list中查找指定item需要遍历,这里可以用一个hash table来记录key与节点之间的对应。并且由于要随时更新节点位置,doubly linked list更为适用。

根据以上两点,有了以下解法。注意由于代码较长,面试时尽可能将一些基本操作写成函数。


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
class LRUCache{
    
    struct Node {
        int val;
        int key;
        Node *next;
        Node *prev;
        Node(int k, int v):key(k),val(v) {}
    };
    
    int maxSize;
    Node* head;
    Node* tail;
    unordered_map<int,Node*> keyToNode;
    
    void insertToEnd(int key, int value) {
        if(isFull() || keyToNode.count(key)!=0) return;
        Node *nd = new Node(key, value);
        keyToNode[key] = nd;
        if(!head) {
            head = tail = nd;
        }
        else {
            tail->next = nd;
            nd->prev = tail;
            tail = tail->next;
        }
    } 
    
    void removeHead() {
        if(!head) return;
        keyToNode.erase(head->key);
        Node *temp = head;
        if(head==tail) // only one node remain
            head = tail = NULL;
        else {
            head = head->next;
            head->prev = NULL;
        }
        delete temp;
    }

    void moveToEnd(int key) {
        // key not exist, or already at the end
        if(keyToNode.count(key)==0 || keyToNode[key]==tail) return;
        Node *nd = keyToNode[key];
        if(nd==head) {
            head = head->next;
            head->prev = NULL;
        }
        else {  // not head, not tail
            nd->prev->next = nd->next;
            nd->next->prev = nd->prev; 
        }
        
        tail->next = nd;
        nd->prev = tail;
        nd->next = NULL;
        tail = tail->next;
    }
    
public:
    LRUCache(int capacity) {
        maxSize = capacity;
        head = NULL;
        tail = NULL;
        keyToNode.clear();
    }
    
    int get(int key) {
        if(keyToNode.count(key)==0) return -1;
        moveToEnd(key);
        return keyToNode[key]->val;
    }
    
    void set(int key, int value) {
        // key already exists
        if(get(key)!=-1) {
            keyToNode[key]->val = value;
            return;
        }
        
        // key not exist, insert new node
        if(isFull()) removeHead();
        insertToEnd(key, value);
    }
    
    bool isFull() {
        return keyToNode.size()>=maxSize;
    }
};

Tuesday, November 25, 2014

[LeetCode] Longest Consecutive Sequence

Given an unsorted array of integers, find the length of the longest consecutive elements sequence.
For example,
Given [100, 4, 200, 1, 3, 2],
The longest consecutive elements sequence is [1, 2, 3, 4]. Return its length: 4.
Your algorithm should run in O(n) complexity.

思路:

既然要O(n)算法,排序显然不行,所以自然想到用hash table。将序列中的所有数存到一个unordered_set中。对于序列里任意一个数A[i],我们可以通过set马上能知道A[i]+1和A[i]-1是否也在序列中。如果在,继续找A[i]+2和A[i]-2,以此类推,直到将整个连续序列找到。为了避免在扫描到A[i]-1时再次重复搜索该序列,在从每次搜索的同时将搜索到的数从set中删除。直到set中为空时,所有连续序列搜索结束。

复杂度:由于每个数字只被插入set一次,并删除一次,所以算法是O(n)的。


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution {
public:
    int longestConsecutive(vector<int> &num) {
        if(num.empty()) return 0;
        unordered_set<int> ht;
        for(int i=0; i<num.size(); i++)
            ht.insert(num[i]);
            
        int maxLen = 1;
        for(int i=0; i<num.size(); i++) {
            if(ht.empty()) break;
            int curLen = 0;
            int curNum = num[i];
            
            while(ht.count(curNum)) {
                ht.erase(curNum);
                curLen++;
                curNum++;
            }
            
            curNum = num[i]-1;
            while(ht.count(curNum)) {
                ht.erase(curNum);
                curLen++;
                curNum--;
            }
            
            maxLen = max(maxLen, curLen);
        }
        
        return maxLen;
    }
};

Sunday, November 23, 2014

[LeetCode] Anagrams

Given an array of strings, return all groups of strings that are anagrams.
Note: All inputs will be in lower-case.

思路:

Anagrams指几个string有相同的字符,但不同的字符顺序。所以一个有效的检查方法是:当两个string排序以后相同,则它们是anagrams。可以使用一个hash table,string s的key是它自己排序后的string,这样anagrams会有相同的key。用一个vector<int>来记录相同key的string在input vector<string>中的index。最后扫描一遍hash table,当有两个或以上string有相同的key时,将它们输出到结果。


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
    vector<string> anagrams(vector<string> &strs) {
        vector<string> ret;
        unordered_map<string,vector<int>> ht;
        
        for(int i=0; i<strs.size(); i++) {
            string key = strs[i];
            sort(key.begin(),key.end());
            ht[key].push_back(i);
        }
            
        for(unordered_map<string,vector<int>>::iterator it=ht.begin(); it!=ht.end(); it++) {
            if(it->second.size()>1) {
                for(int i=0; i<it->second.size(); i++) {
                    ret.push_back(strs[it->second[i]]);    
                }
            }
        }
        return ret;
    }
};

Thursday, November 20, 2014

[LeetCode] Clone Graph

Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors.

OJ's undirected graph serialization:
Nodes are labeled uniquely.
We use # as a separator for each node, and , as a separator for node label and each neighbor of the node.
As an example, consider the serialized graph {0,1,2#1,2#2,2}.
The graph has a total of three nodes, and therefore contains three parts as separated by #.
  1. First node is labeled as 0. Connect node 0 to both nodes 1 and 2.
  2. Second node is labeled as 1. Connect node 1 to node 2.
  3. Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a self-cycle.
Visually, the graph looks like the following:
       1
      / \
     /   \
    0 --- 2
         / \
         \_/


思路

和Copy List with Random Pointer那题的思路一样。用一个hash table记录原图节点和复制图节点间的对应关系,以防止重复建立节点。和那题的不同在于遍历原图相对比linked list的情况复杂一点。可以用BFS或DFS来遍历原图。而hash table本身除了记录对应关系外,还有记录原图中每个节点是否已经被visit的功能。

BFS遍历:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution {
public:
    UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
        if(!node) return NULL;
        UndirectedGraphNode *p1 = node;
        UndirectedGraphNode *p2 = new UndirectedGraphNode(node->label);
        unordered_map<UndirectedGraphNode*, UndirectedGraphNode*> ht;
        queue<UndirectedGraphNode*> q;        
        q.push(node);
        ht[node] = p2;
        
        while(!q.empty()) {
            p1 = q.front();
            p2 = ht[p1];
            q.pop();
            for(int i=0; i<p1->neighbors.size(); i++) {
                UndirectedGraphNode *nb = p1->neighbors[i];
                if(ht.count(nb)) {
                    p2->neighbors.push_back(ht[nb]);
                }
                else {
                    UndirectedGraphNode *temp = new UndirectedGraphNode(nb->label);
                    p2->neighbors.push_back(temp);
                    ht[nb] = temp;
                    q.push(nb);
                }
            }
        }
        
        return ht[node];
    }
};


DFS遍历:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public:
    UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
        if(!node) return NULL;
        unordered_map<UndirectedGraphNode*, UndirectedGraphNode*> ht;
        stack<UndirectedGraphNode*> s;        
        s.push(node);
        ht[node] = new UndirectedGraphNode(node->label);
        
        while(!s.empty()) {
            UndirectedGraphNode *p1 = s.top(), *p2 = ht[p1];
            s.pop();
            
            for(int i=0; i<p1->neighbors.size(); i++) {
                UndirectedGraphNode *nb = p1->neighbors[i];
                if(ht.count(nb)) {
                    p2->neighbors.push_back(ht[nb]);
                }
                else {
                    UndirectedGraphNode *temp = new UndirectedGraphNode(nb->label);
                    p2->neighbors.push_back(temp);
                    ht[nb] = temp;
                    s.push(nb);
                }
            }
        }
        
        return ht[node];
    }
};

Wednesday, November 19, 2014

[LeetCode] Copy List with Random Pointer

A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.
Return a deep copy of the list.

思路:

这题的关键是如何track一个节点是否已经被copy了。假如我们要copy如下list,用指针p1来扫描每个节点,另一个指针p2建立copy。
______
|           |
|          V
1->2->3

p1扫描1时,p2复制1,以及1->next (2), 1->random (3)。之后p1, p2分别移到各自的2节点。此时我们必须得知道节点3在之前已经被复制了,并且得知道复制节点的地址。
______
|           |
|          V
1->2    3 

所以这里可以使用一个hash table来记录原节点和复制节点的地址对应关系。这样每次要建立当前节点p的next和random前,先在hash table中查找。如果找到,则直接连接;否则建立新节点连上,并把和原节点的对应关系存入hash table中。



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution {
public:
    RandomListNode *copyRandomList(RandomListNode *head) {
        if(!head) return NULL;
        unordered_map<RandomListNode*, RandomListNode*> ht;
        RandomListNode *p1 = head;
        RandomListNode *p2 = new RandomListNode(head->label);
        ht[head] = p2;
        while(p1) {
            if(p1->next) {
                if(ht.count(p1->next))
                    p2->next = ht[p1->next];
                else {
                    p2->next = new RandomListNode(p1->next->label);
                    ht[p1->next] = p2->next;
                }
            }
            if(p1->random) {
                if(ht.count(p1->random))
                    p2->random = ht[p1->random];
                else {
                    p2->random = new RandomListNode(p1->random->label);
                    ht[p1->random] = p2->random;
                }
            }
            p1 = p1->next;
            p2 = p2->next;
        }
        return ht[head];
    }
};

Monday, November 17, 2014

[LeetCode] Substring with Concatenation of All Words

You are given a string, S, and a list of words, L, that are all of the same length. Find all starting indices of substring(s) in S that is a concatenation of each word in L exactly once and without any intervening characters.
For example, given:
S"barfoothefoobarman"
L["foo", "bar"]
You should return the indices: [0,9].
(order does not matter).


思路:

和strStr那题的双指针解法类似。关键在于如何判断以任意i起始的S的substring是否整个L的concatenation。这里显然要用到hash table。由于L中可能存在重复的word,所以hash table的key = word,val = count of the word。

在建立好L的hash table后,对每个S[i]进行检查。这里的一个技巧建立一个新的hash table记录已经找到的word。因为L的hash table需要反复利用,不能被修改,并且如果以hash table作为参数进行值传递的化,时间空间消耗都很大。


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
public:
    vector<int> findSubstring(string S, vector<string> &L) {
        vector<int> allPos;
        if(L.empty()) return allPos;
        int totalWords = L.size();
        int wordSize = L[0].size();
        int totalLen = wordSize * totalWords;
        if(S.size()<totalLen) return allPos;
        
        unordered_map<string,int> wordCount;
        for(int i=0; i<totalWords; i++)
            wordCount[L[i]]++;
        
        for(int i=0; i<=S.size()-totalLen; i++) {
            if(checkSubstring(S, i, wordCount, wordSize, totalWords))
                allPos.push_back(i);
        }
        return allPos;
    }
    
    bool checkSubstring(string S, int start, unordered_map<string,int> &wordCount, int wordSize, int totalWords) {
        if(S.size()-start+1 < wordSize*totalWords) return false;
        unordered_map<string,int> wordFound;
        
        for(int i=0; i<totalWords; i++) {
            string curWord = S.substr(start+i*wordSize,wordSize);
            if(!wordCount.count(curWord)) return false;
            wordFound[curWord]++;
            if(wordFound[curWord]>wordCount[curWord]) return false;
        }
        return true;
    }
};

Sunday, November 16, 2014

[LeetCode] Longest Substring Without Repeating Characters

Given a string, find the length of the longest substring without repeating characters. For example, the longest substring without repeating letters for "abcabcbb" is "abc", which the length is 3. For "bbbbb" the longest substring is "b", with the length of 1.


思路:

hash table经常是一种有效的工具来判断重复,C++的STL中的unordered_map和unordered_set实现了hash table/set的O(1) 查找功能。

用hash table 来存储当前没有重复字符的substring的所有字符(key)以及它们的位置(val)。

假如当前有效substring为:s[k : i-1]
hash table存储了:{s[k], k}, {s[k+1], k+1}, ... {s[i-1], i-1}

当下一个字符s[i]仍然是新字符时,插入即可。而当下一个字符已经存在于hash table中,则hash table记录了S[i]字符上一次出现的坐标j (k<=j<i),而S[j+1:i]形成了新的有效substring。这里需要额外操作的是在hash table中删除{s[k], k} ... {s[j], j},因为它们已经不在新的substring中。

如何在hash table中删除s[k:j]?当然可以遍历hash table来检查每个坐标,但如果hash table很大则会很浪费。所以我们需要记录每个有效substring的起始位置k,用双指针+hash table来解决。

复杂度分析:
空间复杂度最差情况显然是O(n):当整个s没有重复时。
时间复杂度看似复杂,因为我们并不知道每次对hash table删除的复杂度。但对于每个s[i]来说,最多只会插入/更新hash table一次,以及被删除一次。不存在被多次插入删除的可能。所以最终复杂度仍然是O(n)



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char,int> ht;
        int maxLen = 0, curLen = 0, start = 0;
        for(int i=0; i<s.size(); i++) {
            if(ht.count(s[i])) {
                for(int j=start; j<ht[s[i]]; j++) 
                    ht.erase(s[j]);
                start = ht[s[i]]+1;
                curLen = i-ht[s[i]];
            }
            else {
                curLen++;
            }
                
            ht[s[i]] = i;
            maxLen = max(maxLen,curLen);
        }
        return maxLen;
    }
};

Saturday, November 15, 2014

[LeetCode] Two Sum

Given an array of integers, find two numbers such that they add up to a specific target number.
The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.
You may assume that each input would have exactly one solution.
Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2


思路1:hash table

对于数组中每个数来A[i]来说,需要在数组的其他元素中寻找target - A[i]。所以问题转化为数组查找元素问题,当给定一个值时,需要能快速判断是否存在于数组中,如果存在,index是多少。

可以将所有元素插入一个hash table <key = A[i], val = i>,然后重新遍历每个元素,计算target - A[i],然后在hash table中查找。这里仍需解决2个问题:

(1) 重复元素的问题:比如{2 2 3}, target = 4。解决方法为hash table的val变为一个vector<int>来记录所有j:A[j] = A[i]。
(2) index 1 < index 2:假设A[i] + A[j] = target并且i<j。那么在遍历查找时从左向右,一定在扫描i的时候就找到个solution。返回按照这个顺序即可。

时间和额外空间复杂度均为O(n)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public:
    vector<int> twoSum(vector<int> &numbers, int target) {
        vector<int> res;
        int first = -1, second = -1;
        unordered_map<int, vector<int> > ht;
        for(int i=0; i<numbers.size(); i++) 
            ht[numbers[i]].push_back(i);
        
        for(int i=0; i<numbers.size(); i++) {
            int val = target - numbers[i];
            if(ht.count(val)) {
                if(numbers[i]!=val) {
                    first = i+1;
                    second = ht[val][0]+1;
                    break;
                }
                else if(ht[val].size()>1) {
                    first = ht[val][0]+1;
                    second = ht[val][1]+1;
                    break;
                }
            }
        }
        
        res.push_back(first);
        res.push_back(second);
        return res;
    }
};

思路2:two pointers

将array排序,双指针left/right分别指向头尾。然后两个指针分别向中间移动寻找目标。
(1) A[left] + A[right] = target:直接返回(left+1, right+1)。
(2) A[left] + A[right] > target:说明A[right]不可能是解,right--
(3) A[left] + A[right] < target:说明A[left]不可能是解,left++
中止条件:left >= right

但排序会打乱原来数组index的顺序。我们可以建立一个class/struct/pair来存储val/index,并overload operator < 来以val值排序。这样我们可以track排序后每个数的原有index。

重复元素这里无须特殊处理。index 1和index 2分别取找到的两个index的min/max即可。时间复杂度由于排序的关系为O(n log n),额外空间复杂度O(n)。


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
    class elem {
    public:    
        int val;
        int index;
        elem(int v, int i):val(v),index(i) {}
        bool operator<(const elem &e) const {
            return val<e.val;
        }
    };
    
public:
    vector<int> twoSum(vector<int> &numbers, int target) {
        vector<int> res(2,-1);
        vector<elem> arr;
        for(int i=0; i<numbers.size(); i++) 
            arr.push_back(elem(numbers[i],i));

        sort(arr.begin(),arr.end());
        int left = 0, right = arr.size()-1;
        while(left<right) {
            if(arr[left].val+arr[right].val==target) {
                res[0] = min(arr[left].index,arr[right].index)+1;
                res[1] = max(arr[left].index,arr[right].index)+1;
                break;
            }
            else if(arr[left].val+arr[right].val<target) 
                left++;
            else
                right--;
        }
        return res;
    }
};