算法编程题面试高频

string 相加 & 链表相加

Leetcode字符串相加

    // 计算 "19" + "2" 得到 "21"
    string addStrings(string num1, string num2) {
        int n1 = num1.size()-1, n2 = num2.size()-1;
        string ans;
        int carry = 0; //进位
        while(n1 >= 0 || n2 >= 0 || carry > 0) {
            int v1 = 0, v2 = 0;
            if (n1 >= 0) v1 = num1[n1--] - '0';
            if (n2 >= 0) v2 = num2[n2--] - '0';
            int sum = v1 + v2 + carry;
            char tmp = sum%10 + '0';
            ans = tmp + ans;   // 注意!!! 不是ans + tmp 而是 tmp + ans
            carry = sum/10;
        }
        return ans;
    }

Leetcode链表相加
输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295
输出:2 -> 1 -> 9,即912

    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode *p1 = l1, *p2 = l2;
        ListNode *phead = new ListNode(-1);
        ListNode *ans = phead;
        int carry = 0;
        while(p1 || p2 || carry > 0) {
            int v1 = 0, v2 = 0;
            if (p1) {
                v1 = p1->val;
                p1 = p1->next;
            } 
            if (p2) {
                v2 = p2->val;
                p2 = p2->next;
            }
            int sum = v1 + v2 + carry;
            ListNode *tmp = new ListNode(sum%10);
            ans->next = tmp;
            ans = ans->next;
            carry = sum/10;
        }
        return phead->next;
    }
两数之和,三数之和… N个数之和
  • Leetcode题目 : 不使用运算符 + 和 - ​​​​​​​,计算两整数 ​​​​​​​a 、b ​​​​​​​之和。
int getSum(int a, int b) {
        // a^b 异或,可以实现相加但不进位
        // (a&b)<<1可以实现进位但不想加,
        // 以上两者相加即可
        return b==0? a : getSum(a^b, ((unsigned int)(a&b))<<1);
    }
  • Leetcode 返回两数的下标
    给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
 vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> ret;
        int n = nums.size();
        if (n < 2) return ret;
        std::unordered_map<int,int> hashm; 
        for (int i = 0; i < n; ++i) {
            // hashm[target - nums[i]]不等于0说明 target-nums[i]这个值 在之前的遍历已经出现
            // hashm[target - nums[i]]不等于 i+1 说明 不是当前正在遍历的数字,这是为了避免重复
            if (hashm[target - nums[i]]  // 如果找不到就默认是0,(初始化0)
                && hashm[target - nums[i]] != i+1) {
                    ret.push_back(i);
                    ret.push_back(hashm[target - nums[i]]-1);
                    return ret;
            } else {
                hashm[nums[i]] = i+1;  // 注意
            }
            
        }
        return ret; 
    }
  • Leetcode 四数之和
    给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
    以下解法适用求 任意个数之和
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end()); // 先排成升序数组
        return nSumTarget(nums, 4, 0, target);
    }
    // 以下是计算 n个数之和的函数
    // nSumTarget函数,计算数组nums 中是否有 n 个数的和为target, 把所有满足条件的n个数返回
    //nums 目标数组,必须是升序数组
    // start:从数组下标为start处开始只考虑后面的元素;
    // target:n个数的目标和sum
    vector<vector<int>> nSumTarget(vector<int>& nums, int n, int start, int target) {
        int sz = nums.size();
        vector<vector<int>> res;
        if(n<2 || n>sz) return res;
        if(n==2) {
            res = twoSum(nums, start, target);
        } else {
            for(int i = start; i < sz; ++i){
                int firstnum = nums[i];
                vector<vector<int>> sub = nSumTarget(nums, n-1, i+1, target-firstnum);
                for(auto &arr : sub) {
                    arr.push_back(firstnum);
                    res.push_back(arr);
                }
                while(i < sz-1 && nums[i+1] == nums[i]) i++; // 避免重复
            }
        }
        return res;
    }

    // 以下是计算两数之和的函数, 注意数组nums 必须是已经排好序的升序数组,否则出错
    // nums 目标数组; start:从数组下标为start处开始只考虑后面的元素; target:两数的目标和sum
    vector<vector<int>> twoSum(vector<int>& nums, int start, int target) {
        int l = start, r = nums.size()-1; // 只考虑 start 到 nums.size()-1这一段
        vector<vector<int>> ans;
        while(l<r){            // 双指针经典循环,左指针l从左往右移动,右指针r从右往左移动
            int left = nums[l];
            int right = nums[r];
            int sum = left + right;  // 当前 的 sum 和
            if(sum < target) {                  //如果和比目标值小,那么 l 右移
                while(l<r && nums[l] == left) l++;  // 避免重复
            } else if (sum > target) {          //如果和比目标值小,那么 r 右移
                while(l<r && nums[r] == right) r--;  // 避免重复
            } else {                         // 如果 和 等于目标值
                ans.push_back({left, right});  // 那么 先计入结果数组中,
                while(l<r && nums[l]==left) l++;  // 然后让左指针l右移,右指针r左移,
                while(l<r && nums[r]==right) r--; // 并且一定要注意用while 来避免重复
            }
        }
        return ans;
    }
};
LRU缓存机制

