Something learned from leetcode (2)

21、string的sort()、compare()
Anagram: 字符相同但字母顺序打乱的两个短语

class Solution {
public:
    bool isAnagram(string s, string t) {
        if(s.size()!=t.size())
        return false;

        sort(s.begin(),s.end());
        sort(t.begin(),t.end());

       if(!s.compare(t))
       return true;

        return false;

    }

};

此题用字符哈希更快,解二:

class Solution {
public:
    bool isAnagram(string s, string t) {
        int charHash[26]={0};  //显性初始化

for(int i=0;i<s.size();i++)
charHash[(int)s[i]-97]++;

for(int j=0;j<t.size();j++)
charHash[t[j]-'a']--;   //第二种写法 等效为charHash[(int)t[j]-97]--;

for(int k=0;k<26;k++){
if(charHash[k])
return false;
}

return true;

    }

};

不显性初始化数组是未定义行为。

int charHash[5];
for(int i=0;i<5;i++)
cout<<charHash[i]<<endl;

运行结果为:
1968344277
1137358577
-2
1968247138
1968593852
可见charHash中元素为乱码

int charHash[5]={0};//可以将所有元素初始化为0

from 242. Valid Anagram
22、动态规划(DP)
拆分整数使得乘积最大
解法一:DP
dp[n]是n对应的最大拆分乘积
递推方程: dp[n]=max( i*dp[n-i], i*(n-i) ) (i从1到n-1)
边界条件:dp[2]=1
举例说明递推方程:求n=5的最大拆分。假设得到之前的部分正确结果,即dp[1]到dp[4]已知等于{1,1,3,4}。5的最大拆分为以下值中的最大值:1*dp[4]、1*4 、2*dp[3]、2*3、3*dp[2]、3*2、4*dp[1]、4*1

class Solution {
public:
    int integerBreak(int n) {
        int dp[n];
        dp[1]=1;
        dp[2]=1;

        for(int i=3;i<=n;i++)
         {
             dp[i]=0;

             for(int j=1;j<i;j++)
             {
                 dp[i]=max( j*dp[i-j], max(dp[i], j*(i-j)) );
             }
         }

        return dp[n];
    }
};

解法二:数学推导
由均值不等式 HnGnAnQn GnAn

Gn=x1x1xnnAn=x1+x2++xnn
即算数平均数大于几何平均数,所以拆分成几个相等的数乘积最大。
设把n拆分成 nx 个数,每个数都等于x。
转换为求他们的乘积 f(x)=xnx 的最大值,求导
f(x)=(1lnx)×nx2×xnx=0 得x=e时取到极大值。
e 2.7182, 所以取3时结果最大。
此题转化为能分出多少个3,正好分完或分到4时终止。

class Solution {
public:
    int integerBreak(int n) {
        if(n==2 || n==3) return n-1;
        if(n==4) return 4; //此句多余,巧合
        int res=1;
        while(n>4){
            res*=3;
            n-=3;
        }
        res*=n;
        return res;
    }
};

from 343. Integer Break
23、主元素
主元素(Majority Element)是数组中出现次数严格大于 n2 的元素,例如{1,1,2}中的1为主元素 。若是{1,1,2,2}则不符合要求。
编程之美 O(n) 解法基于如下原理:
同时丢弃数组中两个不同的元素,主元素不变。
1、默认第一个元素为主元素res,count置1
2、从第二个元素开始遍历数组,如果res==nums[i] 时count++,否则count– 。如果count=0,则置下一个元素为res。如此遍历直到结束,res中保存的元素即为主元素。

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int res=nums[0],count=1;
        for(int i=1;i<nums.size();i++){
            if(res==nums[i])
            count++;
            if(res!=nums[i])
            count--;
            if(count==0)
            res=nums[i+1];

        }
        return res;
    }
};

from 169. Majority Element
24、大数BigInteger相加
非负num1与num2均保存在string中,求和。
每一位相加产生的进位只可能为0或1,若是1则置carry=1,加下一次循环的tempSum中。

