leetcode 1-10

2.1 leetcode1 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那两个整数,并返回它们的数组下标。

思路: 遍历到 x 时,在 x 之前查找一个数 target - x, 如果存在则返回 x 和这个数的下标,可以创建一个哈希表,在遍历一个数 x 时,查找哈希表内有没有满足target - x 的数,如果有则满足题意,否则将 x 添加到哈希表中。由于哈希表插入、查找的时间复杂度都为 O(1) ,所以总的实践复杂度为 O(n)。

class Solution{
public:
    vector<int>twoSum(vector<int> & nums, int target){
        vector<int> res;
        unordered_map<int,int> hash;
        for(int i = 0; i < nums.size(); ++i){
            auto x = target - nums[i];
            if(hash.count(x))
                return {hash[x],i};
            hash[nums[i]] = i;
        }
        return {};
    }
}

2.2 leetcode2 两数相加

给你两个非空的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位数字。请你将两个数相加,并以相同形式返回一个表示和的链表。你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

**思路:**设两个相加的链表的值分别为a , b ,t 为进位,设定虚拟头节点dummy, 以及指向和链表尾节点的cur,当l1,l2以及进位t 不为0 时 进行循环,循环过程中在每一位运算中,和的新节点的值为 (a + b + t) % 10; 进位为 (a + b + t) / 10; cur 在每个循环内更新到指向新的尾节点。

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        // 设定虚拟头节点,以及指向和链表的尾节点的cur
        ListNode * dummy = new ListNode(-1), cur = dummy;
        int t = 0; // 进位
        // 当l1,l2,以及进位为1时进行运算
        while(l1 || l2 || t){
            if(l1) t += l1->val;
            if(l2) t += l2->val;
            cur->next = new ListNode(t % 10);
            cur = cur->next;
            t /= 10;
        }
        return dummy->next;
    }
};

2.3 无重复字符的最长子串

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

思路: 求一个序列里子串的最长值,可以按以 i 结尾做分类分为 n 类(0,1,2…n-1)。然后求这n类的最大值,可以使用哈希表记录到 i 的字符出现的次数,用 j 从0 开始向 i 方向移动,当hash[s[i]] > 1 时,就移动 j ,使 hash[s[i]] 变为1,此时 i - j + 1 就是 i 与 j 之间的最大 距离,依次枚举这 n 类,找出最大值即可。

双指针优化: 注意到当 i 向右移动时, j 也只能 向右移动,原因如图:所以问题存在单调性,可以使用双指针优化。

在这里插入图片描述

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char,int>hash;
        int res = 0;
        for(int i = 0,j = 0; i < s.size(); ++ i){
            hash[s[i]] ++;
            while(hash[s[i]] > 1)   hash[s[j ++]] --;
            res = max(res,i - j + 1);
        }
        return res;
    }
};

2.4 寻找两个正序数组的中位数

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。算法的时间复杂度应该为 O(log (m+n))

思路: 使用递归解决问题,可以将原问题转化为 在两个正序数组中寻找第 k 小的数。 如图,可以考虑 A[k/2] 和 B[k/2] 的关系将问题分为三种情况:

  • 当 A[k/2] < B[k/2] 时 则分析如图,
  • 当A[k/2] > B[k/2] 时,则可以将B[0-k/2] 部分删除,因为这一段一定在前 k 个数前面;
  • 当 A[k/2] == B[k/2] 时,则A[k/2] 和 B[k/2] 就是要找的第k小的数。

