剑指offer:其他算法

JZ50 第一个只出现一次的字符
简单 通过率:31.77% 时间限制:1秒 空间限制:64M
知识点字符串
描述
在一个长为 字符串中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
数据范围:0≤n≤10000,且字符串只有字母组成。
要求:空间复杂度 O(n),时间复杂度 O(n)
示例1
输入:
“google”
返回值:
4
示例2
输入:
“aa”
返回值:
-1

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        if (str.empty()) {
            return 0;
        }
        unordered_map<char, int> m;
        for (auto ch : str) {
            m[ch]++;
        }
        for (int i = 0; i < str.size(); i++) {
            if (m[str[i]] == 1) {
                return i;
            }
        }
        return -1;
    }
};

JZ5 替换空格
简单 通过率:57.96% 时间限制:1秒 空间限制:256M
知识点字符串
描述
请实现一个函数,将一个字符串s中的每个空格替换成“%20”。
例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

数据范围:0≤len(s)≤1000 。保证字符串中的字符为大写英文字母、小写英文字母和空格中的一种。

示例1
输入:
“We Are Happy”
返回值:
“We%20Are%20Happy”
示例2
输入:
" "
返回值:
“%20”

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param s string字符串 
     * @return string字符串
     */
    string replaceSpace(string s) {
        string res;
        for (int i = 0; i < s.length(); ++i){
            if (s[i] == ' '){
                res.push_back('%');
                res.push_back('2');
                res.push_back('0');
            } else {
                res.push_back(s[i]);
            }
        }
        return res;
    }
};

JZ21 调整数组顺序使奇数位于偶数前面(一)
中等 通过率:53.75% 时间限制:1秒 空间限制:256M
知识点数组
描述
输入一个长度为 n 整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前面部分,所有的偶数位于数组的后面部分,
并保证奇数和奇数,偶数和偶数之间的相对位置不变。

数据范围:0≤n≤5000,数组中每个数的值 0≤val≤10000
要求:时间复杂度 O(n),空间复杂度 O(n)
进阶:时间复杂度 O(n2),空间复杂度 O(1)
示例1
输入:
[1,2,3,4]
返回值:
[1,3,2,4]
示例2
输入:
[2,4,6,5,7]
返回值:
[5,7,2,4,6]
示例3
输入:
[1,3,5,6,7]
返回值:
[1,3,5,7,6]

要求:时间复杂度 O(n),空间复杂度 O(n)
方法:创建一个新的数组

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param array int整型vector 
     * @return int整型vector
     */
    vector<int> reOrderArray(vector<int>& array) {
        vector<int> res(array.size());
        int odd = 0;
        for (int i = 0; i < array.size(); i++){
            if(array[i] % 2 == 1) {
                odd++;
            }
        }
        int left = 0;
        int right = odd;
        for(int i = 0; i < array.size(); i++){
            if(array[i] % 2 == 1){
                res[left] = array[i];
                left++;
            } else {
                res[right] = array[i];
                right++;
            }
        }
        return res;
    }
};

进阶:时间复杂度 O(n2),空间复杂度 O(1)
方法:冒泡法(每次循环将一个奇数放在最终的位置)

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param array int整型vector 
     * @return int整型vector
     */
    vector<int> reOrderArray(vector<int>& array) {
        if (array.empty()) {
            return array; 
        }
        for (int i = 0; i < array.size(); ++i) {
            for (int j = array.size()-1; j > i; --j) {
                if ((array[j-1] % 2 == 0) && (array[j] % 2 == 1)) {
                    // 如果左边是偶数,右边是奇数,就交换
                    swap(array[j-1], array[j]);
                }
            }
        }
        return array;
    }
};

例子:

left = 0

1 2 3 4 5 6

1 2 3 5 4 6

1 3 2 5 4 6

left = 1

1 3 5 2 4 6

left = 2
left = 3
left = 4
left = 5