class Solution {
public:
    string addStrings(string num1, string num2) {
        string res="";

       int carry=0;
       for(int i=num1.size()-1,j=num2.size()-1;i>=0 || j>=0 || carry>0; ){
           int tempSum=0;
           if(i>=0)
           {
               tempSum+=(int)(num1[i]-'0');
               i--;
           }

           if(j>=0)
           {
               tempSum+=(int)(num2[j]-'0');
               j--;
           }
           if(carry>0)
           {
               tempSum+=carry;
           }
           res=to_string(tempSum%10)+res;

           if(tempSum/10==1)
           carry=1;
           else
           carry=0;

       }

       return res;


    }
};

循环次数等于较长串的长度,存在优化空间:处理完较短字符串,长串的剩余部分直接添加到结果即可。
from 415. Add Strings
25、二进制手表与位操作
二进制手表有四个hour灯(表示 0-11),6个min灯(0-59)

hour8421..
min32168421

给定有几个灯亮,求可能的时间。

设置10个二进制位表示每一位的亮灭,前四位代表hour,后六位代表min。
从0到0x3ff(11 1111 1111)循环,依次判断是否符合条件。(有多余运算,实际不可能达到全1)

class Solution {
public:
    vector<string> readBinaryWatch(int num) {
        vector<string> res;
        if(num>8 || num<0)
        return res;

        for(int i=0;i<=0x3ff;i++){
            if(bitset<16> (i).count()==num)  //如果遍历到1的个数符合条件的,取时间
            getTime(i,res);
        }

        return res;


    }

    void getTime(int n, vector<string> &res){
            string temp="";
            int hour= (n & 0x3f0)>>6;   // n & 0x3f0的作用是取前4位,并把后6位置0.  >>6是右移6位,正数左右移均补零
            int min= n & 0x3f; //取后6位
            if(hour>11 || min > 59)   //判断是否溢出,溢出则该组值不符合条件
            return ;

            temp=to_string(hour)+":";
            if(min<10)
            temp+="0";     //题目要求:分钟要补零,10:08合法,10:8非法
            temp+=to_string(min);

            res.push_back(temp);
        }


};

from 401. Binary Watch
26、单链表反转
题目:将head->1->2->3 变为1<-2<-3<-head
设置三个指针,分别指向当前元素pNode、前一元素pre和后一元素follow。

 /*
  Definition for singly-linked list
  struct ListNode {
      int val;
      ListNode *next;
      ListNode(int x) : val(x), next(NULL) {}
  };
  */

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
       ListNode* pre=NULL, *pNode=head, *follow=NULL;
       while(pNode){
           follow=pNode->next;
           pNode->next=pre;
           pre=pNode;
           pNode=follow;
       }

       return pre;
    }
};

from 206. Reverse Linked List
27、二叉树的前中后序遍历
前序遍历又称先根遍历,首先访问根节点,然后先根访问左子树和右子树。
递归方法伪码:

// cout<< root->val <<endl;放置在位置1、2、3对应前、中、后序遍历
void Order(TreeNode* root){
        if(root!=NULL){
             1
            Order(root->left,res);
             2
            Order(root->right,res);
             3
        }
    }

ac代码:

class Solution {
public:

 vector<int> inorderTraversal(TreeNode* root) {
     vector<int> res;
       inOrder(root,res);  //vector传参方法
       return res;
    }   
 void inOrder(TreeNode* root,vector<int> &res){
        if(root!=NULL)
        {
            inOrder(root->left,res);
            res.push_back(root->val);
            inOrder(root->right,res);
        }
    }
};

from 94. Binary Tree Inorder Traversal
28、8皇后问题,n皇后问题
出处:http://blog.csdn.net/hackbuteer1/article/details/6657109
目前公认N皇后的最高效 算 法