请添加图片描述

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
       int total = nums1.size() + nums2.size();
        // 总长为偶数,则找到中点左右的值,求平均数。
        if(total % 2 == 0){
            int left = findKthNumber(nums1,0,nums2,0,total / 2);
            int right = findKthNumber(nums1,0,nums2,0,total / 2 + 1);
            return (left + right) / 2.0;
        }
        // 总长为奇数,直接返回中点的数值。
        else	return findKthNumber(nums1,0,nums2,0,total / 2 + 1);
    }
	// 在两个正序数组中寻找第K小的数。使用递归,每次减去 k / 2 的部分,总的时间复杂的为O(k / 2)
    int findKthNumber(vector<int> & nums1, int i, vector<int> & nums2, int j,int k ){
        // 边界情况 1 : 假设nums1短一些, nums2长一点,否则就换一下
        if(nums1.size() - i > nums2.size() - j) return findKthNumber(nums2,j,nums1,i,k);
        // 如果nums1到头了,则返回nums2中第k 小的数
        if(nums1.size() == i)   return nums2[j + k - 1];
        // 如果 k == 1 则在nums1,nums2的第一个元素间比较结果。
        if(k == 1) return min(nums1[i],nums2[j]);
        // si 是以 i 为起点的长度为 k / 2 的点后一位, sj 是以 j 为起点的长度为 k / 2 的后一位
        int si = min(i + k / 2, int(nums1.size())), sj = j + k / 2;
        // 如果第一段 k/2 的点大于 nums2的 k / 2 的点,说明无论如何从Nums2开始到sj这一段都在合并后数组的前k 个数之前,所以可以将 nums2这一段删去
        if(nums1[si - 1] > nums2[sj - 1]) return findKthNumber(nums1,i,nums2,sj, k - (sj - j));
        // 同理删除 nums1这一段的值。
        else return findKthNumber(nums1,si,nums2,j, k -(si -i));
    }
};

2.5 最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。

思路: 回文串分为两类,一类是总长是奇数个的回文串,一个是总数为偶数个的回文串,只要从中心点开始定义两个指针 l, r 向两边枚举,在不越界的前提下,看 l 与 r 指向的元素是否相同, 如果相同则继续向两侧走,不同则记录下最大长度, 此时回文串的长度为 r -1 - (l + 1) + 1 = r - l - 1, 然后使用substr function 从 l + 1, 开始截取 长度为 r - l - 1长度的字符串就是最长的子串了。

class Solution {
public:
    string longestPalindrome(string s) {
        string res;
        // 枚举中心点。
        for(int i = 0; i < s.size(); i++){
            // 奇数条件下:l = i - 1, r = i + 1;
            int l = i- 1, r = i + 1;
            // 不越界并且l r 指向的字符相同,则l 向左走, r 向右走
            while(l >= 0 && r < s.size() && s[l] == s[r])   l--,r++;
            // 更新 res 的结果, 截取 l + 1 开始,长度为 r - l = 1 长度的字符串为最长回文子串。
            if(res.size() < r-l-1)  res = s.substr(l+1, r-l -1);
            // 回文串总长偶数条件下, l = i, r = i + 1,开始枚举
            l = i, r = i + 1;
            while(l >= 0 && r < s.size() && s[l] == s[r])   l--,r++;
            if(res.size() < r-l-1)  res = s.substr(l+1, r-l -1);
        }
        return res;
    }
};

2.6 Z 字形变换

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:

P   A   H   N
A P L S I I G
Y   I   R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

思路: 定义一个指标 flag 表示移动方向, 初始值为 -1表示向上移动, 如果row == 1 || row == numrows - 1 时,即走到边界时改变移动的方向,然后之歌遍历字符,填入相应的数组中,最后再读出来即可。

class Solution {
    public:
        string convert(string s, int numRows) {
        // 定义一个字符串数组,数组中存放读入对应位置的字符
        vector<string> vs(numRows);
        // 如果numRows == 1,则直接输出原字符串即可。
        if (numRows < 2) return s;
        // 定义游标flag表示移动的方向,初始-1表示向上移动
        int row = 0, flag = -1;
        for (int i = 0; i < s.size(); i++) {
            // 遍历字符,填入对应位置的字符串里面。
            vs[row] += s[i];
            // 如果达到边界处,则改变移动的方向
            if (row == 0 || row == numRows - 1)
                flag = -flag;
            row += flag;
        }
        string res = "";
        for (auto r : vs)
            res += r;
        return res;
    }
};

2.7 整数反转

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。

如果反转后整数超过 32 位的有符号整数的范围 [−231,  231 − 1] ,就返回 0。

假设环境不允许存储 64 位整数(有符号或无符号)。