优化后的冒泡法:

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param array int整型vector 
     * @return int整型vector
     */
    vector<int> reOrderArray(vector<int>& array) {
        if (array.empty()) {
            return array; 
        }
        for (int i = 0; i < array.size(); ++i) {
            bool is = true;
            for (int j = array.size()-1; j > left; --j) {
                if ((array[j-1] % 2 == 0) && (array[j] % 2 == 1)) {
                    // 如果左边是偶数,右边是奇数,就交换
                    swap(array[j-1], array[j]);
                    is = false;
                }
            }
            if (is == true) {
                break;
            }
        }
        return array;
    }
};

例子:

left = 0

1 2 3 4 5 6

1 2 3 5 4 6

1 3 2 5 4 6

left = 1

1 3 5 2 4 6

全部满足条件,break跳出循环

JZ81 调整数组顺序使奇数位于偶数前面(二)
简单 通过率:59.49% 时间限制:1秒 空间限制:256M
知识点数组排序
描述
输入一个长度为 n 整数数组,数组里面可能含有相同的元素,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前面部分,所有的偶数位于数组的后面部分,
对奇数和奇数,偶数和偶数之间的相对位置不做要求,但是时间复杂度和空间复杂度必须如下要求。

数据范围:0≤n≤50000,数组中每个数的值 0≤val≤10000
要求:时间复杂度 O(n),空间复杂度 O(1)
示例1
输入:
[1,2,3,4]
返回值:
[1,3,2,4]

说明:
[3,1,2,4]或者[3,1,4,2]也是正确答案
示例2
输入:
[1,3,5,6,7]
返回值:
[1,3,5,7,6]

说明:
[3,1,5,7,6]等也是正确答案
示例3
输入:
[1,4,4,3]

要求:时间复杂度 O(n),空间复杂度 O(1)
方法:左右交换法

class Solution {
public:
    vector<int> reOrderArrayTwo(vector<int>& array) {
        int i = 0;
        int j = array.size() - 1;
        //目标:把左边的偶数移动到右边,把右边的奇数移动到左边
        while(i < j) {
            if (array[i] % 2 == 1 && array[j] % 2 == 1) {
                //左右都是奇数,左边指针向右边移动
                i++;
            } else if (array[i] % 2 == 0 && array[j] % 2 == 0) {
                //左右都是偶数,右边指针向左边移动
                j--;
            } else if (array[i] % 2 == 1 && array[j] % 2 == 0) {
                //左边是奇数,右边是偶数,左右指针都向中间移动
                i++;
                j--;
            } else if (array[i] % 2 == 0 && array[j] % 2 == 1) {
                //左边是偶数,右边是奇数,交换,左右指针都向中间移动
                swap(array[i], array[j]);
                i++;
                j--;
            }
        }
        return array;
    }
};

JZ74 和为S的连续正数序列
中等 通过率:29.88% 时间限制:1秒 空间限制:64M
知识点穷举
描述
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列?

数据范围:0 进阶:时间复杂度 O(n)
返回值描述:
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
示例1
输入:
9
返回值:
[[2,3,4],[4,5]]
示例2
输入:
0
返回值:
[]

知识点:滑动窗口
滑动窗口是指在数组、字符串、链表等线性结构上的一段,类似一个窗口,而这个窗口可以依次在上述线性结构上从头到尾滑动,且窗口的首尾可以收缩。我们在处理滑动窗口的时候,常用双指针来解决,左指针维护窗口左界,右指针维护窗口右界,二者同方向不同速率移动维持窗口。
思路:
从某一个数字开始的连续序列和等于目标数如果有,只能有一个,于是我们可以用这个性质来使区间滑动。
两个指针l、r指向区间首和区间尾,公式(l+r)∗(r−l+1)/2计算区间内部的序列和,如果这个和刚好等于目标数,说明以该区间首开始的序列找到了,记录下区间内的序列,同时以左边开始的起点就没有序列了,于是左区间收缩;如果区间和大于目标数,说明该区间过长需要收缩,只能收缩左边;如果该区间和小于目标数,说明该区间过短需要扩大,只能向右扩大,移动区间尾。
具体做法:
step 1:从区间[1,2]开始找连续子序列。
step 2:每次用公式计算区间内的和,若是等于目标数,则记录下该区间的所有数字,为一种序列,同时左区间指针向右。
step 3:若是区间内的序列和小于目标数,只能右区间扩展,若是区间内的序列和大于目标数,只能左区间收缩。

