当前位置:   article > 正文

leetcode刷题记录---19.9.14 LRU(map,list,迭代器),排序链表(cut,merge,dummyhead),乘积最大子序列,dfs(bfs),二分查找模板_list+map+模板实现lru

list+map+模板实现lru

1.LRU缓存机制       微软二面

题目描述:设计和实现(LRU)缓存机制,具有get和put的功能。

get(key),如果key存在,则返回。如果key不存在,返回-1;

put(key,value),如果缓冲区满,替换掉最近最少使用的key。否则直接插入。

参考:https://leetcode-cn.com/problems/lru-cache/solution/lru-ce-lue-xiang-jie-he-shi-xian-by-labuladong/

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

 

思路:o(1)的时间实现get和put,即o(1)的时间完成,查找,删除,插入,还必须是顺序排列的,因为有尺寸限制。

分析:

数组                  查找快                  删除慢                      插入慢                 数据固定位置

链表                  查找慢                  删除快                      插入快                 数据固定位置

哈希表               查找快                  删除快                      插入快                 数据位置不定

 

所以讲道理,这题哈希数组和哈希链表都可以做。在这里使用哈希链表(双向链表和哈希表的结合)来做。

双向链表list说明:https://www.cnblogs.com/BeyondAnyTime/archive/2012/08/10/2631191.html

其实cache就是缓存,map只是一种方便查找的映射!!!!所以更新的时候一定先更新cache!

为什么使用双向链表?尾节点进行删除,要以o(1)的时间找到他的前驱

1.private中声明合适的数据结构,一个list,一个unordered_map

2.get(key)先查哈希表中,是否存在,不存在-1,存在的话,把他从cache中删除,插入头部,更新哈希表,返回查找值

3.put(key)先查是否存在,如果不存在,在查cache是否满,满的话删除cache,和map中该元组。如果不满直接插入,更新map。

     如果存在,删除cache中元组,但是map中不用删,只用更新map[k]的值。

注意:list是基于stl实现的双向链表,与vector相比较,插入删除比较方便,但是随机访问比较麻烦。

  1. class LRUCache {
  2. public:
  3. LRUCache(int capacity) {
  4. this->cap = capacity;
  5. }
  6. int get(int key) {
  7. auto it = map.find(key);
  8. if(it == map.end()) return -1;
  9. pair<int,int> kv = *map[key];
  10. cache.erase(map[key]);
  11. cache.push_front(kv);
  12. map[key] = cache.begin();
  13. return kv.second;
  14. }
  15. void put(int key, int value) {
  16. auto it = map.find(key);
  17. //没找到,说明没存过
  18. if(it == map.end()){
  19. //缓冲区满了
  20. if(cache.size() == cap){
  21. auto temp = cache.back();
  22. int td = temp.first;
  23. map.erase(td);
  24. cache.pop_back();
  25. }
  26. cache.push_front(make_pair(key,value));
  27. map[key] = cache.begin();
  28. }
  29. else{
  30. //找到了已经存在的,要更新,但是不用删map记录,覆盖就可以
  31. cache.erase(map[key]);
  32. cache.push_front(make_pair(key,value));
  33. map[key] = cache.begin();
  34. }
  35. }
  36. private:
  37. int cap;
  38. list<pair<int,int>> cache;
  39. unordered_map<int,list<pair<int,int>>::iterator> map;
  40. };
  41. /**
  42. * Your LRUCache object will be instantiated and called as such:
  43. * LRUCache* obj = new LRUCache(capacity);
  44. * int param_1 = obj->get(key);
  45. * obj->put(key,value);
  46. */

2.排序链表

题目描述:以o(nlogn)的时间复杂度和常数级的空间复杂度实现对链表进行排序。

参考:https://leetcode-cn.com/problems/sort-list/solution/148-pai-xu-lian-biao-bottom-to-up-o1-kong-jian-by-/

示例 1:

输入: 4->2->1->3
输出: 1->2->3->4
示例 2:

输入: -1->5->3->4->0
输出: -1->0->3->4->5

思路:

本题的三个知识点函数应该作为常识使用。

1.cut(node,n):此函数是指将链表node切掉前n个节点,并返回后半部分的头节点。

