抱佛脚-刷题系列之其他经典题

抱佛脚一时爽,一直抱佛脚一直爽!这篇文章总结除了本系列总结的分类外的经典题~
参考链接:leetcode 剑指offer

rand


rand5 [0,1,2,3,4] 实现 rand7[0,1,2,3,4,5,6]

  • 先random5,如果生成的是0-2,就视为0,否则视为1;重复3次这个操作,生成一个三位的二进制数

rand7实现rand10

  • 先用rand7实现rand10*N(以rand40为例):(rand7 - 1) * 7 + rand7,就转为了 rand49
  • 用 rand49() 生成一个 [1, 49] 范围内的随机数,如果其在 [1, 40] 范围内,则进入下一步,否则重新rand49;这样就转为了rand40
  • rand40 % 10 + 1转为 rand10

Trie(前缀树、字典树)


实现Trie(lc208)

class TrieNode {
public:
    bool isWord;
    TrieNode* child[26];
    TrieNode(): isWord(false) {
        for (auto& a : child) a = nullptr;
    }
};

class Trie {
public:
    /** Initialize your data structure here. */
    Trie() {
        root = new TrieNode();
    }
    
    /** Inserts a word into the trie. */
    void insert(string word) {
        TrieNode* cur = root;
        for (char c : word) {
            int i = c - 'a';
            if (!cur->child[i]) cur->child[i] = new TrieNode();
            cur = cur->child[i];
        }
        cur->isWord = true;
    }
    
    /** Returns if the word is in the trie. */
    bool search(string word) {
        TrieNode* cur = root;
        for (char c : word) {
            int i = c - 'a';
            if (!cur->child[i]) return false;
            cur = cur->child[i];
        }
        return cur->isWord;
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    bool startsWith(string prefix) {
        TrieNode* cur = root;
        for (char c : prefix) {
            int i = c - 'a';
            if (!cur->child[i]) return false;
            cur = cur->child[i];
        }
        return true;
    }
private:
    TrieNode* root;
};

队列与栈


字符串解码(lc394)

class Solution {
public:
    string decodeString(string s) {
        string t = "";
        stack s_num;
        stack s_str;
        int cnt = 0;
        for (int i = 0; i < s.size(); ++i) {
            if (s[i] >= '0' && s[i] <= '9') {
                cnt = 10 * cnt + s[i] - '0';
            } else if (s[i] == '[') {
                s_num.push(cnt);
                s_str.push(t);
                cnt = 0; t.clear();
            } else if (s[i] == ']') {
                int k = s_num.top(); s_num.pop();
                for (int j = 0; j < k; ++j) s_str.top() += t;
                t = s_str.top(); s_str.pop();
            } else {
                t += s[i];
            }
        }
        return s_str.empty() ? t : s_str.top();
    }
};

用队列实现栈(lc225)

  • 法一:一个队列用来放最后加进来的数,模拟栈顶元素。剩下所有的数都按顺序放入另一个队列中
class MyStack {
public:
    /** Initialize your data structure here. */
    MyStack() { }
    
    /** Push element x onto stack. */
    void push(int x) {
        if (!q2.empty()) {
            q1.push(q2.front());
            q2.pop(); 
        }
        q2.push(x);
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int res = top();
        q2.pop();
        return res;
    }
    
    /** Get the top element. */
    int top() {
        if (q2.empty()) {
            int n = q1.size();
            for (int i = 0; i < n - 1; ++i) {
                q1.push(q1.front());
                q1.pop();
            }
            q2.push(q1.front());
            q1.pop();
        }
        return q2.front();
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return q1.empty() && q2.empty();
    }
private:
    queue q1;
    queue q2;
};
  • 法二:每次把新加入的数插到前头;只要实现对了 push() 函数,后面三个直接调用队列的函数即可
class MyStack {
public:
    /** Initialize your data structure here. */
    MyStack() { }
    
    /** Push element x onto stack. */
    void push(int x) {
        q.push(x);
        int n = q.size();
        for (int i = 0; i < n - 1; ++i) {
            q.push(q.front());
            q.pop();
        }
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int res = q.front();
        q.pop();
        return res;
    }
    
    /** Get the top element. */
    int top() {
        return q.front();
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return q.empty();
    }
private:
    queue q;
};

实现队列O(1)返回当前最大值

当一个元素入列时,在最大值序列尾部的所有小于当前元素的都可以直接删除,然后当前元素直接入到最大值队列中

N-SUM问题


2sum(jz42)