class Solution {
public:
long sum=0,upperlim=1;
    int totalNQueens(int n) {

 upperlim = (upperlim << n) - 1;
nQueen(0,0,0);

    return sum;
    }

void nQueen(long row, long ld, long rd) {
    if (row != upperlim)  
    {  
        // row,ld,rd进行“或”运算,求得所有可以放置皇后的列,对应位为0,  
        // 然后再取反后“与”上全1的数,来求得当前所有可以放置皇后的位置,对应列改为1  
        // 也就是求取当前哪些列可以放置皇后  
        long pos = upperlim & ~(row | ld | rd);   
        while (pos)    // 0 -- 皇后没有地方可放,回溯  
        {  
            // 拷贝pos最右边为1的bit,其余bit置0  
            // 也就是取得可以放皇后的最右边的列  
            long p = pos & -pos;                                                

            // 将pos最右边为1的bit清零  
            // 也就是为获取下一次的最右可用列使用做准备,  
            // 程序将来会回溯到这个位置继续试探  
            pos -= p;                             

            // row + p,将当前列置1,表示记录这次皇后放置的列。  
            // (ld + p) << 1,标记当前皇后左边相邻的列不允许下一个皇后放置。  
            // (ld + p) >> 1,标记当前皇后右边相邻的列不允许下一个皇后放置。  
            // 此处的移位操作实际上是记录对角线上的限制,只是因为问题都化归  
            // 到一行网格上来解决,所以表示为列的限制就可以了。显然,随着移位  
            // 在每次选择列之前进行,原来N×N网格中某个已放置的皇后针对其对角线  
            // 上产生的限制都被记录下来了  
            nQueen(row + p, (ld + p) << 1, (rd + p) >> 1);                                
        }  
    }  
    else     
    {  
        // row的所有位都为1,即找到了一个成功的布局,回溯  
        sum++;  
    }  
}   
};

from 52. N-Queens II
29、灯泡开关
问题:n个灯泡进行如下操作
第一轮:打开所有灯泡 [1,1,1]
第二轮:将第2,4,6…的灯泡改变状态
第三轮:将第3,6,9…的灯泡改变状态

第n-1轮:将第n-1,2(n-1)…的灯泡改变状态
第n轮(特殊轮):将第n个灯泡改变状态
求最后几个亮。
TLE & MLE解法
n=99999正常 Runtime: 322 ms。n=9999999 case TLE

class Solution {
public:
    int bulbSwitch(int n) {
        int arr[n+1]={1},j;
            for(int k=1;k<=n;k++)
        arr[k]=1;

        for(int i=2;i<n;i++){
            for(j=i;j<=n;j+=i){
                if(arr[j]==0)
                arr[j]=1;
                else if(arr[j]==1)
                arr[j]=0;

            }

        }

           if(arr[n]==0)
                arr[n]=1;
                else
                arr[n]=0;
       int count=0;
        for(int k=1;k<=n;k++){
            if(arr[k]==1)
        count++;
        }
        return count;
    }
};

O(1)解法
因数:c=a*b 则 a和b 都是c 的因数
4有3个因数,1,2和4, 1 * 4 =4 2 * 2=4 (奇数个)
5有2个因数,1和5。(偶数个)
因数都是成对出现的,事实上平方数n的两个因数 n 共占了一个位置,导致平方数的因数只有奇数个。
进行该过程后,只有平方数位置的灯泡是开启的(因为只翻转过1次)

int bulbSwitch(int n) {
        return (int)sqrt(n); //开方向下取整
    }

from 319. Bulb Switcher
30、不含相同字符单词的长度最大积
两两比较的方法出现TLE,查看结果发现该case长度达到了30万,就是针对此解法的,感受到LC的恶意。

class Solution {
public:
    int maxProduct(vector<string>& words) {
        if(words.size()==0)
        return 0;
        vector<int> res;
        for(int i=0;i<words.size()-1;i++)
         for(int j=i+1;j<words.size();j++){
             if(noSameChar(words[i],words[j]))
             res.push_back(words[i].size()*words[j].size());

         }


         if(!res.size())
         return 0;
        int maxRes=0;
        for(int k=0;k<res.size();k++){
            if(res[k]>maxRes)
            maxRes=res[k];

        }

        return maxRes;
    }

