leetcode题目总结<1>

开始了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 code

char* 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 code

class 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 code

class 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 code

class 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 code

class 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值