LeetCode OJ is a platform for preparing technical coding
interviews.
LeetCode OJ 是为与写代码有关的技术工作面试者设计的训练平台。
LeetCode OJ:http://oj.leetcode.com/ 
默认题目顺序为题目添加时间倒叙,本文题解顺序与OJ题目顺序一致(OJ会更新,至少目前一致。。。)。
习惯大括号换行,遇到大括号默认不换行的样例代码,导致有的换行有的没换行,实在懒得一个个改……
2015.11.17更新:今天又看leetcode,发现题目列表终于有编号了。
咦,新题目加锁了,要25 dollar一个月的捐赠……
Made By:CSGrandeur : LeetCode
题解 
20170209
 
基本字典树
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 class  Trie  {public :    struct  Node      {         Node *nex[26 ];         bool  word;         Node (){memset (nex, NULL , sizeof (nex)), word = false ;}     };     Node *root;          Trie () {         root = new  Node;     }          void  insert (string word)           Node *p = root;         for (auto  c : word)         {             if (p->nex[c - 'a' ] == NULL )                 p->nex[c - 'a' ] = new  Node;             p = p->nex[c - 'a' ];         }         p->word = true ;     }          bool  search (string word)           Node *p = root;         for (auto  c : word)         {             if (p->nex[c - 'a' ] == NULL )                 return  false ;             p = p->nex[c - 'a' ];         }         return  p->word;     }          bool  startsWith (string prefix)           Node *p = root;         for (auto  c : prefix)         {             if (p->nex[c - 'a' ] == NULL )                 return  false ;             p = p->nex[c - 'a' ];         }         return  true ;     } }; 
20170208
 
先构造个图。一开始没想拓扑排序的事,就用DFS来判断的,标记-1表示尚未考察,0表示暂没结果,1表示确认可选修。
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 :    vector<int > fst, nex, v;     vector<int > ok;     bool  DFS (int  i)       {        if (ok[i] != -1 ) return  ok[i];         ok[i] = 0 ;         for (int  j = fst[i]; j != -1 ; j = nex[j])             if (!DFS (v[j]))                 return  ok[i] = 0 ;         return  ok[i] = 1 ;     }     bool  canFinish (int  numCourses, vector<pair<int , int >>& prerequisites)           fst.resize (numCourses + 1 , -1 );         ok.resize (numCourses + 1 , -1 );         for (int  i = 0 ; i < prerequisites.size (); i ++)         {             nex.push_back (fst[prerequisites[i].first]);             fst[prerequisites[i].first] = nex.size () - 1 ;             v.push_back (prerequisites[i].second);         }         for (int  i = numCourses - 1 ; i >= 0 ; i --)             numCourses -= DFS (i);         return  !numCourses;     } }; 
20170208
 
好像又是老题
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 :    ListNode* reverseList (ListNode* head)   {         ListNode *pre = NULL ;         while (head != NULL )         {             ListNode *tmp = head->next;             head->next = pre;             pre = head;             head = tmp;         }         return  pre;     } }; 
20170208
 
思路一是两个map互相映射,出现矛盾则说明不同构。两个map用于解决其中一个串可能有多个字符对应另一个串同一个字符,只用一个map则会掉这个坑。、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class  Solution  {public :    bool  isIsomorphic (string s, string t)           vector<char > mp1 (128 , 0 ) , mp2 (128 , 0 )  ;         for (int  i = 0 ; i < s.length (); i ++)         {             if (!mp1[s[i]]) mp1[s[i]] = t[i];             if (!mp2[t[i]]) mp2[t[i]] = s[i];             if (mp1[s[i]] != t[i] || mp2[t[i]] != s[i])                 return  false ;         }         return  true ;     } }; 
思路二是把俩字符串按同一规则转换,转换后字符串直接比较。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  Solution  {public :    void  convert (string &s)       {        vector<char > mp (128 , 0 )  ;         int  p = 1 ;         for (int  i = 0 ; i < s.length (); i ++)         {             if (!mp[s[i]]) mp[s[i]] = p ++;             s[i] = mp[s[i]];         }     }     bool  isIsomorphic (string s, string t)           convert (s);         convert (t);         return  s == t;     } }; 
20170208
 
筛素数,以前比赛时候写的滚瓜烂熟的,现在竟然忘了还复习一下……
从2开始把每个其倍数都标记成合数,然后往后枚举,枚举到的没标记的一定是素数(如果它是合数,它的质因数肯定比它小,而比它小的质数的倍数都已经标记过了),继续标记其所有倍数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  Solution  {public :    int  countPrimes (int  n)           int  ret = 0 ;         vector<bool > pr (n, true )  ;         for (int  i = 2 ; i < n; i ++)         {             if (pr[i])             {                 ret ++;                 for (int  j = i << 1 ; j < n; j += i)                     pr[j] = false ;             }         }         return  ret;     } }; 
20170208
 
好像又是老题,开头加个哨兵节点就不用担心head节点被删除的情况了。
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 class  Solution  {public :    ListNode* removeElements (ListNode* head, int  val)   {         ListNode *pre = new  ListNode (0 );         pre->next = head;         for (ListNode *p = pre; head != NULL ; head = head->next)         {             if (head->val == val)             {                 p->next = head->next;             }             else                  p = head;         }         return  pre->next;     } }; 
20170208
 
直接思路是用哈希记录判循环,降低空间复杂度可以用两个值一个每次算一步,另一个每次算两步来判循环。
还有个方法是只要大于 6 就一直进行,能保证计算结束,不知道怎么证明的。
代码放传统思路。
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  SumSquare (int  x)       {        int  ret = 0 ;         while (x)         {             ret += (x % 10 ) * (x % 10 );             x /= 10 ;         }         return  ret;     }     bool  isHappy (int  n)           unordered_set<int > s;         while (!s.count (n))         {             s.insert (n);             n = SumSquare (n);         }         return  s.count (1 );     } }; 
20170208
 
二进制从左往右看,当遇到某位不相等的时候,最终结果后面的位都会是0,因为在n~m之间,连续的数字中这些位肯定会出现0。题目巧在如果思路转换为求二进制最长相同前缀,代码就很简单了。
1 2 3 4 5 6 7 8 9 10 11 12 13 class  Solution  {public :    int  rangeBitwiseAnd (int  m, int  n)           int  cnt = 0 ;         while (m != n)         {             m >>= 1 ;             n >>= 1 ;             cnt ++;         }         return  m << cnt;     } }; 
还有一种理解方式也很好,把n的右侧的1一一去掉,当n不大于m的时候,n剩下的就是那个相同前缀。
1 2 3 4 5 6 7 8 class  Solution  {public :    int  rangeBitwiseAnd (int  m, int  n)           while (n > m)             n = n & (n - 1 );         return  n;     } }; 
20170208
 
我一定是做了道假题。。DFS、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 33 34 35 36 37 38 39 40 41 class  Solution  {public :    const  int  dx[4 ] = {1 , -1 , 0 , 0 };     const  int  dy[4 ] = {0 , 0 , 1 , -1 };     int  row, col;     void  BFS (vector<vector<char >> &grid, int  x, int  y)       {        queue<pair<int , int > > q;         q.push (make_pair (x, y));         grid[x][y] = 0 ;         while (!q.empty ())         {             pair<int , int > now = q.front ();             q.pop ();             for (int  i = 0 ; i < 4 ; i ++)             {                 pair<int , int > nex = make_pair (now.first + dx[i], now.second + dy[i]);                 if (nex.first >= 0  && nex.first < row && nex.second >= 0  && nex.second < col && grid[nex.first][nex.second] == '1' )                  {                     grid[nex.first][nex.second] = 0 ;                     q.push (nex);                 }             }         }     }     int  numIslands (vector<vector<char >>& grid)           int  ret = 0 ;         if (grid.size () == 0 ) return  0 ;         row = grid.size ();         col = grid[0 ].size ();         for (int  i = 0 ; i < grid.size (); i ++)         {             for (int  j = 0 ; j < grid[0 ].size (); j ++)             {                 if (grid[i][j] == '1' )                     ret ++, BFS (grid, i, j);             }         }         return  ret;     } }; 
20170208
 
二叉树层次遍历,加个层数标记。
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 class  Solution  {public :    struct  Node      {         TreeNode *p;         int  layer;         Node (TreeNode *p_, int  layer_):p (p_), layer (layer_){}     };     vector<int > rightSideView (TreeNode* root)   {         queue<Node> q;         vector<int > ret;         if (root != NULL )             q.push (Node (root, 0 ));         int  last = -1 ;         while (!q.empty ())         {             Node now = q.front ();             q.pop ();             if (last != now.layer)             {                 last = now.layer;                 ret.push_back (now.p->val);             }             if (now.p->right != NULL )                 q.push (Node (now.p->right, now.layer + 1 ));             if (now.p->left != NULL )                 q.push (Node (now.p->left, now.layer + 1 ));         }         return  ret;     } }; 
20170207
 
简单的动态规划,两个数一个表示上一个没rob的当前最佳收益,另一个表示上一个rob了的,反复更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 class  Solution  {public :    int  rob (vector<int >& nums)           int  dp[2 ] = {0 };         for (auto  m : nums)         {             int  tmp = dp[1 ];             dp[1 ] = max (dp[1 ], dp[0 ] + m);             dp[0 ] = max (dp[0 ], tmp);         }         return  max (dp[1 ], dp[0 ]);     } }; 
20170207
 
做过树状数组应该对n&(n-1)很熟了,n-1让二进制末尾的1变0,这个1后面的0都变1,和n做按位与就相当于去掉了最后一个1,这样每次操作都能去掉一个1,一位一位统计就快多了。
1 2 3 4 5 6 7 8 9 10 11 12 class  Solution  {public :    int  hammingWeight (uint32_t  n)           int  cnt = 0 ;         while (n)         {             cnt ++;             n = n & (n - 1 );         }         return  cnt;     } }; 
20170207
 
首先是最直接的方法,把所有bit放到新数中的对应位置。 题目提到一句“If
this function is called many times, how would you optimize
it?”是很值得思考的。直接的方法进行了32次操作,每个操作里有若干次位运算,这个数字是否可以优化呢?
一个可能思路是并行的分治。问题二分,并行用位运算实现,这样就log(32)=5次操作了。
直接思路:
1 2 3 4 5 6 7 8 9 class  Solution  {public :    uint32_t  reverseBits (uint32_t  n)           uint32_t  ret = 0 ;         for (int  i = 0 ; i < 32 ; i ++)             ret |= ((n >> i) & 1 ) << (31  - i);         return  ret;     } }; 
并行分治思路:
1 2 3 4 5 6 7 8 9 10 11 class  Solution  {public :    uint32_t  reverseBits (uint32_t  n)           n = (n & 0x0000FFFF ) << 16  | (n & 0xFFFF0000 ) >> 16 ;         n = (n & 0x00FF00FF ) << 8  | (n & 0xFF00FF00 ) >> 8 ;         n = (n & 0x0F0F0F0F ) << 4  | (n & 0xF0F0F0F0 ) >> 4 ;         n = (n & 0x33333333 ) << 2  | (n & 0xCCCCCCCC ) >> 2 ;         n = (n & 0x55555555 ) << 1  | (n & 0xAAAAAAAA ) >> 1 ;         return  n;     } }; 
20170207
 
空间复杂度不是O(1)的想都不要想。网上方法已经很多了,如:
1、后k个翻转,前面的翻转,再整个翻转。
2、前k个和后k个一一swap,就变成后n-k个做k旋转的子问题了,继续进行到结束。
3、把数组分成若干个以k为间隔的“子数组”,而且这个“子数组”是可以循环回去的,比如1, 2, 3, 4, 5,
k=3,“子数组”就是1, 4, 2, 5, 3,对它做step=1的右循环。
这里放 方法3 的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  Solution  {public :    void  rotate (vector<int >& nums, int  k)           k = k % nums.size ();         int  readynum = 0 ;         for (int  i = 0 ; readynum < nums.size (); i ++)         {             int  tmp = nums[i];             for (int  j = i + k; ; j = (j + k) % nums.size ())             {                 swap (tmp, nums[j]);                 readynum ++;                 if (j == i)                     break ;             }         }     } }; 
20170207
 
一开始思路是常规的记忆化搜索,状态记录为[第i天价格][剩下k次][是否在买入状态],是否买入状态是二值的,所以内存主要在于天数和次数的取值,小trick是k取值过大时候,可缩小到天数的1/2。结果还是超内存了,虽然网上看到有类似解法没超内存,不过看最大那组数据,还是不适合开二维数组的。
这题有个特别之处,当考察第i天时,并不关心这是多少天了,而关心的只是当前的盈利情况和剩下多少次交易机会,于是数组缩小为次数k这一维。两个数组分别表示剩下k次的买入状态余额和非买入状态余额,状态方程是buy[k]=max(buy[k], sell[k-1] - price); sell[k]=max(sell[k], buy[k] + price);。
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  maxProfit (int  k, vector<int >& prices)           if (k > prices.size () / 2 )             k = prices.size () / 2 ;         vector<int > buy (k + 1 , -0x3f3f3f3f ) , sell (k + 1 , 0 )  ;         int  ret = 0 ;         for (auto  price : prices)         {             for (int  i = k; i > 0 ; i --)             {                 buy[i] = max (buy[i], sell[i - 1 ] - price);             }             for (int  i = k; i > 0 ; i --)             {                 sell[i] = max (sell[i], buy[i] + price);                 ret = max (sell[i], ret);             }         }         return  ret;     } }; 
20170207
 