class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        vector<vector<int> > res;
        vector<int> temp;
        if (sum < 0) {
            return res;
        }
        int left = 1;
        int right = 2;
        while (left < right) {
            int sum_2 = (left + right) * (right - left + 1) / 2;
            if (sum_2 == sum) {
                temp.clear();
                for (int i = left; i <= right; ++i) {
                    temp.push_back(i);
                }
                res.push_back(temp);
                left++;
            } else if (sum_2 < sum) {
                right++;
            } else {
                left++;
            }
        }
        return res;
    }
};

JZ57 和为S的两个数字
中等 通过率:31.05% 时间限制:1秒 空间限制:64M
知识点数学数组双指针
描述
输入一个升序数组 array 和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,返回任意一组即可,如果无法找出这样的数字,返回一个空数组即可。

数据范围: 0≤len(array)≤105​​ , 1≤array[i]≤106
示例1
输入:
[1,2,4,7,11,15],15
返回值:
[4,11]
说明:
返回[4,11]或者[11,4]都是可以的
示例2
输入:
[1,5,11],10
返回值:
[]
说明:
不存在,返回空数组
示例3
输入:
[1,2,3,4],5
返回值:
[1,4]
说明:
返回[1,4],[4,1],[2,3],[3,2]都是可以的
示例4
输入:
[1,2,2,4],4
返回值:
[2,2]

class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
        vector<int> res;
        int start = 0;
        int end = array.size()-1;
        while (start < end) {
            if (array[start] + array[end] == sum) {
                res.push_back(array[start]);
                res.push_back(array[end]);
                //1 2 3 4 5 6
                // 1 * 6 = 6
                // 2 * 5 = 10
                // 3 * 4 = 12
                //从两边往中间找,第一对和等于S的两个数的乘积最小
                break;
            } else if (array[start] + array[end] < sum) {
                start++;
            } else if (array[start] + array[end] > sum) {
                end--;
            }
        }
        return res;
    }
};

BM91 反转字符串
题目
题解(192)
讨论(305)
排行
面经new
入门 通过率:66.32% 时间限制:1秒 空间限制:256M
知识点
双指针
字符串
描述
写出一个程序,接受一个字符串,然后输出该字符串反转后的字符串。(字符串长度不超过1000)

数据范围: 0 \le n \le 10000≤n≤1000
要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)
示例1
输入:
“abcd”
复制
返回值:
“dcba”
复制
示例2
输入:
“”
复制
返回值:
“”
复制

class Solution {
public:
    /**
     * 反转字符串
     * @param str string字符串 
     * @return string字符串
     */
    string solve(string str) {
        int left = 0;
        int right = str.length()-1;
        while (left < right) {
            swap(str[left], str[right]);
            left++;
            right--;
        }
        return str;
    }
};

BM89 合并区间
题目
题解(146)
讨论(252)
排行
面经new
中等 通过率:30.35% 时间限制:2秒 空间限制:256M
知识点
排序
数组
描述
给出一组区间,请合并所有重叠的区间。
请保证合并后的区间按区间起点升序排列。

数据范围:区间组数 0 \le n \le 2 \times 10^50≤n≤2×10
5
,区间内 的值都满足 0 \le val \le 2 \times 10^50≤val≤2×10
5

要求:空间复杂度 O(n)O(n),时间复杂度 O(nlogn)O(nlogn)
进阶:空间复杂度 O(val)O(val),时间复杂度O(val)O(val)
示例1
输入:
[[10,30],[20,60],[80,100],[150,180]]
复制
返回值:
[[10,60],[80,100],[150,180]]
复制
示例2
输入:
[[0,10],[10,20]]
复制
返回值:
[[0,20]]
复制

/**
 * Definition for an interval.
 * struct Interval {
 *     int start;
 *     int end;
 *     Interval() : start(0), end(0) {}
 *     Interval(int s, int e) : start(s), end(e) {}
 * };
 */
