AsahiMoon的博客

AsahiMoon的博客

leetcode周期小结1

以下所有更好的解法均来自网络

1.两数之和

题目:

给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。

你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

个人解法:暴力扫描,匹配要求

class Solution {
    public int[] twoSum(int[] nums, int target) {
          
        for(int i=0;i<nums.length;i++)
            for(int j=i+1;j<nums.length;j++)
            {
                if(nums[i]+nums[j]==target)
                {
                   int [] a={i,j};
                    return a;
                }
                
            }
        return null;
    }

}

更好的解法:先遍历一遍数组,建立map数据,然后再遍历一遍,开始查找,找到则记录index。

public class Solution {
    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer, Integer> m = new HashMap<Integer, Integer>();
        int[] res = new int[2];
        for (int i = 0; i < nums.length; ++i) {
            if (m.containsKey(target - nums[i])) {
                res[0] = i;
                res[1] = m.get(target - nums[i]);
                break;
            }
            m.put(nums[i], i);
        }
        return res;
    }
}

2.两数相加

题目:

给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。

你可以假设除了数字 0 之外,这两个数字都不会以零开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

个人解法:极其暴力

/**

 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    int push=0;
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if(l1==null && l2==null && push!=0) return new ListNode(push);
             else if(l1==null &&l2==null) return null;
        
        if(l1==null)
            return addTwoNumbers(new ListNode(0),l2);
        if(l2==null)
            return addTwoNumbers(l1,new ListNode(0));
        int sum=l1.val+l2.val+push;
       if(sum>=10)
             push=1;
         else
              push=0;
        ListNode a=new ListNode(sum%10);
        a.next=addTwoNumbers(l1.next,l2.next);
         return a;
    }
    

}

更好的解法:

如题所描述,两个数值使用单链表来存储,链表每一个节点存储一个数字,从链表头开始,由低位到高位,一开始想到把两个链表都遍历一遍,算出所表示的数值,然后相加得到答案后再构建要返回的链表,显然这不可能是出题者的意图。那么就从单链表的属性方面以及对单链表的操作来考虑。因而我们能想到,对应位的数值进行相加然后再判断是否向高位进位即可,而每次向高位的进位最大值是1,最小值是0,因为单个数字最大是9,那么算上上一位的进位,当前向高位的进位值最大是1+9+9=19,小于20。为了实现我们的想法,我们需要知道一个问题,当两条链表不一样长时,哪条链表更长些,以便当我们做完两条链表相对应的部分后判断是否需要对较长链表的剩余部分继续进行进位计算。显然,做完第一遍相加的迭代后,指针所指的节点不为空的链表要长些。另外,为缩短算法的时间,所以我们需要尽量甚至完全做到不创建新的节点,而原题没要求不能影响输入参数(此处应有滑稽),因而通过更改输入参数再将其输出来实现算法。

  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* addTwoNumbers(ListNode* l1, ListNode* l2) {  
  12.         int flag = 0;  
  13.         int val = 0;  
  14.         ListNode* head1 = l1;  
  15.         ListNode* head2 = l2;  
  16.         ListNode* p1;  
  17.         ListNode* p2;  
  18.         ListNode* temp;  
  19.           
  20.         while(l1!=NULL&&l2!=NULL)  
  21.         {  
  22.             val = l1->val+l2->val;  
  23.               
  24.             l1->val = val;  
  25.             l2->val = val;  
  26.             p1 = l1;  
  27.             p2 = l2;  
  28.             l1 = l1->next;  
  29.             l2 = l2->next;  
  30.         }  
  31.     /**  
  32.     *  默认最长链表为l1,若l2较长则将它们交换  
  33.     */  
  34.         if(l2!=NULL&&l1==NULL)  
  35.         {  
  36.             temp = l1;  
  37.             l1 = l2;  
  38.             l2 = temp;  
  39.             temp = head1;  
  40.             head1 = head2;  
  41.             head2 = head1;  
  42.             temp = p1;  
  43.             p1 = p2;  
  44.             p2 = temp;  
  45.         }  
  46.     /**  
  47.     *  做进位判断  
  48.     */  
  49.        l1 = head1;  
  50.        while(l1!=NULL)  
  51.        {  
  52.            val = l1->val + flag;  
  53.            flag = 0;  
  54.            if(val>9)  
  55.            {  
  56.                flag = 1;  
  57.                val = val - 10;  
  58.            }  
  59.            l1->val = val;  
  60.            p1 = l1;  
  61.            l1 = l1->next;  
  62.        }  
  63.        if (flag==1)  
  64.        {  
  65.            p2->val = flag;  
  66.            p1->next = p2;  
  67.        }  
  68.        return head1;  
  69.      
  70.     }  
  71. };  