没遇到网上说的内存问题,直接unordered_map过掉了。用字典树应该会比较快和稳定吧。
对每个长度为10的串进行统计,大于1次就做记录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  Solution  {public :    vector<string> findRepeatedDnaSequences (string s)   {         unordered_map<string, bool > mp;         vector<string> ret;         for (int  i = 10 ; i <= s.length (); i ++)         {             string tmp = s.substr (i - 10 , 10 );             if (!mp.count (tmp))                 mp[tmp] = 0 ;             else  if (mp[tmp] == 0 )             {                 ret.push_back (tmp);                 mp[tmp] = 1 ;             }         }         return  ret;     } }; 
一开始用递归写了个复杂逻辑的comp函数,后来发现,主要确定两个字符串 a,
b 的 a+b 和 b+a 的大小关系,就能确定 a 和 b 的顺序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  Solution  {  public :      static  bool  cmp (const  string &a, const  string &b)       {        return  a + b > b + a;     }     string largestNumber (vector& nums)   {         vector vs;         for (auto  num : nums)             vs.push_back (to_string (num));         sort (vs.begin (), vs.end (), cmp);         if (atoi (vs[0 ].c_str ()) == 0 ) return  "0" ;         string res = "" ;         for (auto  v : vs) res += v;         return  res;     } }; 
从左上角开始 DP 的话,无法做到既贪心当前最优,又保证路径最优。
从右下角开始 DP,则可以只考虑路径最优,dp[i][j]表示从(i,
j)位置到终点所需最小health值。
这道题也可以用二分答案来直接从左上角贪心,不过速度会慢很多。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class  Solution  {  public :      int  calculateMinimumHP (vector& dungeon)           int  rows = dungeon.size (), cols = dungeon[0 ].size ();         vectordp (rows);         for (int  i = rows - 1 ; i >= 0 ; i --)         {             dp[i] = vector (cols, 0 );             for (int  j = cols - 1 ; j >= 0 ; j --)             {                 if (i < rows - 1  && j < cols - 1 )                     dp[i][j] = max (1 , min (dp[i][j + 1 ], dp[i + 1 ][j]) - dungeon[i][j]);                 else  if (i < rows - 1 ) dp[i][j] = max (1 , dp[i + 1 ][j] - dungeon[i][j]);                 else  if (j < cols - 1 ) dp[i][j] = max (1 , dp[i][j + 1 ] - dungeon[i][j]);                 else  dp[i][j] = max (1 , -dungeon[i][j] + 1 );             }         }         return  dp[0 ][0 ];     } }; 
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 class  Solution  {  public :      int  calculateMinimumHP (vector& dungeon)           int  left = 1 , right = 0x3f3f3f3f , mid;         while (left < right) { mid = left + right >> 1 ;             if (Test (mid, dungeon)) right = mid;             else  left = mid + 1 ;         }         return  left;     }     bool  Test (int  mid, vector tmp)       {        int  rows = tmp.size (), cols = tmp[0 ].size ();         for (int  i = 0 ; i < rows; i ++)         {             for (int  j = 0 ; j < cols; j ++) { if (i && j && tmp[i - 1 ][j] + mid > 0  && tmp[i][j - 1 ] + mid > 0 )                     tmp[i][j] = max (tmp[i - 1 ][j] + tmp[i][j], tmp[i][j - 1 ] + tmp[i][j]);                 else  if (i && tmp[i - 1 ][j] + mid > 0 )                     tmp[i][j] = tmp[i - 1 ][j] + tmp[i][j];                 else  if (j && tmp[i][j - 1 ] + mid > 0 )                     tmp[i][j] = tmp[i][j - 1 ] + tmp[i][j];                 else  if (i || j)                     tmp[i][j] = -mid;             }         }         return  tmp[rows - 1 ][cols - 1 ] + mid > 0 ;     } }; 
变形的非递归中序遍历。
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 class  BSTIterator  {  public :      stack s;     TreeNode *now;     BSTIterator (TreeNode *root) {         while (!s.empty ()) s.pop ();         now = root;         while (now)         {             s.push (now);             now = now->left;         }     }          bool  hasNext ()           return  !s.empty ();     }          int  next ()           now = s.top ();         s.pop ();         TreeNode *tmp = now;         now = now->right;         while (now)         {             s.push (now);             now = now->left;         }         return  tmp->val;     } }; 
阶乘的每个数中,每个因子 2 和每个因子 5 可以构成一个0,而 2 远比 5
多,比如 8 就有三个 2,于是统计因子 5 的个数。
让输入 n 迭代除以 5,第一次得到 5 的倍数的个数,第二次是 25
倍数的个数,最后得到所有因子 5 的个数。
1 2 3 4 5 6 7 8 9 class  Solution  {  public :      int  trailingZeroes (int  n)           int  ret = 0 ;         while (n)             ret += n /= 5 ;         return  ret;     } }; 
比用Number算Title容易点,在算每一位时候记得加 1 。
1 2 3 4 5 6 7 8 9 class  Solution  {  public :      int  titleToNumber (string s)           int  ret = 0 ;         for (auto  c : s)             ret = ret * 26  + c - 'A'  + 1 ;         return  ret;     } }; 
很有意思的题目,要求的数占超过一半,那么在O(n)时间里把不同的数抵消掉,“多数数”肯定会被留下来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class  Solution  {  public :      int  majorityElement (vector& nums)           int  x, cnt = 0 ;         for (auto  num : nums)         {             if (cnt == 0 )                 x = num, cnt ++;             else                  cnt += num == x ? 1  : -1 ;         }         return  x;     } }; 
跳过 0 的 26 进制,处理每一位的时候要减 1 。
1 2 3 4 5 6 7 8 9 10 11 12 13 class  Solution  {  public :      string convertToTitle (int  n)   {         string ret;         while (n)         {             ret += (n - 1 ) % 26  + 'A' ;             n = (n - 1 ) / 26 ;         }         reverse (ret.begin (), ret.end ());         return  ret;     } }; 
有边界数据,所以直接转成long long做,处理正负号。
对小数部分,每次求余数乘 10
再做下一位,当余数重复出现就是循环了。找重复用unordered_map。
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 class  Solution  {  public :      string fractionToDecimal (int  numerator, int  denominator)   {         string ret = "" ;         long  long  num = numerator, deno = denominator, nm;         if (num * deno < 0 ) ret = "-" ;         num = abs (num), deno = abs (deno);         ret += to_string (num / deno);         if (num % deno == 0 ) return  ret;         unordered_map mp;         ret += '.' ;         for (long  long  now = num % deno * 10 ; now; )         {             mp[now] = ret.length ();             ret += '0'  + now / deno;             now = now % deno * 10 ;             if (mp.count (now))             {                 ret.insert (mp[now], 1 , '(' );                 ret += ')' ;                 break ;             }         }         return  ret;     } }; 
按.分开,逐个按数字大小比较。
C++怎么就没个方便的split函数呢。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class  Solution  {  public :      int  compareVersion (string version1, string version2)           int  vpos1 (0 ) , vpos2 (0 ) , lp1 (0 ) , lp2 (0 )          version1 += "." , version2 += "." ;         while (vpos1 < version1.l ength() || vpos2 < version2.l ength())         {             int  vnum1 (0 ) , vnum2 (0 )              vpos1 = version1.f ind("." , lp1);             vpos2 = version2.f ind("." , lp2);             if (vpos1 < version1.l ength())                 vnum1 = atoi (version1. substr (lp1, vpos1 - lp1).c_str ()), lp1 = vpos1 + 1 ;             if (vpos2 < version2.l ength())                  vnum2 = atoi (version2. substr (lp2, vpos2 - lp2).c_str ()), lp2 = vpos2 + 1 ;              if (vnum1 > vnum2) return  1 ;             else  if (vnum1 <vnum2) return  -1 ;         }         return  0 ;     } }; 
很久没碰过桶排序了,这道题有个很有意思的推理——把 n 个数放到 n
个桶里,两种情形,1、每个桶一个数。2、存在有多个数的桶,那也一定有其他的空桶。
这两种情况下,最大gap都是某个桶的最小值与它前面最近的有数的桶的最大值之差,所以避免了桶内排序,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 class  Solution  {  public :      int  maximumGap (vector& nums)           if (nums.size () < 2 ) return  0 ;         int  maxNum = *max_element (nums.begin (), nums.end ());         int  minNum = *min_element (nums.begin (), nums.end ());         int  gap = ceil ((double )(maxNum - minNum) / (nums.size () - 1 ));         int  bcnt = nums.size ();         if (gap == 0 ) return  0 ;         vector<pair<int , int > > bucket (bcnt, make_pair (-1 , -1 ));         for (int  i = 0 ; i < nums.size (); i ++)          {              int  bsite = (nums[i] - minNum) / gap;              if (bucket[bsite].first == -1  || bucket[bsite].first > nums[i])                 bucket[bsite].first = nums[i];             if (bucket[bsite].second == -1  || bucket[bsite].second < nums[i])                 bucket[bsite].second = nums[i];         }         int  last = minNum, ans = 0 ;         for (int  i = 0 ; i < bucket.size (); i ++)         {             if (bucket[i].first != -1 )                 ans = max (ans, bucket[i].first - last);             if (bucket[i].second != -1 )                 last = bucket[i].second;         }         return  ans;     } }; 
O(logn)
1 2 3 4 5 6 7 8 9 10 11 12 class  Solution  {  public :      int  findPeakElement (vector& nums)           int  left = 0 , right = nums.size () - 1 , mid;         while (left < right) { mid = left + right >> 1 ;             if (nums[mid] < nums[mid + 1 ])                 left = mid + 1 ;             else  right = mid;         }         return  left;     } }; 
先统计两个链表长度,将 长链表 对准 距离末尾 与短链表长度相同
位置,同步遍历返回交叉点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class  Solution  {  public :      ListNode *getIntersectionNode (ListNode *headA, ListNode *headB)   {         int  na, nb;         ListNode *ha = headA, *hb = headB;         for (na = nb = 0 ; ha || hb;)         {             if (ha) na ++, ha = ha->next;             if (hb) nb ++, hb = hb->next;         }         while (na > nb) headA = headA->next, na --;         while (nb > na) headB = headB->next, nb --;         while (headA != headB) headA = headA->next, headB = headB->next;         return  headA;     } }; 
一开始做的是每个元素存两个数的数组,一个表示数值,一个表示“上一个最小值下标”。然后觉得这样的话,不是最小值的那些位置,其实浪费了空间。
还是用两个数组或者两个栈,一个存数,一个存最小值好了。
看Discuss里24ms的答案也没什么特别的,运行时间的浮动吧。
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 class  MinStack  {  public :      stack v;     stack m;     void  push (int  x)           if (v.empty () || x <= m.top ())             m.push (x);         v.push (x);     }     void  pop ()           if (v.empty ()) return ;         if (!m.empty () && m.top () == v.top ())             m.pop ();         v.pop ();     }     int  top ()           return  v.top ();     }     int  getMin ()           return  m.top ();     } }; 
首先如果首尾大量相同元素,那么“先去掉末尾连续相同元素”和“二分的时候nums[mid]==nums[left]时left++”这样的方法,都会退化为O(n)。于是尽可能想二分的方法,有了下面这个分治的方法,然而经不起推敲,其实即便分治,还是会退化成O(n)。贴上两种代码吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class  Solution  {  public :      int  findMin (vector& nums)           int  left = 0 , right = nums.size () - 1 , mid = 0 ;         while (left < right && nums[left] >= nums[right])         {             mid = left + right >> 1 ;             if (nums[mid] > nums[left]) left = mid + 1 ;             else  if (nums[mid] == nums[left]) left ++;             else  right = mid;         }         return  nums[left];     } }; 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class  Solution  {  public :      int  findMin (vector& nums)           findRelMin (nums, 0 , nums.size ());     }     int  findRelMin (vector& nums, int  left, int  right)       {        if (left >= right - 1 ) return  nums[left];         int  mid = left + right >> 1 ;         if (nums[left] == nums[right - 1 ])         {             int  l = findRelMin (nums, left, mid);             int  r = findRelMin (nums, mid, right);             return  min (l, r);         }         int  s = left, e = right;         while (left < right) { mid = left + right >> 1 ;             if (nums[mid] >= nums[s]) left = mid + 1 ;             else  right = mid;         }         return  left == e ? nums[s] : nums[left];     } }; 
二分
1 2 3 4 5 6 7 8 9 10 11 class  Solution  {  public :      int  findMin (vector& nums)           int  left = 0 , right = nums.size (), mid;         while (left < right) { mid = left + right >> 1 ;             if (nums[mid] >= nums[0 ]) left = mid + 1 ;             else  right = mid;         }         return  left == nums.size () ? nums[0 ] : nums[left];     } }; 
维护当前位置连续乘积的最大值 tmpp 和最小值 tmpn
,最大值和最小值都可能由三种情况得到:上一个数的
tmppA[i],上一个数的 tmpn A[i],A[i]本身。
不断更新答案,最终输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class  Solution  {public :    int  maxProduct (int  A[], int  n)           int  tmpp = A[0 ], tmpn = A[0 ], tmp, ans = A[0 ];         for (int  i = 1 ; i < n; i ++)         {             tmp = tmpp;             tmpp = max (max (A[i], A[i] * tmpp), A[i] * tmpn);             tmpn = min (min (A[i], A[i] * tmp), A[i] * tmpn);             ans = max (ans, tmpp);         }         return  ans;     } }; 
先翻转整个字符串,然后从前往后一个单词一个单词地再翻转一次,同时去除多余空格,等于是扫描两遍,O(n)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  Solution  {public :    void  reverseWords (string &s)           reverse (s.begin (), s.end ());         int  start = 0 , end = 0 , j = 0 ;         while (start != s.length ())         {             while (start != s.length () && s[start] == ' ' ) start ++;             for (end = start; end != s.length () && s[end] != ' ' ; end ++);             if (j != 0  && start <= end - 1 ) s[j ++] = ' ' ;             for (int  i = end - 1 ; start < i; start ++, i --)                 swap (s[i], s[start]), s[j ++] = s[start];             while (start < end) s[j ++] = s[start ++];         }         s.resize (j);     } }; 
逆波兰表达式计算四则运算。用栈。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class  Solution  {public :    int  evalRPN (vector &tokens)           int  a, b;         stack s;         for (int  i = 0 ; i < tokens.size (); i ++) { if (isdigit (tokens[i][0 ]) || tokens[i].length () > 1 )             {                 s.push (atoi (tokens[i].c_str ()));                 continue ;             }             a = s.top ();s.pop ();             b = s.top ();s.pop ();             switch (tokens[i][0 ])             {                 case  '+' : s.push (b + a); break ;                 case  '-' : s.push (b - a); break ;                 case  '*' : s.push (b * a); break ;                 case  '/' : s.push (b / a); break ;             }         }         return  s.top ();     } }; 
平面上一条直线最多穿过多少点。乍一看好熟悉的问题,做了这么久计算几何。。。却还真没做过这个小问题。
第一反应当然不能O(n^3)枚举了,枚举圆周好像也不行,毕竟是考察所有点,不是某个点。那么应该就是哈希斜率了吧。
肯定少不了竖直的线,哈希斜率这不像是这类OJ让写的题吧。。忘了map这回事了。
确定思路之后,还是看看别人博客吧,少走点弯路,然后就学习了还有unordered_map这么个东西,还有一个博客 
的思路挺好,避免double问题,把斜率转化成化简的x、y组成字符串。
再另外就是重叠的点了,想让题目坑一点,怎能少得了这种数据,单独处理一下。
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 class  Solution  {public :    int  maxPoints (vector &points)           int  ans = 0 ;         for (int  i = 0 ; i < points.size (); i ++)         {             unordered_map<string, int > mp;             int  tmpans = 0 , same = 0 ;             for (int  j = i + 1 ; j < points.size (); j ++)             {                 int  x = points[j].x - points[i].x, y = points[j].y - points[i].y;                 int  g = gcd (x, y);                 if (g != 0 ) x /= g, y /= g;                 else  {same ++; continue ;}                 if (x < 0 ) x = -x, y = -y;                 string tmp = to_string (x) + " "  + to_string (y);                 if (!mp.count (tmp)) mp[tmp] = 1 ;                 else  mp[tmp] ++;                 tmpans = max (tmpans, mp[tmp]);             }             ans = max (tmpans + 1  + same, ans);         }         return  ans;     }     int  gcd (int  a, int  b)       {        return  a ? gcd (b % a, a) : b;     } }; 
又长见识了,原来链表也可以O(nlogn)排序的。没往下想就查了一下,看到人说用归并,于是才开始想能不能实现。。。
O(n)找到中点,把中点的next变成NULL,对两部分递归。递归结束后对两部分归并,先找到newhead,即两部分的头部val较小的那个,然后归并就把小的从newhead往后续。
把最后的next赋值NULL,返回newhead。
又有空数据@_@.
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 class  Solution  {public :    ListNode *sortList (ListNode *head)   {         int  n = 0 ;         ListNode *p = head;         while (p != NULL )             n ++, p = p->next;         if (n <= 1 ) return  head; n >>= 1 ;         p = head;         while (-- n)             p = p->next;         ListNode *tmp = p->next;         p->next = NULL ;         ListNode *nl = sortList (head);         ListNode *nr = sortList (tmp);         ListNode *newhead;         if (nl->val < nr->val)         {             newhead = nl;             nl = nl->next;         }         else          {             newhead = nr;             nr = nr->next;         }         p = newhead;         while (nl != NULL  && nr != NULL )         {             if (nl->val < nr->val) p->next = nl, p = p->next, nl = nl->next;             else  p->next = nr, p = p->next, nr = nr->next;         }         while (nl != NULL ) p->next = nl, p = p->next, nl = nl->next;         while (nr != NULL ) p->next = nr, p = p->next, nr = nr->next;         p->next = NULL ;         return  newhead;     } }; 
指针操作很烦啊。。暴力枚举插入位置,注意细节就能过了。
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 class  Solution  {public :    ListNode *insertionSortList (ListNode *head)   {         ListNode *newhead = head;         if (head == NULL ) return  NULL ;         head = head->next;         newhead->next = NULL ;         while (head != NULL )         {             if (head->val < newhead->val)             {                 ListNode *tmp = head->next;                 head->next = newhead;                 newhead = head;                 head = tmp;                 continue ;             }             ListNode *pre = newhead, *p = newhead->next;             while (p != NULL  && p->val < head->val)             {                 p = p->next;                 pre = pre->next;             }             pre->next = head;             head = head->next;             pre = pre->next;             pre->next = p;         }         return  newhead;     } }; 
新建数据类class Val,保存key、val和访问自增标记updatecnt。
用unordered_map更新数据,增加updatecnt,并把更新的数据放入队列,最关键是处理capacity满了的时候,队列依次出队,map中不存在的和updatecnt和最新数据不相等的项目都忽略,直到发现updatecnt和map中存的最新状态相等,则为(最近未使用)数据,出队后在map中erase。思路有点像STL队列实现版本的Dijkstra。
有一个博客 
的方法更好,map中存的是链表的节点指针,链表顺序表示访问情况,这样就把map内容和链表的每个节点一一对应了,没有冗余节点,且更新操作也是O(1)的。
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 class  Val {public :    int  key;     int  val;     int  updatecnt; }; class  LRUCache {public :    int  cap;     unordered_map<int , Val> mp;     queue q;     LRUCache (int  capacity) {         cap = capacity;     }     int  get (int  key)           if (mp.count (key))         {             mp[key].updatecnt ++;             q.push (mp[key]);             return  mp[key].val;         }         return  -1 ;     }     void  set (int  key, int  value)           if (mp.count (key))         {             mp[key].val = value;             mp[key].updatecnt ++;             q.push (mp[key]);         }         else          {             if (mp.size () == cap)             {                 Val tmp;                 while (!q.empty ())                 {                     tmp = q.front ();                     q.pop ();                     if (mp.count (tmp.key) && tmp.updatecnt == mp[tmp.key].updatecnt)                         break ;                 }                 mp.erase (mp.find (tmp.key));                 mp[key].key = key;                 mp[key].val = value;                 mp[key].updatecnt = 0 ;                 q.push (mp[key]);             }             mp[key].key = key;             mp[key].val = value;             mp[key].updatecnt = 0 ;             q.push (mp[key]);         }     } }; 
二叉树的非递归后序遍历,考研的时候非常熟悉了,现在写又要想好久。重点是关于右子树遍历时候需要一个标记,或者标记根节点出栈次数,或者标记右子树是否访问。
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 class  Solution  {public :    vector postorderTraversal (TreeNode *root)   {         vector ans;         if (root == NULL ) return  ans;         stack<TreeNode*> s;         TreeNode *visited;         while (root != NULL  || !s.empty ())         {             while (root != NULL )                 s.push (root), root = root->left;             root = s.top ();             if (root->right == NULL  || visited == root->right)             {                 ans.push_back (root->val);                 s.pop ();                 visited = root;                 root = NULL ;             }             else              {                 root = root->right;             }         }         return  ans;     } }; 
前序遍历的非递归就容易多了。
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 class  Solution  {public :    vector preorderTraversal (TreeNode *root)   {         stack<TreeNode*> s;         vector ans;         if (root == NULL ) return  ans;         s.push (root);         while (!s.empty ())         {             root = s.top ();             s.pop ();             ans.push_back (root->val);             if (root->right != NULL ) s.push (root->right);             if (root->left != NULL ) s.push (root->left);         }     } }; 
找到中间位置,把中间之后的链表反转,两个指针一个从头一个从尾开始互插,奇偶和指针绕得有点晕,理清就好了。。
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 class  Solution  {public :    void  reorderList (ListNode *head)           int  n = 0 ;         ListNode *pre, *p = head;         while (p)             n ++, p = p->next;         if (n < 3 ) return ; n >>= 1 ;         pre = p = head;         p = p->next;         while (n --) p = p->next, pre = pre->next;         while (p != NULL )         {             ListNode *tmp = p->next;             p->next = pre;             pre = p;             p = tmp;         }         ListNode *tail = pre;         p = head;         while (true )         {             ListNode *tmp1 = p->next, *tmp2 = tail->next;             p->next = tail;             tail->next = tmp1;             p = tmp1;             if (p == tail || p == tmp2) break ;             tail = tmp2;         }         p->next = NULL ;     } }; 
设置两个指针slow和fast,从head开始,slow一次一步,fast一次两步,如果fast能再次追上slow则有圈。
设slow走了n步,则fast走了2*n步,设圈长度m,圈起点到head距离为k,相遇位置距离圈起点为t,则有:
n = k + t + pm; (1)
2*n = k + t + qm;(2)
这里p和q是任意整数。(不过fast速度是slow二倍,则肯定在一圈内追上,p就是0了)
2 * (1) - (2)
得k = lm - t;(l = q - 2 * p)
即 k 的长度是若干圈少了 t 的长度。
因此这时候,一个指针从head开始,另一个从相遇位置开始,都每次只走一步,当从head开始的指针走到圈开始位置时,两指针刚好相遇。
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 :    ListNode *detectCycle (ListNode *head)   {         if (head == NULL ) return  NULL ;         ListNode *slow, *fast;         slow = fast = head;         int  n = 0 ;         do          {             n ++;             if (slow == NULL  || fast == NULL ) return  NULL ;             slow = slow->next;             fast = fast->next;             if (fast == NULL ) return  NULL ;             fast = fast->next;             if (fast == NULL ) return  NULL ;         }while (slow != fast);         fast = head;         while (slow != fast)             slow = slow->next, fast = fast->next;         return  fast;     } }; 
呃,时间逆序做的结果。。。成买一送一了。
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 class  Solution  {public :    bool  hasCycle (ListNode *head)           if (head == NULL ) return  false ;         ListNode *slow, *fast;         slow = fast = head;         int  n = 0 ;         do          {             n ++;             if (slow == NULL  || fast == NULL ) return  NULL ;             slow = slow->next;             fast = fast->next;             if (fast == NULL ) return  NULL ;             fast = fast->next;             if (fast == NULL ) return  NULL ;         }while (slow != fast);         return  true ;     } }; 
先递推,dp[i] == true 表示 s 中前
i 个字符的串是符合要求的,枚举位置 i ,对于
i 枚举位置 j < i,如果
dp[j] == true且
j ~ i的串在字典中,则dp[i] = true。
同时对于这样的 j,
i,site[i].push_back(j),即在 i
位置的可行迭代表中增加位置 j。
完成site之后,从尾部倒着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 31 32 33 34 35 36 class  Solution  {public :    vector DFS (const  string &s, vector *site, int  ith)        {        vector res;         for (int  i = 0 ; i < site[ith].size (); i ++)         {             vector tmp;             string str = s.substr (site[ith][i], ith - site[ith][i]);             if (site[site[ith][i]].size () == 0 )                 res.push_back (str);             else              {                 tmp = DFS (s, site, site[ith][i]);                 for (int  j = 0 ; j < tmp.size (); j ++)                     res.push_back (tmp[j] + " "  + str);             }         }         return  res;     }     vector wordBreak (string s, unordered_set &dict)   {         vector *site = new  vector[s.length () + 1 ];         bool  *dp = new  bool [s.length () + 1 ];         memset (dp, 0 , sizeof (bool ) * s.length ());         dp[0 ] = true ;         for (int  i = 1 ; i <= s.length (); i ++)         {             for (int  j = 0 ; j < i; j ++)             {                 if (dp[j] == true  && dict.count (s.substr (j, i - j)))                    site[i].push_back (j), dp[i] = true ;             }         }         return  DFS (s, site, s.length ());     } }; 
参考Word Break II,对于dp标记,当dp[i]为true时候可以停止枚举后面的
j,优化一下常数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class  Solution  {public :    bool  wordBreak (string s, unordered_set &dict)           bool  *dp = new  bool [s.length () + 1 ];         memset (dp, 0 , sizeof (bool ) * (s.length () + 1 ));         dp[0 ] = true ;         for (int  i = 1 ; i <= s.length (); i ++)             for (int  j = 0 ; j < i; j ++)             {                 dp[i] = dp[i] || dp[j] && dict.count (s.substr (j, i - j));             }         return  dp[s.length ()];     } }; 
第一次遍历,把每个节点复制一份放到对应节点的下一个,即组成二倍长的链表:ori1->copy1->ori2->copy2->...
第二次遍历,利用复制节点总是对应节点的下一个节点特性,将每个ori->next->random指向ori->random->next,中间判断一下空指针。
第三次遍历,把两个链表拆开,恢复原链表。
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 class  Solution  {public :    RandomListNode *copyRandomList (RandomListNode *head)   {         RandomListNode *p = head, *newhead = NULL , *tmp;         if (p == NULL ) return  NULL ;         while (p != NULL )         {             tmp = new  RandomListNode (p->label);             tmp->next = p->next;             p->next = tmp;             p = tmp->next;         }         newhead = head->next;         p = head;         while (p != NULL )         {             tmp = p->next;             tmp->random = p->random == NULL  ? NULL  : p->random->next;             p = tmp->next;         }         p = head;         while (p != NULL )         {             tmp = p->next;             p->next = tmp->next;             p = tmp->next;             tmp->next = p == NULL  ? NULL  : p->next;         }         return  newhead;     } }; 
方法一:设置cnt[32]记录
32个比特位的1的个数,出现3次的数的对应位的1总数为3的倍数,则统计之后每个位对3取模,剩下的位为1的则对应个数为1的那个数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class  Solution  {public :    int  singleNumber (int  A[], int  n)           int  cnt[32 ] = {0 };         for (int  i = 0 ; i < n; i ++)         {             int  tmp = A[i];             for (int  j = 0 ; j < 33 ; tmp >>= 1 , j ++)                 cnt[j] += tmp & 1 ;         }         int  ans = 0 ;         for (int  i = 0 ; i < 32 ; i ++)             ans |= (cnt[i] % 3 ) << i;         return  ans;     } }; 
方法二:设置int one,
two模拟两位二进制来统计各比特位1次数,每当one和two对应二进制位都为1的时候把one和two都清零,最后剩下的one就是要求的数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class  Solution  {public :    int  singleNumber (int  A[], int  n)           int  one = 0 , two = 0 ;         for (int  i = 0 ; i < n; i ++)         {             two |= one & A[i];             one ^= A[i];             int  tmp = one & two;             two ^= tmp;             one ^= tmp;         }         return  one;     } }; 
一路异或过去就可以了。
1 2 3 4 5 6 7 8 9 class  Solution  {public :    int  singleNumber (int  A[], int  n)           int  tmp = 0 ;         for (int  i = 0 ; i < n; i ++)             tmp ^= A[i];         return  tmp;     } }; 
时间复杂度
O(n)的方法还是容易想了,优化为空间复杂度O(1)的话也不难,只是思考代码的时候会有点绕。
上坡一步步来,下坡走个等差数列,波峰位置比较一下上坡时候记录的最大值和下坡求的的最大值,取较大的,具体看代码:
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  candy (vector &ratings)           int  cnt = 0 , i, j, start, nownum;         for (i = 0 ; i < ratings.size (); i ++)          {              if (i == 0  || ratings[i] == ratings[i - 1 ])                  nownum = 1 ;              else  if (ratings[i] > ratings[i - 1 ])                 nownum ++;             if (i + 1  < ratings.size () && ratings[i + 1 ] < ratings[i])             {                 start = 1 ;                 for (j = i + 1 ; j < ratings.size () && ratings[j] < ratings[j - 1 ]; start++, j ++);                  if (start > nownum)                     cnt += (start + 1 ) * start >> 1 ;                 else                      cnt += ((start - 1 ) * start >> 1 ) + nownum;                 nownum = 1 ;                 i = j - 1 ;             }             else                  cnt += nownum;         }         return  cnt;     } }; 
证明题。
一、如果从 i 到 j 的时候理论计算气量刚好为负数,则 i ~ j
的加气站都不可以作为起点。
反证一下,从前往后去掉站,如果去掉的站能增加气,即正数,则结果更糟。如果去掉的站是负数,那么负数如果抵消了之前的正数,则在到
j 之前已经负数了,如果不能抵消之前的正数,那么结果还是更糟。
二、判断是否能成行,一个环的和为非负就可以。
假设环为正, 0 ~ j 刚好为负, j + 1 ~ k 刚好为负数,k + 1
之后为正,则 k + 1 为答案。
也反证一下,k + 1 出发,到gas.size() - 1都为正,则转回来到 j - 1
都会为正。如果到 j 时候为负,则整个环不可能为正,所以到 j
的时候也为正,剩下的一样。这样就能够成功转一圈。
1 2 3 4 5 6 7 8 9 10 11 class  Solution  {public :    int  canCompleteCircuit (vector &gas, vector &cost)          int  i, ans, sum, all;        for (i = ans = sum = all = 0 ; i < gas.size (); i ++)        {            sum += gas[i] - cost[i];            all += gas[i] - cost[i];            if (sum < 0 ) { sum = 0 ; ans = i + 1 ; } } return  all >= 0  ? ans : -1 ;     } }; 
label是唯一的,递归,用unordered_map标记。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class  Solution  {public :    unordered_map<int , UndirectedGraphNode *> mp;     UndirectedGraphNode *cloneGraph (UndirectedGraphNode *node)   {         if (node == NULL  || mp.count (node->label)) return  NULL ;         UndirectedGraphNode *tmp = new  UndirectedGraphNode (node->label);         mp[node->label] = tmp;         for (int  i = 0 ; i < node->neighbors.size (); i ++)         {             cloneGraph (node->neighbors[i]);             tmp->neighbors.push_back (mp[node->neighbors[i]->label]);         }         return  tmp;     } }; 
O(n^2)的动态规划。
cutdp[i] 表示前 i 个字符最小切割几次。
paldp[i][j] == true 表示 i ~ j 是回文。
在枚举 i 和 i 之前的所有 j 的过程中就得到了 paldp[j][i]
的所有回文判断,而对于 i + 1,paldp[j][i + 1]可由 s[j]、s[i + 1]、dp[j +
1][i]在O(1)判断。
cutdp[i]为所有 j (j < i),当paldp[j + 1][i] true的 cutdp[j] +
1的最小值。注意一下边界。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class  Solution  {public :    int  minCut (string s)           bool  paldp[s.length ()][s.length ()];         int  cutdp[s.length ()];         for (int  i = 0 ; i < s.length (); i ++) { cutdp[i] = 0x3f3f3f3f ; for (int  j = i - 1 ; j >= -1 ; j --)             {                 if (s.at (j + 1 ) == s.at (i) && (j + 2  >= i - 1  || paldp[j + 2 ][i - 1 ]))                 {                     paldp[j + 1 ][i] = true ;                     cutdp[i] = min (cutdp[i], (j >= 0  ? (cutdp[j] + 1 ) : 0 ));                 }                 else                      paldp[j + 1 ][i] = false ;             }         }         return  cutdp[s.length () - 1 ];     } }; 
O(n^2)动态规划,paldp[i][j] == true表示
i ~ j 是回文。这里DP的方法是基本的,不再多说。
得到paldp之后,DFS一下就可以了。因为单字符是回文,所以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 31 32 33 class  Solution  {public :    vectorres;     vector tmp;     bool  **paldp;     void  DFS (string s, int  ith)       {        if (ith == s.length ())         {             res.push_back (tmp);             return ;         }         for (int  i = ith; i < s.length (); i ++)         {             if (paldp[ith][i])             {                 tmp.push_back (s.substr (ith, i - ith + 1 ));                 DFS (s, i + 1 );                 tmp.pop_back ();             }         }         return ;     }     vector partition (string s)   {         paldp = new  bool *[s.length ()];         for (int  i = 0 ; i < s.length (); i ++)             paldp[i] = new  bool [s.length ()];         for (int  i = 0 ; i < s.length (); i ++) for (int  j = i; j >= 0 ; j --)                 paldp[j][i] = s.at (i) == s.at (j) && (j + 1  >= i - 1  || paldp[j + 1 ][i - 1 ]);         DFS (s, 0 );         return  res;     } }; 
周围四条边的O做起点搜索替换为第三种符号,再遍历所有符号把O换成X,第三种符号换回O。
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 class  Solution  {public :    typedef  pair<int , int > pii;     int  dx[4 ] = {1 , -1 , 0 , 0 };     int  dy[4 ] = {0 , 0 , 1 , -1 };     queue q;     void  solve (vector &board)           if (board.size () == 0 ) return ;         int  width = board[0 ].size ();         int  height = board.size ();         for (int  i = 0 ; i < width; i ++)         {             if (board[0 ][i] == 'O' )                 board[0 ][i] = '#' , q.push (pair <int , int >(0 , i));             if (board[height - 1 ][i] == 'O' )                 board[height - 1 ][i] = '#' , q.push (pii (height - 1 , i));         }         for (int  i = 1 ; i < height - 1 ; i ++)         {             if (board[i][0 ] == 'O' )                 board[i][0 ] = '#' , q.push (pii (i, 0 ));             if (board[i][width - 1 ] == 'O' )                 board[i][width - 1 ] = '#' , q.push (pii (i, width - 1 ));         }         while (!q.empty ())         {             pii now = q.front ();             q.pop ();             for (int  i = 0 ; i < 4 ; i ++)              {                 int  ty = now.first + dx[i];                 int  tx = now.second + dy[i];                 if (tx >= 0  && tx < width && ty >= 0  && ty < height && board[ty][tx] == 'O' )                {                    board[ty][tx] = '#' ;                    q.push (pii (ty, tx));                }             }         }         for (int  i = 0 ; i < height; i ++)             for (int  j = 0 ; j < width; j ++)             {                 if (board[i][j] == 'O' ) board[i][j] = 'X' ;                 else  if (board[i][j] == '#' ) board[i][j] = 'O' ;             }     } }; 
遍历一遍加起来。。。
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  ans;     void  DFS (TreeNode *now, int  tmp)       {        if (now->left == NULL  && now->right == NULL )         {             ans += tmp * 10  + now->val;             return ;         }         if (now->left != NULL )         {             DFS (now->left, tmp * 10  + now->val);         }         if (now->right != NULL )         {             DFS (now->right, tmp * 10  + now->val);         }     }     int  sumNumbers (TreeNode *root)           if (root == NULL ) return  0 ;         ans = 0 ;         DFS (root, 0 );         return  ans;     } }; 
方法一:一开始竟然想了并查集,其实绕弯了,多此一举。哈希+并查集,把每个数哈希,枚举每个数看相邻的数在不在数组里,并查集合并,只是并查集的复杂度要比O(1)大一些。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class  Solution  {public :    unordered_map<int , int > mp, cnt;     int  ans = 1 ;     int  fa (int  i)       {        i == mp[i] ? i : (mp[i] = fa (mp[i]));     }     int  longestConsecutive (vector &num)           for (int  i = 0 ; i < num.size (); i ++)             mp[num[i]] = num[i], cnt[num[i]] = 1 ;         for (int  i = 0 ; i < num.size (); i ++)         {             if (mp.count (num[i] + 1 ) && fa (num[i]) != fa (num[i] + 1 ))             {                 cnt[fa (num[i] + 1 )] += cnt[fa (num[i])];                 ans = max (cnt[fa (num[i] + 1 )], ans);                 mp[fa (num[i])] = fa (num[i] + 1 );             }         }         return  ans;     } }; 
方法二:哈希+枚举相邻数。相邻的数在数组里的话,每个数至多访问一次;相邻的数不在数组里的话,枚举会中断。所以设哈希复杂度为O(1)的话,这个方法是严格的O(n)。
其实这个题的数据挺善良,如果出了2147483647,
-2147483648,那还是用long long 稳妥些。
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 :    unordered_map<int , bool > vis;     int  longestConsecutive (vector &num)           int  ans = 0 ;         for (int  i = 0 ; i < num.size (); i ++)             vis[num[i]] = false ;         for (int  i = 0 ; i < num.size (); i ++)         {             if (vis[num[i]] == false )             {                 int  cnt = 0 ;                 for (int  j = num[i]; vis.count (j); j ++, cnt ++)                 {                     vis[j] = true ;                 }                 for (int  j = num[i] - 1 ; vis.count (j); j --, cnt ++)                 {                     vis[j] = true ;                 }                 ans = max (ans, cnt);             }         }         return  ans;     } }; 
用数组类型的队列,BFS过程中记录pre路径,搜完后迭代回去保存路径。
似乎卡了常数,用queue队列,另外存路径的方法超时了。
想更快就双向广搜吧。让我想起了POJ那个八数码。
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 class  Node { public :    string str;     int  pace;     int  pre;     Node (){}     Node (string s, int  pa, int  pr)     {         str = s;         pace = pa;         pre = pr;     } }; class  Solution  {public :    vector ans;     vector findLadders (string start, string end, unordered_set &dict)   {         vector q;         q.push_back (Node (end, 1 , -1 ));         unordered_map<string, int > dis;         dis[end] = 1 ;         for (int  i = 0 ; i < q.size (); i ++)          {              Node now = q[i];              if (dis.count (start) && now.pace >= dis[start])                 break ;             for (int  j = 0 ; j < now.str.length (); j ++)             {                 string tmp = now.str;                 for (char  c = 'a' ; c <= 'z' ; c ++)                  {                     tmp[j] = c;                     if ((dict.count (tmp) || tmp == start) && (!dis.count (tmp) || dis[tmp] == now.pace + 1 ))                     {                         dis[tmp] = now.pace + 1 ;                         q.push_back (Node (tmp, now.pace + 1 , i));                     }                  }              }          }          for (int  i = q.size () - 1 ; i >= 0  && q[i].pace == dis[start]; i --)         {             if (q[i].str == start)             {                 vector tmp;                 for (int  j = i; j != -1 ; j = q[j].pre)                     tmp.push_back (q[j].str);                 ans.push_back (tmp);             }         }         return  ans;     } }; 
直接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 class  Solution  {public :    int  ladderLength (string start, string end, unordered_set &dict)           typedef  pair<string, int > pii;         unordered_set flag;         queue q;         q.push (pii (start, 1 ));         while (!q.empty ())         {             pii now = q.front ();             q.pop ();             for (int  i = 0 ; i < now.first.length (); i ++)             {                 string tmp = now.first;                 for (char  j = 'a' ; j <= 'z' ; j ++)                 {                     tmp[i] = j;                     if (tmp == end) return  now.second + 1 ;                     if (dict.count (tmp) && !flag.count (tmp))                     {                         q.push (pii (tmp, now.second + 1 ));                         flag.insert (tmp);                     }                 }             }         }         return  0 ;     } }; 
做过刘汝佳 白书的人想必都知道ctype.h和isdigit(), isalpha(),
tolower(), toupper()。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  Solution  {public :    bool  valid (char  &x)       {        x = tolower (x);         return  isdigit (x) || isalpha (x);     }     bool  isPalindrome (string s)           if (s.length () == 0 ) return  true ;         for (int  i = 0 , j = s.length () - 1 ; i < j; i ++, j --)         {             while (!valid (s[i]) && i < s.length ()) i ++; while (!valid (s[j]) && j >= 0 ) j --;             if (i < j && s[i] != s[j]) return  false ;         }         return  true ;     } }; 
后续遍历,子问题为子树根节点向叶子节点出发的最大路径和。
即 l = DFS(now->left), r = DFS(now->right)。
此时,ans可能是
now->valid,可能是左边一路上来加上now->valid,可能是右边一路上来,也可能是左边上来经过now再右边一路下去,四种情况。
四种情况更新完ans后,now返回上一层只能是
now->valid或左边一路上来或右边一路上来,三种情况。
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  ans;     int  DFS (TreeNode *now)       {        if (now == NULL ) return  0 ;         int  l = max (DFS (now->left), 0 );         int  r = max (DFS (now->right), 0 );         ans = max (ans, l + r + now->val);         return  max (l + now->val, r + now->val);     }     int  maxPathSum (TreeNode *root)           if (root == NULL ) return  0 ;         ans = -0x3f3f3f3f ;         DFS (root);         return  ans;     } }; 
前缀pre[i]处理 0 ~ i 买卖一次最优解,后缀suf[i]处理 i ~ prices.size()
- 1 买卖一次最优解。
所有位置pre[i] + suf[i]最大值为答案O(n)。
处理最优解的时候是维护前(后)缀prices最小(大)值,与当前prices做差后和前(后)缀最优解比较取最优,O(n)。
总复杂度O(n)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class  Solution  {public :    int  maxProfit (vector &prices)           int  ans = 0 ;         vector pre (prices.size()) , suf (prices.size())  ;         for (int  i = 0 , mtmp = 0x3f3f3f3f ; i < prices.size (); i ++)          {             mtmp = i ? min (mtmp, prices[i]) : prices[i];             pre[i] = max (prices[i] - mtmp, i ? pre[i - 1 ] : 0 );         }          for (int  i = prices.size () - 1 , mtmp = 0 ; i >= 0 ; i --)         {             mtmp = i != prices.size () - 1  ? max (mtmp, prices[i]) : prices[i];             suf[i] = max (mtmp - prices[i], i != prices.size () - 1  ? suf[i + 1 ] : 0 );         }         for (int  i = 0 ; i < prices.size (); i ++)             ans = max (ans, pre[i] + suf[i]);         return  ans;     } }; 
可以买卖多次,把所有上坡差累加即可。
1 2 3 4 5 6 7 8 9 10 class  Solution  {public :    int  maxProfit (vector &prices)           int  ans = 0 ;         for (int  i = 1 ; i < prices.size (); i ++) { if (prices[i] > prices[i - 1 ])                 ans += prices[i] - prices[i - 1 ];         }         return  ans;     } }; 
维护前(后)缀最小(大)值,和当前prices做差更新答案。
1 2 3 4 5 6 7 8 9 10 11 12 class  Solution  {public :    int  maxProfit (vector &prices)           int  ans = 0 ;         for (int  i = prices.size () - 1 , mtmp = 0 ; i >= 0 ; i --)         {             mtmp = max (mtmp, prices[i]);             ans = max (mtmp - prices[i], ans);         }         return  ans;     } }; 
竟然遇到了ACM递推入门题,想必无数ACMer对这题太熟悉了。
从下往上递推,一维数组滚动更新即可。这里懒省事,直接把原数组改了。
1 2 3 4 5 6 7 8 9 10 11 class  Solution  {public :    int  minimumTotal (vector &triangle)           for (int  i = triangle.size () - 2 ; i >= 0 ; i --)         {             for (int  j = 0 ; j < triangle[i].size (); j ++)                 triangle[i][j] = min (triangle[i][j] + triangle[i + 1 ][j], triangle[i][j] + triangle[i + 1 ][j + 1 ]);         }         return  triangle.size () == 0  ? 0  : triangle[0 ][0 ];     } }; 
滚动数组递推,从后往前以便不破坏上一层递推数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 class  Solution  {public :    vector getRow (int  rowIndex)   {         vector ans (rowIndex + 1 , 0 )  ;         ans[0 ] = 1 ;         for (int  i = 0 ; i <= rowIndex; i ++) { for (int  j = i; j >= 0 ; j --)             {                 ans[j] = (i == 0  || j == 0  || j == i ? 1  : ans[j] + ans[j - 1 ]);             }         }         return  ans;     } }; 
递推。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class  Solution  {public :    vector generate (int  numRows)   {         vector v;         for (int  i = 0 ; i < numRows; i ++)         {             vector tmp;             for (int  j = 0 ; j <= i; j ++)             {                 tmp.push_back (i == 0  || j == 0  || j == i ? 1  : v[i - 1 ][j] + v[i - 1 ][j - 1 ]);             }             v.push_back (tmp);         }         return  v;     } }; 
题目要求空间复杂度O(1),所以递归、队列等传统方法不应该用。
本题可以利用生成的next指针来横向扫描,即得到一层的next指针之后,可以利用这一层的next指针来给下一层的next指针赋值。
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 class  Solution  {public :    TreeLinkNode *findNext (TreeLinkNode *head)        {        while (head != NULL  && head->left == NULL  && head->right == NULL )             head = head->next;         return  head;     }     void  connect (TreeLinkNode *root)           if (root == NULL ) return ;         TreeLinkNode *head, *last, *nexhead;         for (head = root; head != NULL ; head = nexhead)         {             head = findNext (head);             if (head == NULL ) break ;             if (head->left != NULL ) nexhead = head->left;             else  nexhead = head->right;             for (last = NULL ; head != NULL ; last = head, head = findNext (head->next))             {                 if (head->left != NULL  && head->right != NULL )                     head->left->next = head->right;                 if (last == NULL ) continue ;                 if (last->right != NULL )                     last->right->next = head->left != NULL  ? head->left : head->right;                 else                      last->left->next = head->left != NULL  ? head->left : head->right;             }         }     } }; 
不用考虑连续的空指针,就不用额外实现找下一个子树非空节点,比Populating
Next Right Pointers in Each Node II 容易处理。
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 :    void  connect (TreeLinkNode *root)           if (root == NULL ) return ;         TreeLinkNode *head, *nexhead, *last;         for (head = root; head->left != NULL ; head = nexhead)         {             nexhead = head->left;             last = NULL ;             while (head != NULL )             {                 head->left->next = head->right;                 if (last != NULL ) last->right->next = head->left;                 last = head;                 head = head->next;             }         }     } }; 
典型动态规划。dp[i][j] 表示 T 的前 j 个字符在 S 的前 i
个字符中的解。
对于dp[i + 1][j + 1],由两部分组成:
一、 j + 1 对应到 S 前 i 个字符中的解,忽略 S 的第 i + 1 个字符。
二、判断 S 的第 i + 1 个字符是否和 T 的第 j + 1
个字符相同,如果相同,则加上dp[i][j],否则不加。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  Solution  {public :    int  numDistinct (string S, string T)           if (S.length () < T.length ()) return  0 ;         vector dp (S.length() + 1 , vector(T.length() + 1 , 0 ))  ;         for (int  i = 0 ; i < S.length (); i ++)             dp[i][0 ] = 1 ;         dp[0 ][1 ] = 0 ;         for (int  i = 0 ; i < S.length (); i ++)         {             for (int  j = 0 ; j < T.length (); j ++)             {                 dp[i + 1 ][j + 1 ] = dp[i][j + 1 ];                 if (S[i] == T[j]) dp[i + 1 ][j + 1 ] += dp[i][j];             }         }         return  dp[S.length ()][T.length ()];     } }; 
题意是优先左子树靠前,且排成一列用右子树指针,不管val的大小关系。
后序遍历一遍即可,递归返回子树中尾节点指针,注意各种条件判断。
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 :    TreeNode *DFS (TreeNode *now)        {        if (now->left == NULL  && now->right == NULL ) return  now;         TreeNode *leftok = NULL , *rightok = NULL ;         if (now->left != NULL ) leftok = DFS (now->left);         if (now->right != NULL ) rightok = DFS (now->right);         if (leftok != NULL )         {             leftok->right = now->right;             now->right = now->left;             now->left = NULL ;             return  rightok ? rightok : leftok;         }         else  return  rightok;     }     void  flatten (TreeNode *root)           if (root == NULL ) return ;         DFS (root);     } }; 
传统递归,把路径上的数字插入vector,终点判断是否插入答案。
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 class  Solution  {public :    int  goal;     vectorv;     vector curv;     void  DFS (TreeNode *now, int  cursum)       {        curv.push_back (now->val);         if (now->left == NULL  && now->right == NULL )         {             if (cursum + now->val == goal)             {                 v.push_back (curv);                 curv.pop_back ();                 return ;             }         }         if (now->left != NULL ) DFS (now->left, cursum + now->val);         if (now->right != NULL ) DFS (now->right, cursum + now->val);         curv.pop_back ();     }     vector pathSum (TreeNode *root, int  sum)   {         goal = sum;         if (root == NULL ) return  v;         DFS (root, 0 );         return  v;     } }; 
遍历树。
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 class  Solution  {public :    int  goal;     bool  DFS (TreeNode *now, int  cursum)       {        if (now->left == NULL  && now->right == NULL )             return  cursum + now->val == goal;         if (now->left != NULL  && DFS (now->left, cursum + now->val)) return  true ;         if (now->right != NULL  && DFS (now->right, cursum + now->val)) return  true ;     }     bool  hasPathSum (TreeNode *root, int  sum)           goal = sum;         if (root == NULL ) return  false ;         return  DFS (root, 0 );     } }; 
还是遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  Solution  {public :    int  minDepth (TreeNode *root)           if (root == NULL ) return  0 ;         if (root->left == NULL ) return  minDepth (root->right) + 1 ;         else  if (root->right == NULL ) return  minDepth (root->left) + 1 ;         else  return  min (minDepth (root->left), minDepth (root->right)) + 1 ;     } }; 
遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  Solution  {public :    int  maxDepth (TreeNode *now)       {        if (now == NULL ) return  0 ;         int  l = maxDepth (now->left) + 1 ;         int  r = maxDepth (now->right) + 1 ;         return  abs (l - r) > 1  || l < 0  || r < 0  ? -2  : max (l, r); } bool  isBalanced (TreeNode *root)  return  maxDepth (root) >= 0 ;     } }; 
每次找中点作为根节点,将两边递归,返回根节点指针作为左右节点。
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 class  Solution  {public :    TreeNode *sortedListToBST (ListNode *head)   {         if (head == NULL ) return  NULL ;         ListNode *p, *mid, *pre;         for (p = mid = head, pre = NULL ; p->next != NULL ; mid = mid->next)         {             p = p->next;             if (p->next == NULL ) break ;             p = p->next;             pre = mid;         };         TreeNode *root = new  TreeNode (mid->val);         if (pre != NULL ) pre->next = NULL , root->left = sortedListToBST (head);         else  root->left = NULL ;         root->right = sortedListToBST (mid->next);         if (pre != NULL ) pre->next = mid;         return  root;     } }; 
递归做,比链表的容易些。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class  Solution  {public :    TreeNode *convert (vector &num, int  left, int  right)        {        if (right == left) return  NULL ;         int  mid = right + left >> 1 ;         TreeNode *root = new  TreeNode (num[mid]);         root->left = convert (num, left, mid);         root->right = convert (num, mid + 1 , right);     }     TreeNode *sortedArrayToBST (vector &num)   {         return  convert (num, 0 , num.size ());     } }; 
宽搜和深搜都可以,找对层数就行了。
本以为这题亮点是如何一遍实现从底向上顺序的vector,AC之后上网一查也全是最后把vector翻转的。。。
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 :    vectorv;     void  DFS (TreeNode *now, int  depth)       {        if (v.size () <= depth) v.push_back (vector (0 )); v[depth].push_back (now->val);         if (now->left != NULL ) DFS (now->left, depth + 1 );         if (now->right != NULL ) DFS (now->right, depth + 1 );     }     vector levelOrderBottom (TreeNode *root)   {         if (root == NULL ) return  v;         DFS (root, 0 );         for (int  i = 0 , j = v.size () - 1 ; i < j; i ++, j --)             swap (v[i], v[j]);         return  v;     } }; 
数据结构经典题。后序遍历的结尾是根节点
Proot,在中序遍历中找到这个节点 Iroot,则
Iroot两边即为左右子树。根据左右子树节点个数,在后序遍历中找到左右子树分界(左右子树肯定不交叉),则几个关键分界点都找到了,对左右子树递归。
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 class  Solution  {public :    TreeNode *build (vector &inorder, int  ileft, int  iright, vector &postorder, int  pleft, int  pright)        {        if (iright == ileft)             return  NULL ;         int  root;         for (root = ileft; inorder[root] != postorder[pright - 1 ]; root ++);         TreeNode *node = new  TreeNode (inorder[root]);         node->left = build (inorder, ileft, root, postorder, pleft, pleft + root - ileft);         node->right = build (inorder, root + 1 , iright, postorder, pleft + root - ileft, pright - 1 );         return  node;     }     TreeNode *buildTree (vector &inorder, vector &postorder)   {         return  build (inorder, 0 , inorder.size (), postorder, 0 , postorder.size ());     } }; 
和上一题Construct Binary Tree from Inorder and Postorder
Traversal方法一样,前序和后序的信息作用相同。
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 :    TreeNode *build (vector &inorder, int  ileft, int  iright, vector &preorder, int  pleft, int  pright)        {        if (iright == ileft)             return  NULL ;         int  root;         for (root = ileft; inorder[root] != preorder[pleft]; root ++);         TreeNode *node = new  TreeNode (inorder[root]);         node->left = build (inorder, ileft, root, preorder, pleft + 1 , pleft + root - ileft);         node->right = build (inorder, root + 1 , iright, preorder, pleft + root - ileft + 1 , pright);         return  node;     }     TreeNode *buildTree (vector &preorder, vector &inorder)   {         return  build (inorder, 0 , inorder.size (), preorder, 0 , preorder.size ());     } }; 
遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  Solution  {public :    int  maxDepth (TreeNode *root)           if (root == NULL ) return  0 ;         if (root->left == NULL ) return  maxDepth (root->right) + 1 ;         if (root->right == NULL ) return  maxDepth (root->left) + 1 ;         return  max (maxDepth (root->left), maxDepth (root->right)) + 1 ;     } }; 
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 33 34 35 36 37 38 39 40 41 42 class  Solution  {public :    vector ans;     vector zigzagLevelOrder (TreeNode *root)   {         if (root == NULL ) return  ans;         vector<TreeNode*> q1, q2;         q1. push_back (root);         int  depth = 0 ;         while (!q1. empty () || !q2. empty ())         {             ans.push_back (vector (0 ));             for (int  i = q1. size () - 1 ; i >= 0 ; i --)             {                 ans[depth].push_back (q1[i]->val);                 if (q1[i]->left != NULL ) q2. push_back (q1[i]->left);                 if (q1[i]->right != NULL ) q2. push_back (q1[i]->right);             }             depth ++;             q1. clear ();             if (q2. empty ()) continue ;             ans.push_back (vector (0 ));             for (int  i = q2. size () - 1 ; i >= 0 ; i --)             {                 ans[depth].push_back (q2[i]->val);                 if (q2[i]->right != NULL ) q1. push_back (q2[i]->right);                 if (q2[i]->left != NULL ) q1. push_back (q2[i]->left);             }             q2. clear ();             depth ++;         }         return  ans;     } }; 
懒省事直接在上一题Binary Tree Zigzag Level Order
Traversal的代码上改了一下。
只用一个队列的话,增加个层数信息存队列里即可。
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 class  Solution  {public :    vector ans;     vector levelOrder (TreeNode *root)   {  if (root == NULL ) return  ans;         vector<TreeNode*> q1, q2;         q1. push_back (root);         int  depth = 0 ;         while (!q1. empty () || !q2. empty ())         {             ans.push_back (vector (0 ));             for (int  i = 0 ; i < q1. size (); i ++) { ans[depth].push_back (q1[i]->val);                 if (q1[i]->left != NULL ) q2. push_back (q1[i]->left);                 if (q1[i]->right != NULL ) q2. push_back (q1[i]->right);             }             depth ++;             q1. clear ();             if (q2. empty ()) continue ;             ans.push_back (vector (0 ));             for (int  i = 0 ; i < q2. size (); i ++) { ans[depth].push_back (q2[i]->val);                 if (q2[i]->left != NULL ) q1. push_back (q2[i]->left);                 if (q2[i]->right != NULL ) q1. push_back (q2[i]->right);             }             q2. clear ();             depth ++;         }         return  ans;     } }; 
递归:左指针和右指针,对称递归,即(左的左)和(右的右)对应,(左的右)和(右的左)对应。
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 class  Solution  {public :    bool  judge (TreeNode *l, TreeNode *r)       {        if (l->val != r->val) return  false ;         if (l->left != r->right && (l->left == NULL  || r->right == NULL )         || l->right != r->left && (l->right == NULL  || r->left == NULL ))             return  false ;         if (l->left != NULL  && !judge (l->left, r->right)) return  false ;         if (l->right != NULL  && !judge (l->right, r->left)) return  false ;         return  true ;     }     bool  isSymmetric (TreeNode *root)           if (root == NULL ) return  true ;         if (root->left == NULL  && root->right == NULL ) return  true ;         else  if (root->left != NULL  && root->right == NULL              || root->left == NULL  && root->right != NULL ) return  false ;         return  judge (root->left, root->right);     } }; 
非递归:左右子树分别做一个队列,同步遍历。
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 class  Solution  {public :    bool  isSymmetric (TreeNode *root)           if (root == NULL ) return  true ;         if (root->left == NULL  && root->right == NULL ) return  true ;         else  if (root->left != NULL  && root->right == NULL              || root->left == NULL  && root->right != NULL ) return  false ;         queue q1, q2;         q1. push (root->left);         q2. push (root->right);         while (!q1. empty ())         {             TreeNode *now1 = q1.f ront(), *now2 = q2.f ront();             q1. pop ();             q2. pop ();             if (now1->val != now2->val) return  false ;             if (now1->left != NULL  || now2->right != NULL )             {                 if (now1->left == NULL  || now2->right == NULL ) return  false ;                 q1. push (now1->left);                 q2. push (now2->right);             }             if (now1->right != NULL  || now2->left != NULL )             {                 if (now1->right == NULL  || now2->left == NULL ) return  false ;                 q1. push (now1->right);                 q2. push (now2->left);             }         }         return  true ;     } }; 
同步遍历,比较判断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  Solution  {public :    bool  isSameTree (TreeNode *p, TreeNode *q)           if (p == NULL  && q == NULL ) return  true ;         if (p != q && (p == NULL  || q == NULL ) || p->val != q->val) return  false ;         return  isSameTree (p->left, q->left) && isSameTree (p->right, q->right);     } }; 
中序遍历是二叉查找树的顺序遍历,a,
b表示前驱节点和当前节点,因为只有一对数值翻转了,所以肯定会遇到前驱节点val比当前节点val大的情况一次或两次,遇到一次表示翻转的是相邻的两个节点。 ans1和 ans2指向两个被翻转的节点,当遇到前驱val比当前val大的情况时候,根据第一次还是第二次给ans1和ans2赋值,最终翻转回来。
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 :    TreeNode *a, *b;     TreeNode *ans1, *ans2;     bool  DFS (TreeNode *now)       {        if (now->left != NULL )             DFS (now->left);         a = b;         b = now;         if (a != NULL  && a->val > b->val)         {             if (ans1 == NULL ) ans1 = a;             ans2 = b;         }         if (now->right != NULL )             DFS (now->right);     }     void  recoverTree (TreeNode *root)           if (root == NULL ) return ;         a = b = ans1 = ans2 = NULL ;         DFS (root);         swap (ans1->val, ans2->val);     } }; 
中序遍历,更新前驱节点,与当前节点比较。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class  Solution  {public :    TreeNode *pre = NULL ;     bool  isValidBST (TreeNode *root)           if (root == NULL ) return  true ;         if (root->left != NULL  && !isValidBST (root->left)) return  false ;         if (pre != NULL  && pre->val >= root->val) return  false ;         pre = root;         if (root->right != NULL  && !isValidBST (root->right)) return  false ;         return  true ;     } }; 
动态规划。如果结果是true,则任意 i,
j,s1[i] 之前的字符 和
s2[j]之前的字符,都能够交叉为 s3[i + j]
之前的字符。
由此,当dp[i][j]时,如果s1[i]==s3[i+j],则尝试s1[i]与s3[i+j]对应,如果dp[i-1][j]是true,则dp[i][j]也为true。如果s2[j]==s3[i+j]则同样处理。
直到最后,判断dp[s1.length()-1][s2.length()-1]是否为true。为方便初始化,坐标后移了一位。
题目不厚道的出了s1.length()+s2.length() != s3.length()的数据,特判一下。
看到网上也都是O(n^2)的解法,我也就放心了。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class  Solution  {public :    bool  isInterleave (string s1, string s2, string s3)           if (s1.l ength() + s2.l ength() != s3.l ength())             return  false ;         vector dp (s1.l ength() + 1 , vector(s2.l ength() + 1 , false ))  ;         for (int  i = 0 ; i <= s1.l ength(); i ++)             for (int  j = 0 ; j <= s2.l ength(); j ++)              {                 if (!i && !j) dp[i][j] = true ;                 dp[i][j] = dp[i][j] || i > 0  && s3[i + j - 1 ] == s1[i - 1 ] && dp[i - 1 ][j];                dp[i][j] = dp[i][j] || j > 0  && s3[i + j - 1 ] == s2[j - 1 ] && dp[i][j - 1 ];             }         return  dp[s1.l ength()][s2.l ength()];     } }; 
LeetCode目前为止感觉最暴力的。递归遍历所有情况,每次返回子问题(左右子树)的vector的解,两层循环组合这些解。
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 :    vector generate (int  start, int  end)        {        vectorres;         if (start > end)         {             TreeNode *tmp = NULL ;             res.push_back (tmp);             return  res;         }         for (int  i = start; i <= end; i ++)         {             vector l = generate (start, i - 1 ), r = generate (i + 1 , end);             for (int  j = 0 ; j < l.size (); j ++)                 for (int  k = 0 ; k < r.size (); k ++) { TreeNode *tmp = new  TreeNode (i); tmp->left = l[j];                     tmp->right = r[k];                     res.push_back (tmp);                 }         }         return  res;     }     vector generateTrees (int  n)   {         return  generate (1 , n);     } }; 
经典问题,卡特兰数,可递推,可用公式(公式用组合数,也要写循环)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  Solution  {public :    int  COM (int  n, int  m)       {        m = n - m < m ? n - m : m;         int  res, i, j;         for (i = n, res = j = 1 ; i > n - m; i --)         {             res *= i;             for (; j <= m && res % j == 0 ; j ++)                 res /= j;         }         return  res;     }     int  numTrees (int  n)           return  COM (n << 1 , n) / (n + 1 );     } }; 
数据结构基础
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class  Solution  {public :    vector res;     void  inorder (TreeNode *now)       {        if (now == NULL ) return ;         inorder (now->left);         res.push_back (now->val);         inorder (now->right);     }     vector inorderTraversal (TreeNode *root)   {         inorder (root);         return  res;     } }; 
四层递归枚举分割位置,判断数字范围和前导零,处理字符串。
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 class  Solution  {public :    vector res;     void  DFS (string s, int  last, int  cur, string now)       {        if (cur == 3 )         {             if (last == s.length ()) return ;             string tmp = s.substr (last, s.length () - last);             if (atoi (tmp.c_str ()) <= 255  && (tmp.length () == 1  || tmp[0 ] != '0' ))                 res.push_back (now + tmp);             return ;         }         string lin;         for (int  i = last; i < s.length (); i ++)         {             string tmp = s.substr (last, i - last + 1 );             if (atoi (tmp.c_str ()) <= 255  && (tmp.length () == 1  || tmp[0 ] != '0' ))                 DFS (s, i + 1 , cur + 1 , now + tmp + "." );         }     }     vector restoreIpAddresses (string s)   {         DFS (s, 0 , 0 , "" );         return  res;     } }; 
在表头添加一个(哨兵)会好写很多,额外的newhead可以帮助标记翻转之后更换了的头指针。
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 :    ListNode *reverseBetween (ListNode *head, int  m, int  n)   {         ListNode *newhead = new  ListNode (0 );         newhead->next = head;         ListNode *pre = newhead, *p = head, *start = NULL ;         ListNode *tmp;         for (int  i = 1 ; p != NULL ; i ++)         {             tmp = p->next;             if (i == m)                 start = pre;             if (i > m && i <= n) p->next = pre;             if (i == n)             {                 start->next->next = tmp;                 start->next = p;             }             pre = p;             p = tmp;         }         tmp = newhead->next;         free (newhead);         return  tmp;     } }; 
递推:dp[i]表示前 i 个数字的解码种数。
`dp[i] = if(一)dp[i-1] + if(二)dp[i-2]``
当 i 位置不为0,可加上
i - 1位置的解。当当前位置和前一位置组成的两位数满足解码且高位不为0,可加上
i - 2 位置的解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class  Solution  {public :    int  numDecodings (string s)           if (s.length () == 0 ) return  0 ;         vector dp (s.length() + 1 , 0 )  ;         dp[0 ] = 1 ;         for (int  i = 0 ; i < s.length (); i ++)          {             dp[i + 1 ] = (s[i] != '0'  ? dp[i] : 0 ) +                 (i > 0  && s[i - 1 ] != '0'  && atoi (s.substr (i - 1 , 2 ).c_str ()) <= 26  ? dp[i - 1 ] : 0 );         }         return  dp[s.length ()];     } }; 
统计地存map里,map[i]= j 表示 S 中有 j 个
i。map是有序的,用迭代器递归枚举放入集合的个数。
也可以先排序,用set标记每个数时候被放入过,第一次放入之后才可以继续放同一个数。
代码是用map的方法。
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 res;     vector now;     map<int , int > mp;     void  DFS (map<int , int >::iterator i)       {        if (i == mp.end ())         {             res.push_back (now);             return ;         }         map<int , int >::iterator tmp = i;         i ++;         DFS (i);         for (int  j = 0 ; j < tmp->second; j ++)         {             now.push_back (tmp->first);             DFS (i);         }         for (int  j = 0 ; j < tmp->second; j ++)             now.pop_back ();     }     vector subsetsWithDup (vector &S)   {         for (int  i = 0 ; i < S.size (); i ++)             !mp.count (S[i]) ? (mp[S[i]] = 1 ) : mp[S[i]] ++;         DFS (mp.begin ());         return  res;     } }; 
格雷码有多种生成方法,可参考维基百科 
。
1 2 3 4 5 6 7 8 class  Solution  {public :    vector grayCode (int  n)   {         vector res;         for (int  i = 0 ; i < (1  << n); i ++) res.push_back ((i >> 1 ) ^ i);         return  res;     } }; 
从后往前,对 A
来说一个萝卜一个坑,肯定不会破坏前面的数据。具体看代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 class  Solution  {public :    void  merge (int  A[], int  m, int  B[], int  n)           int  p = m + n - 1 , i = m - 1 , j = n - 1 ;         for (; i >= 0  && j >= 0 ;)         {             if (A[i] > B[j]) A[p --] = A[i --];             else  A[p --] = B[j --];         }         while (i >= 0 ) A[p --] = A[i --];         while (j >= 0 ) A[p --] = B[j --];     } }; 
直接搜索可以过,记忆化搜索可提高效率。
dp[i][j][k]表示从 s1[i] 和 s2[j] 开始长度为 k 的字符串是否是scrambled
string。
枚举分割位置,scrambled
string要求字符串对应字母的个数是一致的,可以直接排序对比。递归终点是刚好只有一个字母。
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 class  Solution  {public :    string S1, S2;     vector<vector > dp;     bool  judge (string a, string b)       {        sort (a.begin (), a.end ());         sort (b.begin (), b.end ());         for (int  i = 0 ; i < a.length (); i ++)             if (a[i] != b[i]) return  false ;         return  true ;     }     int  DFS (int  s1start, int  s2start, int  len)       {        int  &ans = dp[s1start][s2start][len - 1 ];         if (len == 1 ) return  ans = S1[s1start] == S2[s2start];         if (ans != -1 ) return  ans;         if (!judge (S1. substr (s1start, len), S2. substr (s2start, len)))             return  ans = 0 ;         ans = 0 ;         for (int  i = 1 ; i < len; i ++)         {             ans = ans             || DFS (s1start, s2start, i) && DFS (s1start + i, s2start + i, len - i)             || DFS (s1start, s2start + len - i, i) && DFS (s1start + i, s2start, len - i);         }         return  ans;     }     bool  isScramble (string s1, string s2)           S1 = s1, S2 = s2;         dp = vector <vector >             (s1.l ength(), vector                  (s1.l ength(), vector                      (s1.l ength(), -1 )));         return  DFS (0 , 0 , s1.l ength());     } }; 
分存大小最后合并。
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 :    ListNode *partition (ListNode *head, int  x)   {         ListNode *shead, *bhead, *smaller, *bigger, *p;         for (shead = bhead = smaller = bigger = NULL , p = head; p != NULL ; p = p->next)         {             if (p->val < x) { if (shead == NULL ) shead = p; if (smaller != NULL ) smaller->next = p;                 smaller = p;             }             else              {                 if (bhead == NULL )                     bhead = p;                 if (bigger != NULL )                     bigger->next = p;                 bigger = p;             }         }         if (smaller != NULL ) smaller->next = bhead;         if (bigger != NULL ) bigger->next = NULL ;         return  shead != NULL  ? shead : bhead;     } }; 
方法一:linecnt[i][j]统计第 i 行到第
j 位置有多少个连续的
'1',接下来枚举列,每一列相当于一次直方图最大矩形统计,计算每个位置向前和向后最远的不少于当前位置值的位置,每次更新结果,总复杂度O(n^2)。
找(最远位置)用迭代指针,理论复杂度略高于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 35 36 37 38 39 40 41 42 43 44 45 class  Solution  {public :    int  maximalRectangle (vector &matrix)           if (matrix.size () == 0 ) return  0 ;         int  H = matrix.size (), W = matrix[0 ].size ();         int  ans = 0 ;         vector left (H) , right (H)  ;         vector linecnt (H, vector(W, 0 ))  ;         for (int  i = 0 ; i < H; i ++)         {             int  last = 0 ;             for (int  j = 0 ; j < W; j ++)             {                 if (matrix[i][j] == '1' ) last ++;                 else  last = 0 ;                 linecnt[i][j] = last;             }         }         for (int  k = 0 ; k < W; k ++)         {             for (int  i = 0 ; i < H; i ++)              {                 if (i == 0 ) left[i] = -1 ;                 else                  {                     left[i] = i - 1 ;                     while (left[i] > -1  && linecnt[left[i]][k] >= linecnt[i][k])                         left[i] = left[left[i]];                }             }             for (int  i = H - 1 ; i >= 0 ; i --)             {                 if (i == H - 1 ) right[i] = H;                 else                  {                     right[i] = i + 1 ;                     while (right[i] < H && linecnt[right[i]][k] >= linecnt[i][k])                         right[i] = right[right[i]];                 }                 ans = max (ans, (right[i] - left[i] - 1 ) * linecnt[i][k]);             }         }         return  ans;     } }; 
用单调栈,理论复杂度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 35 36 37 38 39 40 41 class  Solution  {public :    int  maximalRectangle (vector &matrix)           if (matrix.size () == 0 ) return  0 ;         vector linecnt (matrix.size(), vector(matrix[0 ].size(), 0 ))  ;         for (int  i = 0 ; i < matrix.size (); i ++)         {             int  last = 0 ;             for (int  j = 0 ; j < matrix[0 ].size (); j ++)             {                 if (matrix[i][j] == '1' ) last ++;                 else  last = 0 ;                 linecnt[i][j] = last;             }         }         int  ans = 0 ;         for (int  k = 0 ; k < matrix[0 ].size (); k ++)         {             stack s, site;             vectorlast (matrix.size ());             for (int  i = 0 ; i < matrix.size (); i ++) { while (!s.empty () && s.top () >= linecnt[i][k])                     s.pop (), site.pop ();                 if (!s.empty ()) last[i] = site.top () + 1 ;                 else  last[i] = 0 ;                 s.push (linecnt[i][k]);                 site.push (i);             }             while (!s.empty ()) s.pop (), site.pop ();             for (int  i = matrix.size () - 1 ; i >= 0 ; i --)             {                 while (!s.empty () && s.top () >= linecnt[i][k])                     s.pop (), site.pop ();                 if (!s.empty ()) ans = max (ans, (site.top () - last[i]) * linecnt[i][k]);                 else  ans = max (ans, (int )(matrix.size () - last[i]) * linecnt[i][k]);                 s.push (linecnt[i][k]);                 site.push (i);             }         }         return  ans;     } }; 
方法二:每个 1
的点当作一个矩形的底部,left[j]、right[j]、height[j]表示当前行第
j个位置这个点向左、右、上伸展的最大矩形的边界,作为滚动数组,下一行的数据可以由上一行结果得到,总复杂度O(n^2)。
left[j] = max(这一行最左, left[j](上一行最左) );
right[j] = min(这一行最右,right[j](上一行最右) );
height[j] = height[j - 1] + 1;
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 class  Solution  {public :    int  maximalRectangle (vector &matrix)           if (matrix.size () == 0 ) return  0 ;         int  H = matrix.size (), W = matrix[0 ].size ();         vector left (W, -1 ) , right (W, W) , height (W, 0 )  ;         int  ans = 0 ;         for (int  i = 0 ; i < H; i ++)         {             int  last = -1 ;             for (int  j = 0 ; j < W; j ++)              {                 if (matrix[i][j] == '1' )                 {                     if (last == -1 ) last = j;                     left[j] = max (left[j], last);                     height[j] ++;                }                 else                  {                     last = -1 ; left[j] = -1 ; height[j] = 0 ;                 }               } last = -1 ;               for (int  j = W - 1 ; j >= 0 ; j --)              {                 if (matrix[i][j] == '1' )                 {                     if (last == -1 ) last = j;                     right[j] = min (right[j], last);                     ans = max (ans, height[j] * (right[j] - left[j] + 1 ));                 }                 else                  {                     last = -1 ;                     right[j] = W;                 }             }         }         return  ans;     } }; 
参考上一题Maximal Rectangle方法一。
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 :    int  largestRectangleArea (vector &height)           if (height.size () == 0 ) return  0 ;         vector left (height.size()) , right (height.size())  ;         int  ans = 0 ;         for (int  i = 0 ; i < height.size (); i ++)          {             if (i == 0 ) left[i] = -1 ;             else              {                 left[i] = i - 1 ;                 while (left[i] > -1  && height[i] <= height[left[i]])                    left[i] = left[left[i]];             }          }          for (int  i = height.size () - 1 ; i >= 0 ; i --)         {             if (i == height.size () - 1 ) right[i] = height.size ();             else              {                 right[i] = i + 1 ;                 while (right[i] < height.size () && height[i] <= height[right[i]])                     right[i] = right[right[i]];             }             ans = max (ans, (right[i] - left[i] - 1 ) * height[i]);         }         return  ans;     } }; 
加个表头乱搞吧。
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 class  Solution  {public :    ListNode *deleteDuplicates (ListNode *head)   {         if (head == NULL  || head->next == NULL ) return  head;         ListNode *newhead = new  ListNode (0 );         newhead->next = head;         for (ListNode *pre = newhead, *now = head, *nex = head->next; nex != NULL ;)         {             if (now->val == nex->val)             {                 while (nex != NULL  && now->val == nex->val)                 {                     free (now);                     now = nex;                     nex = nex->next;                 }                 free (now);                 pre->next = nex;                 if (nex == NULL ) break ;             }             else  pre = now;             now = nex;             nex = nex->next;         }         head = newhead->next;         free (newhead);         return  head;     } }; 
直接操作。
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 :    ListNode *deleteDuplicates (ListNode *head)   {         if (head == NULL  || head->next == NULL ) return  head;         for (ListNode *pre = head, *p = head->next; p != NULL ;)         {             while (p != NULL  && pre->val == p->val)             {                 pre->next = p->next;                 free (p);                 p = pre->next;             }             if (p == NULL ) break ;             pre = p;             p = p->next;         }         return  head;     } }; 
以mid为界,左右两边至少有一边是有序的。由于不可避免地会有O(n)的可能性,所以确定的时候二分,不确定的时候单位缩减边界。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  Solution  {public :    bool  search (int  A[], int  n, int  target)           int  left = 0 , right = n - 1 , mid;         while (left < right) { mid = left + right >> 1 ;             if (target < A[mid] && A[left] < target) right = mid;             else  if (target < A[right] && A[mid] < target) left = mid + 1 ;             else              {                 if (A[left] == target || A[right] == target) return  true ;                 else  if (A[left] < target) left ++;                 else  if (target < A[right]) right --;                 else  return  false ;             }         }         return  A[left] == target ? true  : false ;     } }; 
记下放了几个,多了不放。
1 2 3 4 5 6 7 8 9 10 11 12 13 class  Solution  {public :    int  removeDuplicates (int  A[], int  n)           int  i, j, cnt;         for (i = j = cnt = 0 ; i < n; i ++)         {             if (j != 0  && A[j - 1 ] == A[i]) cnt ++;             else  cnt = 0 ;             if (cnt < 2 ) A[j ++] = A[i];         }         return  j;     } }; 
基础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 31 32 33 34 35 class  Solution  {public :    int  dx[4 ] = {1 , -1 , 0 , 0 };     int  dy[4 ] = {0 , 0 , 1 , -1 };     bool  DFS (int  x, int  y, vector &board, string word, int  ith)       {        if (board[x][y] != word[ith]) return  false ;         if (ith == word.length () - 1 ) return  true ;         board[x][y] = '.' ;         for (int  i = 0 ; i < 4 ; i ++)          {             int  nx = x + dx[i]; int  ny = y + dy[i];             if (nx >= 0  && ny >= 0  && nx < board.size () && ny < board[0 ].size ())             {                 if (DFS (nx, ny, board, word, ith + 1 ))                 {                     board[x][y] = word[ith];                     return  true ;                 }             }         }         board[x][y] = word[ith];         return  false ;     }     bool  exist (vector &board, string word)           for (int  i = 0 ; i < board.size (); i ++)         {             for (int  j = 0 ; j < board[0 ].size (); j ++)             {                 if (DFS (i, j, board, word, 0 )) return  true ;             }         }         return  false ;     } }; 
基础DFS。
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 now;     vector res;     void  DFS (vector &S, int  ith)       {        if (ith == S.size ())         {             res.push_back (now);             return ;         }         DFS (S, ith + 1 );         now.push_back (S[ith]);         DFS (S, ith + 1 );         now.pop_back ();     }     vector subsets (vector &S)   {         sort (S.begin (), S.end ());         DFS (S, 0 );         return  res;     } }; 
基础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 class  Solution  {public :    vector now;     vector res;     void  DFS (int  n, int  ith, int  sum, int  k)       {        if (sum == k)         {             res.push_back (now);             return ;         }         if (sum + n - ith + 1  > k)         {             DFS (n, ith + 1 , sum, k);         }         now.push_back (ith);         DFS (n, ith + 1 , sum + 1 , k);         now.pop_back ();     }     vector combine (int  n, int  k)   {         DFS (n, 1 , 0 , k);         return  res;     } }; 
先统计 T
中各字符都有多少个,然后两个下标一前(i)一后(j)在
S 上跑, 当 i 跑到把 T
中字符都包含的位置时候,让 j 追到第一个包含 T
的字符的地方,更新结果,去掉 j 这个位置字符的统计,让
i 继续跑,如此反复。
i 和 j 都只遍历一遍 S,复杂度
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 class  Solution  {public :    string minWindow (string S, string T)   {         vector cnt (256 , 0 ) , need (256 , 0 )  ;         int  sum = 0 , len = 0x3f3f3f3f ;         string ans;         for (int  i = 0 ; i < T.size (); i ++)             need[T[i]] ++;         for (int  i = 0 , j = 0 ; i < S.length (); j ++)         {             while (i < S.length () && sum < T.length ())             {                 if (cnt[S[i]] < need[S[i]])                     sum ++;                 cnt[S[i]] ++;                 i ++;             }             while (sum == T.length () && j < S.length ())             {                 cnt[S[j]] --;                 if (cnt[S[j]] < need[S[j]])                     break ;                 j ++;             }             if (sum < T.length ()) break ;             if (i - j < len)                 ans = S.substr (j, i - j), len = i - j;             sum --;         }         return  ans;     } }; 
轮流找:
1 2 3 4 5 6 7 8 9 10 class  Solution  {public :    void  sortColors (int  A[], int  n)           int  find = 0 ;         for (int  i = 0 , j = n - 1 ; i < n; i ++) { if (A[i] == find) continue ; while (j > i && A[j] != find) j --;             if (j > i) swap (A[i], A[j]);             else  i --, j = n - 1 , find ++;         }     } }; 
找到哪个放哪个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class  Solution  {public :    void  sortColors (int  A[], int  n)           int  p0, p1, p2;         for (p0 = 0 , p1 = p2 = n - 1 ; p0 < p1; )         {             if (A[p0] == 0 ) p0 ++;             if (A[p0] == 1 ) swap (A[p0], A[p1 --]);             if (A[p0] == 2 )             {                 swap (A[p0], A[p2 --]);                 p1 = p2;             }         }     } }; 
写两个二分查找。或者把整个矩阵看作一维,直接二分,换算坐标。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  Solution  {public :    bool  searchMatrix (vector &matrix, int  target)           int  left, right, mid;         for (left = 0 , right = matrix.size (); left < right - 1 ; ) { mid = left + right >> 1 ;             if (matrix[mid][0 ] > target)                 right = mid;             else  left = mid;         }         if (left == matrix.size () || right == 0 ) return  false ;         vector &a = matrix[left];         for (left = 0 , right = a.size (); left < right - 1 ;) { mid = left + right >> 1 ;             if (a[mid] > target) right = mid;             else  left = mid;         }         if (left == a.size () || right == 0 ) return  false ;         return  a[left] == target;     } }; 
O(m+n)的方法是容易想到的,而空间复杂度O(1),只要利用原矩阵的一行和一列来使用O(m+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 class  Solution  {public :    void  setZeroes (vector &matrix)           if (matrix.size () == 0 ) return ;         int  x = -1 , y = -1 ;         for (int  i = 0 ; i < matrix.size (); i ++)         {             for (int  j = 0 ; j < matrix[0 ].size (); j ++)             {                 if (matrix[i][j] == 0 )                 {                     if (x == -1 )                     {                         x = i, y = j;                     }                     else                      {                         matrix[x][j] = 0 ;                         matrix[i][y] = 0 ;                     }                 }             }         }         if (x == -1 ) return ;         for (int  i = 0 ; i < matrix.size (); i ++)             for (int  j = 0 ; j < matrix[0 ].size (); j ++)                 if ((matrix[x][j] == 0  || matrix[i][y] == 0 ) && (i != x && j != y))                     matrix[i][j] = 0 ;         for (int  i = 0 ; i < matrix.size (); i ++) matrix[i][y] = 0 ;         for (int  j = 0 ; j < matrix[0 ].size (); j ++) matrix[x][j] = 0 ;     } }; 
动态规划,先初始化 dp[i][0] 和
dp[0][i],即每个字符串对应空串的编辑距离为串长度,之后对每个位置取子问题加上当前位置
改、删、增得解的最小值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  Solution  {public :    int  minDistance (string word1, string word2)           vector dp (word1.l ength() + 1 , vector(word2.l ength() + 1 , 0 ))  ;         for (int  i = 0 ; i < word1.l ength(); i ++) dp[i + 1 ][0 ] = i + 1 ;         for (int  i = 0 ; i < word2.l ength(); i ++) dp[0 ][i + 1 ] = i + 1 ;         for (int  i = 0 ; i < word1.l ength(); i ++)             for (int  j = 0 ; j < word2.l ength(); j ++)             {                 if (word1[i] != word2[j])                     dp[i + 1 ][j + 1 ] = min (dp[i][j] + 1 , min (dp[i][j + 1 ] + 1 , dp[i + 1 ][j] + 1 ));                 else                      dp[i + 1 ][j + 1 ] = min (dp[i][j], min (dp[i][j + 1 ] + 1 , dp[i + 1 ][j] + 1 ));             }         return  dp[word1.l ength()][word2.l ength()];     } }; 
好烦人的题,没什么好说的。
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 :    string simplifyPath (string path)   {         stack s;         string str;         for (int  i = 0 ; i < path.length (); i ++)         {             if (path[i] == '/' )             {                 if (str == ".." )                 {                     if (!s.empty ())                         s.pop ();                 }                 else  if (str != "."  && str != "" )                     s.push (str);                 str.clear ();             }             else                  str += path[i];         }         if (str == ".." )         {             if (!s.empty ())                 s.pop ();         }         else  if (str != "."  && str != "" )             s.push (str);         if (s.empty ()) return  "/" ;         for (str.clear (); !s.empty (); s.pop ())             str = "/"  + s.top () + str;         return  str;     } }; 
递推,就是斐波那契数列。
1 2 3 4 5 6 7 8 class  Solution  {public :    int  climbStairs (int  n)           return  (int )             (pow ((1 +sqrt (5 ))/2 , n + 1 ) / sqrt (5 ) -             pow ((1 -sqrt (5 ))/2 , n + 1 ) / sqrt (5 ) + 0.1 );     } }; 
牛顿迭代。
设输入为n,f(x)=x^2-n,解就是f(x)=0时的x。
设猜了一数x[0],那么在f(x)在x[0]处的切线与x轴的交点x[1]更接近目标解(可画图看看)。
那么递推下去,x[i]=(x[i-1]+n/x[i-1])/2,用double,越推越精确,直到自己想要的精度。
1 2 3 4 5 6 7 8 9 10 11 12 13 class  Solution  {public :    int  sqrt (int  x)           double  now, last;         if (x == 0 ) return  0 ;         for (now = last = (double )x; ; last = now)         {             now = (last + (x / last)) * 0.5 ;             if (fabs (last - now) < 1e-5 ) break ;         }         return  (int )(now + 1e-6 );     } }; 
每行限制长度,空格均匀插入,不能完全平均的情况下优先靠前的单词间隔。
最后一行特别处理,单词间只有一个空格,剩下的放在末尾。
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 class  Solution  {public :    vector fullJustify (vector &words, int  L)   {         vector ans;         int  cnt = 0 , i, j, k, l;         for (i = 0 , j = 0 ; j < words.size (); i ++)         {             if (i < words.size ())             {                 cnt += words[i].length ();                 if (i == j) continue ;             }             if (i == words.size () || (L - cnt) / (i - j) < 1 )             {                 int  blank = 0 ;                 if (i < words.size ())                     blank = (i - j - 1 ) ? (L - cnt + words[i].length ()) / (i - j - 1 ) : 0 ;                 string tmp = "" ;                 l = i < words.size () ? (L - cnt + words[i].length () - blank * (i - j - 1 )) : 0 ;                 for (k = j; k < i; k ++, l --)                 {                     tmp += words[k];                     if (k != i - 1 )                     {                         if (i != words.size ())                         {                             for (int  bl = 0 ; bl < blank; bl ++) tmp += " " ; if (l > 0 ) tmp += " " ;                         }                         else                              tmp += " " ;                     }                 }                 while (tmp.length () < L) tmp += " " ;                 ans.push_back (tmp);                 cnt = 0 ;                 j = i;                 i --;             }         }         return  ans;     } }; 
大整数加法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class  Solution  {public :    vector plusOne (vector &digits)   {         int  cur, i;         if (digits.size () == 0 ) return  digits;         for (i = digits.size () - 1 , cur = 1 ; i >= 0 ; i --)         {             int  tmp = digits[i] + cur;             cur = tmp / 10 ;             digits[i] = tmp % 10 ;         }         if (cur) digits.insert (digits.begin (), cur);         return  digits;     } }; 
用DFA也不麻烦,题目定义太模糊,为了理解规则错很多次也没办法。
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 class  Solution  {public :    int  f[11 ][129 ];     const  int  fail = -1 ;         const  int  st = 0 ;            const  int  pn = 1 ;            const  int  di = 2 ;            const  int  del = 3 ;           const  int  ddi = 4 ;           const  int  ndel = 5 ;          const  int  dibl = 6 ;          const  int  ex = 7 ;            const  int  epn = 8 ;           const  int  edi = 9 ;           const  int  end = 10 ;          void  buildDFA ()       {        memset (f, -1 , sizeof (f));         f[st][' ' ] = st;         f[st]['+' ] = f[st]['-' ] = pn;         for (int  i = '0' ; i <= '9' ; i ++)         {             f[st][i] = f[pn][i] = f[di][i] = di;             f[del][i] = f[ndel][i] = f[ddi][i] = ddi;             f[ex][i] = f[epn][i] = f[edi][i] = edi;         }         f[di]['.' ] = ndel;         f[st]['.' ] = f[pn]['.' ] = del;         f[di][' ' ] = f[ndel][' ' ] = f[ddi][' ' ] = f[dibl][' ' ] = f[edi][' ' ] = dibl;         f[di][0 ] = f[ndel][0 ] = f[dibl][0 ] = f[ddi][0 ] = f[edi][0 ] = end;         f[di]['e' ] = f[ndel]['e' ] = f[ddi]['e' ] = ex;         f[ex][' ' ] = ex;         f[ex]['+' ] = f[ex]['-' ] = epn;     }     bool  DFA (const  char  *s)       {        int  situ = st;         for (int  i = 0 ;; i ++)         {             situ = f[situ][s[i]];             if (situ == end) return  true ;             if (situ == fail) return  false ;         }         return  true ;     }     bool  isNumber (const  char  *s)           buildDFA ();         return  DFA (s);     } }; 
翻转,大整数加法,再翻转。无心情优化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  Solution  {public :    string addBinary (string a, string b)   {         reverse (a.begin (), a.end ());         reverse (b.begin (), b.end ());         string c;         int  cur = 0 , i;         for (i = 0 ; i < min (a.length (), b.length ()); i ++) { int  tmp = a[i] - '0'  + b[i] - '0'  + cur; cur = tmp >> 1 ;             c += (tmp & 1 ) + '0' ;         }         string &t = a.length () > b.length () ? a : b;         for (; i < t.length (); i ++) { int  tmp = t[i] - '0'  + cur; cur = tmp >> 1 ;             c += (tmp & 1 ) + '0' ;         }         if (cur) c += '1' ;         reverse (c.begin (), c.end ());         return  c;     } }; 
递推
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class  Solution  {public :    int  minPathSum (vector &grid)           if (grid.size () == 0 ) return  0 ;         for (int  i = 0 ; i < grid.size (); i ++)         {             for (int  j = 0 ; j < grid[0 ].size (); j ++) { int  tmp = 0x3f3f3f3f ; if (i > 0 ) tmp = min (tmp, grid[i][j] + grid[i - 1 ][j]);                 if (j > 0 ) tmp = min (tmp, grid[i][j] + grid[i][j - 1 ]);                 grid[i][j] = tmp == 0x3f3f3f3f  ? grid[i][j] : tmp;             }         }         return  grid[grid.size () - 1 ][grid[0 ].size () - 1 ];     } }; 
递推
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class  Solution  {public :    int  uniquePathsWithObstacles (vector &obstacleGrid)           if (obstacleGrid.size () == 0 ) return  0 ;         obstacleGrid[0 ][0 ] = obstacleGrid[0 ][0 ] != 1 ;         for (int  i = 0 ; i < obstacleGrid.size (); i ++)             for (int  j = 0 ; j < obstacleGrid[0 ].size (); j ++) { if (i == 0  && j == 0 ) continue ; if (obstacleGrid[i][j] == 1 ) { obstacleGrid[i][j] = 0 ; continue ; } if (i > 0 )                     obstacleGrid[i][j] += obstacleGrid[i - 1 ][j];                 if (j > 0 )                     obstacleGrid[i][j] += obstacleGrid[i][j - 1 ];             }         return  obstacleGrid[obstacleGrid.size () - 1 ][obstacleGrid[0 ].size () - 1 ];     } }; 
这是当年学组合数时候的经典题型吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  Solution  {public :    int  COM (int  a, int  b)       {        b = min (b, a - b);         int  ret = 1 , i, j;         for (i = a, j = 1 ; i > a - b; i --)         {             ret *= i;             for (; j <= b && ret % j == 0 ; j ++)                 ret /= j;         }         return  ret;     }     int  uniquePaths (int  m, int  n)           return  COM (m + n - 2 , m - 1 );     } }; 
因为k可能比长度大,需要求长度然后k对长度取模。那么就不要矫情地追求双指针一遍扫描了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class  Solution  {public :    ListNode *rotateRight (ListNode *head, int  k)   {         if (head == NULL ) return  NULL ;         int  cnt;         ListNode *en, *p;         for (cnt = 1 , en = head; en->next != NULL ; cnt ++, en = en->next);         k %= cnt;         for (p = head, cnt --; cnt != k; cnt --, p = p->next);         en->next = head;         en = p->next;         p->next = NULL ;         return  en;     } }; 
一位一位算,每一位优先没使用过的较小的数字,而其后剩下的m个位置有 m!
种排列方法,用 k
减去,直到k不大于这个方法数,则这一位就是枚举到的这个数。
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 class  Solution  {public :    int  permu[10 ];     bool  vis[10 ];     string getPermutation (int  n, int  k)   {         permu[0 ] = 1 ;         for (int  i = 1 ; i < 10 ; i ++) permu[i] = permu[i - 1 ] * i;         memset (vis, 0 , sizeof (vis));         string ans;         for (int  i = 1 ; i <= n; i ++)         {             for (int  j = 1 ; j <= n; j ++) { if (!vis[j]) { if (k > permu[n - i])                         k -= permu[n - i];                     else                       {                         ans += '0'  + j;                         vis[j] = true ;                         break ;                     }                 }             }         }         return  ans;     } }; 
直接算每个位置的数是多少有木有很霸气→_→。
先看当前位置之外有几个嵌套的正方形,再看当前位置在当前正方形四条边的第几条,求出坐标(x,y)位置的数。
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 res;     vector nsq;     int  calnum (int  i, int  j, int  n)       {        int  num, tmp;         tmp = min (min (i, j), min (n - 1  - i, n - 1  - j));         num = nsq[tmp];         if (i == tmp) return  num + j - tmp + 1 ;         if (n - j - 1  == tmp) return  num + n - 2  * tmp + i - tmp;         if (n - i - 1  == tmp) return  num + 2  * (n - 2  * tmp) - 2  + n - j - tmp;         return  num + 3  * (n - 2  * tmp) - 3  + n - i - tmp;     }     vector generateMatrix (int  n)   {         nsq.push_back (0 );         for (int  i = n; i > 0 ; i -= 2 ) nsq.push_back (4  * i - 4 );         for (int  i = 1 ; i < nsq.size (); i ++) nsq[i] += nsq[i - 1 ];         for (int  i = 0 ; i < n; i ++)         {             vector tmp;             for (int  j = 0 ; j < n; j ++)             {                 tmp.push_back (calnum (i, j, n));             }             res.push_back (tmp);         }         return  res;     } }; 
从后往前找。
1 2 3 4 5 6 7 8 9 class  Solution  {public :    int  lengthOfLastWord (const  char  *s)           int  i, j;         for (i = strlen (s) - 1 ; i >= 0  && s[i] == ' ' ; i --);         for (j = i - 1 ; j >= 0  && s[j] != ' ' ; j --);         return  i < 0  ? 0  : i - j;     } }; 
end 比 newInterval 的 start 小的 intervals 直接插入,从 end 比
newInterval 的 start 大的 intervals 开始,到 start 比 newInterval 的 end
大的 intervals 结束,对这部分区间合并,再把之后的
intervals直接插入,特判 newInterval 最小和最大两种极端情况。
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 :    vector res;     vector insert (vector &intervals, Interval newInterval)   {         if (intervals.size () == 0 )          {             res.push_back (newInterval);             return  res;         }         int  i, j;         for (i = 0 ; i < intervals.size () && newInterval.start > intervals[i].end; i ++)             res.push_back (intervals[i]);         for (j = i; j < intervals.size () && newInterval.end >= intervals[j].start; j ++);         if (j != 0  && i != intervals.size ())             res.push_back (Interval (min (intervals[i].start, newInterval.start),                                 max (intervals[j - 1 ].end, newInterval.end)));         else              res.push_back (newInterval);         for (; j < intervals.size (); j ++)             res.push_back (intervals[j]);         return  res;     } }; 
先按start排个序,然后慢慢合并。。。
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 :    vector res;     static  bool  cxompp (const  Interval &a, const  Interval &b)       {return  a.start < b.start;}    vector merge (vector &intervals)   {         if (intervals.size () == 0 ) return  res;         sort (intervals.begin (), intervals.end (), cxompp);         Interval last = intervals[0 ];         for (int  i = 1 ; i < intervals.size (); i ++) { if (last.end >= intervals[i].start)                 last.end = max (last.end, intervals[i].end);             else                  res.push_back (last), last = intervals[i];         }         res.push_back (last);         return  res;     } }; 
维护最大可跳距离,每个位置都枚举一次。
1 2 3 4 5 6 7 8 9 10 class  Solution  {public :    bool  canJump (int  A[], int  n)           if (n == 0 ) return  false ;         int  i, jumpdis;         for (i = jumpdis = 0 ; i < n && jumpdis >= 0 ; i ++, jumpdis --)             jumpdis = max (A[i], jumpdis);         return  i == 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 class  Solution  {public :    int  dx[4 ] = {0 , 1 , 0 , -1 };     int  dy[4 ] = {1 , 0 , -1 , 0 };     vector res;     bool  JudgeValid (int  x, int  y,          vector &vis, vector &matrix)      {        return  x >= 0  && x < matrix.size () && y >= 0  && y < matrix[0 ].size () &&             vis[x][y] == false ;     }     vector spiralOrder (vector &matrix)   {         int  dir, x, y, nx, ny;         if (matrix.size () == 0 ) return  res;         vector vis (matrix.size(), vector(matrix[0 ].size(), false ))  ;         for (dir = x = y = 0 ; JudgeValid (x, y, vis, matrix); x = nx, y = ny)         {             res.push_back (matrix[x][y]);             vis[x][y] = true ;             nx = x + dx[dir];             ny = y + dy[dir];             if (!JudgeValid (nx, ny, vis, matrix))             {                 dir = (dir + 1 ) % 4 ;                 nx = x + dx[dir];                 ny = y + dy[dir];             }         }         return  res;     } }; 
代码二:
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 :    int  dx[4 ] = {0 , 1 , 0 , -1 };     int  dy[4 ] = {1 , 0 , -1 , 0 };     vector res;     vector spiralOrder (vector &matrix)   {         int  dir, x, y, nx, ny;         int  l, r, u, d;         if (matrix.size () == 0 ) return  res;         l = u = -1 ;         r = matrix[0 ].size ();         d = matrix.size ();         for (dir = x = y = 0 ; res.size () < matrix.size () * matrix[0 ].size ();             x = nx, y = ny)         {             res.push_back (matrix[x][y]);             nx = x + dx[dir];             ny = y + dy[dir];             if (nx == d || nx == u || ny == r || ny == l)             {                 dir = (dir + 1 ) % 4 ;                 if (dir == 0 ) l ++, r --, d --;                 else  if (dir == 3 ) u ++;                 nx = x + dx[dir];                 ny = y + dy[dir];             }         }         return  res;     } }; 
最大子串和,子串要求至少包含一个数字。
一个变量 sum 表示当前求得的子串和,当 sum
小于0时,对后面的子串没有贡献,则把 sum
置零,中间处理一下要求至少包含一个数字的要求即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class  Solution  {public :    int  maxSubArray (int  A[], int  n)           int  ans = A[0 ], sum = 0 ;         for (int  i = 0 ; i < n; i ++)         {             sum += A[i];             if (sum < 0 )                 sum = 0 , ans = max (ans, A[i]);             else  ans = max (ans, sum);         }         return  ans;     } }; 
题目没说 n 的取值范围,就不用 位运算 做标记了。
老老实实开三个 bool
数组,一个标记纵列,另外两个标记两个斜列,一行一行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 :    vector col, lc, rc;     int  ans;     void  DFS (int  cur, int  n)       {        if (cur == n)         {             ans ++;             return ;         }         for (int  i = 0 ; i < n; i ++)         {             if (!col[i] && !lc[n - cur - 1  + i] && !rc[cur + i])             {                 col[i] = lc[n - cur - 1  + i] = rc[cur + i] = true ;                 DFS (cur + 1 , n);                 col[i] = lc[n - cur - 1  + i] = rc[cur + i] = false ;             }         }     }     int  totalNQueens (int  n)           ans = 0 ;         col.resize (n, 0 );         lc.resize (n << 1 , 0 );         rc.resize (n << 1 , 0 );         DFS (0 , n);         return  ans;     } }; 
同上
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 :    vector tmp;     vector res;     vector col, lc, rc;     void  DFS (int  cur, int  n)       {        if (cur == n)         {             res.push_back (tmp);             return ;         }         string now (n, '.' )  ;         for (int  i = 0 ; i < n; i ++)         {             if (!col[i] && !lc[n - cur - 1  + i] && !rc[cur + i])             {                 col[i] = lc[n - cur - 1  + i] = rc[cur + i] = true ;                 now[i] = 'Q' ;                 tmp.push_back (now);                 DFS (cur + 1 , n);                 tmp.pop_back ();                 now[i] = '.' ;                 col[i] = lc[n - cur - 1  + i] = rc[cur + i] = false ;             }         }     }     vector solveNQueens (int  n)   {         col.resize (n, 0 );         lc.resize (n << 1 , 0 );         rc.resize (n << 1 , 0 );         DFS (0 , n);         return  res;     } }; 
很多人用特判错过了 n=-2147483648 这么优美的
trick,而不特判的话,似乎只能 long long 了。
经典的快速幂,用二进制理解也好,用折半理解也好,网上很多资料。
1 2 3 4 5 6 7 8 9 10 class  Solution  {public :    double  pow (double  x, int  n)           double  res = 1 ;         long  long  nn = n;         if (nn < 0 ) x = 1  / x, nn = -nn; while (nn) { if (nn & 1 ) res *= x; x *= x; nn >>= 1 ;         }         return  res;     } }; 
这概念以前没听过诶。。题也没看到样例,不知道以后会不会更新,网上查了才明白啥意思。
调换单词字母顺序能一致的单词集合全放进答案。比如有tea, eat,
aet,就都要放进答案,有cat,
atc,就都要放进答案,而如果孤零零有个dog,没其他可和他一组的,那么就不放进答案。
手写hash能更快些,但是题目没给数据范围,给hash数组定多大都没合理性,干脆用unordered_map好了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  Solution  {public :    vector res;     vector anagrams (vector &strs)   {         unordered_map<string, int > mp;         for (int  i = 0 ; i < strs.size (); i ++)         {             string tmp = strs[i];             sort (tmp.begin (), tmp.end ());             if (!mp.count (tmp)) mp[tmp] = 0 ;             else  mp[tmp] ++;         }         for (int  i = 0 ; i < strs.size (); i ++) { string tmp = strs[i]; sort (tmp.begin (), tmp.end ()); if (mp.count (tmp) && mp[tmp] > 0 )                 res.push_back (strs[i]);         }         return  res;     } }; 
四个一组,就地旋转。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  Solution  {public :    void  rotate (vector &matrix)           if (matrix.size () == 0 ) return ;         int  len = matrix.size ();         int  lenlimi = len + 1  >> 1 ;         for (int  i = 0 ; i < lenlimi; i ++)             for (int  j = 0 ; j < (len & 1  ? lenlimi - 1  : lenlimi); j ++)             {                 int  tmp = matrix[i][j];                 matrix[i][j] = matrix[len - j - 1 ][i];                 matrix[len - j - 1 ][i] = matrix[len - i - 1 ][len - j - 1 ];                 matrix[len - i - 1 ][len - j - 1 ] = matrix[j][len - i - 1 ];                 matrix[j][len - i - 1 ] = tmp;             }     } }; 
有重复数字,把数字统计起来好了。因为题目没说数字大小,所以统计用了unordered_map。
也可以把数组排序,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 31 32 33 34 35 class  Solution  {public :    unordered_map<int , int > mp;     vector tmp;     vector res;     int  numsize;     void  DFS (int  cnt)       {        if (cnt == numsize)         {             res.push_back (tmp);         }         for (unordered_map<int , int >::iterator it = mp.begin (); it != mp.end (); it ++)         {             if (it->second != 0 )             {                 tmp.push_back (it->first);                 it->second --;                 DFS (cnt + 1 );                 tmp.pop_back ();                 it->second ++;             }         }     }     vector permute (vector &num)   {         numsize = num.size ();         for (int  i = 0 ; i < num.size (); i ++)         {             if (!mp.count (num[i])) mp[num[i]] = 1 ;             else  mp[num[i]] ++;         }         DFS (0 );         return  res;     } }; 
虽然题目没说有没有重复数字。。既然 Permutations II
说有了,那就当这个没有吧。
传统DFS。
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 res;     void  DFS (int  cur, vector &num)       {        if (cur == num.size ())         {             res.push_back (num);             return ;         }         for (int  i = cur; i < num.size (); i ++)         {             swap (num[cur], num[i]);             DFS (cur + 1 , num);             swap (num[cur], num[i]);         }     }     vector permute (vector &num)   {         DFS (0 , num);         return  res;     } }; 
维护一步最远到达的位置,到达这个位置之前的位置需要的步数都是一样的,到达这个位置的时候,下一步的最远位置已经更新完毕。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class  Solution  {public :    int  jump (int  A[], int  n)           int  nex = 0 , pace = 0 , far = 0 ;         for (int  i = 0 ; i <= nex && i < n - 1 ; i ++)         {             far = max (far, A[i] + i);             if (i == nex)             {                 pace ++;                 nex = far;             }         }         return  pace;     } }; 
同步扫描两个字符串,每当 p 遇到 *
,记录s和p的当前扫描位置,当 s 与 p 不匹配时,跑扫描指针回到
* 后一个字符, s 扫描指针回到上次遇到 * 之后与
p 开始匹配位置的下一个位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class  Solution  {public :    bool  isMatch (const  char  *s, const  char  *p)           int  last_star = -1 , last_s = -1 , i, j;         for (i = j = 0 ; s[i]; )         {             if (s[i] == p[j] || p[j] == '?' ) i ++, j ++;             else  if (p[j] == '*' ) last_star = ++ j, last_s = i;             else  if (last_star != -1 ) i = ++ last_s, j = last_star;             else  return  false ;         }         while (p[j] == '*' ) j ++;         return  !s[i] && !p[j];     } }; 
翻转num1和num2,大整数乘法,把结果再翻转。注意 int 和 char
的转换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  Solution  {public :    string multiply (string num1, string num2)   {         string ans (num1.l ength() + num2.l ength() + 1 , 0 )  ;         reverse (num1. begin (), num1. end ());         reverse (num2. begin (), num2. end ());         int  cur = 0 , i, j, k;         for (i = 0 ; i < num1.l ength(); i ++)         {             for (j = 0 ; j < num2.l ength(); j ++) { ans[i + j] += cur + (num1[i] - '0' ) * (num2[j] - '0' ); cur = ans[i + j] / 10 ; ans[i + j] %= 10 ; } for (k = i + j; cur; k ++) { ans[k] += cur; cur = ans[k] / 10 ; ans[k] %= 10 ; } } for (k = ans.length () - 1 ; k > 0  && ans[k] == 0 ; k --);         ans.resize (k + 1 );         for (int  i = 0 ; i < ans.length (); i ++)             ans[i] += '0' ;         reverse (ans.begin (), ans.end ());         return  ans;     } }; 
对于每个位置,取这个位置左边最高的和右边最高的的较低者,如果较低者比这个位置高,则这个位置存水高度为较低者减该位置高度。
1 2 3 4 5 6 7 8 9 10 11 12 13 class  Solution  {public :    int  trap (int  A[], int  n)           vector pre;         int  i, maxheight, ans;         for (i = maxheight = 0 ; i < n; i ++) { maxheight = max (A[i], maxheight); pre.push_back (maxheight); } for (maxheight = ans = 0 , i = n - 1 ; i > 0 ; i --)         {             maxheight = max (A[i], maxheight);             ans += max (0 , min (pre[i] - A[i], maxheight - A[i]));         }         return  ans;     } }; 
题目要求时间O(n),空间O(1),经分析,不得不破坏原数组 A。
方法一:
剔除非整数,把原数组 A 当作存在标记,存在的数 x 则 A[x-1]取负数。
1 2 3 4 5 6 7 8 9 10 11 12 13 class  Solution  {public :    int  firstMissingPositive (int  A[], int  n)           int  i, j;         for (i = j = 0 ; i < n; i ++) if (A[i] > 0 )                 A[j ++] = A[i];         for (i = 0 ; i < j; i ++)             if (abs (A[i]) <= j)                 A[abs (A[i]) - 1 ] = -abs (A[abs (A[i]) - 1 ]);         for (i = 0 ; i < j; i ++) if (A[i] > 0 ) return  i + 1 ;         return  j + 1 ;     } }; 
方法二:把出现的符合范围的数swap到下标和数对应的位置,再次遍历,数和下标不对应则是第一个没出现的数。注意处理有重复数字。
1 2 3 4 5 6 7 8 9 10 11 12 class  Solution  {public :    int  firstMissingPositive (int  A[], int  n)           int  i;         for (i = 0 ; i < n; i ++)             while (A[i] <= n && A[i] > 0  && A[i] != i + 1  && A[A[i] - 1 ] != A[i])                 swap (A[i], A[A[i] - 1 ]);         for (i = 0 ; i < n; i ++)             if (A[i] != i + 1 ) return  i + 1 ;         return  i + 1 ;     } }; 
基础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 class  Solution  {public :    vector tmp;     vector ans;     void  DFS (vector &num, int  ith, int  now, int  target)       {        if (now == target)         {             ans.push_back (tmp);             return ;         }         if (ith == num.size ()) return ;         int  cnt = 0 ;         while (now <= target)         {             DFS (num, ith + 1 , now, target);             now += num[ith];             cnt ++;             tmp.push_back (num[ith]);         }         while (cnt --) tmp.pop_back ();     }     vector combinationSum (vector &candidates, int  target)   {         sort (candidates.begin (), candidates.end ());         DFS (candidates, 0 , 0 , target);         return  ans;     } }; 
如果一个数没有被用,那么后面重复的这个数就别用,避免重复解。
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 class  Solution  {public :    vector tmp;     vector ans;     void  DFS (vector &num, int  ith, int  now, int  target)       {        if (now == target)         {             ans.push_back (tmp);             return ;         }         if (ith == num.size ()) return ;         int  nex;         for (nex = ith + 1 ; nex < num.size () && num[nex] == num[ith]; nex ++);         DFS (num, nex, now, target);         if (num[ith] + now <= target)         {             now += num[ith];             tmp.push_back (num[ith]);             DFS (num, ith + 1 , now, target);             tmp.pop_back ();         }     }     vector combinationSum2 (vector &num, int  target)   {         sort (num.begin (), num.end ());         DFS (num, 0 , 0 , target);         return  ans;     } }; 
直接模拟,递推。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class  Solution  {public :    string countAndSay (int  n)   {         string f[2 ];         f[0 ] = "1" ;         for (int  i = 1 ; i < n; i ++)         {             f[i & 1 ].clear ();             for (int  j = 0 ; j < f[i & 1  ^ 1 ].length ();)             {                 int  cnt;                 char  x = f[i & 1  ^ 1 ][j];                 for (cnt = 0 ; j < f[i & 1  ^ 1 ].length () && f[i & 1  ^ 1 ][j] == x; cnt ++, j ++);                 f[i & 1 ] += '0'  + cnt;                 f[i & 1 ] += x;             }         }         return  f[n & 1  ^ 1 ];     } }; 
这道题考察回溯和数独结果的判断。ACM做过,就直接拿dancing
links代码了,4ms。
关于dancing links,对于面试题来说变态了些,应该不至于考察。
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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 class  Solution  {public :    int  rw[10 ], cl[10 ], in[10 ], RW[81 ], CL[81 ], IN[81 ], goal;     char  buf[100 ];     void  Mark (int  i, int  num)       {        rw[RW[i]] ^= 1  << num;         cl[CL[i]] ^= 1  << num;         in[IN[i]] ^= 1  << num;     }     void  init ()       {        int  i;         for (i = 0 ; i < 10 ; ++ i)             cl[i] = rw[i] = in[i] = 0 ;         for (i = goal = 0 ; buf[i]; ++ i)             goal += buf[i] == '.' ;         for (i = 0 ; i < 81 ; ++ i)         {             RW[i] = i / 9 , CL[i] = i % 9 , IN[i] = i / 3  % 3  + i / 27  * 3 ;             if (buf[i] != '.' )                 Mark (i, buf[i] - '1' );         }     }     inline  int  Judge (int  i, int  num)       {return  ~(rw[RW[i]] | cl[CL[i]] | in[IN[i]]) & (1  << num);}    int  Oper (int  sx, int  k, int  cur)       {        Mark (sx, k), buf[sx] = k + '1' ;         if (dfs (cur + 1 )) return  1 ;         Mark (sx, k), buf[sx] = '.' ;         return  0 ;     }     int  JudgeRWCLIN (int  cur)       {        int  i, j, k, x, cnt, sx;         for (i = 0 ; i < 9 ; ++ i)             for (k = 0 ; k < 9 ; ++ k)             {                 if (~rw[i] & (1  << k))                 {                     for (j = cnt = 0 ; j < 9 ; ++ j)                     {                         x = i * 9  + j;                         if (buf[x] == '.'  && Judge (x, k)) ++ cnt, sx = x;                     }                     if (cnt == 0 ) return  0 ;                     else  if (cnt == 1 )                         return  Oper (sx, k, cur);                 }                 if (~cl[i] & (1  << k))                 {                     for (j = cnt = 0 ; j < 9 ; ++ j)                     {                         x = j * 9  + i;                         if (buf[x] == '.'  && Judge (x, k)) ++ cnt, sx = x;                     }                     if (cnt == 0 ) return  0 ;                     else  if (cnt == 1 )                         return  Oper (sx, k, cur);                 }                 if (~in[i] & (1  << k))                 {                     for (j = cnt = 0 ; j < 9 ; ++ j)                     {                         x = i / 3  * 27  + j / 3  * 9  + i % 3  * 3  + j % 3 ;                         if (buf[x] == '.'  && Judge (x, k)) ++ cnt, sx = x;                     }                     if (cnt == 0 ) return  0 ;                     else  if (cnt == 1 )                         return  Oper (sx, k, cur);                 }             }         return  2 ;     }     bool  dfs (int  cur)       {        int  i, j, num, cnt;         if (cur == goal) return  true ;         for (i = 0 ; i < 81 ; ++ i)             if (buf[i] == '.' )             {                 for (j = cnt = 0 ; j < 9 ; ++ j)                     if (Judge (i, j)) ++ cnt, num = j;                 if (cnt == 0 ) return  false ;                 if (cnt == 1 )                         return  Oper (i, num, cur);             }         if ((num = JudgeRWCLIN (cur)) == 0 ) return  false ;         else  if (num == 1 ) return  true ;         for (i = 0 ; i < 81 ; ++ i)             if (buf[i] == '.' )             {                 for (j = 0 ; j < 9 ; ++ j)                     if (Judge (i, j))                     {                         Mark (i, j), buf[i] = j + '1' ;                         if (dfs (cur + 1 )) return  true ;                         Mark (i, j), buf[i] = '.' ;                     }             }         return  false ;     }     void  solveSudoku (vector &board)           int  site = 0 ;         for (int  i = 0 ; i < 9 ; i ++)             for (int  j = 0 ; j < 9 ; j ++)                 buf[site ++] = board[i][j];         init ();         dfs (0 );         site = 0 ;         for (int  i = 0 ; i < 9 ; i ++)             for (int  j = 0 ; j < 9 ; j ++)                 board[i][j] = buf[site ++];     } }; 
行列九宫格都判断一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class  Solution  {public :    bool  isValidSudoku (vector &board)           bool  flag[3 ][9 ][9 ];         memset (flag, false , sizeof (flag));         for (int  i = 0 ; i < 9 ; i ++)         {             for (int  j = 0 ; j < 9 ; j ++)             {                 if (board[i][j] != '.' )                 {                     int  x = board[i][j] - '1' ;                     if (flag[0 ][i][x] == true ) return  false ;                     flag[0 ][i][x] = true ;                     if (flag[1 ][j][x] == true ) return  false ;                     flag[1 ][j][x] = true ;                     if (flag[2 ][i / 3  * 3  + j / 3 ][x] == true ) return  false ;                     flag[2 ][i / 3  * 3  + j / 3 ][x] = true ;                 }             }         }         return  true ;     } }; 
二分
1 2 3 4 5 6 7 8 9 10 11 12 class  Solution  {public :    int  searchInsert (int  A[], int  n, int  target)           int  left, right, mid;         for (left = 0 , right = n; left < right; ) { mid = left + right >> 1 ;             if (A[mid] == target) return  mid;             if (A[mid] > target) right = mid;             else  left = mid + 1 ;         }         return  left;     } }; 
二分,容易错。可以用lower_bound和upper_bound。
手工代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  Solution  {public :    vector searchRange (int  A[], int  n, int  target)   {         int  left, right, mid, l, r;         for (left = 0 , right = n; left < right; ) { mid = left + right >> 1 ;             if (A[mid] >= target) right = mid;             else  left = mid + 1 ;         }         l = left;         for (left = 0 , right = n; left < right; ) { mid = left + right >> 1 ;             if (A[mid] > target) right = mid;             else  left = mid + 1 ;         }         r = left - 1 ;         if (l >= n || A[l] != target) return  vector (2 , -1 );         vector ans = {l, r};         return  ans;     } }; 
STL:
1 2 3 4 5 6 7 8 9 10 class  Solution  {public :    vector searchRange (int  A[], int  n, int  target)   {         int  l = lower_bound (A, A + n, target) - A;         int  r = upper_bound (A, A + n, target) - A;         if (l == n || A[l] != target) return  vector (2 , -1 );         vector ans = {l, r - 1 };         return  ans;     } }; 
还是二分,但是要判断一下 mid 在哪部分里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class  Solution  {public :    int  search (int  A[], int  n, int  target)           int  left = 0 , right = n - 1 , mid;         while (left < right) { mid = left + right >> 1 ;             if (A[mid] == target) return  mid;             if (A[mid] >= A[left])             {                 if (target < A[mid] && A[left] <= target) right = mid;                 else  left = mid + 1 ;             }             else              {                 if (target <= A[right] && A[mid] < target) left = mid + 1 ;                 else  right = mid;             }         }         return  A[left] == target ? left : -1 ;     } }; 
这道题时间限制在O(n),用一个 stack 实现括号配对+统计,
为了方便实现,写成数组的形式。
对不同深度的括号配对统计个数,一层配对成功把该层统计结果加给上一层,这一层清空。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  Solution  {public :    int  longestValidParentheses (string s)           vector cnt (1 , 0 )  ;         int  i, ans;         for (i = ans = 0 ; i < s.length (); i ++) { if (s[i] == '(' ) cnt.push_back (0 ); else  { if (cnt.size () > 1 )                 {                     cnt[cnt.size () - 2 ] += *cnt.rbegin () + 2 ;                     cnt.pop_back ();                     ans = max (ans, *cnt.rbegin ());                 }                 else                      cnt[0 ] = 0 ;             }         }         return  ans;     } }; 
从后往前找到第一个非降序的
num[i],再重新从后往前找到第一个比 num[i]
大的,swap(num[i], num[j]),再把 i
之后的排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class  Solution  {public :    void  nextPermutation (vector &num)           int  i, j;         for (i = num.size () - 2 ; i >= 0  && num[i] >= num[i + 1 ]; i --);         for (j = num.size () - 1 ; j > i && num[j] <= num[i]; j --);         if (i < j)         {             swap (num[i], num[j]);             sort (num.begin () + i + 1 , num.end ());         }         else              reverse (num.begin (), num.end ());     } }; 
直观的方法是枚举起点,判断这个起点下的子串是否合法,O(S.length()*L.size())。
其实可以把 S 分成 L[0].length()
个序列,每个序列都是元素间相隔 L[0].length()
的(string开头),这些序列互不相干。
如下表,假设
L[0].length()=4,第一行数字为分组组号,第二行数字表示 S
的序号。 1 2 (0)|(1)|(2)|(3)|(0)|(1)|(2)|(3)|(0)|(1)|(2)|(3)|(0)|(1)|(2)|(3)|(0)|(1)|(2)|(3)|(0)|  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 20| 
这样复杂度为O(L[0].length() * S.length() / L[0].length()) = O(S.length())。目前提交结果是180ms。
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 class  Solution  {public :    vector findSubstring (string S, vector &L)   {         vector ans;         if (L.size () == 0 ) return  ans;         unordered_map<string, int > mp, sum;         int  llen = L[0 ].length (), i, front, rear;         for (int  i = 0 ; i < L.size (); i ++)         {             if (!mp.count (L[i])) mp[L[i]] = 1 ;             else  mp[L[i]] ++;         }         for (i = 0 ; i < llen; i ++)         {             sum = mp;             int  cnt = 0 ;             for (front = rear = i; front + llen <= S.length (); front += llen) {                 string tmp = S.substr (front, llen);                 if (sum.count (tmp))                 {                     if (sum[tmp] > 0 )                     {                         sum[tmp] --;                         cnt ++;                         if (cnt == L.size ())                         {                             ans.push_back (rear);                         }                     }                     else                      {                         while (sum[tmp] == 0 )                         {                             string ntmp = S.substr (rear, llen);                             sum[ntmp] ++;                             cnt --;                             rear += llen;                         }                         sum[tmp] --;                         cnt ++;                         if (cnt == L.size ())                         {                             ans.push_back (rear);                         }                     }                 }                 else                  {                     while (rear < front)                     {                         string ntmp = S.substr (rear, llen);                         sum[ntmp] ++;                         cnt --;                         rear += llen;                     }                     rear += llen;                     cnt = 0 ;                 }             }         }         return  ans;     } }; 
假设 dividend 与 divisor 正负一致, divisor^(2^n)
为最接近 dividend 的 divisor 的幂,那么令
newdividend = dividend - divisor^(2^n),ans = ans + 2^n,问题就更新为
newdividend 除以 divisor,如此迭代。用 divisor(2 n) 是因为
divisor 不停地辗转加自己就可以得到了。
有 -2147483648 这样的极限数据,因为 int 范围是
-2147483648~+2147483647,发现负数比正数范围(多1),干脆把所有数都转成负数算,这样就避免用
long long 了。最后考察一下flag。
(如果转成正数的话,int 的 -(-2147483648)还是 -2147483648。。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class  Solution  {public :    int  divide (int  dividend, int  divisor)           bool  flag = false ;         if (divisor > 0 ) divisor = -divisor, flag ^= true ;         if (dividend > 0 ) dividend = -dividend, flag ^= true ;         int  ans = 0 , res = divisor, ex = 1 ;         if (divisor < dividend) return  0 ; while (res >= dividend - res)         {             res += res;             ex += ex;         }         while (res <= divisor && dividend) { if (res >= dividend)             {                 dividend -= res;                 ans += ex;             }             res >>= 1 ;             ex >>= 1 ;         }         return  flag ? -ans : ans;     } }; 
KMP。
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 :    char  *strStr (char  *haystack, char  *needle)           int  hlen = (int )strlen (haystack), nlen = (int )strlen (needle);         if (nlen == 0 ) return  haystack;         vector next (nlen + 1 )  ;         next[0 ] = -1 ;         for (int  i = 0 , j = -1 ; i < nlen;)         {             if (j == -1  || needle[i] == needle[j])             {                 i ++, j ++;                 if (needle[i] != needle[j]) next[i] = j;                 else  next[i] = next[j];             }             else  j = next[j];         }         for (int  i = 0 , j = 0 ; i < hlen;)         {             if (j == -1  || haystack[i] == needle[j])                 i ++, j ++;             else  j = next[j];             if (j == nlen) return  haystack + i - j;         }         return  NULL ;     } }; 
两个游标 i, j 异步挪动,把不等于给定值的数往前挪。
1 2 3 4 5 6 7 8 9 class  Solution  {public :    int  removeElement (int  A[], int  n, int  elem)           int  i, j;         for (i = j = 0 ; i < n; i ++)             if (A[i] != elem) A[j ++] = A[i];         return  j;     } }; 
两个游标 i, j 异步挪动,不重复值往前挪。
1 2 3 4 5 6 7 8 9 class  Solution  {public :    int  removeDuplicates (int  A[], int  n)           int  i, j;         for (i = j = 1 ; i < n; i ++)             if (A[i] != A[i - 1 ]) A[j ++] = A[i];         return  n ? j : 0 ;     } }; 
用头插法来做的,顺序插入到首节点之后,就反转了。每 k
个节点处理之后,把首节指针点移动到下 k 个的开头。最后面不足 k
个的话,再反转回来。
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 class  Solution  {public :    int  Reverse (ListNode *&pre, ListNode *&p, int  k)       {        int  i;         ListNode *nex, *tmp;         for (i = 1 ; p != NULL ; i ++, p = tmp)         {             if (i == 1 ) nex = p;             tmp = p->next;             p->next = pre->next;             pre->next = p;             if (i == k) i = 0 , pre = nex;         }         nex->next = NULL ;         return  i;     }     ListNode *reverseKGroup (ListNode *head, int  k)   {         if (head == NULL ) return  NULL ;         ListNode *tmphead = new  ListNode (0 ), *pre = tmphead, *p = head;         tmphead->next = head;         if (Reverse (pre, p, k) != 1 )         {             p = pre->next;             Reverse (pre, p, k);         }         return  tmphead->next;     } }; 
Reverse Nodes in k-Group的简化版。
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 class  Solution  {public :    ListNode *swapPairs (ListNode *head)   {         if (head == NULL ) return  NULL ;         ListNode *tmphead = new  ListNode (0 ), *pre = tmphead, *p = head, *tmp, *nex;         tmphead->next = head;         for (int  i = 0 ; p != NULL ; i ++, p = tmp)         {             if (i & 1  ^ 1 ) nex = p;             tmp = p->next;             p->next = pre->next;             pre->next = p;             if (i & 1 ) pre = nex;         }         nex->next = NULL ;         return  tmphead->next;     } }; 
一个堆(这里用了优先级队列),把所有 list
的首元素放堆里,O(logn)取得最小值插入新队列,异步推进。
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 class  Solution  {public :    struct  comp      {         bool  operator () (ListNode *a,ListNode *b)           {return  a->val > b->val;}    };     ListNode *mergeKLists (vector &lists)   {         ListNode *tmphead = new  ListNode (0 ), *p = tmphead;         priority_queue<ListNode*, vector<ListNode*>, comp> q;         for (int  i = 0 ; i < lists.size (); i ++) if (lists[i] != NULL ) q.push (lists[i]); while (!q.empty ()) { p->next = q.top ();             p = p->next;             q.pop ();             if (p ->next != NULL ) q.push (p->next);         }         return  tmphead->next;     } }; 
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 class  Solution  {public :    string tmp;     vector ans;     void  DFS (int  left, int  right, int  n)       {        if (left == right && left == n)         {             ans.push_back (tmp);             return ;         }         if (left < n)         {             tmp[left + right] = '(' ;             DFS (left + 1 , right, n);         }         if (right < left)         {             tmp[left + right] = ')' ;             DFS (left, right + 1 , n);         }     }     vector generateParenthesis (int  n)   {         tmp.resize (n << 1 );         DFS (0 , 0 , n);         return  ans;     } }; 
归并排序的一次操作,设个哨兵头结点,结束后free。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class  Solution  {public :    ListNode *mergeTwoLists (ListNode *l1, ListNode *l2)   {         ListNode *thead = new  ListNode (0 ), *p = thead;         while (l1 != NULL  && l2 != NULL )         {             if (l1->val < l2->val) p->next = l1, p = l1, l1 = l1->next;             else  p->next = l2, p = l2, l2 = l2->next;         }         while (l1 != NULL ) p->next = l1, p = l1, l1 = l1->next;         while (l2 != NULL ) p->next = l2, p = l2, l2 = l2->next;         p = thead->next;         free (thead);         return  p;     } }; 
用栈配对。
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 class  Solution  {public :    bool  isValid (string s)           stack st;         for (int  i = 0 ; i < s.length (); i ++)         {             switch (s[i])             {                 case  '(' : st.push ('(' ); break ;                 case  '[' : st.push ('[' ); break ;                 case  '{' : st.push ('{' ); break ;                 case  ')' :                     if (st.empty () || st.top () != '(' ) return  false ;                     st.pop (); break ;                 case  ']' :                     if (st.empty () || st.top () != '[' ) return  false ;                     st.pop (); break ;                 case  '}' :                     if (st.empty () || st.top () != '{' ) return  false ;                     st.pop (); break ;             }         }         return  st.empty ();     } }; 
两个指针相隔 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 class  Solution  {public :    ListNode *removeNthFromEnd (ListNode *head, int  n)   {         ListNode *pre, *slow, *quick;         ListNode *newhead = new  ListNode (0 );         newhead->next = head;         int  i = 0 ;         for (pre = slow = quick = newhead; quick != NULL ; i ++)         {             pre = slow;             if (i >= n) slow = slow->next;             quick = quick->next;         }         pre->next = slow->next;         free (slow);         return  newhead->next;     } }; 
尝试了O(n^2)的,但是应该常数很大吧,超时了。就是哈希存两两的和,然后通过查哈希表找到
两两+两两,要判断数字重复情况。这题数据量挺大的,O(n^3)如果用不太好的方式实现的话也会超。
O(n^3)方法:先对num排序,然后从两头枚举两个数,O(n^2),后两个数在前两个数之间的两端开始,和小了左边的往右,和大了右边的往左调整,O(n),总共O(n^3)。
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 :    vector ans;     vector fourSum (vector &num, int  target)   {         if (num.size () < 4 ) return  ans;         sort (num.begin (), num.end ());         for (int  left = 0 ; left < num.size () - 3 ;) { for (int  right = num.size () - 1 ; right > left + 2 ;)              {                 int  ml = left + 1 , mr = right - 1 ;                 while (ml < mr)                  {                      int  tmpsum = num[left] + num[right] + num[ml] + num[mr];                      if (tmpsum > target) mr --;                     else  if (tmpsum < target) ml ++;                     else                      {                         vector tmp = {num[left], num[ml], num[mr], num[right]};                         ans.push_back (tmp);                         ml ++;                         mr --;                     }                     for (; ml != left + 1  && ml < mr && num[ml] == num[ml - 1 ]; ml ++);                     for (; mr != right - 1  && ml < mr && num[mr] == num[mr + 1 ]; mr --);                  }                  for (right --; right > left + 2  && num[right] == num[right + 1 ]; right --);             }             for (left ++; left < num.size () - 3  && num[left] == num[left - 1 ]; left ++);         }         return  ans;     } }; 
基础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 class  Solution  {public :    const  vector v = {"" , "" , "abc" , "def" , "ghi" , "jkl" , "mno" , "pqrs" , "tuv" , "wxyz" };     vector ans;     string tmp;     void  DFS (int  cur, string d)       {        if (cur == d.length ())         {             ans.push_back (tmp);             return ;         }         for (int  i = 0 ; i < v[d[cur] - '0' ].length (); i ++)         {             tmp[cur] = v[d[cur] - '0' ][i];             DFS (cur + 1 , d);         }     }     vector letterCombinations (string digits)   {         tmp.resize (digits.length ());         DFS (0 , digits);         return  ans;     } }; 
O(n^2),先排序,枚举第一个数,后两个数一个在第一个数后边一个开始,一个从
末尾开始,和4Sum类似调整。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class  Solution  {public :    int  threeSumClosest (vector &num, int  target)           bool  findans = false ;         int  ans;         sort (num.begin (), num.end ());         for (int  i = 0 ; i < num.size (); i ++)         {             for (int  left = i + 1 , right = num.size () - 1 ; left < right;)              {                  int  tmpsum = num[i] + num[left] + num[right];                  if (tmpsum > target) right --;                 else  if (tmpsum < target) left ++;                 else  return  tmpsum;                 if (!findans || abs (tmpsum - target) < abs (ans - target))                     ans = tmpsum, findans = true ;             }         }         return  ans;     } }; 
同上。
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 :    vector ans;     vector threeSum (vector &num)   {         if (num.size () < 3 ) return  ans;         sort (num.begin (), num.end ());         for (int  i = 0 ; i < num.size ();)         {             for (int  left = i + 1 , right = num.size () - 1 ; left <right;)             {                 int  tmpsum = num[i] + num[left] + num[right];                 if (tmpsum < 0 ) left ++; else  if (tmpsum > 0 ) right --;                 else                  {                     vector tmp = {num[i], num[left], num[right]};                     ans.push_back (tmp);                     left ++;                     right --;                 }                 for (; left != i + 1  && left < right && num[left] == num[left - 1 ]; left ++);                 for (; right != num.size () - 1  && left < right && num[right] == num[right + 1 ]; right --);             }             for (i ++; i < num.size () && num[i] == num[i - 1 ]; i ++);         }         return  ans;     } }; 
一个一个扫
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class  Solution  {public :    string ans;     string longestCommonPrefix (vector &strs)   {         if (strs.size () == 0 ) return  ans;         if (strs.size () == 1 ) return  strs[0 ];         for (int  j = 0 ; ; j ++)         {             for (int  i = 1 ; i < strs.size (); i ++)                 if (strs[i].size () == j || strs[i][j] != strs[i - 1 ][j]) return  ans;             ans += strs[0 ][j];         }         return  ans;     } }; 
各有各的方法,重点是记录(上一个)数比(这个)数大或小,来确定谁减谁。基本是右结合的,所以从后往前扫好处理些。
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 class  Solution  {  public :      int  ro[128 ];     int  romanToInt (string s)           ro['I' ] = 1 ;         ro['V' ] = 5 ;         ro['X' ] = 10 ;         ro['L' ] = 50 ;         ro['C' ] = 100 ;         ro['D' ] = 500 ;         ro['M' ] = 1000 ;         int  ans = -1 , last;         for (int  i = s.length () - 1 ; i >= 0 ; i --)         {             if (ans == -1 ) ans = ro[s[i]];             else              {                 if (last > ro[s[i]]) ans -= ro[s[i]];                 else  ans += ro[s[i]];             }             last = ro[s[i]];         }         return  ans;     } }; 
每个十进制位格式是一样的,只是字母替换一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class  Solution  {public :    vector table = {"" , "I" , "II" , "III" , "IV" , "V" , "VI" , "VII" , "VIII" , "IX" };     string ro = "IVXLCDM" ;     char  convert (char  x, int  i)       {        if (x == 'I' ) return  ro[i];         if (x == 'V' ) return  ro[i + 1 ];         if (x == 'X' ) return  ro[i + 2 ];     }     string intToRoman (int  num)   {         string ans;         for (int  i = 0 ; num; i += 2 , num /= 10 )         {             int  x = num % 10 ;             string tmp = table[x];             for (int  j = 0 ; j < tmp.size (); j ++)                 tmp[j] = convert (tmp[j], i);             ans = tmp + ans;         }         return  ans;     } }; 
从两端开始枚举,较高的挡板往中间枚举的话一定无法得到更优解,故反复从较低挡板向中间枚举,O(n)。
1 2 3 4 5 6 7 8 9 10 11 12 13 class  Solution  {public :    int  maxArea (vector &height)           int  left = 0 , right = height.size () - 1 , ans = -1 ;         while (left < right)         {             ans = max (ans, min (height[left], height[right]) * (right - left));             if (height[left] < height[right]) left ++;             else  right --;         }         return  ans;     } }; 
每遇到一个 *
,问题都会出现分枝,需要用到栈或者递归。
没有 * 的情况好处理,遇到 *
的时候,穷举所有匹配长度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  Solution  {public :    bool  isMatch (const  char  *s, const  char  *p)           if (*p == 0 ) return  *s == 0 ;         if (*(p + 1 ) != '*' )         {             if (*s && (*s == *p || *p == '.' ))                return  isMatch (s + 1 , p + 1 );             return  false ;         }         else          {             for (; *s && (*s == *p || *p == '.' ); s ++)                 if (isMatch (s, p + 2 )) return  true ;             return  isMatch (s, p + 2 );         }     } }; 
首先处理负数的trick。然后主要思路就是通过
while(...) a = a * 10 + x % 10; 来将 x 翻转。
但是注意到 x 很大的时候,翻转的 x 会超出 int
范围,也许会刚好成为另一个和 a
得出的数相等的正数,所以不能完全翻转后判断,而可以在翻转恰好一半的时候判断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class  Solution  {public :    bool  isPalindrome (int  x)           if (x < 0 ) return  false ;          if (x == 0 ) return  true ;          int  a = 0 , b = x, cnt = 1 ;          while (x /= 10 ) cnt ++;          for (; b && cnt >= 0 ; b /= 10 , cnt -= 2 )         {             if (cnt == 1 ) return  a == b / 10 ;             else  if (cnt == 0 ) return  a == b;             a = a * 10  + b % 10 ;         }         return  false ;     } }; 
任何类似多符号、符号数字间有空格的小问题都直接输出
0,这就好办了。处理越界用 long long。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  Solution  {public :    int  atoi (const  char  *str)           long  long  ans = 0 ;         bool  flag = false ;         for (; *str == ' ' ; str ++);         if (*str == '+' ) str ++;         else  if (*str == '-' ) flag = true , str ++;         for (; isdigit (*str); str ++)         {             ans = ans * 10  + *str - '0' ;             if ((flag ? -ans : ans) > INT_MAX) return  INT_MAX;             else  if ((flag ? -ans : ans) < INT_MIN) return  INT_MIN;         }         return  (int )(flag ? -ans : ans);     } }; 
还是关于越界的讨论,不过这道题本身没有设置处理方式,重点在于面试时的交流。
1 2 3 4 5 6 7 8 9 class  Solution  {public :    int  reverse (int  x)           int  a = 0 ;         for ( int  b = x >= 0  ? x : -x; b; b /= 10 )             a = a * 10  + b % 10 ;         return  x >= 0  ? a : -a;     } }; 
题意的 "z"
字形指一列nRows个,然后斜着往右上一格一个回到第一行,然后再一列nRows个。比如nRows=5,如下:
1 2 3 4 5 1          9          17          25 2       8 10       16 18       24 26 3    7    11    15    19    23    27    … 4 6       12 14       20 22       28 30 5         13          21          29 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  Solution  {public :    string convert (string s, int  nRows)   {         if (nRows == 1 ) return  s;         string ans;         int  a = (nRows << 1 ) - 2 , b = 0 ;         for (int  i = 0 ; i < nRows; i ++, a -= 2 , b += 2 )         {             bool  flag = false ;             for (int  j = i; j < s.length ();                     j += flag ? (b ? b : a) : (a ? a : b), flag ^= 1 )                 ans += s[j];         }         return  ans;     } }; 
网上O(n)的方法是厉害啊。。。简单解释如下:
1、预处理字符串,前后加(哨兵)字符比如
!,每个字母旁边加辅助字符比如#,这样例如字符串
s="ababbcbb" 就变成
tmp="!#a#b#a#b#b#c#b#b#!"。这样的好处是不用讨论回文串长度的奇偶。
2、对转化后的串,维护一个 center 和一个 reach,center 是当前已发现的
reach 最远的回文串中心位置,reach
是这个回文串最右端的位置,center和reach可初始化为
1,即第一个#的位置。
3、维护一个数组vector r(tmp.length()),r[i]
表示 i 位置为中心的回文串半径。
4、在考察位置 i 的时候,所有 j < i 的 r[j] 都是已知的子问题。如果
i 在 reach 的左边,则 i 包含在以 center
为中心的回文串中,那么可以想到,如果和 i 关于 center 对称位置的 mirrori
为中心的回文串覆盖范围没有到达 center 为中心的回文串边缘,则 i
为中心的回文串肯定和 mirrori 的一样。而如果 mirrori
的回文串到达了边缘甚至超过,或者 i 本来就在 reach 的右边,那么对 i
为中心的回文串进行一次扩展,则结果
或者刚好不扩展,或者一定更新了reach。无论怎样,这里都得到了
r[i]。知道了所有 r[i],答案就出来了。
核心问题在于第4步“对 i 为中心的回文串进行扩展”的复杂度。每次发生“对 i
扩展”,必然是对 reach 的扩展(也可能刚好不扩展,这个不影响复杂度),而
reach 的扩展范围是 tmp 的长度大约 2n,所以总复杂度为 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 :    string longestPalindrome (string s)   {         int  center = 1 , reach = 1 , ansstart = 0 , anslength = 0 ;         string tmp = "!#" ;         for (int  i = 0 ; i < s.length (); i ++)             tmp += s[i], tmp += '#' ;         tmp + '!' ;         vector r (tmp.length())  ;         for (int  i = 2 ; i < tmp.length (); i ++) { int  mirrori = center * 2  - i; r[i] = reach > i ? min (r[mirrori], reach - i) : 0 ;             for (; tmp[i + r[i] + 1 ] == tmp[i - r[i] - 1 ]; r[i] ++);             if (i + r[i] > reach)                 reach = i + r[i], center = i;             if (r[i] > anslength)             {                 ansstart = i - r[i] >> 1 ;                 anslength = r[i];             }         }         return  s.substr (ansstart, anslength);     } }; 
如果 A[pa] < B[pb],那么 A[pa] 一定在 A 与 B 合并后的前 pa + pb +
2 个数中。
证明: A 中有 pa + 1 个数 <= A[pa],B 中有小于 pb + 1 个数 <=
A[pa],合并后有少于pa + pb + 2 个数 <= A[pa]。
利用这个性质迭代找 A 与 B 合并后的第 k 大数。
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 class  Solution  {public :    int  findKth (int  A[], int  m, int  B[], int  n, int  k)       {        int  pm, pn;         while (true )         {             if (m == 0 ) return  B[k - 1 ];             if (n == 0 ) return  A[k - 1 ];             if (k == 1 ) return  min (A[k - 1 ], B[k - 1 ]);             if (m <= n) pm = min (k >> 1 , m), pn = k - pm;             else                  pn = min (k >> 1 , n), pm = k - pn;             if (A[pm - 1 ] < B[pn - 1 ]) A += pm, m -= pm, k -= pm; else  if (A[pm - 1 ] > B[pn - 1 ])                  B += pn, n -= pn, k-= pn;             else  break ;         }         return  A[pm - 1 ];     }     double  findMedianSortedArrays (int  A[], int  m, int  B[], int  n)           if ((m + n) & 1 ) return  findKth (A, m, B, n, (m + n >> 1 ) + 1 );         else  return  (findKth (A, m, B, n, m + n >> 1 ) +             findKth (A, m, B, n, (m + n >> 1 ) + 1 )) * 0.5 ;     } }; 
维护一个不重复字符的区间。
代码一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class  Solution  {public :    int  lengthOfLongestSubstring (string s)           vector isin (128 , false )  ;         int  ans = 0 ;         for (int  front = 0 , rear = 0 ; front < s.length (); front ++)         {             if (isin[s[front]])                 for (; rear < front && isin[s[front]]; isin[s[rear]] = false , rear ++);             isin[s[front]] = true ;             ans = max (ans, front - rear + 1 );         }         return  ans;     } }; 
代码二:
1 2 3 4 5 6 7 8 9 10 11 12 13 class  Solution  {public :    int  lengthOfLongestSubstring (string s)           vector site (128 , -1 )  ;         int  nowstart = -1 , ans = 0 ;         for (int  i = 0 ; i < s.length (); i ++) { if (site[s[i]] >= nowstart)                 nowstart = site[s[i]] + 1 ;             site[s[i]] = i;             ans = max (i - nowstart + 1 , ans);         }         return  ans;     } }; 
大整数加法的链表版。
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 :    ListNode *addTwoNumbers (ListNode *l1, ListNode *l2)   {         ListNode *ans = new  ListNode (0 ), *p = ans;         int  cur = 0 ;         while (l1 != NULL  || l2 != NULL  || cur)         {             p->val = (l1 ? l1->val : 0 ) + (l2 ? l2->val : 0 ) + cur;             cur = p->val / 10 ;             p->val %= 10 ;             if (l1) l1 = l1->next;             if (l2) l2 = l2->next;             if (l1 || l2 || cur)                 p->next = new  ListNode (0 );             p = p->next;         }         return  ans;     } }; 
哈希存位置,O(n)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class  Solution  {public :    vector twoSum (vector &numbers, int  target)   {         unordered_map<int , int > mp;         vector ans;         for (int  i = 0 ; i < numbers.size (); i ++)         {             if (mp.count (target - numbers[i]))             {                 ans.push_back (mp[target - numbers[i]] + 1 );                 ans.push_back (i + 1 );                 break ;             }             if (!mp.count (numbers[i])) mp[numbers[i]] = i;         }         return  ans;     } };