class Solution {
public:
    static bool cmp(Interval &x, Interval &y) {
        return x.start < y.start;
    }
    vector<Interval> merge(vector<Interval> &intervals) {
        vector<Interval> res;
        if (intervals.empty()) {
            return res;
        }
        sort(intervals.begin(), intervals.end(), cmp);
        res.push_back(intervals[0]);
        for (int i = 1; i < intervals.size(); ++i) {
            if (intervals[i].start <= res.back().end) {
                res.back().end = max(intervals[i].end, res.back().end);
            } else {
                res.push_back(intervals[i]);
            }
        }
        return res;
    }
};

BM88 判断是否为回文字符串
题目
题解(157)
讨论(182)
排行
面经new
入门 通过率:61.61% 时间限制:1秒 空间限制:256M
知识点
字符串
描述
给定一个长度为 n 的字符串,请编写一个函数判断该字符串是否回文。如果是回文请返回true,否则返回false。

字符串回文指该字符串正序与其逆序逐字符一致。

数据范围:0 < n \le 10000000 要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
示例1
输入:
“absba”
复制
返回值:
true
复制
示例2
输入:
“ranko”
复制
返回值:
false
复制
示例3
输入:
“yamatomaya”
复制
返回值:
false
复制
示例4
输入:
“a”
复制
返回值:
true
复制
备注:
字符串长度不大于1000000,且仅由小写字母组成