3.无重复字符的最长子串

给定一个字符串,找出不含有重复字符的最长子串的长度。

示例:

给定 "abcabcbb" ,没有重复字符的最长子串是 "abc" ,那么长度就是3。

给定 "bbbbb" ,最长的子串就是 "b" ,长度是1。

给定 "pwwkew" ,最长子串是 "wke" ,长度是3。请注意答案必须是一个子串"pwke" 是 子序列  而不是子串。


个人解法:先存所有字符,避免字符串重复操作,降低时间复杂度

class Solution {
     public static int lengthOfLongestSubstring(String s) {

        int[] ASCII=new int[127];
        for(int i=0;i<127;i++) ASCII[i]=-1;
        int Flag=0;
        int length=0;

        for(int i=0;i<s.length();i++)
        {
            if(ASCII[s.charAt(i)]>=Flag)          //check repeat
                {
                    length=max(length,i-Flag);
                    Flag=ASCII[s.charAt(i)]+1;
                }
            ASCII[s.charAt(i)]=i;
        }
        length=max(length,s.length()-Flag);
        return length;
    }
    public static int max(int a,int b)
    {
        if(a>=b)
            return a;
        else
            return b;
    }
    

}


更好的解法:

通常我们要降低时间复杂度,可以考虑牺牲空间来获得。

我们通过hash表,并附设两个“指针”i和j;用字符的整数值当索引,值为0或1表示是否之前存在。当存在了,就将i++并清零,直到j所表示的字符被清零为止。