Leetcode - LRU

  • 以下是使用哈希表和 STL 里的 list 链表容器实现
class Solution {
public:
    /**
     * lru design
     * @param operators int整型vector<vector<>> the ops
     * @param k int整型 the k
     * @return int整型vector
     */
    vector<int> LRU(vector<vector<int> >& operators, int k) {
        // write code here
        cap = k;
        vector<int> ans;
        for (auto &opt : operators) {
            if (opt[0] == 1) put(opt.at(1), opt.at(2));
            else if (opt[0] == 2) ans.push_back(get(opt.at(1)));
        }
        return ans;
    }
    
private:
    int cap;
    list<pair<int, int>> kv;  // 记录数据的链表
    // 用哈希表记录 key值和对应的链表迭代器
    unordered_map<int, list<pair<int, int>>::iterator> ump;
    // put新数据进去,如果key已经存在,则更新,并将最新的放在链表头部
    void put(int key, int value) {
        auto itr = ump.find(key);
         //如果已经存在,就先删除他,这样就可以和不存在做相同的处理了
        if( itr != ump.end()) {
            kv.erase(itr->second);
            ump.erase(key);
        }
        pair<int, int> p{key, value};
        // 新数据放在链表头部
        kv.push_front(p);
        // 数据要记录进哈希表
        ump.emplace(key, kv.begin());
        // 如果 size 大于规定的cap,就删除链表尾部元素
        while (kv.size()>cap) {
            ump.erase(kv.back().first);//注意 map删除元素方法,erase(key)
            kv.pop_back();
        }
    }
    // 获取已经存入的数据,没有就返回-1,
    // 如果有就返回value,并且把对应的数据放到链表头部
    int get(int key) {
        if(ump.count(key) == 0) {
            return -1; //如果没有就返回-1
        }
        auto iter = ump[key];
        //将kv(可以是别的list)的iter迭代器指定的元素移到kv.begin()前面
        kv.splice(kv.begin(), kv, iter);
        return kv.begin()->second;
    }
    
};
  • 以下是使用哈希表和 自己实现的 双向 链表实现
struct dListNode {
    int key, value;
    dListNode *prev;
    dListNode *next;
    dListNode(): key(0), value(0), prev(NULL), next(NULL) {}
    dListNode(int k, int v) : key(k), value(v), prev(NULL), next(NULL) {}
};

class LRUCache {
// 数据结构要求满足以下条件:
// get 要求要快速访问,unordered_map 哈希表 可以满足这个条件
// 当容量达到上限时,最久未使用(使用包括get,put增加新值或者修改旧值)的数据要删去,
// 这要求数据结构要满足快速删除,快速插入(快速转移位置),这就只能用 双向链表了。
// 双向链表越靠近tail,就表示越久没有使用,刚刚使用的一律remove然后add到head后面。
public:
    unordered_map<int, dListNode *> key_node;
    dListNode *head;
    dListNode *tail;
    int size;
    int capacity;
    LRUCache(int capacity) : size(0), capacity(capacity) {
        head = new dListNode();
        tail = new dListNode();
        head->next = tail;
        tail->prev = head;
    }
    
