LeetCode-C++ 题解笔记(随缘更新中)

记录部分热题top100比较好理解(不是最快解)的题解和个人注释

  • 热题top100

1. 两数之和:哈希表

哈希表的迭代器是一个指针it

class Solution {
public:
    vector twoSum(vector& nums, int target) {
    int n = nums.size();
    //创建哈希表
    unordered_map  hashtable;
    for(int i = 0;i < n;i++)
    {
        //查
        auto it = hashtable.find(target - nums[i]);
        //如果find()存在于哈希表,则返回迭代器it,如果不存在则返回表的末尾:hashtable.end()
        if(it != hashtable.end()) return {it->second,i};
        //哈希表表示为(first second)
        //对应(key,value),映射成数组可以理解为(值,下标)
        
        //添加:hashtable[key] = value;
        //通过键来查值
        hashtable[nums[i]] = i;
    }
    return {};
    }
};

2. 两数相加:单链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    //创建一个可以返回listNode*的方法,l1/l2是两条链的头结点指针
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        //创建头尾结点指针,头结点指针负责初始化链和返回链,尾结点负责移动存取(next)
        ListNode* head = nullptr;
        ListNode* tail = nullptr;
        //进位数curry
        int curry = 0;
        //取数变量n1n2,求和数sum
        int n1,n2,sum;
        //遍历l1l2,只要其中有一条当前结点不为空仍然继续循环
        while(l1 || l2)
        {
            //取数
            n1 = l1 ? l1->val : 0;
            n2 = l2 ? l2->val : 0;
            //求和
            sum = n1 + n2 + curry;
            //如果head为空,则初始化,不为空则添加并移动尾结点
            if(!head)
            {
                head = tail = new ListNode(sum % 10);
            }
            else
            {
                tail->next = new ListNode(sum % 10);
                tail = tail->next;
            }
            //计算进位值,至少先sum一次才能计算进位,第一次默认为0
            curry = sum / 10;
            //判断是否为空并移位,如果为空也没关系,上面取数的时候会赋0
            if(l1)
            {
                l1 = l1->next;
            }
            if(l2)
            {
                l2 = l2->next;
            }
        }
        //循环结束之后最后还要看是否有进位值(最高位)
        if(curry > 0)
        {
            tail->next = new ListNode(curry);
        }
        //返回
        return head;
    }
};

3. 无重复字符的最长子串:类似哈希表的桶/哈希集合

