leetcode题目总结<1>

来源:互联网 发布:艾吉森空气净化器 知乎 编辑:程序博客网 时间:2024/06/09 23:06

开始了leetcode的刷题之路,话不多说,从头开始。。。

344.Reverse String

//my code#include <string>using namespace std;class Solution {public:    string reverseString(string s) {        int numb = s.size();        string s1 = "";        for(int i = numb - 1; i >= 0; i--)        {            s1 += s[i];        }        return s1;    }};
//others c codechar* reverseString(char* s) {  char *t,*r,tmp;  r=s;  t=s+strlen(s)-1;  while(s<t){    tmp=*t;    *t=*s;    *s=tmp;    s++;    t--;  }  return r;}
总结:自己编写的思想就是利用for循环,从尾部到头部把一个个的字符加入到新建的空字符变量中,这样,最后生成的新字符串自然就实现了字符反转。

           而看了别人用c写的代码,没有用到for循环,使用两个指针,一个指向第一个字符,一个指向最后一个字符,将两个字符实现交换,然后指向第一个字符的指针+1,指向最后一个字符的指针-1,这跟我的方法相比,只循环了一半的次数,就实现了字符反转,好吧。。

345.Reverse Vowels of a String

//My codeclass Solution {public:    string reverseVowels(string s) {        int numb = s.size();        string s1 = "", s2 = s;        for(int i = 0; i < numb; i++)        {            if( s[i] == 'a' || s[i] == 'A' || s[i] == 'e' || s[i] == 'E' || s[i] == 'i' || s[i] == 'I' || s[i] == 'o' || s[i] == 'O' || s[i] == 'u' || s[i] == 'U' )            {                s1 += s[i];            }        }        int n = 0;        for(int j = numb-1; j >= 0; j--)        {        if(s[j] == 'a' || s[j] == 'A' || s[j] == 'e' || s[j] == 'E' || s[j] == 'i' || s[j] == 'I' || s[j] == 'o' || s[j] == 'O' || s[j] == 'u' || s[j] == 'U' )            {                s2[j] = s1[n];                n++;            }        }        return s2;    }}; 
//others codeclass Solution {public:string reverseVowels(string s) {    int start = 0;    int end = s.size()-1;        while(start<end) {        if (isVowel(s[start]) && isVowel(s[end])) {                        char temp = s[start];            s[start++] = s[end];            s[end--] = temp;                    }else if (isVowel(s[start])) {            end--;        }else if (isVowel(s[end])) {            start++;        } else {            end--;            start++;        }    }        return s;}bool isVowel(char l){    return (l == 'a' || l == 'A' || l == 'e' || l == 'E' || l == 'i' || l == 'I' || l == 'o' || l == 'O' || l == 'u' || l == 'U');}
总结:我的代码思路是:第一步提取出字符串中的所有元音字母,保存到一个字符串里面,这里面用到了一个for循环,下一步是从尾到头再遍历一遍字符串,如果遇到元音字母,则把刚刚提取出来的元音字母按照从小到大的顺序依次进行替换。

           而别人的思路是设置两个指针,分别指向字符串的第一位和最后一位,然后进行一个while循环,对两个指针所指的字符进行分类判断,当前后指针都指向了元音字母,那么交换数据,如果前指针指向元音字符而后指针没有,那么后指针向前进一位,相反,前指针向前进一位,如果两个指针都没有指向元音字母,那么各进一位,知道前指针等于后指针,字符串全部遍历了一遍结束。整个代码还是只循环了一半的字符串长度的次数,而我的是循环了两遍,真的蠢哭了。

1. Two Sum

<span style="font-size:18px;">//My code//功能:给定一个vector数组和一个目标数traget,若在数组中能寻找到两个数a,b,使得a+b=target,        则输出a,b对应的vertor下标[i,j];class Solution {public:    vector<int> twoSum(vector<int>& nums, int target) {        vector<int> result;        int count = nums.size();        for(int i = 0; i < count-1; i ++)        {            for(int j = i+1; j < count; j++)            {                if(nums[i] + nums [j] == target)                {                    result.push_back(i);                    result.push_back(j);                }                                  }        }        return result;    }</span>};