    bool noSameChar(string a,string b){
        int arry[26]={0};
       for(int i=0;i<a.size();i++)
       arry[a[i]-'a']++;

       for(int j=0;j<b.size();j++){
           if(arry[b[j]-'a']>0)
           { 
           return false;}
       }

       return true;
    }


};

AC解法,位操作比较字符串:
相较于第一种解法优化了相同字母检测,循环次数依旧不变。

int i=0;
i |=1<<5; //将i的二进制右数第五位(实际是第六位,从0计数即为第5位),置1 
//分步骤解读:
//  1<<5 是1左移5位得到100000
// i=i | 100000 此时i=100000,

可以将words中的每个字符串转为掩码形式,例如words[0]=”abc”转为mask[0]=111,words[1]=def转为111000。 words[0] & words[1] 等于0,说明两字符串没有相同的字符。

class Solution {
public:
    int maxProduct(vector<string>& words) {
        int res=0;
        if(words.size()==0)
        return 0;
        vector<int> mask(words.size(),0);
        for(int i=0;i<words.size();i++)
        for(int j=0;j<words[i].size();j++)
        mask[i] |=1<<(words[i][j]-'a');

        for(int i=0;i<words.size()-1;i++)
         for(int j=i+1;j<words.size();j++){
             if(! (mask[i] & mask [j]) )
             res=max(res, (int)(words[i].size()*words[j].size() ) );
         }
        return res;

    }
};

from 318. Maximum Product of Word Lengths
31.找重复的数 [只读数组,空间O(1)]
问题:数组中包含N+1个数字,每个数字范围在1到n(含),找出重复的数字。
解法1:鸽巢原理
时间O(nlogn) 空间O(1)

三个抽屉放4只袜子,必然有一个抽屉至少两只。使用二分法选取中间的数mid,如果整个数组中小于等于mid的数大于mid个,则区间[0,mid)中含有重复的数,否则重复的数在中(mid,max)中。

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
       int min=0, max=nums.size()-1,mid,cnt;

       while(min<=max)
       {
           cnt=0;
          mid=min+(max-min) /2;

          for(int i=0;i<nums.size();i++)
           {
               if(nums[i]<=mid)
               cnt++;
           }

           if(cnt>mid)
           max=mid-1;
            else
             min=mid+1;



       }

       return min;
    }
};

解法2. 快慢指针
时间O(n) 空间O(1)
利用数组和下标的映射关系。

例如数组nums[1,2,3]
1.从下标0出发,nums[0]=1,则下一步映射为1
2.nums[2]=3,下一步映射为3,此时到达结尾,结束映射,下标映射序列为0->1->3
考虑数组nums[2,1,2,3],其映射序列为0->2->0->2->.. ,出现了环,此时即存在重复元素。

此时问题转换为求环路起点。快慢指针从0开始,快指针每次映射两次,慢指针每次映射一次。当指针相遇时,保持慢指针不动。此时使用新指针从0开始,新慢两个指针每次映射一次,当两指针相遇时即为环起点(Floyd’s tortoise and hare algorithm)。

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
       int slowPointer=0,fastPointer=0;

       do {
           slowPointer=nums[slowPointer];

           fastPointer=nums[nums[fastPointer]];
       }

       while(slowPointer!=fastPointer);

       int resPointer=0;

       while(slowPointer!=resPointer)
         {
             slowPointer=nums[slowPointer];
             resPointer=nums[resPointer];
         }

         return resPointer;
    }
};

from 287.Find the Duplicate Number
32.最大字串和
解法1.Kadane algorithm
两个前提:
1.最大子串的任意前缀非负。即对于array[ 1,2,n ],设arry[i..j]为满足条件的和最大字串,则对于任意k∈(i,j)都满足array[i,k]>0。反证法,已知arry[i..j]为和最大子串,若array[i,k]<0, 则array[k,j]为最大子串与题设矛盾。
2.将数组切割为若干子串,使得除最后一个子串外,之前各子串的和均小于0,则此时满足条件的子串不可能跨越多个子串。例如array={−2, 1, −3, 4, −1, 2, 1, −5, 4} ,切割为{2} ,{1,-3},{4, −1, 2, 1, −5, 4} ,此时满足条件的子串在第三个子串中(该串本身,或为该子串的前缀子串)。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int max = INT_MIN,sum=0;   
        /*
        源于极限头文件limits.h,INT_MIN表示带符号int的最小值
        */
        for(int i=0;i<nums.size();i++)
         {
           sum+=nums[i];

           if(max < sum)
            max=sum;

           if(sum<0)
           sum=0;
         }
         return max;
    }
};