//方法1:类哈希表桶
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        //创建桶(数组),设定128个元素对应0-127ASCII码值,全部赋0
        vector m(128, 0);
        //存最大长度
        int maxlen = 0;
        //head表示窗口最左边的字母序号:如果出现重复的,比如两个相同的字母a,上一个a在桶里存的m[s[i]]是a+1表示a的下一个位置
        //那么第二个a出现时,head就=a+1也就是max(head,m[s[i]]),去除了窗口里上一个a,保证窗口里无重复字母
        int head = 0;
        //遍历字符串
        for (int i = 0; i < s.size(); i++) {
            //修改最左边的字母序号head
            head = max(head, m[s[i]]);
            //当前字母对应的ASCII码桶里存下一个位置(i+1),用于更新head
            m[s[i]] = i + 1;
            //更新长度
            maxlen = max(maxlen, i - head + 1);
        }
        return maxlen;
    }
};
//方法2:哈希集合
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_set hashset;
        int len=s.size();
        int left=0,maxstr=0;
        for(int i=0;i

5. 最长回文子串:动态规划

动态规划,二维数组

class Solution {
public:
    string longestPalindrome(string s) {
        int len = s.size();
        //长度小于2,只有一个字符,单字符是回文的
        if(len < 2)
        {
            return s;
        }
        //字串从1个字符开始
        int maxlen = 1;
        //截取开始下标:最后截取的时候用
        int begin = 0;
        //二维vector数组的创建
        /*vector< vector > dp(n, vector(n) );
        定义了一个vector容器,
        元素类型为vector,
        初始化为包含n个vector对象,
        每个对象都是一个新创立的vector对象的拷贝,
        而这个新创立的vector对象被初始化为包含n个0。*/
        vector> dp(len,vector(len));
        //i代表左边界,j代表右边界
        //对角线ii相当于i=j左边界等于右边界,即为单个字符,所以为true
        for(int i = 0;i < len;i++)
        {
            dp[i][i] = true;
        }
        //动态规划相当于填写一张二维的表
        //而回文子串需要判断s[i]==s[j],如果相等,就要继续判断去掉两边的中间字串是不是回文
        //若中间字串长度小于2,也就是等于1,那么必然是回文,也就是(j-1)-(i+1)+1<2
        //也就是j-i<3,也可以写成j-i+1<4,理解为:原长度为3或者2或者1,那么必定为回文串
        //另外,如果不满足j-i<3,就需要进一步判断字串是不是回文串s[i+1]...s[j-1]
        //参考的值dp[i][j]= (s[i]==s[j] &&(s[i+1][j-1] || j-i<3))
        //s[i+1][j-1]也就是参考左下角
        //而第零列dp[0][0]没有右上角,所以j从1开始,从左向右,i从0开始,从上到下开始判断,
        //每次都要先看看左下角
        for(int j = 1;j < len;j++)
        {
            for(int i = 0;i < j;i++)
            {
                if(s[i] != s[j])
                {
                    dp[i][j] = false;
                }
                else 
                {
                    if(j-i+1 < 4)
                    {
                        dp[i][j] = true;
                    }
                    else
                    {
                        dp[i][j] = dp[i+1][j-1];
                    }
                }
                //更新最长子串的头尾下标
                if(dp[i][j] && j-i+1 > maxlen)
                {
                    maxlen = j-i+1;
                    begin = i;
                }
            }
        }
        //截取最长字串
        s = s.substr(begin,maxlen);
        return s;
    }
};

6. N 字形变换:二维矩阵

class Solution {
public:
    string convert(string s, int numRows) {
        int n = s.length(), r = numRows;
        if (r == 1 || r >= n) {
            return s;
        }
        //每个周期t有r+r-2个字符
        int t = r * 2 - 2;
        //因为int会去尾,所以需要将长度凑成最长n+t-1,t-1(一个周期-1),不然有些最后一个周期不满的时候会被去尾
        //(n + t - 1) / t得周期数,r-1是每个周期占用的列数,得c:矩阵列数
        int c = (n + t - 1) / t * (r - 1);
        vector mat(r, string(c, 0));
        for (int i = 0, x = 0, y = 0; i < n; ++i) {
            mat[x][y] = s[i];
            //判断下一个位置的走向
            //i

7. 整数反转

// 将数字 digit 推入 rev 末尾rev = rev * 10 + digit

−2^31≤rev*10+digit≤2^31−1 可以推导成 rev < INT_MIN / 10 || rev > INT_MAX / 10

class Solution {
public:
    int reverse(int x) {
        int rev = 0;
        while (x != 0) {
            if (rev < INT_MIN / 10 || rev > INT_MAX / 10) {
                return 0;
            }
            int digit = x % 10;
            x /= 10;
            rev = rev * 10 + digit;
        }
        return rev;
    }
};

8. 字符串转换整数 (atoi):自动机

class Automaton{
    string state = "start";
    //用哈希表画了一个字符串映射字符串数组的自动机
    unordered_map> table = {
        {"start", {"start", "signed", "in_number", "end"}},
        {"signed", {"end", "end", "in_number", "end"}},
        {"in_number", {"end", "end", "in_number", "end"}},
        {"end", {"end", "end", "end", "end"}}
    };
    //写一个取下标的函数
    int getindex(char c)
    {
        //isspace判断是否是空(空格或者换行符等等都算空)
        if(isspace(c)) return 0;
        if(c=='+'||c=='-') return 1;
        //isdigit判断是否为0-9数字
        if(isdigit(c)) return 2;
        return 3;
    }
public:
    //符号
    int sign = 1;
    //long和int一般都是32位(4字节),longlong为64位(8字节)
    long long ans = 0;
    //写了一个取字符串的字符并进位累加的函数
    void get(char c)
     {
        state = table[state][getindex(c)];
        if (state == "in_number") {
            //字符串或者字符-'0'代表转string/char转int类型(ascii)
            ans = ans * 10 + c - '0';
            ans = sign == 1 ? min(ans, (long long)INT_MAX) : min(ans, -(long long)INT_MIN);
        }
        else if (state == "signed")
            sign = c == '+' ? 1 : -1;
    }
};
class Solution {
public:
    int myAtoi(string s) {
      Automaton automaton;
        for (char c : s)
            automaton.get(c);
        return automaton.sign * automaton.ans;
    }
};

9. 回文数:一半数字/**整数反转**

解法1:取后一半数字和前一半数字比较

class Solution {
public:
    bool isPalindrome(int x) {
        // 特殊情况:
        // 如上所述,当 x < 0 时,x 不是回文数。
        // 同样地,如果数字的最后一位是 0,为了使该数字为回文,
        // 则其第一位数字也应该是 0
        // 只有 0 满足这一属性
        if (x < 0 || (x % 10 == 0 && x != 0)) {
            return false;
        }

        int revertedNumber = 0;
        while (x > revertedNumber) {
            revertedNumber = revertedNumber * 10 + x % 10;
            x /= 10;
        }

        // 当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。
        // 例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,revertedNumber = 123,
        // 由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
        return x == revertedNumber || x == revertedNumber / 10;
    }
};

解法2:整数反转(挺快的)

利用第7题的溢出控制 完成整数反转比较原数

当然也可以直接longlong防止溢出(doge)

class Solution {
public:
    bool isPalindrome(int x) {
        int temp = x;
        if(x<0 ||(x%10==0&&x!=0)) return false;
        int rev = 0;
        while (x != 0) {
            if (rev < INT_MIN / 10 || rev > INT_MAX / 10) {
                return 0;
            }
            int digit = x % 10;
            x /= 10;
            rev = rev * 10 + digit;
        }
        return rev==temp;
    }
};
  • 番外题

  1. 连续的非严格递增字序列数组:三指针/贪心

朋友分享的美团javascript一面面试题,这边给一个c++解和一个javascript解

大意是给你个数组,让你返回一个连续的最长非严格递增子序列数组

vector ans;
int head, tmp, tail;
vector numarr = { 37,2,3,4,4,9 };
//用哈希表管理用三指针遍历时遇见的所有连续递增下标,key是头,value就是尾
unordered_map hashtable;
int maxkey = 0;
int maxvalue = 0;
int len = numarr.size();
void printArray(vector &numarr,vector &ans)
{
    //三指针
     head = 0;
     tmp = 0;
     tail = 0;
     hashtable[0] = 0;
     while (tail < len-1)
     {
          tail++;
          if (numarr[tail] > numarr[tmp])
          {
           tmp++;
           hashtable[head] = tail;
          }
          else
          {
           tmp=tail;
           head=tail;
           hashtable[head] = tail;
          }
     }
     for (auto a : hashtable)
     {
         int key = a.first;
         int value = a.second;
         if (value - key + 1 > maxvalue - maxkey + 1)
         {
          maxvalue = value;
          maxkey = key;
         }
     }
     for (int i = 0; i < maxvalue - maxkey+1; i++)
     {
          cout << numarr[maxkey+i];
     }
}

贪心算法javascript

var findLengthOfLCIS = function(nums) {
    let maxhead = 0;
    let maxtail = 0;
    let ans = 0;
    const n = nums.length;
    let start = 0;
    for (let i = 0; i < n; i++) 
    {
        if (i > 0 && nums[i] <= nums[i - 1]) 
        {
            start = i;
        }
        if (ans < i - start + 1)
        {
            ans = i - start + 1;
            maxhead = start;
            maxtail = i;
        }
    }
    var arr = new Array();
    for (let i = 0; i < ans; i++)
    {
    arr[i] = nums[maxhead+i];
    }
    return arr;
};

你可能感兴趣的:(leetcode,算法,职场和发展)