    int get(int key) {
        if(key_node.count(key)==0) return -1;
        removeNode(key_node[key]);
        addNodeToHead(key_node[key]);
        return key_node[key]->value;
    }
    
    void put(int key, int value) {
        if(key_node.count(key)!=0) {
            removeNode(key_node[key]);
            key_node[key]->value = value;
            addNodeToHead(key_node[key]);
            
        } else {
            while (size >= capacity) {
                key_node.erase(tail->prev->key);
                removeNode(tail->prev);
                size--;
            }
            dListNode *node = new dListNode(key, value);
            addNodeToHead(node);
            key_node[key] = node;
            size++;
        }
    }

    void removeNode(dListNode *node) {
        node->next->prev = node->prev;
        node->prev->next = node->next;
    }
    void addNodeToHead(dListNode *node) {
        node->prev = head;
        node->next = head->next;
        head->next->prev = node;
        head->next = node;
    }
    void addNodeToTail(dListNode *node) {
        node->prev = tail->prev;
        node->next = tail;
        tail->prev->next = node;
        tail->prev = node;
    }

};
寻找两个正序数组的中位数

Leetcode题: 给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数。

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) 
    {
        int m = nums1.size();
        int n = nums2.size();
        //中位数 = (left + right)/2
        int left = (m + n + 1) / 2;
        int right = (m + n + 2) / 2;
        return (findKth(nums1, 0, nums2, 0, left) + findKth(nums1, 0, nums2, 0, right)) / 2.0;
    }
    //在两个有序数组中找到第k个元素(例如找第一个元素,k=1,即nums[0])
    //i: nums1的起始位置 j: nums2的起始位置(i,j都是从0开始)
    int findKth(vector<int>& nums1, int i, vector<int>& nums2, int j, int k)
    {
        //若nums1为空(或是说其中数字全被淘汰了)
        //在nums2中找第k个元素,此时nums2起始位置是j,所以是j+k-1
        if(i >= nums1.size())    return nums2[j + k - 1];
        //nums2同理
        if(j >= nums2.size())    return nums1[i + k - 1];

        //递归出口
        if(k == 1)  return std::min(nums1[i], nums2[j]);

        //这两个数组的第K/2小的数字,若不足k/2个数字则赋值整型最大值,以便淘汰另一数组的前k/2个数字
        int midVal1 = (i + k/2 - 1 < nums1.size()) ? nums1[i + k/2 - 1] : INT_MAX;
        int midVal2 = (j + k/2 - 1 < nums2.size()) ? nums2[j + k/2 - 1] : INT_MAX;
        //二分法核心部分
        if(midVal1 < midVal2)
            return findKth(nums1, i + k/2, nums2, j, k - k/2);
        else
            return findKth(nums1, i, nums2, j + k/2, k - k/2);
    }
};
最长回文子串

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

    string longestPalindrome(string s) {
        int n = s.size();
        if(n<2) return s;
        vector<vector<bool>> dp(n, vector<bool>(n));
        for(int i = 0; i < n; ++i) dp[i][i] = true;
        //状态转移方程,dp[i][j]表示以i开始,以j结尾的子字符串是否为回文子串
        // dp[i][j]为true的条件有(1)s[i]==s[j];(2) dp[i+1][j-1]为true;
        // 临界条件,dp[i][i]为true, 且 j-i<3 时,不需要考虑以上的条件(2)
        int start = 0, len = 1;
        // 因为 dp[i][j]依赖于 dp[i+1][j-1],即依赖于其左下角的值,则循环方向如下。
        // 从第0列往最后一列遍历,每列从对角线位置处往上遍历
        for(int j = 0; j < n; ++j) { 
            for(int i = j+1; i >= 0; --i) {
                if (j-i<3) {
                    if (s[i]==s[j] ) {
                        dp[i][j] = true;
                        if(j-i+1 > len) {
                            start = i;
                            len = j-i+1;
                        }
                    }
                } else {
                    if(s[i]==s[j] && dp[i+1][j-1]) {
                        dp[i][j] = true;
                        if(j-i+1 > len) {
                            start = i;
                            len = j-i+1;
                        }
                    }
                }
            }
        }
        return s.substr(start, len);
    }