class Solution {
public:
    bool judge(string str) {
        //首指针
        int left = 0; 
        //尾指针
        int right = str.length() - 1;  
        //首尾往中间靠
        while(left < right){
            //比较前后是否相同 
            if(str[left] != str[right]) {
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
};

BM87 合并两个有序的数组
题目
题解(264)
讨论(437)
排行
面经new
简单 通过率:36.95% 时间限制:1秒 空间限制:256M
知识点
数组
双指针
描述
给出一个有序的整数数组 A 和有序的整数数组 B ,请将数组 B 合并到数组 A 中,变成一个有序的升序数组

数据范围: 0 \le n,m \le 1000≤n,m≤100,|A_i| <=100∣A
i

∣<=100, |B_i| <= 100∣B
i

∣<=100

注意:
1.保证 A 数组有足够的空间存放 B 数组的元素, A 和 B 中初始的元素数目分别为 m 和 n,A的数组空间大小为 m+n
2.不要返回合并的数组,将数组 B 的数据合并到 A 里面就好了,且后台会自动将合并后的数组 A 的内容打印出来,所以也不需要自己打印
3. A 数组在[0,m-1]的范围也是有序的
示例1
输入:
[4,5,6],[1,2,3]
复制
返回值:
[1,2,3,4,5,6]
复制
说明:
A数组为[4,5,6],B数组为[1,2,3],后台程序会预先将A扩容为[4,5,6,0,0,0],B还是为[1,2,3],m=3,n=3,传入到函数merge里面,然后请同学完成merge函数,将B的数据合并A里面,最后后台程序输出A数组
示例2
输入:
[1,2,3],[2,5,6]
复制
返回值:
[1,2,2,3,5,6]
复制
关联企业

class Solution {
public:
    void merge(int A[], int m, int B[], int n) {
        int i = m - 1; 
        int j = n - 1; 
        int k = m + n - 1; 
        //从两个数组最大的元素开始,直到某一个数组遍历完
        while(i >= 0 && j >= 0){ 
            //将较大的元素放到最后
            if(A[i] > B[j]) {
                A[k--] = A[i--];
            } else {
                A[k--] = B[j--];
            }
        }
        //数组A遍历完了,数组B还有,则还需要添加到数组A前面
        if(i < 0){ 
            while(j >= 0) {
                A[k--] = B[j--];
            }
        } 
        //数组B遍历完了,数组A前面正好有,不用再添加
    }
};

JZ67 把字符串转换成整数(atoi)
题目
题解(48)
讨论(58)
排行
面经new
中等 通过率:18.15% 时间限制:1秒 空间限制:256M
知识点
字符串
描述
写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。传入的字符串可能有以下部分组成:
1.若干空格
2.(可选)一个符号字符(‘+’ 或 ‘-’)
3. 数字,字母,符号,空格组成的字符串表达式
4. 若干空格

转换算法如下:
1.去掉无用的前导空格
2.第一个非空字符为+或者-号时,作为该整数的正负号,如果没有符号,默认为正数
3.判断整数的有效部分:
3.1 确定符号位之后,与之后面尽可能多的连续数字组合起来成为有效整数数字,如果没有有效的整数部分,那么直接返回0
3.2 将字符串前面的整数部分取出,后面可能会存在存在多余的字符(字母,符号,空格等),这些字符可以被忽略,它们对于函数不应该造成影响
3.3 整数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231的整数应该被调整为 −231 ,大于 231 − 1 的整数应该被调整为 231 − 1
4.去掉无用的后导空格

数据范围:
1.0 <=字符串长度<= 100
2.字符串由英文字母(大写和小写)、数字(0-9)、’ ‘、’+‘、’-’ 和 ‘.’ 组成

示例1
输入:
“82”
复制
返回值:
82
复制
示例2
输入:
" -12 "
复制
返回值:
-12
复制
说明:
去掉前后的空格,为-12
示例3
输入:
“4396 clearlove”
复制
返回值:
4396
复制
说明:
6后面的字符不属于有效的整数部分,去除,但是返回前面提取的有效部分
示例4
输入:
“clearlove 4396”
复制
返回值:
0
复制
示例5
输入:
“-987654321111”
复制
返回值:
-2147483648
复制

第一阶段:
44/45 组用例通过
运行时间
4ms
占用内存
452KB

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param s string字符串 
     * @return int整型
     */
    int StrToInt(string s) {
        int res = 0;
        int index = 0;
        int  len = s.length();
        while (index < len) {
            if (s[index] == ' ') {
                index++;
            } else {
                break;
            }
        }
        if (index == len) {
            return 0;
        }
        int sign = 1;
        if (s[index] == '+') {
            index++;
        } else if (s[index] == '-') {
            index++;
            sign = -1;
        }
        if (index == len) {
            return 0;
        }
        while (index < len) {
            if (s[index] < '0' || s[index] > '9') {
                break;
            }

            if (res > INT_MAX / 10) {
                return INT_MAX;
            }
            if (res < INT_MIN / 10) {
                return INT_MIN;
            }

            res = res * 10 + sign * (s[index] - '0');
            index++;
        }
        return res;

    }
};

第二阶段:
全部case通过

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param s string字符串 
     * @return int整型
     */
    int StrToInt(string s) {
        int res = 0;
        int index = 0;
        int  len = s.length();
        while (index < len) {
            if (s[index] == ' ') {
                index++;
            } else {
                break;
            }
        }
        if (index == len) {
            return 0;
        }
        int sign = 1;
        if (s[index] == '+') {
            index++;
        } else if (s[index] == '-') {
            index++;
            sign = -1;
        }
        if (index == len) {
            return 0;
        }
        while (index < len) {
            if (s[index] < '0' || s[index] > '9') {
                break;
            }

            if (res > INT_MAX / 10 || (res == INT_MAX / 10 && (s[index] - '0') > INT_MAX % 10)) {
                return INT_MAX;
            }
            if (res < INT_MIN / 10 || (res == INT_MIN / 10 && (s[index] - '0') > -(INT_MIN % 10))) {
                return INT_MIN;
            }

            res = res * 10 + sign * (s[index] - '0');
            index++;
        }
        return res;

    }
};

JZ17 打印从1到最大的n位数
题目
题解(72)
讨论(74)
排行
面经new
简单 通过率:51.33% 时间限制:1秒 空间限制:256M
知识点
数组
描述
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