思路: c++中取余操作,正数取余得正数,负数取余得负数,对一个整数来说,翻转整数可以对其 % 10 获取最后一位数字,然后 除以 10 将原数最后一位删除,注意 对结果 r 来说,防止其越过 int 的范围, 在循环更新 r 之前为: r = r × 10 + x % 10 r = r \times 10 + x \% 10 r=r×10+x%10,先判断 r > ( I N T _ M A X − x % 10 ) ÷ 10 r > (INT\_MAX - x \% 10) \div 10 r>(INT_MAXx%10)÷10 如果不等式成立则说明后续更新后溢出,所以返回 0.

class Solution{
    public:
    int reverse(int x){
        int r = 0;
        while(x){
            // 更新之前先判断是否会溢出
            if(r > 0 && r > (INT_MAX - x % 10) / 10)	return 0;
            if(r < 0 && r < (INT_MIN - x % 10) / 10)	return 0;
            r = r * 10 + x % 10;
            x /= 10;
        }
        return r;
    }
}

2.8 字符串转换整数 (atoi)

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

函数 myAtoi(string s) 的算法如下:

读入字符串并丢弃无用的前导空格
检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
如果整数数超过 32 位有符号整数范围 [−231,  231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
返回整数作为最终结果。
注意:

本题中的空白字符只包括空格字符 ' ' 。
除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。

思路: 模拟题, 首先去除前置空格,注意c++中空格为 ’ ', 随后判断读入的符号是 + 还是 - , 最后读入数字 字符是数字的条件是 s[k] >= ‘0’ && s[k] <= ‘9’, 最后每次另res * 10 + s[k] - ‘0’; 将字符串转化为数字, 注意 res * 10 + s[k]会爆 int, 必须判断一下, 还要注意, 正整数的最大值是2147483647, 负整数的最小值是 -2147483648, 所以在不考虑 正负号的res的过程中,res * 10 + s[k] - ‘0’ 可能正好等于 -2147483648,所以这一个要特判一下。

class Solution {
public:
    int myAtoi(string s) {
        int k = 0;
        while( k < s.size() && s[k] == ' ') k ++; // 去除前置空格, k < s.size() 防止越界

        if(k == s.size())   return 0; // 去除完空格后字符为空则返回0

        // 根据读入的正负号记录下来
        int minus = 1;
        if(s[k] == '-') minus = -1, k++;
        else if(s[k] == '+') k ++;

        int res = 0;
        // 读入数字, s[k] >= '0' && s[k] <= '9' 是数字
        while(k < s.size() && s[k] >= '0' && s[k] <= '9'){
            // -'0'将字符变为数字。
            int x = s[k] - '0';
            if(minus > 0 && res > (INT_MAX - x) / 10)   return INT_MAX;
            if(minus < 0 && -res < (INT_MIN + x) / 10)  return INT_MIN;
            if(-res * 10 - x == INT_MIN)   return INT_MIN; // 必须特判。
            res = res * 10 + x;
            k++;
        }
        res *= minus;
        return res;
    }
};

2.9 回文数

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

思路: 1、字符串做法: 将 x 转化为字符串,然后通过反向迭代器**string(s.rbegin(), s.rend())**将字符串翻转,然后比较两个字符串,如果相同则是回文数,否则不是回文数。

class Solution {
public:
    bool isPalindrome(int x) {
        if(x < 0)   return false;
        // to_string()函数将整数转换为字符串
        string s = to_string(x);
        // string()构造函数传入两个迭代器构造一个新的字符串通过反向迭代器调用。
        return s==string(s.rbegin(),s.rend());
    }
};

2、数值做法: 通过类似整数取反的操作, 将x 不断 % 10 取最低位, 再 / 10, 然后 r 不断 * 10 + x % 10 得到翻转之后的整数,然后比较两个数的值相等返回true,否则返回 false;

class Solution {
public:
    bool isPalindrome(int x) {
        if(x < 0)   return false;
        long long r = 0;
        int px = x;
        while(x){
            r = r * 10 + x % 10;
            x /= 10;
        }
        if(r == px) return true;
        else    return false;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值