滑动窗口:最小覆盖子串,无重复字符的最长子串
  • Leetcode最小覆盖子串 : 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
string minWindow(string s, string t) {
        unordered_map<char, int> window, need;
        for(char &c : t) need[c]++; // 把目标字符串放到哈希表
        int l = 0, r = 0; // 滑动窗口的左边界和右边界
        int valid = 0; // 满足要求的字符
        int start = 0, len = INT_MAX; // 符合要求的字符串的起点和长度
        // 窗口滑动
        // 窗口扩大循环,右扩
        while(r < s.size()) {
            char c = s[r];   // 即将处理的字符
            r++;  // 窗口右边界右移
            if (need.count(c)) {  // 如果 c 字符在目标字符串t 中出现
                window[c]++; // window 记录窗口中目标字符串含有的字符,计数
                if(window[c] == need[c])  // 如果对于字符c,窗口中含有的数目与 t中含有的数目相同,表示 满足数目要求的字符 增加了一个。
                    valid ++;
            }
            // 窗口先向右扩大,直到满足目标要求,然后窗口左边界右移以缩小窗口。
            // 窗口缩小循环
            while (valid == need.size()) {
                if(r-l < len) { // 前面r++了,所以这里计算长度不需要加1了。
                    start = l;  // 更新符合要求的字符串的起点和长度
                    len = r - l;
                }
                char d = s[l]; //即将移出窗口的字符
                l++; //窗口左边界右移
                if(need.count(d)) { // 如果即将移出窗口的字符d 含于t 中
                    if(window[d]==need[d]) 
                        //如果字符d,移出前,窗口的含有数目与t中数目相同,则移出后,符合数目要求的字符就要减 1 。窗口缩小循环就将会跳出,窗口右边界再开始右移。
                        valid--;
                    window[d]--;
                }
            }
        }
        return len == INT_MAX ? "" : s.substr(start, len);
    }
 int lengthOfLongestSubstring(string s) {
        unordered_map<char, int> window;
        int l = 0, r = 0;
        int len = 0;
        while(r<s.size()) {
            char c = s[r];
            r++;
            window[c]++;
            while(window[c]>1) { // 当不符合要求时开始让窗口左边界右移
                window[s[l]]--;
                l++;
            }
            len = max(len, r-l);
        }
        return len;
    }
正则表达式

Leetcode题目
在这里插入图片描述

 bool isMatch(string s, string p) {
        if(p.empty()) return s.empty();  // 如果p为空, 那么 s 如果不空则不匹配
        int ns = s.size(), np = p.size();
        bool first_match = !s.empty() && (s[0]==p[0] || p[0]=='.'); // 第一个字符是否相同
        if (p.length()>=2 && p[1]=='*')
        //  // 如果 p有第2个字符且为 * 
        // (a) * 匹配了 0 个p[0]
        //  (b) * 匹配了1个p[0],如果s="abcd",p="a*" ,而 * 不是匹配0 个 p[0]那这个条件肯定是错的
        //                      如果 p=".*","*"可以匹配多个任意字符".",这里消耗一个
            return isMatch(s, p.substr(2, np-2)) ||   // 表示 * 匹配了 0 个p[0]
                (first_match && isMatch(s.substr(1,ns-1), p)); // 表示 * 匹配了 1个 p[0]。
        else
            return first_match && isMatch(s.substr(1,ns-1), p.substr(1, np-1)); // 没有*
    }
螺旋矩阵

Leetcode题目: 给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。

vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if(!matrix.size()) return vector<int>();
        vector<int> ans;
        int up = 0;  
        int down = matrix.size() - 1;
        int left = 0;
        int right = matrix.at(0).size() - 1;
        // 遍历一行删去一行,遍历一列就删去一列,通过控制以上 up, down, left, right 的值来操作删去行列。如下,简单,但容易出错。
        while(true) {
            for(int i = left; i <= right; ++i) ans.push_back(matrix[up][i]);
            if(++up > down) break; // 易错,要检查
            for(int i = up; i <= down; ++i) ans.push_back(matrix[i][right]);
            if(--right < left) break;  // 易错,要检查
            for(int i = right; i >= left; --i) ans.push_back(matrix[down][i]);
            if(--down < up) break;  // 易错,要检查
            for(int i = down; i >= up; --i) ans.push_back(matrix[i][left]);
            if(++left > right) break; // 易错,要检查
        }
        return ans;
    }
分数转小数

Leetcode题目:给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以 字符串形式返回小数 。如果小数部分为循环小数,则将循环的部分括在括号内。如果存在多个答案,只需返回 任意一个 。对于所有给定的输入,保证 答案字符串的长度小于 10^4 。
输入:numerator = 4, denominator = 333
输出:“0.(012)”

    //小数部分如果余数出现两次就表示该小数是循环小数了
    string fractionToDecimal(int numerator, int denominator) {
        if(denominator==0) return "";//边界条件,分母为0
        if(numerator==0) return "0";//边界条件,分子为0
        string result;
        
        //转换为longlong防止溢出
        long long num = static_cast<long long>(numerator);
        long long denom = static_cast<long long>(denominator);
        
        //处理正负号,一正一负取负号
        if((num>0)^(denom>0))result.push_back('-');
        
        //分子分母全部转换为正数
        num=llabs(num);denom=llabs(denom);
        
        //处理整数部分
        result.append(to_string(num/denom));
        
        //处理小数部分
        num%=denom;                         //获得余数
        if(num==0)return result;             //余数为0,表示整除了,直接返回结果
        result.push_back('.');              //余数不为0,添加小数点
        int index=result.size()-1;          //获得小数点的下标
        unordered_map<int,int> record;      //map用来记录出现重复数的下标,然后将'('插入到重复数前面就好了
        while(num&&record.count(num)==0){   //小数部分:余数不为0且余数还没有出现重复数字
            record[num]=++index;
            num*=10;                        //余数扩大10倍,然后求商,和草稿本上运算方法是一样的
            result+=to_string(num/denom);
            num%=denom;
        }
        if(record.count(num)==1){           //出现循环余数,我们直接在重复数字前面添加'(',字符串末尾添加')'
            result.insert(record[num],"(");
            result.push_back(')');
        }
        return result;
    }

会议室2 && 合并区间

Leetcode 会议室2: 给定一个会议时间安排的数组,每个会议时间都会包括开始和结束的时间 [[s1,e1],[s2,e2],…] (si < ei),为避免会议冲突,同时要考虑充分利用会议室资源,请你计算至少需要多少间会议室,才能满足这些会议安排

int minMeetingRooms(vector<vector<int>>& intervals) {
        if(intervals.empty()) return 0;
        // 先将数组排序,先比较 第一个元素,再比较第二个
        sort(intervals.begin(), intervals.end());
        // 建立优先队列 小顶堆,比较当前遍历时间段的左边界与之前时间段的右边界的最小值,判断是否需要开启新的房间。
        // 注意当遇到需要从一堆值里面挑出最小或最大值,或者最小或者最大的 k 个值,就可以考虑 优先队列。
        priority_queue<int, vector<int>, greater<int>> pq;
        pq.push(intervals[0][1]);
        for(int i = 1; i < intervals.size(); ++i) {
            int lessright = pq.top();
            if(intervals[i][0] >= lessright) {
                pq.pop();
            }
            pq.push(intervals[i][1]);
        }
        return pq.size();
    }

