【考前救急】

考 前 救 急

206. 反转链表

掌握两种写法即可

class Solution {
public:
	//tail是指待反转的链表的下一个元素,prev是指待反转链表的前一个元素

	// 迭代写法
    ListNode* reverseList(ListNode* head, ListNode* prev, ListNode* tail) {
    	if(head == nullptr) return nullptr;
        // tail指的是链表尾结点的下一个结点
        ListNode* cur = head, *pre = prev;
        while(cur != tail) {
            ListNode* nxt = cur->next;
            cur->next = pre;
            pre = cur;
            cur = nxt;
        }
        return pre;
    }
	// 递归写法 
	ListNode* reverseList(ListNode* head, ListNode* prev, ListNode* tail) {
        if(head == tail || head->next == tail) return head;
        
        ListNode* reverse_tail = head->next;
        ListNode* reverse_head = reverseList(head->next, head, tail);
        reverse_tail->next = head;
        head->next = prev;
        return reverse_head;
    }
	    
    ListNode* reverseList(ListNode* head) {
        return reverseList(head, nullptr, nullptr);
    }
};

146. LRU缓存

关键在于操作时,添加(put)或查询(get)的数据被最新访问了,所以需要将其加入到最开始将其优先级升为最高。
使用双向链表ListNode,同时有一个初始的哨兵头结点head和哨兵尾结点tail,便于插入新结点和弹出优先级最低的结点
使用hash表hash,使得key对应的ListNode可以快速被定位到,ListNode中需要存储key,方便我们弹出优先级最低的元素时,也将其从hash表中删除。
需要一个cap记录当前可以put的元素个数

addListNode用于将结点加入双向链表
removeListNode用于将结点从双向链表中弹出
put用于将新数据存储
get用于快速获取元素,直接调用hash表即可

class LRUCache {
private:
    struct ListNode {
        int key;
        int value;
        ListNode* left;
        ListNode* right;
        ListNode(int key, int value) : key(key), value(value), left(nullptr), right(nullptr) {}
    };
    
    int cap;
    ListNode* head;
    ListNode* tail;
    unordered_map<int, ListNode*> hash;
public:
    
    LRUCache(int capacity) {
        cap = capacity;
        hash.clear();
        head = new ListNode(-1, -1);
        tail = new ListNode(-1, -1);
        head->right = tail;
        tail->left = head;
    }
    
    void removeListNode(ListNode* node) {
        node->left->right = node->right;
        node->right->left = node->left;
    }
    
    void addListNode(ListNode* node) {
        node->right = tail;
        node->left = tail->left;
        tail->left->right = node;
        tail->left = node;
    }
    
    int get(int key) {
        // 不存在
        if(!hash.count(key)) return -1;
        
        // 获取结点node
        ListNode* node = hash[key];
        // 更新node的优先级
        // 1. 将其从List中弹出
        removeListNode(node);
        // 2. 将其加入到链表尾
        addListNode(node);
    
        // 返回结果
        return node->value;
    }
    
    void put(int key, int value) {
        // 查看是否存在
        if(hash.count(key)) {
            // 已存在
            ListNode* node = hash[key];
            node->value = value;
            
            removeListNode(node);
            addListNode(node);
        } else {
            // 不存在
            ListNode* node = new ListNode(key, value);
            // 需要删除优先级最低的元素
            if(cap <= 0) {
                ListNode* delNode = head->right;
                // 从双向链表中删除
                removeListNode(delNode);
                // 从hash表中删除
                hash.erase(delNode->key);
                cap += 1;
            }
            
            hash[key] = node;
            addListNode(node);
            cap -= 1;
        }
    }
};

3. 无重复字符的最长子串

双指针,关键点在于每次向右扩展,只有你扩展的那个字符是可能重复的,所以只需要移动左指针使得扩展的那个字符数量不重复即可。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int cnt[256] = {0};
        int n = s.size();
        int res = 0;
        for(int r = 0, l = 0; r < n; ++r) {
            cnt[s[r]] += 1;
            while(l < r && cnt[s[r]] > 1) cnt[s[l]] -= 1, l += 1;
            res = max(res, r - l + 1);
        }
        return res;
    }
};

215. 数组中的第k个最大元素

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        // 借用priority_queue存储至多k个元素
        priority_queue<int, vector<int>, greater<int>> q;
        for(auto u : nums) {
            if(q.size() < k) q.push(u);
            else if(u > q.top()) q.pop(), q.push(u);
        }
        
        return q.top();
    }
};

手写 p r i o r i t y _ q u e u e priority\_queue priority_queue

class PQ {

    vector<int> q;
    int n;
    function<bool(int,int)> cmp;
    
    void up(int u) {
        while(u > 1 && cmp(q[u], q[u / 2])) {
            swap(q[u], q[u / 2]);
            u /= 2;
        }
    }
    
    void down(int u) {
        while(true) {
            int t = u;
            if(u * 2 <= n && cmp(q[u * 2], q[t])) t = u * 2;
            if(u * 2 + 1 <= n && cmp(q[u * 2 + 1], q[t])) t = u * 2 + 1;
            if(u != t) {
                swap(q[u], q[t]);
                u = t;
            } else break;
        }
    }
    
public:
    