from 53. Maximum Subarray
33.最长子序列
Given [100, 4, 200, 1, 3, 2],
The longest consecutive elements sequence is [1, 2, 3, 4]. Return its length: 4 要求时间复杂度为O(n)
由于STL map基于红黑树,建立过程需要排序,导致超时。
故使用C++11 unordered_set作hash表,建立代价为O(n),查询代价为O(1)

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        if(!nums.size())
        return 0;

       unordered_set<int> hash_table(nums.begin(),nums.end());
        int res = 1;
        for(auto i :nums){  
        /*此处若使用for(auto i=0;i<nums.size();i++)
        在case[2147483646,-2147483647,0,2,2147483644,-2147483645,2147483645]出现Wrong Answer,C11不熟,原因不明。
        */
            if(hash_table.find(i)==hash_table.end()) //unordered_set::find 查找成功返回i的iter,查找失败则返回unordered_set::end (end为容器最后一个元素的下一个空间)
              continue;
            hash_table.erase(i);
            int left = i-1,right = i+1;

            while(hash_table.find(left)!=hash_table.end()) 
            hash_table.erase(left--);
            while(hash_table.find(right)!=hash_table.end()) 
            hash_table.erase(right++);

            res = max(res,right-left-1);
        }
        return res;
    }
};

from 128. Longest Consecutive Sequence
34.合并有序数组
数组1中有足够空间放下两个数组,若从前向后合并会涉及大量的元素移动,此时选择从后向前合并。

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int p1=m-1, p2=n-1,pos=m+n-1;
        while(p1>=0 && p2>=0)
        {
            if(nums1[p1]>=nums2[p2])
            {
            nums1[pos]=nums1[p1];
            p1--;
            }
            else
            {
            nums1[pos]=nums2[p2];
            p2--;
            }
            pos--;
        }

        while(p1>=0)  //p1数组没处理完
        {
            nums1[pos]=nums1[p1];
            p1--;
            pos--;
        }

        while(p2>=0)
        {
          nums1[pos]=nums2[p2];
            p2--;  
            pos--;
        }


    }
};

from 88. Merge Sorted Array
35.有序数组去重
要求:在同一数组中操作,常量空间。返回元素个数, 数组中有效数据之后东西不管It doesn’t matter what you leave beyond the new length

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {

        if ( nums.size()<2)
        return nums.size();
        int count=0;
        for(int i=1; i<nums.size();i++)
        {
            if(nums[i] == nums[i-1])
            count++;
            else
            nums[i-count]=nums[i];
        }
        return nums.size()-count;
    }
};

from 26. Remove Duplicates from Sorted Array
36.旋转数组
For example, with n = 7 and k = 3, the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4]
解法一:新数组

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int a[nums.size()];
        for(int i=0;i<nums.size();i++)
        {
            a[(i+k)%nums.size()]=nums[i];
        }

        for(int k=0;k<nums.size();k++)
        nums[k]=a[k];

    }
};

解法二,三次reverse操作
1.reverse整个数组
2.reverse前k个元素
3.reverse剩下的元素

如[1,2,3,4,5,6,7],k=3

第一次反转:7,6,5,4,3,2,1

第二次反转:5,6,7,4,3,2,1

第三次反转:5,6,7,1,2,3,4

class Solution {
public:

void reverse(vector<int>& rNums,int begin,int end)
    {
        int temp;
        while(begin<end)
        {
          temp=rNums[begin];
          rNums[begin]=rNums[end];
          rNums[end]=temp;
          begin++;
          end--;
        }
    }