Leetcode合并区间: 给出一个区间的集合,请合并所有重叠的区间。

    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        int n = intervals.size();
        vector<vector<int>> ans;
        //注意vector是可以直接比较大小的,以第0个元素为准比较,如果相同,就以后一个元素为准
        sort(intervals.begin(), intervals.end());
        //先将各个区间以左边界 升序排列,左边界相同就以右边界升序排,那么可能融合的区间凑到一起去了。
        for(int i = 0; i < n; ++i) {
            int l = intervals[i][0], r = intervals[i][1];
            if(!ans.size() || l > ans.back()[1]) { // 注意 ans.back() 是ans最后一个元素的引用
                ans.push_back(intervals[i]);
            } else {
                ans.back()[1] = max(ans.back()[1], r); 
            }
        }
        return ans;
    }
最大数

Leetcode题目:给定一组非负整数 nums,重新排列它们每个数字的顺序(每个数字不可拆分)使之组成一个最大的整数。

    //注意灵活使用字符串按 字典序 比较大小!
    struct {
    bool operator() (const string &a, const string &b) {
            return a + b > b + a;
        }
    } comp; // 仿函数
    string largestNumber(vector<int>& nums) {
        if(std::all_of(nums.begin(), nums.end(), [](int x){return x==0;})) return "0";
        vector<string> ans(nums.size()); // 注意使用transform前要先分配内存。
        std::transform(nums.begin(),nums.end(), ans.begin(), [](int x){return to_string(x);});
        //std::sort(ans.begin(), ans.end(), [](const string &a, const string &b){return a + b > b + a;});
        std::sort(ans.begin(), ans.end(), comp);
        return std::accumulate(ans.begin(), ans.end(), string());
    }
至少有K个重复字符的最长子串

Leetcode题目 : 找到给定字符串(由小写字符组成)中的最长子串 T , 要求 T 中的每一字符出现次数都不少于 k 。输出 T 的长度。

//一句话概括思路:数量不足k个的那种字符不会包含在符合题意的子串中,那么这些字符可以作为分割点。
    int longestSubstring(string s, int k) {
        int n = s.size();
        /
        // 第一步:将数量不足k个的字符定为字符串分割点,记录这些分割点的下标,计入split数组
        unordered_map<char, int> ump;
        for(char &c : s) ump[c]++;
        vector<int> split;
        for(int i = 0; i < n; i++) {
            if(ump[s[i]]<k) split.push_back(i);
        }
        
        // 第二步: 设置触发 递归结束条件
        if(!split.size()) return n;  // 递归 触发结束条件,即当前字符串中没有数量小于k的字符
        split.push_back(n);  // 分割字符串,将字符串的下一个点也作为分割点
        int left = 0;  // 记录子串起始点
        int len;   // 记录子串长度
        int ans = 0;
        // 第三步: 递归每个子串
        for(int i = 0; i < split.size(); ++i) {
            // 注意分割点的字符不能算入子串,所以 len 的计算不能加1
            len = split[i] - left;
            // 如果当前子串比已有答案还小,那就不用考虑了。
            if(ans<len) ans = max(ans, longestSubstring(s.substr(left, len), k));
            left = split[i] + 1; // 更新子串起始点。
        }
        return ans;
    }
解码( 数字解码成字母的可能情况数量)
  • 数字 -> 字母 Leetcode题目
    在这里插入图片描述输入:s = “12”
    输出:2
    解释:它可以解码为 “AB”(1 2)或者 “L”(12)