比如abcdba,当j等于4的时候,arr[1]是为1的,此时i为0,那么先将arr[0]和arr[1]清零,然后再对arr[1]赋值为1。

  1. int LengthOfLongestSubstring(char *str)  
  2. {  
  3.     int i,j;  
  4.     int maxlen = 0; //要初始化,不然产生意外结果!  
  5.     int n = strlen(str);  
  6.     int arr[256] = {0};     //设置hash表,索引为字符的值,值为是否存在该字符!  
  7.   
  8.     for(i = 0,j = 0;j < n;j++){  
  9.         while(arr[str[j]] == 1){  
  10.             arr[str[i]] = 0;  
  11.             ++i;  
  12.         }  
  13.         arr[str[j]] = 1;  
  14.         maxlen = maxlen > (j-i) ? maxlen : j-i+1;  
  15.     }  
  16.   
  17.     return maxlen;  


5.最长回文子串


给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。

示例 1:

输入: "babad"
输出: "bab"
注意: "aba"也是一个有效答案。

示例 2:

输入: "cbbd"
输出: "bb"


个人解法:回文有左右对称和中间对称两种,遍历的时候假设每个字符都是对称轴和左右对称中的左点

class Solution {
      public static String longestPalindrome(String s) {
        if(s.length()==1) return s;
        int [] midsize=new int[s.length()];
        int [] sidesize=new int[s.length()];
        for (int i = 0; i < s.length(); i++) {
               runmid(s,i,midsize);
               runsize(s,i,sidesize);
        }
        int maxmid=0;int midpoint=0;
        int maxside=0;int sidepoint=0;
        for(int i=0;i<s.length();i++)
        {
            if(midsize[i]>maxmid) {midpoint=i;maxmid=midsize[i];}
            if(sidesize[i]>maxside) {sidepoint=i;maxside=sidesize[i];}
        }
        if(midpoint==0&&sidepoint==0&&s.charAt(0)!=s.charAt(1)) return s.charAt(0)+"";
        if(maxmid*2+1>maxside*2&&maxmid!=0) return midtostring(midpoint,s,midsize[midpoint]);
        else return sidetostring(sidepoint,s,sidesize[sidepoint]);
    }
    public static void  runmid(String s,int mid,int  [] size) {
        int i=mid+1;
        for(;i<s.length()&&2*mid-i>=0;i++)
            {
                if(s.charAt(i)==s.charAt(2*mid-i)) ;
                else break;
            }
            size[mid]=i-mid-1;


        }
        public static void runsize(String s,int left,int[] size)
        {   int i=left;
            String temp="";
            for(;i>=0&&left+left+1-i<s.length();i--)
            {

                if(s.charAt(i)==s.charAt(2*left+1-i)) ;
                else break;
            }
             size[left]=left-i;
        }
        public static String midtostring(int mid,String s,int size)
        {
            String a=""+s.charAt(mid);
            for(int i=mid+1;i<=mid+size;i++) a=s.charAt(i)+a+s.charAt(i);
            return a;
        }
        public  static String sidetostring(int side,String s,int size)
        {   String a="";
            for(int i=side;i>side-size;i--) a=s.charAt(i)+a+s.charAt(i);
            return a;
        }


}


更好的解法1:

此题还可以用动态规划Dynamic Programming来解,根Palindrome Partitioning II 拆分回文串之二的解法很类似,我们维护一个二维数组dp,其中dp[i][j]表示字符串区间[i, j]是否为回文串,当i = j时,只有一个字符,肯定是回文串,如果i = j + 1,说明是相邻字符,此时需要判断s[i]是否等于s[j],如果i和j不相邻,即i - j >= 2时,除了判断s[i]和s[j]相等之外,dp[j + 1][i - 1]若为真,就是回文串,通过以上分析,可以写出递推式如下:

dp[i, j] = 1                                               if i == j

           = s[i] == s[j]                                if j = i + 1

           = s[i] == s[j] && dp[i + 1][j - 1]    if j > i + 1   



class Solution {
public:
    string longestPalindrome(string s) {
        int dp[s.size()][s.size()] = {0}, left = 0, right = 0, len = 0;
        for (int i = 0; i < s.size(); ++i) {
            for (int j = 0; j < i; ++j) {
                dp[j][i] = (s[i] == s[j] && (i - j < 2 || dp[j + 1][i - 1]));
                if (dp[j][i] && len < i - j + 1) {
                    len = i - j + 1;
                    left = j;
                    right = i;
                }
            }
            dp[i][i] = 1;
        }
        return s.substr(left, right - left + 1);
    }
};

更好的解法2:马拉车算法Manacher's Algorithm

class Solution {
public:
    string longestPalindrome(string s) {
        string t ="$#";
        for (int i = 0; i < s.size(); ++i) {
            t += s[i];
            t += '#';
        }
        int p[t.size()] = {0}, id = 0, mx = 0, resId = 0, resMx = 0;
        for (int i = 0; i < t.size(); ++i) {
            p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;
            while (t[i + p[i]] == t[i - p[i]]) ++p[i];
            if (mx < i + p[i]) {
                mx = i + p[i];
                id = i;
            }
            if (resMx < p[i]) {
                resMx = p[i];
                resId = i;
            }
        }
        return s.substr((resId - resMx) / 2, resMx - 1);
    }
};

阅读更多
个人分类: 算法
下一篇leetcode周期小结2
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