  void rotate(vector<int>& nums, int k) {
         k%=nums.size();
        reverse(nums,0,nums.size()-1);
        reverse(nums,0,(k-1)<0?0:(k-1)%(nums.size()));
        reverse(nums,k%(nums.size()),nums.size()-1 );

     }



};

同一个类里的函数调用不用声明,跨类声明在最上方即可
from 189.189. Rotate Array
37.股票交易
数组中第i个元素表示第i天的股价
问题1:只允许买卖一次求最大利润
一次遍历即可

class Solution {
public:

    int maxProfit(vector<int>& prices) {
        if(prices.size()==0)
        return 0;
        int sum=0,low=prices[0];
        for(int i=1;i<prices.size();i++)
        {
            if(prices[i]<low)
             low=prices[i];

            if( (prices[i]-low) > sum )
            sum= prices[i]-low;
        }

         return sum;
    }
};

问题2:允许买卖多次,但买入前必须卖出之前持有的(允许同一天同时买入卖出)
贪心法,只要后一项比前一项大,二者差值就可以算进利润。

class Solution {
public:
    int maxProfit(vector<int>& prices) {

        int sum=0;

        for(int i=1;i<prices.size();i++)
        {
         if(prices[i] > prices[i-1])
         sum+=(prices[i]-prices[i-1]);

        }

        return sum;
    }
};

from
121. Best Time to Buy and Sell Stock
122. Best Time to Buy and Sell Stock II

38.杨辉三角(帕斯卡三角)
输入层数,二维vector形式输出形态

class Solution {
public:
    vector<vector<int>> generate(int numRows) {

        vector<vector<int>> res;
        if(numRows==0)
        return res;

        vector<int> firstLine(1,1);  // vector<int> vec(n,i) 用n个i初始化容器
        res.push_back(firstLine);  //第一行形态固定

        for(int i=0;i< numRows-1;i++)
        {
            vector<int> temp(1,1);    // 最左边的1
            int p=0;
            for(int j=0;j<=i-1;j++)
            {
                temp.push_back(res[i][p]+res[i][p+1]);
                p++;
            }

            temp.push_back(1);  // 最右边的1
            res.push_back(temp);
        }
        return res;
    }
};

from 118. Pascal’s Triangle
39.求开方sqrt(x)
例如:输入100,输出10
暴力法:循环求 a2 , 当其大于x时,a-1即为解。使用long long或者 unsigned long long ,防止溢出。

牛顿迭代法:
使用切线逼近。
x2=nf(x)=x2nf(x)=0
牛顿迭代法图

1.f(xi)02.xixi1x0,x0(x0,f(x0))线线xx1

class Solution {
public:
    int mySqrt(int x) {
        if (x==0)
        return 0;
        double pre,p=1;

        do{
            pre=p;
            p=x/(2*pre)+pre/2;
        }
        while(abs(p-pre)>0.1);  //精度越高越准确
        return (int)p;

    }
};

致敬数学家!
from 69. Sqrt(x)
40. 装水最多的容器
非负数组[1,2,3], 表示坐标上三个点(0,1), (1,2), (2,3) ,过三个点做x轴的三条垂线,两条垂线加上x轴形成一个容器,求容器的最大面积。
贪心法:
aiajSiaiai1S,ai1,ajS
ii
x

class Solution {
public:
    int maxArea(vector<int>& height) {
        int maxLeft=height[0],maxRight=height[height.size()-1];

        int low=0,high=height.size()-1,i=low,j=high;

        int area=0, maxArea=0;

        while(low<=high)
        {

            if(maxLeft<maxRight)
            {
                if(height[low]>maxLeft)
              {
                 maxLeft=height[low];
                 i=low;

              }
              low++;
            }



            else
            {  
              if(height[high]>maxRight)
              {
              maxRight=height[high];
              j=high;
              }
              high--;
            }

            area=(j-i)*min(maxLeft,maxRight);
            maxArea=area>maxArea?area:maxArea;
        }


        return maxArea;
    }
};

from 11. Container With Most Water

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值