int numDecodings(string s) {
    if (s[0] == '0') return 0;
    int pre = 1, curr = 1;//dp[-1] = dp[0] = 1
    // pre 表示 s[0:i-2]的解码数量
    // curr 当前的值表示 s[0:i-1] 的解码数量
    // 根据 pre 和前一个 curr 计算 当前 i 对应的 curr
    for (int i = 1; i < s.size(); i++) {
        int tmp = curr;
        // curr 此时的值表示 s[0:i-1] 的解码数量
        if (s[i] == '0')
            if (s[i - 1] == '1' || s[i - 1] == '2') curr = pre;
            else return 0;
        else if (s[i - 1] == '1' || (s[i - 1] == '2' && s[i] >= '1' && s[i] <= '6'))
            curr = curr + pre;
        // curr 此时的值表示 s[0:i] 的解码数量
        pre = tmp;
    }
    return curr;
}
罗马数字与整数互转
    int romanToInt(string s) {
        map<char, int> romanhash = {
            {'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}
        };
        int result = 0;
        for(int i = 0; i < s.size() - 1; i++){
            if(romanhash[s[i]] < romanhash[s[i+1]]){
                result -= romanhash[s[i]];
            }else{
                result += romanhash[s[i]];
            }
        }
        result += romanhash[s[s.size()-1]];
        return result;
    }
    string intToRoman(int num) {
        int values[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        string reps[] = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};

        string res;
        for (int i = 0; i < 13; i ++ )  //这里不使用图里的count了,一遍一遍来就行了
            while(num >= values[i])
            {
                num -= values[i];
                res += reps[i];
            }
        return res;
    }
二分查找(求左右边界)

数字在升序数组中出现的次数 牛客网题目

class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        if(data.empty()) return 0;
        int lf = getleft(data, k);
        if(data[lf] != k) return 0; //如果左边界都找不到,直接返回0
        int rg = getright(data, k);
        return rg-lf+1;
    }
    int getleft(vector<int> data, int k) {
        int n = data.size();
        int l = 0, r = n;
        // 分为[l,mid); mid; [mid,r) 三个区域,注意是前闭后开区
        while(l<r) { 
            int mid = l + (r-l)/2;// 防止整数溢出,比(l+r)/2要好
            // 注意 求左边界 是应这样
            if (data[mid] >= k) r = mid; 
            else l = mid+1;
        }
        return l; // 注意求左边界是返回 l,
    }
     int getright(vector<int> data, int k) {
        int n = data.size();
        int l = 0, r = n;
        // 分为[l,mid); mid; [mid,r) 三个区域,注意是前闭后开区
        while(l<r) {
            int mid = l + (r-l)/2;
            // 注意 求右边界 是应这样
            if (data[mid] <= k) l = mid+1;
            else r = mid;
        }
        return l-1; //注意求右边界是返回l-1
    }
};
不用加减乘除做加法

牛客网题目 : 写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

  • 位运算
  1. 两个数异或:相当于每一位相加,而不考虑进位;
  2. 两个数相与,并左移一位:相当于求得进位;
  3. 将上述两步的结果相加
 int Add(int num1, int num2) {
        while(num2) { // 结束条件为 进位等于0
            int sum = num1^num2;
            int carry = (num1&num2)<<1;
            num1 = sum;
            num2 = carry;
        }
        return num1;
    }
表示数值的字符串

Leetcode 题目 :请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、“5e2”、"-123"、“3.1416”、"-1E-16"、“0123"都表示数值,但"12e”、“1a3.14”、“1.2.3”、"±5"及"12e+5.4"都不是。

  • 分析:用四个变量区分状态:hasNum,hasOp,hasE,hasDot。代码结构如下:
  1. while循环1号–略去前置空格
  2. while循环2号–逐字符操作
    0-9   hasNum置1
    e/E   hasE置1,E前应该hasNum,后面是新的数字,其他三项置0
    +/-   hasOp置1,+/-应该出现在数字首部
    .    hasDot置1,点不能出现在e/E之后
    空格   break
    other  不应该含有其他字符
  3. while循环3号–略去后置空格
  4. 判断hasNum并且下标移动到了字符串末尾
class Solution {
public:
    bool isNumber(string s) {
        int n = s.size();
        int index = -1;
        bool hasDot = false,hasE = false,hasOp = false,hasNum = false;
        while(index<n && s[++index]==' ');
        while(index<n){
            if('0'<=s[index] && s[index]<='9'){
                hasNum = true;
            }else if(s[index]=='e' || s[index]=='E'){
                if(hasE || !hasNum) return false;
                hasE = true;
                hasOp = false;hasDot = false;hasNum = false;
            }else if(s[index]=='+' || s[index]=='-'){
                if(hasOp || hasNum || hasDot) return false;
                hasOp = true;
            }else if(s[index]=='.'){
                if(hasDot || hasE) return false;
                hasDot = true;
            }else if(s[index]==' '){
                break;
            }else{
                return false;
            }
            ++index;
        }
        while(index<n && s[++index]==' ');
        return hasNum && index==n;
    }
};
数据流中的中位数