  1. 用返回一个整数列表来代替打印
  2. n 为正整数,0 < n <= 5
    示例1
    输入:
    1
    复制
    返回值:
    [1,2,3,4,5,6,7,8,9]
    复制
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param n int整型 最大位数
     * @return int整型vector
     */
    vector<int> printNumbers(int n) {
        vector<int> res;
        if (n < 0 || n > 5) {
            return res;
        }
        int num = 9;
        int sum = 0;
        while (n > 0) {
            sum = sum * 10 + num;
            n--;
        }
        for (int i = 1; i <= sum; ++i) {
            res.push_back(i);
        }
        return res;
    }
};

BM94 接雨水问题
较难 通过率:41.12% 时间限制:1秒 空间限制:256M
知识点双指针单调栈动态规划
描述
给定一个整形数组arr,已知其中所有的值都是非负的,将这个数组看作一个柱子高度图,计算按此排列的柱子,下雨之后能接多少雨水。(数组以外的区域高度视为0)

数据范围:数组长度 0≤n≤2×105,数组中每个值满足 0 要求:时间复杂度 O(n)
示例1
输入:
[3,1,2,5,2,4]
返回值:
5
说明:
数组 [3,1,2,5,2,4] 表示柱子高度图,在这种情况下,可以接 5个单位的雨水,蓝色的为雨水 ,如题面图。
示例2
输入:
[4,5,1,3,2]
返回值:
2

class Solution {
public:
    /**
     * max water
     * @param arr int整型vector the array
     * @return long长整型
     */
    long long maxWater(vector<int>& arr) {
        if (arr.empty()) {
            return 0;
        }
        long long res = 0;
        int left = 0;
        int right = arr.size()-1;
        int left_max = 0;
        int right_max = 0;
        while (left < right) {
            left_max = max(left_max, arr[left]);
            right_max = max(right_max, arr[right]);
            if (left_max < right_max) {
                res += left_max - arr[left];
                left++;
            } else {
                res += right_max - arr[right];
                right--;
            }
        }
        return res;
    }
};

BM93 盛水最多的容器
中等 通过率:47.36% 时间限制:1秒 空间限制:256M
知识点双指针数组
描述
给定一个数组height,长度为n,每个数代表坐标轴中的一个点的高度,height[i]是在第i点的高度,请问,从中选2个高度与x轴组成的容器最多能容纳多少水
1.你不能倾斜容器
2.当n小于2时,视为不能形成容器,请返回0
3.数据保证能容纳最多的水不会超过整形范围,即不会超过231-1

数据范围:
0<=height.length<=105
0<=height[i]<=104

如输入的height为[1,7,3,2,4,5,8,2,7],那么如下图:

示例1
输入:
[1,7,3,2,4,5,8,2,7]
返回值:
49

示例2
输入:
[2,2]
返回值:
2

示例3
输入:
[5,4,3,2,1,5]
返回值:
25

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param height int整型vector 
     * @return int整型
     */
    int maxArea(vector<int>& height) {
        if (height.size() < 2) {
            return 0;
        }
        int res = 0;
        int left = 0;
        int right = height.size()-1;
        while (left < right) {
            int capacity = min(height[left], height[right]) * (right - left);
            res = max(res, capacity);
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }
        return res;
    }
};

BM86 大数加法
中等 通过率:41.49% 时间限制:1秒 空间限制:256M
知识点字符串模拟
描述
以字符串的形式读入两个数字,编写一个函数计算它们的和,以字符串形式返回。

数据范围:s.length,t.length≤100000,字符串仅由’0’~‘9’构成
要求:时间复杂度 O(n)
示例1
输入:
“1”,“99”
返回值:
“100”
说明:
1+99=100
示例2
输入:
“114514”,“”
返回值:
“114514”

s字符串
数值:1 2 3 4 5 6
下标:0 1 2 3 4 5
i=3

t字符串
数值: 6 7 8 9
下标: 0 1 2 3
j=1

两个字符串的总长度不同,但是右边的已经相加的元素的长度相同。
i 和 j的关系公式:
s.length() - i = t.length() - j

根据这个公式,可以通过i请出j
j = i - s.length() + t.length()