  • 使用hash表,建立HashMap map,键值对为。遍历数组nums,对每个数nums[i],查询hash表中是否有key等于target-nums[i]的记录,若有,则找到;否则,将num[i]和下标i存入hash表

3sum(lc15)

  • 固定一位+双指针

3sum closest(lc16)

int threeSumClosest(vector& nums, int target) {
    int n = nums.size();
    if (n < 3) return -1;
    int res = nums[0] + nums[1] + nums[2];
    int diff = abs(res - target);
    sort(nums.begin(), nums.end());
    for (int i = 0; i < n - 2; ++i) {
        int left = i + 1, right = n - 1;
        while (left < right) {
            int curSum = nums[i] + nums[left] + nums[right];
            int newDiff = abs(curSum - target);
            if (newDiff < diff) {
                res = curSum;
                diff = newDiff;
            }
            if (curSum > target) --right;
            else ++left;
        }
    }
    return res;
}

其他题目


第k大的数(lc215)

  • 堆排序法:小根堆;num > heap.top 才push num;元素不足k个时返回空vector
  • 快排法
    int findKthLargest(vector& nums, int k) {
        int left = 0, right = nums.size() - 1;
        while (true) {
            int pos = partition(nums, left, right);
            if (pos == k - 1) return nums[pos];
            if (pos > k - 1) right = pos - 1;
            else left = pos + 1;
        }
    }
    int partition(vector& nums, int left, int right) {
        int pivot = nums[left], l = left + 1, r = right;
        while (l <= r) {
            if (nums[l] < pivot && nums[r] > pivot) {
                swap(nums[l++], nums[r--]);
            }
            if (nums[l] >= pivot) ++l;
            if (nums[r] <= pivot) --r;
        }
        swap(nums[left], nums[r]);
        return r;
    }

丑数(jz33)

    int GetUglyNumber_Solution(int index) {
        if (index <= 0) return 0;
        vector results;
        results.reserve(index);
        int p2 = 0, p3 = 0, p5 = 0;
        results[0] = 1;
        for (int i = 1; i < index; ++i) {
            results[i] = min(results[p2] * 2, min(results[p3] * 3, results[p5] * 5));
            if (results[i] == results[p2] * 2) ++p2;
            if (results[i] == results[p3] * 3) ++p3;
            if (results[i] == results[p5] * 5) ++p5;
        }
        return results[index - 1];
    }

扑克牌顺子(jz45)

  • 最大值-最小值<5 且无重复数字即可
  • 遇到为0的元素不做处理

圆圈游戏(jz46)

  • 当我们知道了 f(n - 1, m) 对应的答案 x 之后,我们也就可以知道,长度为 n 的序列最后一个删除的元素,应当是从 m % n 开始数的第 x 个元素。因此有 f(n, m) = (m % n + x) % n = (m + x) % n
  • f[n] = (f[n-1] + m) % n
  • 也可以用链表来模拟整个过程

数据流中位数(jz63)

  • 后半段是小顶堆,前半段是大顶堆
  • 奇数放大顶堆:但需先检查它是否比小顶堆的最小值大,大则放小顶堆,然后把小顶堆的最小值放到大顶堆中
  • 偶数放小顶堆:但需先检查它是否比大顶堆的最大值小,小则放大顶堆,然后把大顶堆的最大值放到小顶堆中
  • 小顶堆:priority_queue, greater>
  • 大顶堆:priority_queue
class Solution {
    private:
    int cnt = 0;
    priority_queue maxHeap;
    priority_queue, greater> minHeap;
    public:
    void Insert(int num) {
        ++cnt;
        if (cnt % 2) {
            if (!minHeap.empty() && num > minHeap.top()) {
                minHeap.push(num);
                num = minHeap.top();
                minHeap.pop();
            }
            maxHeap.push(num);
        } else {
            if (!maxHeap.empty() && num < maxHeap.top()) {
                maxHeap.push(num);
                num = maxHeap.top();
                maxHeap.pop();
            }
            minHeap.push(num);
        }
    }

    double GetMedian() {
        if (cnt % 2) {
            return (double) maxHeap.top();
        } else {
            return (double) (maxHeap.top() + minHeap.top()) / 2;
        }
    }

};

滑动窗口的最大值(jz64)