<span style="font-size:18px;">//others codeclass Solution {public:    vector<int> twoSum(vector<int>& nums, int target) {        unordered_map<int,int> hash;        unordered_map<int,int>::iterator got;        int len = nums.size();        int idx1, idx2;        //将vector中的内容存储到关联容器,格式为(vector中的值,其对应的下标)。        for (int i = 0; i < len; i++) {            got = hash.find(nums[i]);            if (got == hash.end()) {                hash.insert(make_pair(nums[i],i));            } else {                got->second = i;            }        }                for (vector<int>::const_iterator it = nums.begin(); it != nums.end(); ++it) {            if (hash.count(target-*it)) {    //判断加法对应*it的另一个加数是否在容器中                idx1 = (it - nums.begin());  //若在,返回*it的位置索引                idx2 = hash[target-*it];     //并返回hash表中另一个加数对应的值                if (idx1 != idx2) {                    break;                }            }        }                return {idx1,idx2};    }};</span>
总结:我的代码是很直观的思路,构建两个循环,查找两个数判断结果。时间复杂度为o(n^2);

           别人的代码中用到了关联容器unordered_map,容器中保存的关键字是vector中的值,其值则是对应的vector中的下标。让后循环遍历vector,并在遍历中让target减去当前遍历vector位置的值,所得结果为该位置下所对应的另一个加数的值,然后在哈希表中查找,若找到,则表明vector容器中存在这样的一对值,加起来的结果为target,于是返回这两个值对应的下标。这种方式使得一次循环就能得出结果。


2. Add Two Numbers

//My code /** * Definition for singly-linked list. * struct ListNode { *     int val; *     ListNode *next; *     ListNode(int x) : val(x), next(NULL) {} * }; */class Solution {public:    ListNode* Create()  //创建一个头结点    {        ListNode * lNode = (ListNode*)malloc(sizeof(struct ListNode));        lNode->val = 0;        lNode->next = NULL;        return lNode;    }    void Insert(ListNode* l,int a)    {        ListNode *pNode;        while(l->next != NULL)           l = l->next;        pNode = (ListNode*)malloc(sizeof(struct ListNode));        pNode->val = a;        pNode->next = NULL;        l->next = pNode;    }    int Read(ListNode* l,int x)  //头结点不计入    {        int value;        ListNode *pNode = l;        for(int i = 1; i < x; i++)        {           pNode = pNode->next;        }        value = pNode->val;        return value;    }    int Size(ListNode *l)      //头结点不计入    {        ListNode *pNode = l;        int count = 0;        while(pNode != NULL)        {            pNode = pNode->next;            ++count;        }        return count;    }public:    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {        int size1,size2,min,max,value,value1,value2,fillout = 0;        ListNode *pNode;        pNode = Create();        size1 = Size(l1);        size2 = Size(l2);        if(size1 >= size2)        {            max = size1;            min = size2;        }        else        {            max = size2;            min = size1;        }                for(int i = 1, j = 1 ; i <= max; i++,j++)        {            if(size1 >= size2)            {               value1 = Read(l1,i);               if(j <= min)               {                   value2 = Read(l2,j);               }               else               {                   value2 = 0;               }             }            else            {                value1 = Read(l2,i);                if(j <= min)                {                     value2 = Read(l1,j);                }                else                {                     value2 = 0;                }             }                                    value = value1 + value2 + fillout;                        if(value >= 10)            {                value = value % 10;                Insert(pNode,value );                fillout = 1;            }            else            {                Insert(pNode,value );                fillout = 0;            }                    }        if(fillout == 1)        {            Insert(pNode,fillout);        }        return pNode->next;    }};

//others codeclass Solution {public:    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2)     {        int c = 0;        ListNode newHead(0);        ListNode *t = &newHead;        while(c || l1 || l2)        {            c += (l1? l1->val : 0) + (l2? l2->val : 0);            t->next = new ListNode(c%10);            t = t->next;            c /= 10;            if(l1) l1 = l1->next;            if(l2) l2 = l2->next;        }        return newHead.next;    }};
总结:这一题花了很长很长的时间。。。由于没正确的看懂题意,然后写的代码数及质量也很低下,跟优秀的代码比起来真是相差太多了。第一个没弄懂的地方就是题目提示中给出的
ListNode(int x) : val(x), next(NULL) {}
就表示已经提供了初始化的方式,然而我没看明白,就都自己写出来了;其次是相加的过程中一开始一直以为是从后往前加;最恼人的地方时发现自己写这样的的代码中太过于喜欢用for结构而不善于用while循环,导致代码质量和效率都差了好多好多。从别人的代码中也可以看出许多的两点出来,首先就是while循环简单明了,相加的过程中,整型变量c既求出了加数与10的取模量,又保证了是否要往前进一位,while(c)中的c就是判断最后一位是否有进位的要求。

The pseudocode is as following(本题算法伪代码如下):

  • Initialize current node to dummy head of the returning list.
  • Initialize carry to 00.
  • Initialize pp and qq to head of l1l1 and l2l2 respectively.
  • Loop through lists l1l1 and l2l2 until you reach both ends.
    • Set xx to node pp's value. If pp has reached the end of l1l1, set to 00.
    • Set yy to node qq's value. If qq has reached the end of l2l2, set to 00.
    • Set sum = x + y + carrysum=x+y+carry.
    • Update carry = sum / 10carry=sum/10.
    • Create a new node with the digit value of (summod10)(summod10) and set it to current node's next, then advance current node to next.
    • Advance both pp and qq.
  • Check if carry = 1carry=1, if so append a new node with digit 11 to the returning list.
  • Return dummy head's next node.