class Solution {
public:
    string solve(string s, string t) {
        if(s.empty()) {
            return t;
        }
        if(t.empty()) {
            return s;
        }

        //让s为较长的字符串,t为较短的字符串
        if(s.length() < t.length()) {
            swap(s, t);
        }
        //进位标志
        int carry = 0;

        //从后往前遍历较长的字符串
        for(int i = s.length() - 1; i >= 0; i--){ 
            //第一步:加上s字符串中的数字
            int temp = s[i] - '0'; 
            

            //第二步:加上t字符串中的数字
            //转较短的字符串相应的从后往前的下标
            int j = i - s.length() + t.length();
            //如果较短字符串没有结束
            if(j >= 0) {
                temp += t[j] - '0';
            }

            //第三步:加上进位
            temp += carry;

            //取进位
            carry = temp / 10; 
            //取个位
            temp = temp % 10;

            //修改结果
            s[i] = temp + '0'; 
        }

        //最后的进位
        if(carry == 1) {
            s = '1' + s;
        }
        return s;
    }
};

BM85 验证IP地址
中等 通过率:23.09% 时间限制:1秒 空间限制:256M
知识点字符串
描述
编写一个函数来验证输入的字符串是否是有效的 IPv4 或 IPv6 地址

IPv4 地址由十进制数和点来表示,每个地址包含4个十进制数,其范围为 0 - 255, 用(“.”)分割。比如,172.16.254.1;
同时,IPv4 地址内的数不会以 0 开头。比如,地址 172.16.254.01 是不合法的。

IPv6 地址由8组16进制的数字来表示,每组表示 16 比特。这些组数字通过 (“:”)分割。比如, 2001:0db8:85a3:0000:0000:8a2e:0370:7334 是一个有效的地址。而且,我们可以加入一些以 0 开头的数字,字母可以使用大写,也可以是小写。所以, 2001:db8:85a3:0:0:8A2E:0370:7334 也是一个有效的 IPv6 address地址 (即,忽略 0 开头,忽略大小写)。