2.merge(node1,node2):此函数实现将链表node1和链表node2,进行双路归并,返回归并后的头节点。

3.dummyHead:它其实是一个伪头节点,在不知道head节点是否会变化的时候需要用dummy标记。dummyHead.next = head;

(注意ListNode* p = head和ListNode p的区别,一个是声明对象,一个是定义指向对象的指针。对象调用自己的成员函数用.    p.next = .....。而指针则用->不能混淆。)

注意,ListNode dummyHead; auto p = &dummyHead;为什么在p不断的指向后面的节点的时候。dummyHead.next依然能准确的指向头节点?  注意p指针一直在动,但是dummyHead一直没动,所以返回dummyHead.next的时候还是头。 

本题解答思路:

1.统计链表长度

2.归并11,22,44核心.for(int size = 1;size<length;size<<=1){...}

  1. class Solution {
  2. public:
  3. ListNode* sortList(ListNode* head) {
  4. ListNode* p =head;
  5. ListNode dummyHead(0);
  6. dummyHead.next = head;
  7. //ListNode* tail = dummyHead;
  8. int length = 0;
  9. while(p){
  10. length++;
  11. p = p->next;
  12. }
  13. for(int size = 1;size<length;size<<=1){
  14. auto current = dummyHead.next;
  15. auto tail = &dummyHead;
  16. while(current){
  17. ListNode* left = current;
  18. ListNode* right = cut(left,size);
  19. current = cut(right,size);
  20. tail->next = merge(left,right);
  21. while(tail->next){
  22. tail = tail->next;
  23. }
  24. }
  25. }
  26. return dummyHead.next;
  27. }
  28. ListNode* cut(ListNode* node1,int n){
  29. ListNode* temp = node1;
  30. while(--n&&temp){
  31. temp = temp->next;
  32. }
  33. //n大于链表长度了
  34. if(!temp) return nullptr;
  35. ListNode* next = temp->next;
  36. temp->next = nullptr;
  37. return next;
  38. }
  39. ListNode* merge(ListNode* pNode1,ListNode* pNode2){
  40. ListNode dummyHead(0);
  41. auto p = &dummyHead;
  42. while(pNode1&&pNode2){
  43. if(pNode1->val>pNode2->val){
  44. p->next = pNode2;
  45. p = pNode2;
  46. pNode2 = pNode2->next;
  47. }else{
  48. p->next = pNode1;
  49. p = pNode1;
  50. pNode1 = pNode1->next;
  51. }
  52. }
  53. p->next = pNode1? pNode1:pNode2;
  54. return dummyHead.next;
  55. }
  56. };

3.合并两个有序链表,包含是在上一题了,2分钟搞定

题目描述:给定两个有序链表,把他们合并成一个

示例:

  1. 输入:1->2->4, 1->3->4
  2. 输出:1->1->2->3->4->4
  1. class Solution {
  2. public:
  3. ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
  4. ListNode dummyHead(0);
  5. auto p = &dummyHead;
  6. while(l1&&l2){
  7. if(l1->val<l2->val){
  8. p ->next = l1;
  9. p = l1;
  10. l1 = l1->next;
  11. }else{
  12. p->next = l2;
  13. p = l2;
  14. l2 = l2->next;
  15. }
  16. }
  17. p->next = l1 ? l1:l2;
  18. return dummyHead.next;
  19. }
  20. };

4.乘积最大子序列

题目描述:给定一个数组,找出乘积最大的连续子序列。

参考:https://leetcode-cn.com/problems/maximum-product-subarray/solution/c-su-du-100nei-cun-92li-yong-zui-da-zhi-he-fu-zui-/

示例 1:

输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:

输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

思路:乘积最大值只可能从最大值或者最小值相乘得来。因此对于不大不小的结果直接忽略 ,我们只要最大的,或者最小的。

1.参数判断.if(nums.empty()) return 0;

2.变量定义及初始化。pos = neg = ret = nums[0];