Note that we use a dummy head to simplify the code. Without a dummy head, you would have to write extra conditional statements to initialize the head's value.


3. Longest Substring Without Repeating Characters

大致说一下题:

要求:给一个字符串,求其中最长无重复字符的子串的长度
例子:

         给出"abcabcbb",符合要求的子串是"abc", 其长度为 3.

         给出"bbbbb", 符合要求的子串是 "b", 其长度为1.

         给出"pwwkew",符合要求的子串是 "wke", 其长度为 3. 

         注意答案要求的是子串, 因此上面的例子中"pwke" 是一个字字符序列,而不是一个子串.

算法思路:这里面需要做两件事情

       第一是需要能够判别出前面重复出现过的字符。

       第二是需要统计未重复出现的连续字符长度,并且其中最长的作为结果。

我的代码(思路):

       采取的是超级笨的办法,用一个新的字符串变量收集遍历的字符,如果没有出现,就加进去,并统计新子串的长度,如果重复出现了,将遍历的起始位置加一,重新统计,算算时间复杂度,几乎达到n^3,真是愚蠢。虽然结果正确,但是对于较长的字符,效率较低,因此提交的时候未能通过。。。

//My code class Solution {public:    bool IsInclude(string s,char a)    {        int len = s.size();        int i = 0;        while(i < len)        {            if(s[i] == a)               return true;            i++;        }        return false;    }    int lengthOfLongestSubstring(string s) {        int len, lensub, i, j;        int  maxlensub = 0;        len = s.size();        string substring = "";        for(int i = 0; i < len; i++)        {            substring += s[i];            if(maxlensub < 1)                maxlensub = 1;            for(int j = i + 1; j < len; j++)            {                if(false == IsInclude(substring,s[j]))                {                    substring += s[j];                }                else                {                    substring = "";                    break;                }                lensub = substring.size();                if(maxlensub < lensub)                    maxlensub = lensub;                if(maxlensub >= len - i)    return maxlensub;            }        }        return maxlensub;    }};

别人优秀的代码(思路):

       对于需要判别重复出现的字符这个要求,怎样才能做到呢,别人给出的一种巧妙的方法。我们知道,每个字符所对应的ASCII码是不同的,因此我们完全可以申请一个可以统计所有字符大小(比如256?)的空间的数组或vector容器。一开始的时候,我们给定他们一个统一的标记-1,然后再遍历给定的字符串,在遍历的过程中,判断该字符对应的ACSII码的位置是否为-1,如果为-1,说明前面没有出现过,我们给他重新作标记,以表示出现了,如果不为-1,说明该字符前面出现过,这样我们需要作别的处理,这个处理的目的就是为了下面做的事,也就是统计最长子串所做。

       那么该怎么统计最长子串的长度呢:


假设我们的字符串s为adcgacdg:

       申请一个vector<int> dict(256,-1); dict存储着int型的变量,且初始大小为256,全部赋值为-1,这个-1是辨识是否重复的标记。再创建maxlen变量统计最长子串,初始化为0,start变量统计最初的位置,初始化为-1;接着走一个for循环,从头到尾遍历字符串,循环体里首先判断dict[s[i]]>start;对于第一个字符a,s[i]=97,dict[97]=-1;判断不成立,也就是a字符前面没出现过,但是呢,现在出现了,所以dict[97]现在不能再为-1了,否则下次再出现a判断的结果还是不成立,我们给dict[97]=0,也就是代表a在字符串中的序号值,接着计算maxlen,这里的表达式为maxlen = max(maxlen,i - start)。0-(-1)=1,这个表达式用的很妙。其实这里strat和序号i可以算是两个哨兵,一个站在我们当前要计算的子串的前面一个位置(注意不包括),一个站在我们当前要计算的子串的最后面的位置(这里是包括的,他们的差正好是串的长度)。就这样遍历下去,得到dict[98]=1,dict[99]=2,dict[103]=3,一直到i=4,这里判断dict[97]>start。0>-1,判断成立,也就是说前面出现了a,这是可怎么办呢,好办,我们把出现在前面出现的a的前面的字符全部除掉,再接着后面不包括这个出现的a的部分继续统计,这样做能行得通是因为前面计算maxlen时是取前一次的maxlen和当前的串里面大的数,因此即便前面截去了很大一部分也没有关系,他的值还保留着,知道后面有一个更大的来取代他。那么我们该怎么截去呢,更简单不过了,把前面的那个哨兵start移到a的位置上来即可。

        看下面的代码,区区9行就实现了要求,而且时间复杂度为o(n),简直是神来之笔啊。

int lengthOfLongestSubstring(string s) {        vector<int> dict(256, -1);        int maxLen = 0, start = -1;        for (int i = 0; i != s.length(); i++) {            if (dict[s[i]] > start)                start = dict[s[i]];            dict[s[i]] = i;            maxLen = max(maxLen, i - start);        }        return maxLen;    }


0 0
原创粉丝点击