Leetcode题目 : 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
解析: 在这里插入图片描述

class MedianFinder {
public:
    /** initialize your data structure here. */
    priority_queue<int, vector<int>, less<int> > maxheap;
    priority_queue<int, vector<int>, greater<int> > minheap;
    MedianFinder() {}
    void addNum(int num) {
        if(maxheap.size() == minheap.size()) {
            maxheap.push(num);
            minheap.push(maxheap.top());
            maxheap.pop();
        }
        else {
            minheap.push(num);
            maxheap.push(minheap.top());
            minheap.pop();
        }
    }
    double findMedian() {
        int maxSize = maxheap.size(), minSize = minheap.size();
        int mid1 = maxheap.top(), mid2 = minheap.top();
        return maxSize == minSize ? ((mid1 + mid2) * 0.5) : mid2;
    }
};
滑动窗口的最大值

Leetcode题目 : 给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3;输出: [3,3,5,5,6,7]

    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> res;    // 用来存结果的vector
        deque<int> tem;     // 存放窗口中元素的下标值
        for(int i=0; i<nums.size(); ++i) {   
            //while这步操作是为了让当前窗口最大值的索引值放到tem的队头
            while(!tem.empty() && nums[i]>nums[tem.back()]) {
                tem.pop_back();
            }
            //这里的if判断条件中的第二个是为了判断队头是否过期,也就是说队头的索引是否小于当前索引  
            //往左走k步,+1是因为索引是从0开始,若当前滑窗索引便把它清理掉。
            if(!tem.empty() && tem.front()<i-k+1) tem.pop_front();
            tem.push_back(i);
            if(i>=k-1)  res.push_back(nums[tem.front()]);
        }
        return res;
    }
剪绳子(绳子所能拆分的最大乘积)

Leetcode : 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

 int cuttingRope(int n) {
        //如果n<=3, 要求至少分为两部分,实际结果的最大值为n-1
        if(n <= 3)  return n-1;
        vector<int> dp(n+1);
        //当n大于3时, <=3的部分可以不继续拆分,它本身的长度作为dp值
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 3;
        // n > 3 时, dp[i] 表示 长度为 i 的绳子所能拆分的最大乘积
        for(int i = 4; i <= n; ++i)
            // 将 i 拆分成 j 和 i-j 两段
            for(int j = 0; j <= i/2; ++j)
                dp[i] = max(dp[i], dp[j] * dp[i-j]);
        return dp[n];
    }
x 的平方根

Leetcode题目 : 实现 int sqrt(int x) 函数。计算并返回 x 的平方根,其中 x 是非负整数。由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

    int mySqrt(int x) {
        // 二分查找
        if(!x) return 0;
        int l = 0, r = x, ans = -1;
        while(l<=r) {
            int mid = l+(r-l)/2;
            if((long)mid*mid <= x) {
                ans = mid;
                l = mid + 1;
            } else {
                r = mid-1;
            }
        }
        return ans;
    }
旋转数组的最小数字

Leetcode题目 :升序数组 [1,2,3,4,5]的旋转数组为 [3,4,5,1,2]。 找到这数组的最小值。以下是二分查找。

    int minArray(vector<int>& numbers) {
        int l = 0, r = numbers.size()-1;
        while (l<r) {
            int mid = l + (r-l)/2;
            if (numbers[mid] > numbers[r]) {
                l = mid+1;
            } else if(numbers[mid] < numbers[r]) {
                r = mid;
            } else if(numbers[mid] == numbers[r]) {
                r--;
            }
        }
        return numbers[l];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值