    vector maxInWindows(const vector& num, unsigned int size) {
        vector res;
        int n = num.size();
        if (size > n || n <= 0 || size <= 0) return res;
        deque dq; // 维护一个单调递减的双端队列,但存的是index
        for (int i = 0; i < n; ++i) {
            while (!dq.empty() && num[dq.back()] <= num[i]) 
                dq.pop_back();
            dq.push_back(i);
            if (dq.front() + size <= i) 
                dq.pop_front();
            if (i + 1 >= size)
                res.push_back(num[dq.front()]);
        }
        return res;
    }

LFU(lc460)

class LFUCache {
public:
    LFUCache(int capacity) {
        cap = capacity;
    }
    
    int get(int key) {
        if (m.count(key) == 0) return -1;
        freq[m[key].second].erase(iter[key]);
        ++m[key].second;
        freq[m[key].second].push_back(key);
        iter[key] = --freq[m[key].second].end();
        if (freq[minFreq].size() == 0) ++minFreq;
        return m[key].first;
    }
    
    void put(int key, int value) {
        if (cap <= 0) return;
        if (get(key) != -1) {
            m[key].first = value;
            return;
        }
        if (m.size() >= cap) {
            m.erase(freq[minFreq].front());
            iter.erase(freq[minFreq].front());
            freq[minFreq].pop_front();
        }
        m[key] = {value, 1};
        freq[1].push_back(key);
        iter[key] = --freq[1].end();
        minFreq = 1;
    }

private:
    int cap, minFreq;
    unordered_map> m;
    unordered_map> freq;
    unordered_map::iterator> iter;
};

LRU(lc146)

class LRUCache {
    public:
    LRUCache(int capacity) {
        capacity_ = capacity;
    }

    int get(int key) {
        auto m_iter = m.find(key);
        if (m_iter == m.end()) return -1; // 找不到返回-1
        auto l_iter = m_iter->second;
        l.splice(l.begin(), l, l_iter); // 找到了则把它移到表头:在l.begin()前面插入
        return l_iter->second; // 返回key对应的value
    }

    void put(int key, int value) {
        auto m_iter = m.find(key); // 找到list中key对应的kv对
        if (m_iter != m.end()) {
            l.erase(m_iter->second);
            m.erase(key);
        }
        l.push_front(make_pair(key, value)); // kv对放入表头
        m[key] = l.begin();
        if (l.size() > capacity_) {
            int k = l.rbegin()->first;
            l.pop_back();
            m.erase(k);
        }
    }
    private:
    list> l; // 存储kv对,最近使用的在表头
    unordered_map>::iterator> m; // key,key对应的kv对在链表l中的位置
    int capacity_;
};

缺失的第一个正整数(lc41)

  • 如果数组中有数字i+1,把i+1放到nums[i]中
int firstMissingPositive(vector& nums) {
    int n = nums.size();
    if (!n) return 1;
    for (int i = 0; i < n; ++i) { // 如果数组中有数字i+1,把i+1放到nums[i]中
        while (nums[i] >= 1 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {
            swap(nums[i], nums[nums[i] - 1]);
        }
    }
    for (int i = 0; i < n; ++i) {
        if (nums[i] != i + 1) return i + 1;
    }
    return n + 1;
}

幂(lc50)

  • 注意:用n*=-1会导致溢出
double myPow(double x, int n) {
    if (n == 0) return 1;
    double half = myPow(x, n / 2);
    // n 为偶数
    if (n % 2 == 0) return half * half;
    // n 为奇数
    if (n > 0) return half * half * x;
    return half * half / x;
}

长度为n的一个数字,要删掉其中m位,求删掉后最大的数字是什么(lc402)

  • 法一:最高位必然在前m+1位中,获取最高位之后开始递归
  • 法二:维护一个递增栈,只要发现当前的数字小于栈顶元素的话,就将栈顶元素移除;因为此时栈顶元素在高位上,就算后面的数字再大,也是在低位上,我们只有将高位上的数字尽可能的变小,才能使整个剩下的数字尽可能的小
string removeKdigits(string num, int k) {
    string res = "";
    int n = num.size(), keep = n - k;
    for (char c : num) {
        while (!res.empty() && c < res.back() && k) {
            res.pop_back();
            --k;
        }
        res.push_back(c);
    }
    res.resize(keep);
    while (!res.empty() && res[0] == '0') res.erase(res.begin());
    return res.empty() ? "0" : res;
}

n条直线相交时最多时有几个平面

  • 直线最多可以与原来的每一条直线都相交,也就是说与(n-1)条直线都相交,从而产生(n-1)个交点,该直线被分成n部分,而每一部分将所在区域一分为二,从而多出了n个部分

你可能感兴趣的:(抱佛脚-刷题系列之其他经典题)