    PQ(function<bool(int,int)> f) {
        cmp = f;
        n = 0;
        while(!q.empty()) q.pop_back();
        q.push_back(-1);
    }
    
    int top() { return q[1]; }
    
    void pop() {
        if(q.size() <= 0) return ;
        
        swap(q[1], q[n]);
        q.pop_back();
        
        n -= 1;
        down(1);
    }
    
    void push(int x) {
        q.push_back(x);
        
        n += 1;
        up(n);
    }
    
    int size() { return n; }
};

class Solution {
public:
    
    int findKthLargest(vector<int>& nums, int k) {
        PQ q([](int a, int b) {return a < b;});
        
        for(auto u : nums) {
            if(q.size() < k) q.push(u);
            else if(q.top() < u) q.pop(), q.push(u);
        }
        return q.top();
    }
};

215. K个一组翻转链表

用到206的模板了,但是需要做点修改

/**
 * 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* reverseList(ListNode* head, ListNode* prev, ListNode* tail) {
        if(head == tail || head->next == tail) return head;
        ListNode* cur = head, *pre = tail;
        while(cur != tail) {
            ListNode* nxt = cur->next;
            cur->next = pre;
            pre = cur;
            cur = nxt;
        }
        // 前一个序列的尾元素,当前序列需要接在其之后
        prev->next = pre;
        return prev;
    }
    
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* dummy = new ListNode(-1, head);
        ListNode* first = head;
        ListNode* second = head;
        ListNode* prev = dummy;
        
        while(second != nullptr) {
            int tk = k;
            while(tk > 0 && second != nullptr) {
                second = second->next;
                tk -= 1;
            }
            
            if(tk == 0) reverseList(first, prev, second);
            prev = first; // first在翻转后成为当前序列的尾元素
            first = second; // first更新为下一个序列的头元素
        }
        
        return dummy->next;
    }
};

912. 排序数组

class Solution {
private:

	void quick_sort(vector<int>& nums, int l, int r) {
        if(l >= r) return ;
        int x = nums[l + r >> 1];
        int i = l - 1, j = r + 1;
        while(i < j) {
            do i++; while(nums[i] < x);
            do j--; while(nums[j] > x);
            if(i < j) swap(nums[i], nums[j]);
            else quick_sort(nums, l, j), quick_sort(nums, j + 1, r);
        }
    }

    vector<int> temp;
    void merge_sort(vector<int>& nums, int l, int r) {
        if(l >= r) return ;
        int mid = l + r >> 1;
        merge_sort(nums, l, mid);
        merge_sort(nums, mid + 1, r);
        
        int i = l, j = mid + 1, g = 0;
        while(i <= mid && j <= r) {
            if(nums[i] > nums[j]) temp[g] = nums[j], j += 1, g += 1;
            else temp[g] = nums[i], i += 1, g += 1;
        }
        while(i <= mid) temp[g] = nums[i], i += 1, g += 1;
        while(j <= r) temp[g] = nums[j], j += 1, g += 1;
        
        for(i = l, j = 0; j < g; ++i, ++j)
            nums[i] = temp[j];
    }
public:

    vector<int> sortArray(vector<int>& nums) {
        quick_sort(nums, 0, (int)nums.size() - 1);
	
		temp.resize(nums.size());
        merge_sort(nums, 0, (int)nums.size() - 1);
        return nums;
    }
};

15. 三数之和

不可以包含重复的三元组,每次枚举 n u m s [ i ] nums[i] nums[i]必须不同,同时要满足在同一 n u m s [ i ] nums[i] nums[i]下的 n u m s [ j ] nums[j] nums[j]每次必须是不同的。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        vector<vector<int>> res;
        for(int i = 0; i < n; ++i) {
            if(i > 0 && nums[i] == nums[i - 1]) continue ;
            for(int j = i + 1, k = n - 1; j < k; ++j) {
                if(j > i + 1 && nums[j] == nums[j - 1]) continue;
                while(j < k && nums[i] + nums[j] + nums[k] > 0) k -= 1;
                if(j < k && nums[i] + nums[j] + nums[k] == 0) {
                    res.push_back({nums[i], nums[j], nums[k]});
                }
            }
        }
        return res;
    }
};

1. 两数之和

空间和时间只能选择一个
要么空间 O ( n ) O(n) O(n)和时间 O ( n ) O(n) O(n)
要么空间 O ( 1 ) O(1) O(1)和实践 O ( n 2 ) O(n^2) O(n2)

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

21. 合并两个有序链表

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode* dummy = new ListNode(-1);
        ListNode* temp = dummy;
        while(list1 != nullptr && list2 != nullptr) {
            if(list1->val < list2->val) temp->next = list1, list1 = list1->next;
            else temp->next = list2, list2 = list2->next;
            temp = temp->next;
        }
        if(list1 != nullptr) temp->next = list1;
        if(list2 != nullptr) temp->next = list2;
        
        return dummy->next;
    }
};

2021.12.26 2021.12.26 2021.12.26

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值