• LeetCode高频148错题记录


    3. Max Points on a Line 共线点个数3种解法

    思路一:思考如何确定一条直线,两点法,确定斜率后带入一点。有三种情况,1. 两点重合,2. 斜率不存在,3. 正常算,依次以每个点为过直线的点,map映射斜率个数。

    思路二:后两种情况合并,用(dy/d, dx/d)表示,其中d=gcd(dx, dy),这样避免了除法的精度损失

    思路三:暴力O(n^3)的解法,枚举任意一条直线,判断三点共线三角形面积法(1/2*ABxAC==0)叉积为零(行列式为0)

    三点共线 

    4. O(nlogn)链表排序

    思路一:归并排序(注意要做截断处理前一半的末尾赋值为NULL,奇数偶数都考虑可以避免找中间结点的错误,if(two&&two->next))

    思路二:快速排序 

     1 class Solution {
     2 public:
     3     ListNode* sortList(ListNode* head) {
     4         quicksort(head,NULL);
     5         return head;
     6     }
     7     void quicksort(ListNode* head, ListNode* end)
     8     {
     9         if(head!=end)
    10         {
    11             ListNode* partion = partition(head,end);
    12             quicksort(head, partion);
    13             quicksort(partion->next,end);
    14         }
    15     }
    16     ListNode* partition(ListNode* head, ListNode* end)
    17     {
    18         ListNode* slow = head;
    19         ListNode* fast = head->next;
    20         while(fast!=end)
    21         {
    22             if(fast->val<head->val)
    23             {
    24                 slow = slow->next;
    25                 swap(fast->val,slow->val);
    26             }
    27             fast = fast->next;
    28         }
    29         swap(slow->val,head->val);
    30         return slow;
    31     }
    32 };

    6 后序遍历非递归

    思路一:标记变量法,判断是否为遍历完右子树返回。

    要点:标记变量mark,初始化为NULL。后序从右子树返回则mark = root,从左子树返回的,还要重新入栈,注意两个循环的条件都是栈不为空。

    思路二: 判断法

    入栈顺序根节点,右儿子,左儿子。这样出栈顺序就是后序的顺序,判断条件一定要注意pre!=NULL一定不能忘记写,如果忘记了,pre==NULL时有一个子树为空也满足条件就错了。

    if((cur->left==NULL&&cur->right==NULL)||(pre!=NULL&&(cur->left==pre||cur->right==pre)))

    一种情况如果左右子树为空,那么直接记录答案并更新pre;另一种情况,如果当前pre不为空并且表示从任意一个子树返回,那么也是正确的后序返回顺序。为什么从左子树返回也是正确的,因为按照左右中的顺序出栈,那么到当前点,pre==cur->left,表明右子树为空。

    思路三:左右子树入栈顺序改变的先序遍历+reverse

    栈来操作,先:中左右->后:左右中,先反转:右左中,将左右子树入栈顺序调换即可

    8. reorder-list. 

     LL0→L1→…→Ln-1→Ln,  变成这样:L0→Ln →L1→Ln-1→L2→Ln-2→… 思路,快慢指针找中点,找到后尾串反转(双指针法,或头插[第二个放前面,第三个放前面..]),两个链表合并。

    11. word-break-ii:

    枚举所有情况的题,多半是dfs,但如果不用记忆数组做减少重复计算的优化,那么递归方法跟brute force没有区别,所以要避免重复计算。或用dp搞。

     1     vector<string> wordBreak(string s, vector<string>& wordDict) {
     2         unordered_map<string, vector<string>> m;
     3         return helper(s,wordDict,m);
     4     }
     5     vector<string> helper(string s,  vector<string> & dict, unordered_map<string, vector<string>>& m)
     6     {
     7         if(m.count(s))return m[s]; //记忆化搜索
     8         if(s.empty())return {""};  //递归出口
     9         vector<string> res;
    10         for(string word:dict)
    11         {
    12             if(s.substr(0,word.size())!=word)continue;
    13             vector<string> remember = helper(s.substr(word.size()),dict,m);
    14             for(string str:remember)
    15             {
    16                 res.push_back(word + (str.empty()?"":" ")+str);
    17             }
    18         }
    19         return m[s]=res;
    20     }

    single-number-ii

    思路:转自菜鸟葫芦娃

    Single Number的本质,就是用一个数记录每个bit出现的次数,如果一个bit出现两次就归0,这种运算采用二进制底下的位操作^是很自然的。Single Number II中,如果能定义三进制底下的某种位操作,也可以达到相同的效果,Single Number II中想要记录每个bit出现的次数,一个数搞不定就加两个数,用ones来记录只出现过一次的bits,用twos来记录只出现过两次的bits,ones&twos实际上就记录了出现过三次的bits,这时候我们来模拟进行出现3次就抵消为0的操作,抹去ones和twos中都为1的bits

     1 int singleNumber(int A[], int n) {
     2         int ones = 0; // 记录只出现一次的bits
     3         int twos = 0; // 记录只出现二次的bits
     4         int threes;
     5         for(int i=0;i<n;i++)
     6         {
     7             int t=A[i];
     8             twos |= ones&t; // 要在更新ones之前更新twos
     9             ones ^=t;
    10             threes = ones&twos;// ones和twos中都为1即出现3次
    11             ones &= ~threes;//抹去出现3次的bits
    12             twos &= ~threes;
    13         }
    14         return ones;
    15     }

     Integer break

    思路一:O(n^2)的dp,注意拆分的时候,可以不拆分包含自己

     1      dp[2]=1;
     2         dp[3]=2;
     3         for(int i=4;i<=n;i++)
     4         {
     5             for(int j=1;j<i;j++)
     6             {
     7                 dp[i] = max(max(dp[j],j)*max(dp[i-j],i-j),dp[i]);// 注意这里
     8             }
     9         }
    10         return dp[n];

    思路二:O(n)的dp,和台阶题类似,在n >3的情况下,处理一个数要么拆分成2,要么拆分成3,(4的话相当于2个2 , 拆成1的话乘积太小了)

    1         dp[2] = 2
    2         dp[3] = 3
    3         for(int i=4;i<=n;i++)
    4             dp[i] = max(dp[i - 2] * 2, dp[i - 3] * 3)
    5         return dp[n]    

    思路三:

    拆成3的比拆成2的乘积大。 比如6的时候 2*2*2 < 3*3

    希望能尽可能的拆成3,然后才是2.

    所以,如果

    • n % 3 == 0:  那么全部拆成3
    • n % 3 == 1:  2个2剩下的为3    4*3^(x-1) > 1*3^x
    • n % 3 == 2:  1个2剩下的为3

    16 candy

    要求每个小朋友至少分一个,rating高的比邻居分的多

    思路:分两步,从左到右,从右到左。从右到左时注意,如果此时dp[i]>dp[i+1]则continue,没必要加了,别无脑加1!

    18 clone-graph

    类比任意指针的链表深拷贝

    思路:dfs或bfs遍历复制,用map记录以及visited处理

     1 /**
     2  * Definition for undirected graph.
     3  * struct UndirectedGraphNode {
     4  *     int label;
     5  *     vector<UndirectedGraphNode *> neighbors;
     6  *     UndirectedGraphNode(int x) : label(x) {};
     7  * };
     8  */
     9 class Solution {
    10 public:
    11     UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
    12         unordered_map<int, UndirectedGraphNode *> umap;
    13         return clone(node,umap);
    14     }
    15     UndirectedGraphNode * clone(UndirectedGraphNode* node,unordered_map<int, UndirectedGraphNode *> & umap)
    16     {
    17         if(!node)return node;
    18         if(umap.count(node->label))return umap[node->label];
    19         UndirectedGraphNode* newNode = new UndirectedGraphNode(node->label);
    20         umap[node->label]=newNode;
    21         for(int i=0;i<node->neighbors.size();i++)
    22         {
    23             newNode->neighbors.push_back(clone(node->neighbors[i],umap));
    24         }
    25         return newNode;
    26     }
    27 };

    25 word ladder

    一次只能变一个字母,且中间状态必须在dict中存在,可能有很多中间状态,求最小转换次数,最小的搜索方式就是bfs

    思路:从第一个字母开始改,26个字母分别试,用一个map记录是否访问过以及到这个状态所需的次数。

     1 class Solution {
     2 public:
     3     int ladderLength(string start, string end, unordered_set<string> &dict) {
     4         unordered_map<string,int> pathCnt{{{start,1}}};
     5         queue<string> q{{start}};
     6         while(q.size())
     7         {
     8             string word = q.front();q.pop();
     9             for(int i=0;i<word.size();i++)
    10             {
    11                 string newWord = word;
    12                 for(char ch = 'a';ch<='z';ch++)
    13                 {
    14                     newWord[i] = ch;
    15                     if(newWord==end)return pathCnt[word]+1;
    16                     if(dict.count(newWord)&&!pathCnt.count(newWord))
    17                     {
    18                         q.push(newWord);
    19                         pathCnt[newWord]=pathCnt[word]+1;
    20                     }
    21                 }
    22             }
    23         }
    24         return 0;
    25     }
    26 }; 

    *27 binary-tree-maximum-path-sum

    这题之前做过,现在又不会了orz。类似一维数组最大和问题,换到了二叉树上,起点终点在任意结点,可能有负数。

    考察递归和树的dfs很好的一道题。

        4
       / 
      11 13
     / 
    7   2

    试一个例子自己找一下递归的解法

    对于每个结点来说,我们要知道经过其左子结点的path之和大还是经过右子节点的path之和大。那么递归函数返回值就可以定义为以当前结点为根结点,到叶节点的最大路径之和,然后全局路径最大值放在参数中,用引用res来表示。

    1. 用res记录答案

    2. 递归函数始终返回以当前结点为终点的最大路径和

    每次递归中,左子结点为终点的最大path和加上以右子结点为终点的最大path和,再加上当前结点值就组成了一条完整的路径。

     1 class Solution {
     2 public:
     3     int maxPathSum(TreeNode *root) {
     4         int res = INT_MIN;
     5         helper(root, res);
     6         return res;
     7     }
     8     int helper(TreeNode *root, int & res)
     9     {
    10         if(root==NULL)return 0;
    11         int left = max(0,helper(root->left,res));
    12         int right = max(0,helper(root->right,res));
    13         res = max(res,left+right+root->val);
    14         return max(left,right)+root->val;
    15     }
    16 };

     Maximum Subarray 最大子数组

    思路一:dp O(n) dp[i] = max(dp[i-1], 0)+a[i]

    思路二:  分治,每次把数组一分为二,分别找出左边和右边的最大子数组之和,然后还要从中间开始向左右分别扫描,求出的最大值分别和左右两边得出的最大值比较取最大的

     1 class Solution {
     2 public:
     3     int maxSubArray(int A[], int n) {
     4         if (n==0) return 0;
     5         return helper(A, 0, n - 1);
     6     }
     7     int helper(int num[],int l,int r)
     8     {
     9         if(l>=r)return num[l];
    10         int mid = (l+r)/2;
    11         int left = helper(num,l,mid-1);
    12         int right = helper(num,mid+1,r);
    13         int mmax = num[mid], t = mmax;
    14         for (int i = mid - 1; i >= l; --i) {
    15             t += num[i];
    16             mmax = max(mmax, t);
    17         }
    18         t = mmax;
    19         for (int i = mid + 1; i <= r; ++i) {
    20             t += num[i];
    21             mmax = max(mmax, t);
    22         }
    23         return max(max(left,right),mmax);
    24     }
    25 };

     sqrtx

    开根号,返回int

    二分法:

    注意判出条件有两个。

     1 int sqrt(int x) {
     2         if(x < 0)
     3             return -1;
     4         long l = 0, r = x;
     5         while(l<=r)
     6         {
     7             long mid = l+(r-l)/2;
     8             if(mid*mid==x || (mid * mid < x && (mid + 1) * (mid + 1) > x))return mid;
     9             if(mid*mid<x)l=mid+1;
    10             else r = mid-1;
    11         }
    12         return 0;
    13     }

    牛顿法:

    转自Matrix67

    随便设一个近似值x,然后不断令x等于x和a/x的平均数,迭代个六七次后x的值就已经相当精确了。

    仅仅是不断用(x,f(x))的切线来逼近方程x^2-a=0的根。根号a实际上就是x^2-a=0的一个正实根,这个函数的导数是2x。也就是说,函数上任一点(x,f(x))处的切线斜率是2x。那么,x-f(x)/(2x)就是一个比x更接近的近似值。代入f(x)=x^2-a得到x-(x^2-a)/(2x),也就是(x+a/x)/2。

    1 int sqrt(int x) {
    2         if(x < 0)
    3             return -1;
    4         long res = x;
    5         while(res * res > x)
    6             res = ((res + x / res) >> 1);
    7         return res;
    8     }

     Permutation Sequence

    求第k个全排列,直接dfs求超时

    有个O(n)的好方法,从左到右一个一个求出来。因为只要求1个,所以可以按照全排列的规则,一个个数的求出每个位置的数字,而不需要将所有的全排列字符串列出来。

    对于n个字符组成的字符串{1,2,3,...,n},取第k个排列时,首先可以求出第k个排列字符串中的第一个数,即(k-1)/(n-1个数的排列个数)

    下面是递归和非递归的两种写法

     1 class Solution {
     2 public:
     3     string res="";
     4     string nums = "123456789";
     5     string getPermutation(int n, int k) {
     6         string s;
     7         for(int i=0;i<n;i++)
     8                 s += nums[i];
     9         solve(s,k);
    10         return res;
    11     }
    12     void solve(string& s, int k)
    13     {
    14         if(s==""||k==0)return;
    15         int len = s.size();
    16         int cnt=1; // 计算 (n-1)个数的排列个数cnt
    17         for(int i=1;i<len;i++)
    18         {
    19             cnt*=len-i;
    20         }
    21         int pos = (k-1)/cnt;
    22         res+=s[pos];
    23         k-=cnt*pos;
    24         s=s.erase(pos,1);
    25         solve(s,k);
    26     }
    27 };
     1 string getPermutation(int n, int k) {
     2         string s;
     3         string res="";
     4         string nums = "123456789";
     5         for(int i=0;i<n;i++)s += nums[i];
     6         for(int i=0;i<n;i++)
     7         {
     8             int cnt=1;
     9             int len=s.size()-1;
    10             while(len>1)
    11             {
    12                 cnt*=len;
    13                 len--;
    14             }
    15             int pos = (k-1)/cnt;
    16             k-=cnt*pos;
    17             res+=s[pos];
    18             s.erase(pos,1);
    19         }
    20         return res;
    21     }

     pascals-triangle-ii

    只用O(k)空间返回第k个杨辉三角数组

    类比01背包,容量倒循环写法省空间

     1 class Solution {//合理规划dp转移方向降低一个维度
     2 public:
     3     vector<int> getRow(int rowIndex) {
     4         vector<int> dp(rowIndex+1,0);
     5         dp[0]=1;
     6         for(int i=1;i<=rowIndex;i++)
     7             for(int j=i;j>0;j--)
     8             {
     9                 dp[j]=dp[j]+dp[j-1];
    10             }
    11         return dp;
    12     }
    13 };

     populating-next-right-pointers-in-each-node 

    将完全二叉树改写成next指针指向右边结点的形式,这题之前做过,不用多余空间又忘了怎么搞了orz。应该考虑连续两层的信息。只考虑当前层肯定就断了,不要一直卡在这里。

     1 void connect(TreeLinkNode *root) {
     2         if(!root)return;
     3         while(root->left)
     4         {
     5             TreeLinkNode* p=root;
     6             while(p)
     7             {
     8                 p->left->next = p->right;
     9                 if(p->next)
    10                     p->right->next = p->next->left;
    11                 else 
    12                     p->right->next = NULL;
    13                 p=p->next;
    14             }
    15             root = root->left;
    16         }
    17     }

    populating-next-right-pointers-in-each-node II

    凉凉,上一题变一下形又凉了,这题树变为任意二叉树,要灵活运用dummy结点处理,这样从起始到结束都是统一的形式,方便循环处理。当找第一个结点的情况与找后序存在的结点情况相似时,考虑dummy伪结点的使用

     1 void connect(TreeLinkNode *root) {
     2         if(!root)return;
     3         while(root)
     4         {
     5             TreeLinkNode* dummy = new TreeLinkNode(-1); // 每一层的头结点
     6             TreeLinkNode* cur = dummy;
     7             while(root)
     8             {
     9                 if(root->left)
    10                 {
    11                     cur->next = root->left;
    12                     cur = cur->next;
    13                 }
    14                 if(root->right)
    15                 {
    16                     cur->next = root->right;
    17                     cur = cur->next;
    18                 }
    19                 root=root->next;
    20             }
    21             root = dummy->next;
    22         }
    23     }

    distinct-subsequences

    dp :当前位置选或不选

    array[i][j] 表示T[0, j] 在S[0, i] 中的匹配个数
    如果不使用S[i] , 那么f(i , j) = f(i-1 , j)
    如果使用了S[i] , 那么一定有 S[i] == T[j] , f( i , j ) = f(i -1 , j -1 )
    所以当S[i]==T[j]时,有array( i , j ) = array( i -1 , j ) + array(i - 1 , j - 1);
    当S[i]!=T[j]时,有 array( i , j ) = array( i -1 , j );
    在使用中不难发现该dp二维数组可以降维,注意改变数组元素值时由后往前,这样不会覆盖[i-1]的值,dummy处理很巧
     1 int numDistinct(string S, string T) {
     2         if(S.size()==0||T.size()==0)return 0;
     3         vector<int> dp(T.size()+1,0);
     4         dp[0]=1;//dummy 
     5         int len = T.size();
     6         for(int i=1;i<=S.size();i++)
     7         {
     8             for(int j=min(i,len);j>0;j--)
     9                 if(S[i-1]==T[j-1])
    10                     dp[j]=dp[j-1]+dp[j];
    11         }
    12         return dp[len];
    13     }

     binary-tree-level-order-traversal-ii 

    倒序的层序遍历

    1 bfs层序遍历后reverse,虽然能做但这个方法太low不符合命题者出题意图

    2 查出max depth后,dfs从第一层max_depth到最后一层1,用一个数组存储

    3 递归版bfs,很巧,不直接存到result中,先进行下一层递归,存最后一层然后一层一层递归上来,再把当前层的结果保存到result中 。

    binary-tree-zigzag-level-order-traversal

    1. 这题用bfs+size+tag-reverse可以搞,但比较low

    2. 可以双栈搞。注意第二个栈的入栈顺序,先右再左

    rotate-image

    先关于主对角线对称,然后关于中间行对称。

    * merge-k-sorted-lists

    1. merge sort 分治做

    2. 优先队列最小堆做

    分布式常考,解法见blog

    分治写法

     1 /**
     2  * Definition for singly-linked list.
     3  * struct ListNode {
     4  *     int val;
     5  *     ListNode *next;
     6  *     ListNode(int x) : val(x), next(NULL) {}
     7  * };
     8  */
     9 class Solution {
    10 public:
    11     ListNode *mergeKLists(vector<ListNode *> &lists) {
    12         if (lists.size() == 0) return NULL;
    13         return merge(lists,0,lists.size()-1);
    14     }
    15     ListNode* merge(vector<ListNode *> &lists, int l,int r)
    16     {
    17         if(l<r) {
    18             int mid = (l+r) / 2;
    19             ListNode* left = merge(lists, l,mid);
    20             ListNode* right = merge(lists, mid+1,r);
    21             return mergeTwoLists(left,right);
    22         }
    23         else return lists[l];
    24     }
    25     ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) {
    26         ListNode *head = new ListNode(-1);
    27         ListNode *cur = head;
    28         while (l1 && l2) {
    29             if (l1->val < l2->val) {
    30                 cur->next = l1;
    31                 l1 = l1->next;
    32             } else {
    33                 cur->next = l2;
    34                 l2 = l2->next;
    35             }
    36             cur = cur->next;
    37         }
    38         if (l1) cur->next = l1;
    39         if (l2) cur->next = l2;
    40         return head->next;
    41     }
    42 };

    最小堆写法:

    首先把k个链表的首元素都加入最小堆中,它们会自动排好序。然后我们每次取出最小的那个元素加入我们最终结果的链表中,然后把取出元素的下一个元素再加入堆中,下次仍从堆中取出最小的元素做相同的操作,以此类推,直到堆中没有元素了,此时k个链表也合并为了一个链表,返回首节点

     1 struct cmp {
     2     bool operator () (ListNode *a, ListNode *b) {
     3         return a->val > b->val;
     4     }
     5 };
     6  
     7 class Solution {  
     8 public:  
     9     ListNode *mergeKLists(vector<ListNode *> &lists) {  
    10         priority_queue<ListNode*, vector<ListNode*>, cmp> q;//最小堆
    11         for (int i = 0; i < lists.size(); ++i) {
    12             if (lists[i]) q.push(lists[i]);
    13         }
    14         ListNode *head = NULL, *pre = NULL, *tmp = NULL;
    15         while (!q.empty()) {
    16             tmp = q.top();
    17             q.pop();
    18             if (!pre) head = tmp;
    19             else pre->next = tmp;
    20             pre = tmp;
    21             if (tmp->next) q.push(tmp->next);
    22         }
    23         return head;
    24     }  
    25 }; 

     remove-duplicates-from-sorted-array-ii

    给一个排好序的数组,最多允许2个重复元素。注意要和筛选后的数组比较,不是和原数组比较

     1 int removeDuplicates(int A[], int n) {
     2      
     3         if(n<3)return n;
     4         int index=2;
     5         for(int i=2;i<n;i++)
     6         {
     7             if(A[i]!=A[index-2])
     8                 A[index++]=A[i];
     9         }
    10         return index;
    11     }

     subsets

    递增的全排列,要考虑初始数组不是顺序的情况,dp来做看代码,追加法写的很好,最后按元素数排序输出

     1 vector<vector<int> > subsets(vector<int> &S) {
     2         vector<vector<int>> result;
     3         vector<vector<int>> res;
     4         vector<int> v;
     5         result.push_back(v);
     6         res.push_back(v);
     7         for(int i=0;i<S.size();i++){
     8             int n = result.size();
     9             for(int j=0;j<n;j++){
    10                 result.push_back(result[j]);
    11                 result[j].push_back(S[i]);
    12                 sort(result[j].begin(), result[j].end());// 原数组不是有序数组
    13             }
    14               
    15         }
    16         sort(result.begin(), result.end());
    17         for(int j=1;j<=S.size();j++)
    18             for(int k=0;k<result.size();k++)
    19                 if(result[k].size() == j)
    20                     res.push_back(result[k]);
    21         return res;
    22     }

     combinations

    sb了这题,看清样例是啥再做行吗,又tm上来就无脑dfs,人家是有序无重复的结果

     1 vector<vector<int> > combine(int n, int k)
     2     {
     3         vector<vector<int>> res;
     4         vector<int> tmp;
     5         dfs(n,k,1,res,tmp);
     6         return res;
     7     }
     8     void dfs(int n,int k,int start,vector<vector<int>> &res,vector<int> tmp)
     9     {
    10         if(tmp.size() == k)
    11         {
    12             res.push_back(tmp);
    13             return;
    14         }
    15         for(int i=start;i<=n;i++)
    16         {
    17             tmp.push_back(i);
    18             dfs(n,k,i+1,res,tmp);
    19             tmp.pop_back();
    20         }
    21     }

    set-matrix-zeroes

    使用常数空间,如果矩阵某个元素为0,则其所在行和列都为0

    方法1. 主要防止覆盖问题,可以先用-1处理所在行和列元素,最后还原为0。

    方法2. 用第一行和第一列作为统计标识,首先要判断第一行和第一列是否需要清零,然后遍历内部矩阵并记录,再根据记录清零,最后看是否需要清零第一行第一列。

    Simplify Path

    字符串好题

    思路一:针对每一个需求分别处理

     1   string simplifyPath(string path) {
     2         int pos;
     3         while((pos = path.find("//"))!=-1) //去掉//
     4         {
     5             path.erase(pos,1);
     6         }
     7         while((pos = path.find("/./"))!=-1) //去掉/./
     8         {
     9             path.erase(pos,2);
    10         }
    11         while((pos = path.find("/../"))!=-1)//去掉/../
    12         {
    13             path.erase(pos,3);
    14             if(pos==0)continue;
    15             int p = path.rfind("/",pos-1);
    16             path.erase(p,pos-p);
    17         }
    18         if(path.size()>2&&path.substr(path.size()-3,3)=="/.."){//去掉/..
    19             path.erase(path.size()-2,2);
    20             if(int(path.size())-2>=0){
    21             int p = path.rfind("/",path.size()-2);
    22             path.erase(p,path.size()-1-p);
    23             }
    24         }
    25         if(path.size()>1&&path.substr(path.size()-2,2)=="/.")path.erase(path.size()-1,1); //去掉/.
    26         if(path.size()>1&&path[path.size()-1]=='/')path.erase(path.size()-1,1);//处理结尾
    27         return path;
    28     }

    思路二:遍历一遍,遍历过程中综合考虑各种因素

     1 class Solution {
     2 public:
     3     string simplifyPath(string path) {
     4         vector<string> v;
     5         int i = 0;
     6         while (i < path.size()) {
     7             while (path[i] == '/' && i < path.size()) ++i;
     8             if (i == path.size()) break;
     9             int start = i;
    10             while (path[i] != '/' && i < path.size()) ++i;
    11             int end = i - 1;
    12             string s = path.substr(start, end - start + 1);
    13             if (s == "..") {
    14                 if (!v.empty()) v.pop_back(); 
    15             } else if (s != ".") {
    16                 v.push_back(s);
    17             }
    18         }
    19         if (v.empty()) return "/";
    20         string res;
    21         for (int i = 0; i < v.size(); ++i) {
    22             res += '/' + v[i];
    23         }
    24         return res;
    25     }
    26 };

     %是带有符号的,这点要注意

    spiral-matrix-ii

    螺旋数组正确写法(JAVA)

     1 public int[][] generateMatrix(int n) {
     2         int[][] res = new int[n][n];
     3         if (n < 1)
     4             return res;
     5         int index = 1, rowStart = 0, rowEnd = n - 1, colStart = 0, colEnd = n - 1;
     6         while (index <= n * n) {
     7             for (int i = colStart; i <= colEnd; i++) {
     8                 res[rowStart][i] = index++;
     9             }
    10             for (int i = rowStart + 1; i <= rowEnd; i++) {
    11                 res[i][colEnd] = index++;
    12             }
    13             for (int i = colEnd - 1; i >= colStart; i--) {
    14                 res[rowEnd][i] = index++;
    15             }
    16             for (int i = rowEnd - 1; i > rowStart; i--) {
    17                 res[i][colStart] = index++;
    18             }
    19  
    20             rowStart += 1;
    21             rowEnd -= 1;
    22             colStart += 1;
    23             colEnd -= 1;
    24  
    25         }
    26  
    27         return res;
    28     }

     spiral-matrix

     1 while(l<=r&&up<=down)
     2         {
     3             for(int i=l;i<=r;i++)
     4             ans.push_back(matrix[up][i]);
     5             for(int i=up+1;i<=down;i++)
     6             ans.push_back(matrix[i][r]);
     7             for(int i=r-1;up!=down&&i>=l;i--) //由于矩阵是任意的,注意往返不能重复
     8             ans.push_back(matrix[down][i]);
     9             for(int i=down-1;l!=r&&i>up;i--)//注意往返不能重复
    10             ans.push_back(matrix[i][l]);
    11             l++,r--,up++,down--;
    12         }

    search-for-a-range

    两次二分夹逼法,注意等号的选择

     1 int l1=0,r1=n-1,l2=0,r2=n-1;
     2         while(l1 <= r1)
     3         {
     4             int mid = l1+((r1-l1)>>1);
     5             if(A[mid] < target)
     6                 l1 = mid + 1;
     7             else
     8                 r1 = mid - 1;
     9         }
    10         while(l2 <= r2)
    11         {
    12             int mid = l2+((r2-l2)>>1);
    13             if(A[mid] > target)
    14                 r2 = mid - 1;
    15             else
    16                 l2 = mid + 1;
    17         }
    18         vector<int> result(2,-1);
    19         if(l1 <= r2)
    20         {
    21             result[0] = l1;
    22             result[1] = r2;
    23         }
    24         return result;
    25     }

     first-missing-positive 

    思路:将数值和位置形成映射,使得A[i]=i+1

     1 int firstMissingPositive(int A[], int n) {
     2         for(int i=0;i<n;)
     3         {
     4             if((A[i]<=0||A[i]>n)&&i<n)
     5             {
     6                 i++;
     7             }
     8             else if(A[A[i]-1]!=A[i])// 注意不能直接写A[i]!=i+1  因为[1,1]可能死循环
     9             {
    10                 swap(A[i],A[A[i]-1]);
    11             }
    12             else i++;
    13             
    14         }
    15         int i;
    16         for(i=0;i<n;i++)
    17             if(A[i]!=i+1)break;
    18         return i+1;
    19     }

    search-in-rotated-sorted-array

    旋转排序数组找target

    思路:二分后,一定按顺序的找,注意等号

     1 int search(int A[], int n, int target) {
     2         int l=0,r=n-1;
     3         while(l<=r)
     4         {
     5             int mid = (l+r)/2;
     6             if(A[mid]==target)return mid;
     7             if(A[l]<=A[mid])//等号必须在这里,eg:[3,1] 1
     8             {
     9                 if(A[l]<=target&&target<A[mid])
    10                     r=mid-1;
    11                 else l=mid+1;
    12             }
    13             else 
    14             {
    15                 if(A[mid]<target&&target<=A[r])
    16                     l=mid+1;
    17                 else r=mid-1;
    18             }
    19         }
    20         return -1;
    21     }

     unique-binary-search-trees 

    分阶段,先看根节点,可以1到n,当根节点确定时找状态转移,因为是bst,所以左子树为i-1个结点,右子树为n-i个结点,两者乘积

     1     int dp[1000]={0};
     2     int numTrees(int n) {
     3         dp[1]=1;
     4         dp[0]=1;
     5         for(int i=2;i<=n;i++)
     6         {
     7             for(int j=1;j<=i;j++)
     8             dp[i]+=dp[j-1]*dp[i-j];
     9         }
    10         return dp[n];
    11     }

    unique-binary-search-trees II

     1   vector<TreeNode *> generateTrees(int n) {
     2           return dfs(1,n);
     3     }
     4     vector<TreeNode *> dfs(int l,int r){
     5           vector<TreeNode*> ans;
     6           if(l>r) {ans.push_back(NULL);return ans;}
     7           for(int i=l;i<=r;i++){
     8               vector<TreeNode*> vecl=dfs(l,i-1),vecr=dfs(i+1,r);
     9               for(auto p:vecl)
    10                   for(auto q:vecr){
    11                       TreeNode *root=new TreeNode(i);
    12                       root->left=p;
    13                       root->right=q;
    14                       ans.push_back(root);
    15                   }
    16           }
    17           return ans;
    18     }

    minimum-window-substring 

    尺取法O(n), 在S串中遍历一次,找到包含T串中所有元素的最小子串

    思路:因为无序所以hash,map记录元素及对应个数,保留T串整体信息

    l=0,r从头向后遍历,直到子串中元素满足map的记录,用count==T.size()判断,记录子串,l右移直到越过第一个T中的元素使子串不满足map条件,此时确定l边界,r右移确定右边界

     1 class Solution {
     2 public:
     3     string minWindow(string S, string T) {
     4     string res;
     5         map<char,int> t,s;
     6         for(auto c:T)
     7             t[c]++;
     8         int count=0,l=0;
     9         for(int r=0;r<S.length();r++)
    10         {
    11             if(t[S[r]]!=0)
    12             {
    13                 s[S[r]]++;
    14                 if(s[S[r]]<=t[S[r]])
    15                     count++;
    16                 while(count==T.size())
    17                 {
    18                     if(res.empty()||res.length()>r-l+1)
    19                         res=S.substr(l,r-l+1);
    20                     if(t[S[l]])
    21                     {
    22                         s[S[l]]--;
    23                         if(s[S[l]]<t[S[l]])
    24                             count--;
    25                     }
    26                     l++;
    27                 }
    28             }
    29         }
    30         return res;
    31     }
    32 };

     longest-common-prefix:对所有string排序后比较首尾即可

     roman-to-integer:当前一个罗马数字小于后一个时是特例,要减去2倍的前一个数字值

    regular-expression-matching

    方法一:递归求解

    先来判断p是否为空,若为空则根据s的为空的情况返回结果。当p的第二个字符为*号时,由于*号前面的字符的个数可以任意,可以为0,那么先用递归来调用为0的情况,就是直接把这两个字符去掉再比较,或者当s不为空,且第一个字符和p的第一个字符相同时,我们再对去掉首字符的s和p调用递归,注意p不能去掉首字符,因为*号前面的字符可以有无限个;如果第二个字符不为*号,那么就的比较第一个字符,然后对后面的字符串递归

     1 class Solution {
     2 public:
     3     bool isMatch(string s, string p) {
     4         if (p.empty()) return s.empty();
     5         if (p.size() > 1 && p[1] == '*') {
     6             return isMatch(s, p.substr(2)) || (!s.empty() && (s[0] == p[0] || p[0] == '.') && isMatch(s.substr(1), p)); //注意这里
     7         } else {
     8             return !s.empty() && (s[0] == p[0] || p[0] == '.') && isMatch(s.substr(1), p.substr(1));
     9         }
    10     }
    11 };

    方法二:dp

    我们也可以用DP来解,定义一个二维的DP数组,其中dp[i][j]表示s[0,i)和p[0,j)是否match,然后有下面三种情况:

    1.  P[i][j] = P[i - 1][j - 1], if p[j - 1] != '*' && (s[i - 1] == p[j - 1] || p[j - 1] == '.');
    2.  P[i][j] = P[i][j - 2], if p[j - 1] == '*' and the pattern repeats for 0 times;
    3.  P[i][j] = P[i - 1][j] && (s[i - 1] == p[j - 2] || p[j - 2] == '.'), if p[j - 1] == '*' and the pattern repeats for at least 1 times.

     1 class Solution {
     2 public:
     3     bool isMatch(string s, string p) {
     4         int m = s.size(), n = p.size();
     5         vector<vector<bool>> dp(m + 1, vector<bool>(n + 1, false));
     6         dp[0][0] = true;
     7         for (int i = 0; i <= m; ++i) {
     8             for (int j = 1; j <= n; ++j) {
     9                 if (j > 1 && p[j - 1] == '*') {
    10                     dp[i][j] = dp[i][j - 2] || (i > 0 && (s[i - 1] == p[j - 2] || p[j - 2] == '.') && dp[i - 1][j]);
    11                 } else {
    12                     dp[i][j] = i > 0 && dp[i - 1][j - 1] && (s[i - 1] == p[j - 1] || p[j - 1] == '.');
    13                 }
    14             }
    15         }
    16         return dp[m][n];
    17     }
    18 };

    divide-two-integers:倍增法用的好,减少循环次数

    题意:不能用乘法,除法,取余操作,实现整数除法

     1 int divide(int dividend, int divisor) {
     2         if(divisor == 0)
     3             return INT_MAX;
     4              
     5         long long result=0,flag=0;
     6         if((dividend<0)^(divisor<0))
     7             flag=1;
     8          
     9         long long a = abs(dividend), b = abs(divisor);
    10         while(a >= b)
    11         {
    12             long long k = 1, t = b;
    13             while(a >= t)
    14             {
    15                 a -= t;
    16                 result += k;
    17                 k <<= 1;
    18                 t += t;
    19             }
    20         }
    21         return flag?-result:result;
    22     }

    longest-substring-without-repeating-characters

    双指针维护区间,尺取法枚举可能解,这里的词典只记录字母是否出现,并不记录位置。

     1 int lengthOfLongestSubstring(string s) {
     2         int len = s.size();
     3         int dic[128]={0};
     4         int ans=0,p=0;
     5         for(int q=0;q<len;q++)
     6         {
     7            while(dic[s[q]])
     8            {
     9                dic[s[p]]--;
    10                p++;
    11            }
    12             ans=max(ans,q-p+1);
    13             dic[s[q]]++;
    14         }
    15         return ans;
    16     }

     longest-palindromic-substring

    二维dp 0:不回文,1:回文。注意奇数偶数的初始情况。

     1 string longestPalindrome(string s) {
     2     int len = s.length(),ans;
     3     int dp[len+1][len+1];
     4     memset(dp,0,sizeof(dp));
     5     int rec[len+1];
     6     for(int i = 0;i < len;i++) dp[i][i] = 1,ans = 1,rec[1] = 0;
     7     for(int i = 0;i < len-1;i++) if(s[i] == s[i+1]) ans = 2,rec[2] = i,dp[i][i+1] = 1;
     8     for(int i = 3;i <= len;i++){
     9         for(int j = 0;j <= len-i;j++){
    10             if(s[j] == s[j+i-1]) {
    11                 dp[j][i+j-1] = dp[j+1][i+j-2];
    12                 if(dp[j][i+j-1] == 1) {ans = max(ans,i),rec[i] = j;}
    13             }
    14         }
    15     }
    16     return s.substr(rec[ans],ans);
    17 }

     median-of-two-sorted-arrays

    分治,注意递归的出口,一个是k=1,另一个是边界

     1 double findMedianSortedArrays(int A[], int m, int B[], int n) {
     2         if((m+n)%2==0)
     3             return (help(A,0,m,B,0,n,(m+n)/2)+help(A,0,m,B,0,n,(m+n)/2+1))/2;
     4         else 
     5             return help(A,0,m,B,0,n,(m+n)/2+1);
     6     }
     7     double help(int A[], int aStart, int m, int B[], int bStart, int n, int k)
     8     {
     9         if(aStart >= m)
    10             return B[bStart+k-1];
    11         if(bStart >= n)
    12             return A[aStart+k-1];
    13         if(k==1)
    14             return min(A[aStart],B[bStart]);
    15         int aMin = 0x7fffffff, bMin = 0x7fffffff;
    16         if(aStart + k / 2 - 1 < m)
    17             aMin = A[aStart + k/2 -1];
    18         if(bStart + k / 2 - 1 < n)
    19             bMin = B[bStart + k / 2 - 1];
    20         if(aMin<bMin)
    21             return help(A,aStart+k/2,m,B,bStart,n,k-k/2);
    22         else
    23             return help(A,aStart,m,B,bStart+k/2,n,k-k/2);
    24     }

     3sum 

    选出数组中相加为0的3个数,要求非递减,不重复

    思路:fix一个数转换为2sum+set会超时

    先排序,固定一个数,确定后两个数的和。在后面的数组中两头向中间找,注意剪枝与去重的操作

     1 vector<vector<int> > threeSum(vector<int> &num) {
     2         vector<vector<int> > ans;
     3         sort(num.begin(),num.end());
     4         if(num.empty()||num.front()>0||num.back()<0)return {};
     5         for(int i=0;i<num.size()-2;i++)
     6         {
     7             if(num[i]>0)break;
     8             if(i>0&&num[i]==num[i-1])continue;
     9             int target = 0 - num[i];
    10             int j=i+1,k=num.size()-1;
    11             while(j<k)
    12             {
    13                 if(num[j]+num[k]==target)
    14                 {
    15                     ans.push_back({num[i],num[j],num[k]});
    16                     while(j<k&&num[j]==num[j+1])j++;
    17                     while(j<k&&num[k]==num[k-1])k--;
    18                     j++,k--;
    19                 }
    20                 else if(num[j]+num[k]<target)
    21                     j++;
    22                 else 
    23                     k--;
    24             }
    25         }
    26         return ans;
    27     }

     reverse-nodes-in-k-group

    这题考察链表反转,头插的双指针写法

     1 ListNode *reverseKGroup(ListNode *head, int k) {
     2     if(head == NULL || head->next == NULL || k < 2) return head;
     3         ListNode* dummy = new ListNode(0);
     4         dummy->next = head;
     5         ListNode* pre = dummy, *cur = head, *temp;
     6         int len = 0;
     7         while (head != NULL) {
     8             len++ ;
     9             head = head->next;
    10         }
    11         for (int i = 0; i < len / k; i ++ ) {
    12             for (int j = 1; j < k; j ++ ) {
    13                 temp = cur->next;
    14                 cur->next = temp->next;
    15                 temp->next = pre->next;
    16                 pre->next = temp;
    17             }
    18             pre = cur;
    19             cur = cur->next;
    20         }
    21         return dummy->next;
    22     }

     generate-parentheses

    产生括号的全排列

    思路:全排列,容易想到dfs,由于括号包含左括号和右括号两部分,所以一方面不用考虑左括号,让他不断生成就好,对于右括号,设置两种情况,分别是生成和不生成右括号。

     1 vector<string> generateParenthesis(int n) {
     2         vector<string> s;
     3         dfs(0,0,"",s,n);
     4         return s;
     5     }
     6     void dfs(int left, int right, string tmp, vector<string> & s, int n)
     7     {
     8         if(left==n&&right==n)
     9         {
    10             s.push_back(tmp);
    11             return;
    12         }
    13         if(left<n)
    14             dfs(left+1,right,tmp+'(',s,n);
    15         if(left>right&&right<n)
    16             dfs(left,right+1,tmp+')',s,n);
    17     }

     substring-with-concatenation-of-all-words

     这道题空间换时间的思路特别,不同word concate的组合非常多,枚举非常不明智,那么就hash好了。接下来就是暴力匹配,内部循环words.size()次,用另一个hash map记录匹配到的word个数。

    O(n)方法,尺取法,每次尺取一个word长度,还是两个hashmap

     1 vector<int> findSubstring(string S, vector<string>& L) {
     2         if (S.empty() || L.empty())return {};
     3         vector<int> res;
     4         int n = S.size(), cnt = L.size(), len = L[0].size();
     5         unordered_map<string, int> m1;
     6         for (string w : L) ++m1[w];
     7         for (int i = 0; i < len; i++)
     8         {
     9             int left = i, count = 0;
    10             unordered_map<string, int> m2;
    11             for (int j = i; j <= n - len; j += len)
    12             {
    13                 string t = S.substr(j,len);
    14                 if (m1.count(t))
    15                 {
    16                     ++m2[t];
    17                     if (m2[t] <= m1[t])
    18                         ++count;
    19                     else
    20                     {
    21                         while (m2[t] > m1[t])
    22                         {
    23                             string tmp = S.substr(left, len);
    24                             m2[tmp]--;
    25                             if (m2[tmp] < m1[tmp])count--;
    26                             left += len;
    27                         }
    28                     }
    29                     if (count == cnt)
    30                     {
    31                         res.push_back(left);
    32                         m2[S.substr(left, len)]--;
    33                         --count;
    34                         left += len;
    35                     }
    36                 }
    37                 else
    38                 {
    39                     m2.clear();
    40                     count = 0;
    41                     left = j + len;
    42                 }
    43             }
    44         }
    45         sort(res.begin(),res.end());
    46         return res;
    47     }

     longest-valid-parentheses

    找出最长的合法括号子串,输出长度

    常规做法,栈中存左括号,栈空时记录起始节点位置

     1 int longestValidParentheses(string s) {
     2         stack<int> st;
     3         int ans=0,t=-1;
     4         if(s.size()==0)return 0;
     5         for(int i=0;i<s.size();i++)
     6         {
     7             if(s[i]=='(')st.push(i);
     8             else
     9             {
    10                 if(st.empty())t=i;
    11                 else
    12                 {
    13                     st.pop();
    14                     if(st.empty())
    15                         ans = max(ans,i-t);
    16                     else
    17                         ans  = max(ans,i-st.top());
    18                 }
    19             }
    20         }
    21         return ans;
    22     }

     Largest Rectangle in Histogram

     1 class Solution {
     2 public:
     3     int largestRectangleArea(vector<int>& heights) {
     4         int res = 0;
     5         stack<int> st;
     6         heights.push_back(0);
     7         for (int i = 0; i < heights.size(); ++i) {
     8             while (!st.empty() && heights[st.top()] >= heights[i]) {
     9                 int cur = st.top(); st.pop();
    10                 res = max(res, heights[cur] * (st.empty() ? i : (i - 1 - st.top())));// 注意这里是st.top()而不是cur,两者之间有可行的宽度,前一次出栈了
    11             }
    12             st.push(i);
    13         }
    14         return res;
    15     }
    16 };

     Longest Consecutive Sequence 求最长连续序列

    O(n)时间复杂度

    方法(1) hash set求解,将序列存入set,对每个数,从左右两个方向展开搜索hash匹配,然后删除这个数,避免重复搜索

    方法(2) hash map求解,map初始为空,如果map[x]有值则表示已访问,continue(这里强调用count判断,如果用默认map映射判断,会自动生成一个为0的映射),只需判断map[x-1], map[x+1]即可,更新ma[x-left], map[x+right]的值。

     1 class Solution {
     2 public:
     3     int longestConsecutive(vector<int>& nums) {
     4         int res = 0;
     5         unordered_map<int, int> m;
     6         for (int num : nums) {
     7             if (m.count(num)) continue;
     8             int left = m.count(num - 1) ? m[num - 1] : 0;
     9             int right = m.count(num + 1) ? m[num + 1] : 0;
    10             int sum = left + right + 1;
    11             m[num] = sum;
    12             res = max(res, sum);
    13             m[num - left] = sum;
    14             m[num + right] = sum;
    15         }
    16         return res;
    17     }
    18 };

     Course schedule(拓扑排序)

    方法一:bfs+入度

    方法二:dfs+访问标记(标记循环) 0表示还未访问过,1表示已经访问了(为了剪枝),-1 表示有冲突(一次dfs中判断是否有环)

     1 class Solution {
     2 public:
     3     bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
     4         vector<vector<int>> graph(numCourses, vector<int>());
     5         vector<int> visit(numCourses);
     6         for (auto a : prerequisites) {
     7             graph[a[1]].push_back(a[0]);
     8         }
     9         for (int i = 0; i < numCourses; ++i) {
    10             if (!canFinishDFS(graph, visit, i)) return false;
    11         }
    12         return true;
    13     }
    14     bool canFinishDFS(vector<vector<int>>& graph, vector<int>& visit, int i) {
    15         if (visit[i] == -1) return false;
    16         if (visit[i] == 1) return true;
    17         visit[i] = -1;
    18         for (auto a : graph[i]) {
    19             if (!canFinishDFS(graph, visit, a)) return false;
    20         }
    21         visit[i] = 1;
    22         return true;
    23     }
    24 };

     Sliding Window Maximum

    1 最大堆 pair<value, position> 2 双端队列,维护按序单调递减

     1 class Solution {
     2 public:
     3     vector<int> maxSlidingWindow(vector<int>& nums, int k) {
     4         vector<int> res;
     5         deque<int> q;
     6         for (int i = 0; i < nums.size(); ++i) {
     7             if (!q.empty() && q.front() == i - k) q.pop_front(); // 删除左边元素
     8             while (!q.empty() && nums[q.back()] < nums[i]) q.pop_back();// 保持队列单调递减
     9             q.push_back(i);
    10             if (i >= k - 1) res.push_back(nums[q.front()]);
    11         }
    12         return res;
    13     }
    14 };

     Meeting Rooms II 

    计算线段最大重叠个数,对于线段,可以特殊处理,起始点+1,终止点-1,这样遍历的过程可以模拟实线段

     1 class Solution {
     2 public:
     3     int minMeetingRooms(vector<Interval>& intervals) {
     4         map<int, int> m;
     5         for (auto a : intervals) {
     6             ++m[a.start];
     7             --m[a.end];
     8         }
     9         int rooms = 0, res = 0;
    10         for (auto it : m) {
    11             res = max(res, rooms += it.second);
    12         }
    13         return res;
    14     }
    15 };

     LRU cache

    链表 和 map 实现 LRU 内存换页

    get 和 put, get 函数是通过输入 key 来获得 value,如果成功获得后, (key, value) 升至缓存器中最常用的位置(顶部),如果 key 不存在,则返回 -1。put 函数是插入一对新的 (key, value),如果原缓存器中有该 key,则需要先删除掉原有的,将新的插入到缓存器的顶部。如果不存在,则直接插入到顶部。若加入新的值后缓存器超过了容量,则需要删掉一个最不常用底部的值。

     1 class LRUCache {
     2 public:
     3     LRUCache(int capacity) {
     4         cap = capacity;
     5     }
     6     
     7     int get(int key) {
     8         auto it = m.find(key);
     9         if(it==m.end())return -1;
    10         l.splice(l.begin(), l, it->second);
    11         return it->second->second;
    12     }
    13     
    14     void put(int key, int value) {
    15         auto it = m.find(key);
    16         if(it!=m.end())l.erase(it->second);
    17         l.push_front(make_pair(key, value));
    18         m[key]=l.begin();
    19         if(l.size()>cap){
    20             int k = l.rbegin()->first;
    21             l.pop_back();
    22             m.erase(k);
    23         }
    24     }
    25 private:
    26     int cap;
    27     list<pair<int,int>> l;
    28     unordered_map<int, list<pair<int,int>>::iterator> m;
    29 };
    30 
    31 /**
    32  * Your LRUCache object will be instantiated and called as such:
    33  * LRUCache* obj = new LRUCache(capacity);
    34  * int param_1 = obj->get(key);
    35  * obj->put(key,value);
    36  */

     python

     1 class Node:
     2 def __init__(self, k, v):
     3     self.key = k
     4     self.val = v
     5     self.prev = None
     6     self.next = None
     7 
     8 class LRUCache:
     9 def __init__(self, capacity):
    10     self.capacity = capacity
    11     self.dic = dict()
    12     self.head = Node(0, 0)
    13     self.tail = Node(0, 0)
    14     self.head.next = self.tail
    15     self.tail.prev = self.head
    16 
    17 def get(self, key):
    18     if key in self.dic:
    19         n = self.dic[key]
    20         self._remove(n)
    21         self._add(n)
    22         return n.val
    23     return -1
    24 
    25 def set(self, key, value):
    26     if key in self.dic:
    27         self._remove(self.dic[key])
    28     n = Node(key, value)
    29     self._add(n)
    30     self.dic[key] = n
    31     if len(self.dic) > self.capacity:
    32         n = self.head.next
    33         self._remove(n)
    34         del self.dic[n.key]
    35 
    36 def _remove(self, node):
    37     p = node.prev
    38     n = node.next
    39     p.next = n
    40     n.prev = p
    41 
    42 def _add(self, node):
    43     p = self.tail.prev
    44     p.next = node
    45     self.tail.prev = node
    46     node.prev = p
    47     node.next = self.tail

    Coin Change 

    DP有两种写法,一种为自底向上(dp迭代),另一种为自顶向下(dfs+记忆数组),两者等价

     1 class Solution {
     2 public:
     3     int coinChange(vector<int>& coins, int amount) {
     4         vector<int> memo(amount+1,INT_MAX);
     5         memo[0]=0;
     6         return dfs(coins,amount,memo);
     7     }
     8     int dfs(vector<int>& coins, int target, vector<int>& memo){
     9         if(target<0)return -1;
    10         if(memo[target]!=INT_MAX)return memo[target];
    11         for(auto coin : coins){
    12             int tmp = dfs(coins, target - coin, memo);
    13             if(tmp>=0) memo[target] = min(memo[target], tmp+1);
    14         }
    15         return memo[target] = (memo[target] == INT_MAX) ? -1 : memo[target];
    16     }
    17 };

    Validate IP Address 验证IP地址

    注意getline的用法何stoi的用法很省事

    getline(is, t, '.')

    https://www.cnblogs.com/grandyang/p/6185339.html

    Combination Sum II 组合之和之二

    注意sort后

     if (i > start && num[i] == num[i - 1]) continue;

    https://www.cnblogs.com/grandyang/p/4419386.html 

  • 相关阅读:
    X-Plosives (并查集)
    HDU1272小希的迷宫 (并查集)
    React 初学
    js 插件 issue
    js常用方法
    常用网址
    js 零零散散的总结。
    git 常用命令
    es6 babel编译
    屏幕适配
  • 原文地址:https://www.cnblogs.com/demian/p/9692460.html
Copyright © 2020-2023  润新知