然而,我们不能因为某个组的值为 0,而使用一个空的组,以至于出现 (: 的情况。 比如, 2001:0db8:85a3::8A2E:0370:7334 是无效的 IPv6 地址。
同时,在 IPv6 地址中,多余的 0 也是不被允许的。比如, 02001:0db8:85a3:0000:0000:8a2e:0370:7334 是无效的。

说明: 你可以认为给定的字符串里没有空格或者其他特殊字符。

数据范围:字符串长度满足 5≤n≤50
进阶:空间复杂度 O(n),时间复杂度 O(n)
示例1
输入:
“172.16.254.1”
返回值:
“IPv4”

说明:
这是一个有效的 IPv4 地址, 所以返回 “IPv4”
示例2
输入:
“2001:0db8:85a3:0:0:8A2E:0370:7334”
返回值:
“IPv6”

说明:
这是一个有效的 IPv6 地址, 所以返回 “IPv6”
示例3
输入:
“256.256.256.256”
返回值:
“Neither”

说明:
这个地址既不是 IPv4 也不是 IPv6 地址
备注:
ip地址的类型,可能为
IPv4, IPv6, Neither

//必须加正则表达式的库
#include   
class Solution {
public:
    string solve(string IP) {
        //正则表达式限制0-255 且没有前缀0 四组齐全
        regex ipv4("(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])");
        //正则表达式限制出现8组,0-9a-fA-F的数,个数必须是1-4个
        regex ipv6("([0-9a-fA-F]{1,4}\\:){7}[0-9a-fA-F]{1,4}");
        //调用正则匹配函数
        if (regex_match(IP, ipv4)) {
            return "IPv4";
        } else if (regex_match(IP, ipv6)) {
            return "IPv6";
        } else {
            return "Neither";
        }
    }
};

BM100 设计LRU缓存结构
题目
题解(50)
讨论(64)
排行
面经new
较难 通过率:48.18% 时间限制:2秒 空间限制:256M
知识点
链表
哈希
模拟
描述
设计LRU(最近最少使用)缓存结构,该结构在构造时确定大小,假设大小为 capacity ,操作次数是 n ,并有如下功能:

  1. Solution(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
  2. get(key):如果关键字 key 存在于缓存中,则返回key对应的value值,否则返回 -1 。
  3. set(key, value):将记录(key, value)插入该结构,如果关键字 key 已经存在,则变更其数据值 value,如果不存在,则向缓存中插入该组 key-value ,如果key-value的数量超过capacity,弹出最久未使用的key-value

提示:
1.某个key的set或get操作一旦发生,则认为这个key的记录成了最常使用的,然后都会刷新缓存。
2.当缓存的大小超过capacity时,移除最不经常使用的记录。
3.返回的value都以字符串形式表达,如果是set,则会输出"null"来表示(不需要用户返回,系统会自动输出),方便观察
4.函数set和get必须以O(1)的方式运行
5.为了方便区分缓存里key与value,下面说明的缓存里key用""号包裹
数据范围:
1\leq capacity<=10^51≤capacity<=10
5

0\leq key,val \leq 2\times 10^9 \0≤key,val≤2×10
9

1\leq n\leq 10^51≤n≤10
5

示例1
输入:
[“set”,“set”,“get”,“set”,“get”,“set”,“get”,“get”,“get”],[[1,1],[2,2],[1],[3,3],[2],[4,4],[1],[3],[4]],2
复制
返回值:
[“null”,“null”,“1”,“null”,“-1”,“null”,“-1”,“3”,“4”]
复制
说明:
我们将缓存看成一个队列,最后一个参数为2代表capacity,所以
Solution s = new Solution(2);
s.set(1,1); //将(1,1)插入缓存,缓存是{“1”=1},set操作返回"null"
s.set(2,2); //将(2,2)插入缓存,缓存是{“2”=2,“1”=1},set操作返回"null"
output=s.get(1);// 因为get(1)操作,缓存更新,缓存是{“1”=1,“2”=2},get操作返回"1"
s.set(3,3); //将(3,3)插入缓存,缓存容量是2,故去掉某尾的key-value,缓存是{“3”=3,“1”=1},set操作返回"null"
output=s.get(2);// 因为get(2)操作,不存在对应的key,故get操作返回"-1"
s.set(4,4); //将(4,4)插入缓存,缓存容量是2,故去掉某尾的key-value,缓存是{“4”=4,“3”=3},set操作返回"null"
output=s.get(1);// 因为get(1)操作,不存在对应的key,故get操作返回"-1"
output=s.get(3);//因为get(3)操作,缓存更新,缓存是{“3”=3,“4”=4},get操作返回"3"
output=s.get(4);//因为get(4)操作,缓存更新,缓存是{“4”=4,“3”=3},get操作返回"4"

struct Node {
    int _key;
    int _value;
    Node(int key, int value) : _key(key), _value(value) {}
};
class Solution {
public:
    Solution(int capacity){
        _capacity = capacity;
    }
    
    int get(int key) {
        if (_cache_map.find(key) != _cache_map.end()) {
            // hit cache
            _cache_list.splice(_cache_list.begin(), _cache_list, _cache_map[key]);
            _cache_map[key] = _cache_list.begin();
            return _cache_list.front()._value;
        } else {
            // not hit cache
            return -1;
        }
    }
    
    void set(int key, int value){
        if (_cache_map.find(key) != _cache_map.end()) {
            // hit cache
            _cache_list.splice(_cache_list.begin(), _cache_list, _cache_map[key]);
            _cache_list.front()._value = value;
            _cache_map[key] = _cache_list.begin();
        } else {
            // not hit cache
            if (_cache_list.size() >= _capacity) {
                _cache_map.erase(_cache_list.back()._key);
                _cache_list.pop_back();
            }
            Node n(key, value);
            _cache_list.push_front(n);
            _cache_map[key] = _cache_list.begin();
        }
    }
private:
    int _capacity;
    list<Node> _cache_list;
    unordered_map<int, list<Node>::iterator> _cache_map;
};

/**
 * Your Solution object will be instantiated and called as such:
 * Solution* solution = new Solution(capacity);
 * int output = solution->get(key);
 * solution->set(key,value);
 */

你可能感兴趣的:(数据结构和算法,算法,数据结构)