3.遍历一次数组,从一开始,保存最大的,和最小的结果。同时保存当前最大结果。for(int i =1;i<nums.size();++i){temp = pos;pos = max(nums[i],max(pos*nums[i],neg*nums[i]));neg = min(nums[i],min(nums[i]*temp,nums[i]*neg));}

  1. class Solution {
  2. public:
  3. int maxProduct(vector<int>& nums) {
  4. if(nums.empty()) return 0;
  5. int pos = nums[0];
  6. int neg = nums[0];
  7. int ret = nums[0];
  8. for(int i = 1;i<nums.size();++i){
  9. int temp = pos;
  10. pos = max(nums[i],max(pos*nums[i],nums[i]*neg));
  11. neg = min(nums[i],min(temp*nums[i],nums[i]*neg));
  12. ret = max(ret,pos);
  13. }
  14. return ret;
  15. }
  16. };

5.岛屿数量

题目描述:假设岛屿为1,水为0,给定一个由水和岛屿构成的二维网格,求岛屿的数量,假设网格的四周都是水。

参考:https://leetcode-cn.com/problems/number-of-islands/solution/dao-yu-shu-liang-by-leetcode/

示例 1:

输入:
11110
11010
11000
00000

输出: 1
示例 2:

输入:
11000
11000
00100
00011

输出: 3

思路:用深度优先遍历和广度优先遍历都可以解决

深度优先遍历,就是从遍历矩阵,如果遇到1,那就以这个为根深度优先遍历,并且把能遍历到的1全给替换成0 。最后总共遍历了几次,就是有几个岛屿。

1.依次遍历网格

2.如果找到1,那么dfs

3.把传过来的根置为0,向他的上下左右扩展,如果有1,全换为0 。

  1. class Solution {
  2. public:
  3. void dfs(vector<vector<char>>& grid,int i,int j){
  4. int nx = grid.size();
  5. int ny = grid[0].size();
  6. grid[i][j] ='0';
  7. if(i-1>=0&&grid[i-1][j] == '1') dfs(grid,i-1,j);
  8. if(i+1<nx&&grid[i+1][j] == '1') dfs(grid,i+1,j);
  9. if(j-1>=0&&grid[i][j-1] == '1') dfs(grid,i,j-1);
  10. if(j+1<ny&&grid[i][j+1] == '1') dfs(grid,i,j+1);
  11. }
  12. int numIslands(vector<vector<char>>& grid) {
  13. int x = grid.size();
  14. if(x == 0) return 0;
  15. int y = grid[0].size();
  16. int islands_num = 0;
  17. for(int i = 0;i<x;++i){
  18. for(int j = 0;j<y;++j){
  19. if(grid[i][j] == '1') {
  20. islands_num++;
  21. dfs(grid,i,j);
  22. }
  23. }
  24. }
  25. return islands_num;
  26. }
  27. };

6.在排序数组中查找元素的第一个位置和最后一个位置,注意这题需要记二分查找模板

题目描述:给定一个升序数组nums,和一个整数target。。找出target在nums数组中第一次出现的位置和最后一次出现的位置。

要求时间复杂度为o(logn)。如果没有找到target,返回[-1,-1];

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]

思路:这题貌似百度那个面试官问过。我答出来了,但是挂了。。。很难受

1.针对最先出现的位置,和最后出现的位置,两次二分检索。

  1. class Solution {
  2. public:
  3. int finLowerBound(vector<int>& nums,int target){
  4. int size = nums.size();
  5. int left = 0;
  6. int right = size-1;
  7. while(left<right){
  8. int mid = (left+right)>>1;
  9. if(nums[mid]<target) left = mid+1;
  10. else right = mid;
  11. }
  12. if(nums[left] != target) return -1;
  13. return left;
  14. }
  15. int finUpBound(vector<int>& nums,int target){
  16. int size = nums.size();
  17. int left = 0;
  18. int right = size-1;
  19. while(left<right){
  20. int mid = (left+right+1)>>1;
  21. if(nums[mid]>target) right = mid-1;
  22. else left = mid;
  23. }
  24. if(nums[left] != target) return -1;
  25. return left;
  26. }
  27. vector<int> searchRange(vector<int>& nums, int target) {
  28. if(nums.size()==0) return {-1,-1};
  29. int num1 = finLowerBound(nums,target);
  30. if(num1 == -1) return {-1,-1};
  31. int num2 = finUpBound(nums,target);
  32. return {num1,num2};
  33. }
  34. };

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/935587
推荐阅读
相关标签
  

闽ICP备14008679号