面试手撕优化
1 /* 2 每次get一个key有两种情况,要么链表里面没有这个key那么可以直接返回-1, 3 如果链表里面有这个key,我们需要把查到的这个节点给移到链表头部, 4 对于put操作,我们直接通过调用get返回结果: 5 如果返回不是-1,说明链表里面已有key,那么我们的get函数会给他排到链表前面去。 6 如果返回-1,说明链表里面没有key,那么我们需要插入这个key,分两种情况: 7 如果容量满了,就删除最后一个节点再把新的key加进来作为链表头结点 8 否则直接加入当前的key到链表头结点 9 */ 10 class LRUCache { 11 public: 12 int cap; 13 list<int>st;//使用C++自带的双向链表list 14 unordered_map<int,list<int>::iterator>mp;//key需要映射到链表元素的迭代器 15 unordered_map<int,int>mp1;//保存key-value键值对,方便查询元素value 16 LRUCache(int capacity) { 17 cap=capacity; 18 } 19 int get(int key) { 20 if(mp.count(key)){ 21 auto it=mp[key]; 22 //splice(const_iterator pos, list& other, const_iterator it)表示把结点it移到other链表的pos位置之前 23 st.splice(st.begin(),st,it); 24 return mp1[key]; 25 } 26 else return -1; 27 } 28 void put(int key, int value) { 29 if(get(key)!=-1){ 30 mp1[key]=value; 31 } 32 else{ 33 if(mp.size()==cap){ //如果容量满了,就删除最后一个再把新的key加进来作为链表头结点 34 int x=st.back(); 35 mp.erase(x); 36 st.pop_back(); 37 st.push_front(key); 38 mp1[key]=value; 39 auto it=st.begin(); 40 mp[key]=it; 41 } 42 else{ //否则直接加入当前的key到链表头结点 43 st.push_front(key); 44 mp1[key]=value; 45 auto it=st.begin(); 46 mp[key]=it; 47 } 48 } 49 } 50 };
1 class LFUCache { 2 public: 3 int cap; 4 unordered_map<int,list<int>>mp;//key的使用频率-双向链表 5 unordered_map<int,list<int>::iterator>mp1;//key-链表结点迭代器 6 unordered_map<int,int>mp2;//key-key的使用频率 7 unordered_map<int,int>mp3;//存key-value键值对,最终答案 8 LFUCache(int capacity) { 9 cap=capacity; 10 } 11 int id=0;//记录最小的频率 12 int get(int key) { 13 if(mp2.find(key)==mp2.end()) return -1;//key不存在于任何一个链表中返回-1 14 int x=mp2[key];//先获取原先的频率 15 mp[x].erase(mp1[key]);//随即x的所属链表删除对应结点 16 mp1.erase(key);//删除对应迭代器 17 if(id==x&&mp[x].empty()) id++;//如果当前最小频率等于原先频率且x所属链表已经为空,更新最小频率 18 if(mp.find(x+1)!=mp.end()) mp[x+1].push_front(key);//如果x+1频率已经有链表了则直接插入到链表头 19 else mp[x+1]={key};//否则映射到构建新的链表 20 mp1[key]=mp[x+1].begin();//更新key到迭代器的映射 21 mp2[key]++;//频率肯定要增加1 22 return mp3[key];//返回答案 23 } 24 void put(int key, int value) { 25 if(get(key)==-1){//如果key不存在 26 if(mp2.size()==cap){//容量已满 27 int x=mp[id].back();// 取出最小频率所属链表的尾节点,删掉 28 mp1.erase(x); 29 mp2.erase(x); 30 mp3.erase(x); 31 mp[id].pop_back(); 32 id=1;// 最小频率置为1 33 if(mp.find(1)!=mp.end()) mp[1].push_front(key);//如果前面删掉后链表不为空 34 else mp[1]={key};//如果前面删掉后链表为空,创建新链表 35 mp1[key]=mp[1].begin();//更新key到迭代器的映射 36 } 37 else{//容量未满 38 id=1;// 最小频率置为1 39 if(mp.find(1)!=mp.end()) mp[1].push_front(key);//如果链表不为空,插入到链表头 40 else mp[1]={key};//否则创建新链表 41 mp1[key]=mp[1].begin();//更新key到迭代器的映射 42 } 43 mp2[key]=1;//key对应的频率置为1 44 } 45 mp3[key]=value;//最后更新key-value 46 } 47 };
1 /** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode() : val(0), next(nullptr) {} 7 * ListNode(int x) : val(x), next(nullptr) {} 8 * ListNode(int x, ListNode *next) : val(x), next(next) {} 9 * }; 10 */ 11 class Solution { 12 public: 13 ListNode* removeNthFromEnd(ListNode* head, int n) { 14 ListNode *p = head, *q = head, *pre = head; 15 if (!p->next) { 16 delete(head); 17 head = nullptr; 18 return head; 19 } 20 int cnt = 0; 21 while (q) { 22 if (cnt < n) { 23 cnt++; 24 q = q->next; 25 } 26 else { 27 q = q->next; 28 pre = p; 29 p = p->next; 30 } 31 } 32 if (pre == p) { 33 p = p->next; 34 delete(head); 35 head = p; 36 return head; 37 } 38 pre->next = p->next; 39 delete(p); 40 return head; 41 } 42 };
1 class Solution { 2 public: 3 ListNode* reverseKGroup(ListNode* head, int k) { 4 ListNode *p=head; 5 if(k==1||p->next==NULL)return p; 6 ListNode *pre=NULL; 7 ListNode *tail=NULL; 8 ListNode *prehead=NULL; 9 ListNode *ans; 10 int n=0; 11 while(p){ 12 n++; 13 p=p->next; 14 } 15 int h=0; 16 p=head; 17 ListNode *nowhead=NULL; 18 while(p){ 19 h++; 20 if(h%k==1){ 21 nowhead=p; 22 if(h>=(n-n%k)&&(n%k!=0)){ 23 prehead->next=nowhead; 24 break; 25 } 26 } 27 if(h%k==0){ 28 tail=p; 29 if(h==k)ans=p; 30 if(h>k){ 31 prehead->next=tail; 32 } 33 prehead=nowhead; 34 35 } 36 ListNode *q=p->next; 37 p->next=pre; 38 pre=p; 39 p=q; 40 if(h==n){ 41 nowhead->next=NULL; 42 break; 43 } 44 } 45 return ans; 46 } 47 };
1 /** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode() : val(0), next(nullptr) {} 7 * ListNode(int x) : val(x), next(nullptr) {} 8 * ListNode(int x, ListNode *next) : val(x), next(next) {} 9 * }; 10 */ 11 struct node{ 12 int val; 13 ListNode *pt; 14 bool operator <(const node&t)const{ 15 return val>t.val; 16 } 17 }; 18 class Solution { 19 public: 20 ListNode* mergeKLists(vector<ListNode*>& lists) { 21 ListNode *head=new ListNode(0,NULL); 22 ListNode *pre=head; 23 priority_queue<node>q; 24 for(auto p:lists){ 25 if(p)q.push(node{p->val,p}); 26 } 27 while(!q.empty()){ 28 node now=q.top(); 29 pre->next=now.pt; 30 q.pop(); 31 if(now.pt->next)q.push(node{now.pt->next->val,now.pt->next}); 32 pre=now.pt; 33 } 34 return head->next; 35 } 36 };
1 double solve(vector<double>& nums1, vector<double>& nums2){ 2 int n = nums1.size(), m = nums2.size(); 3 int i = 0, j = 0; 4 double m1 = -1, m2 = -1; 5 for(int cnt = 0; cnt <= (n + m) / 2; cnt++) { 6 m2 = m1; 7 if(i != n && j != m) m1 = (nums1[i] < nums2[j]) ? nums1[i++] : nums2[j++]; 8 else if(i == n) m1 = nums2[j++]; 9 else m1 = nums1[i++]; 10 } 11 if((n + m) % 2 == 0) return (m1 + m2) / 2; 12 return m1; 13 }
1 // 经典的递归回溯的解法:就是按照每一个index递增,然后和所有后续位置交换 2 // 优化: 预先按照输入长度n得到数量,避免vector的resize的性能损耗 3 class Solution { 4 private: 5 int resIndex = 0; 6 void dfs(int index, string& S, int n, vector<string>& res){ 7 if (index == n-1){ 8 res[resIndex] = S; 9 ++resIndex; 10 return; 11 } 12 for (int i = index; i < n; ++i){ 13 // 交换 14 swap(S[i], S[index]); 15 dfs(index+1, S, n, res); 16 // 回溯需要修改会去 17 swap(S[i], S[index]); 18 } 19 } 20 int getSize(int n){ 21 return n == 1 ? 1 : n*getSize(n-1); 22 } 23 public: 24 vector<string> permutation(string S) { 25 int n = S.size(); 26 // 预先分配好空间 27 vector<string> res(getSize(n)); 28 dfs(0, S, n, res); 29 return res; 30 } 31 };
1 class MyStack { 2 public: 3 queue<int> q1, q2; 4 MyStack() {} 5 void push(int x) { 6 q2.push(x); 7 while (!q1.empty()) { 8 q2.push(q1.front()); 9 q1.pop(); 10 } 11 swap(q1,q2); 12 } 13 int pop() { 14 if(!q1.empty()) { 15 int x = q1.front(); 16 q1.pop(); 17 return x; 18 } 19 return -1; 20 } 21 int top() { 22 if (q1.empty()) return -1; 23 return q1.front(); 24 } 25 bool empty() { 26 return q1.empty(); 27 } 28 };
1 class MyStack { 2 public: 3 MyStack() { 4 } 5 queue<int>q1, q2; 6 bool empty(){ 7 if (q1.empty()) return true; 8 return false; 9 } 10 int top(){ 11 if (q1.empty()) return -1; 12 if(q1.size() == 1) { 13 return q1.front(); 14 } 15 while (!q2.empty()) q2.pop(); 16 while (q1.size() > 1) { 17 q2.push(q1.front()); 18 q1.pop(); 19 } 20 return q1.front(); 21 } 22 int pop(){ 23 int res = top(); 24 if (res == -1) return -1; 25 q1.pop(); 26 swap(q1, q2); 27 return res; 28 } 29 void push(int x){ 30 q1.push(x); 31 } 32 };
1 class Solution { 2 public: 3 int check(string sp){ 4 int n=sp.size(); 5 if(n<1||n>4)return 0; 6 for(auto c:sp){ 7 if(!isdigit(c))return 0; 8 } 9 int x=stoi(sp); 10 if(x<0||x>255)return 0; 11 if(sp[0]=='0'&&sp.length()>1)return 0; 12 return 1; 13 } 14 int ipv4(string s){ 15 if(s[s.size()-1]=='.')return 0; 16 stringstream ss(s); 17 string sp; 18 vector<string>v; 19 while(getline(ss,sp,'.')){ 20 v.push_back(sp); 21 } 22 if(v.size()!=4)return 0; 23 for(auto sp:v){ 24 if(!check(sp))return 0; 25 } 26 return 1; 27 } 28 int check1(string sp){ 29 int n=sp.size(); 30 if(n<1||n>4)return 0; 31 for(auto c:sp){ 32 if(!isdigit(c)&&!((c>='a'&&c<='f')||(c>='A'&&c<='F')))return 0; 33 } 34 return 1; 35 } 36 int ipv6(string s){ 37 if(s[s.size()-1]==':')return 0; 38 stringstream ss(s); 39 string sp; 40 vector<string>v; 41 int x=0; 42 while(getline(ss,sp,':')){ 43 x++; 44 if(x>8)return 0; 45 v.push_back(sp); 46 } 47 if(v.size()!=8)return 0; 48 for(auto sp:v){ 49 if(!check1(sp))return 0; 50 } 51 return 1; 52 } 53 string validIPAddress(string queryIP) { 54 if(queryIP.size()==0)return "Neither"; 55 if(ipv4(queryIP))return "IPv4"; 56 else if(ipv6(queryIP))return "IPv6"; 57 else return"Neither"; 58 } 59 };
1 class Solution { 2 public: 3 void rotate(vector<int>& nums, int k) { 4 k %= nums.size(); // 轮转 k 次等于轮转 k%n 次 5 ranges::reverse(nums); 6 reverse(nums.begin(), nums.begin() + k); 7 reverse(nums.begin() + k, nums.end()); 8 } 9 };
1 //dp思想,如果加上当前位置的数后累计的和变为负数,则当前位置以及之前的所有元素都可以丢弃 2 class Solution { 3 public: 4 int maxSubArray(vector<int>& nums) { 5 int n=nums.size(); 6 int ans=-1e9,res=0,mx=-1e9; 7 for(int i=0;i<n;i++){ 8 mx=max(mx,nums[i]); 9 res+=nums[i]; 10 if(res>0){ 11 ans=max(ans,res); 12 continue; 13 } 14 else res=0; 15 } 16 if(mx<=0)ans=mx; 17 return ans; 18 } 19 };
1 // 遍历text1,在text1和text2中寻找以text1[i]为末尾元素的最长公共子序列, 2 // 如果text2[j] != text1[i],dp[j]等于text2中以text2[j]为末尾元素的子序列和text1的子串 text1[0]~text1[i - 1] 的最长公共子序列的长度; 3 // 如果text2[j] == text1[i],dp[j]等于text1的子串 text1[0]~text1[i - 1] 和text2的子串 text2[0]~text2[j - 1]的最长公共子序列的长度再加1。 4 // 即dp[j] = max(dp[k]) + 1, k∈[0,j), 且dp[k]为上一轮循环的结果,而不是本轮循环更新过的值。 5 class Solution { 6 public: 7 int longestCommonSubsequence(string text1, string text2) { 8 vector<int> dp(text2.size(), 0); 9 for (int i = 0; i < text1.size(); ++i) { 10 int maxlen = 0; 11 for (int j = 0; j < text2.size(); ++j) { 12 int newlen = max(maxlen, dp[j]); 13 if (text1[i] == text2[j]) { 14 dp[j] = maxlen + 1; 15 } 16 maxlen = newlen; 17 } 18 } 19 return *max_element(dp.cbegin(), dp.cend()); 20 } 21 };
1 class Solution { 2 public: 3 int lengthOfLIS(vector<int>& nums) { 4 int n=nums.size(); 5 vector<int>d(n,1); 6 int ans=1; 7 for(int i=0;i<n;i++){ 8 for(int j=0;j<i;j++){ 9 if(nums[i]>nums[j]){ 10 d[i]=max(d[i],d[j]+1); 11 ans=max(ans,d[i]); 12 } 13 } 14 } 15 return ans; 16 } 17 };
1 //d[i][j]表示区间[i,j]如果满足回文子串的话,长度是多少 2 class Solution { 3 public: 4 string longestPalindrome(string s) { 5 int n=s.size(); 6 vector<vector<int>>d(n,vector<int>(n,0)); 7 for(int i=0;i<n;i++) d[i][i]=1; 8 int l=0,ans=1; 9 for(int i=n-1;i>=0;i--){ 10 for(int j=i+1;j<n;j++){ 11 if(j==i+1&&s[i]==s[j]){ 12 d[i][j]=2; 13 if(ans<2){ 14 ans=2; 15 l=i; 16 } 17 } 18 else if(s[i]==s[j]&&(d[i+1][j-1]||i==j-2)){ 19 d[i][j]=d[i+1][j-1]+2; 20 if(d[i][j]>ans){ 21 ans=d[i][j]; 22 l=i; 23 } 24 } 25 } 26 } 27 return s.substr(l,ans); 28 } 29 };
1 //d[i][j] 表示 s 的前 i 个字符与 p 中的前 j 个字符是否能够匹配 2 class Solution { 3 public: 4 bool isMatch(string s, string p) { 5 int n = s.size(); 6 int m = p.size(); 7 vector<vector<int>> d(n+1,vector<int>(m+1,0)); 8 d[0][0] = true; 9 for(int i=1;i<=m;i++){ 10 if(i<m&&p[i-1]!='*'&&p[i]!='*')break; 11 if(i==m-1&&p[i]!='*')break; 12 d[0][i]=1; 13 } 14 for(int i=1;i<=n;i++){ 15 char pre='A'; 16 for(int j=1;j<=m;j++){ 17 if(p[j-1]!='*')pre=p[j-1]; 18 if(p[j-1]=='*'){ 19 if(pre=='.'){ 20 d[i][j]=d[i-1][j]|d[i][j-1]; 21 if(j>=2)d[i][j]=d[i][j]|d[i][j-2]; 22 d[i-1][j]=d[i-1][j-2]|d[i-1][j]; 23 } 24 else{ 25 if(pre==s[i-1]){ 26 d[i][j]=d[i-1][j]|d[i][j-1]; 27 d[i][j]=d[i][j]|d[i][j-2]; 28 } 29 else{ 30 d[i-1][j]=d[i-1][j-2]|d[i-1][j]; 31 d[i][j]=d[i][j-2]; 32 } 33 } 34 } 35 else if(p[j-1]=='.'||p[j-1]==s[i-1]){ 36 d[i][j]=d[i-1][j-1]; 37 } 38 } 39 } 40 return d[n][m]; 41 } 42 };
通配符匹配(包括*和?)
1 class Solution { 2 public: 3 bool isMatch(string s, string p) { 4 int n=s.size(); 5 int m=p.size(); 6 vector<vector<int>>d(n+1,vector<int>(m+1)); 7 d[0][0]=true;//d[i][j]表示s的前i个和p的前j个能否匹配 8 for(int i=1;i<=m;i++){ 9 if(p[i-1]!='*')break; 10 d[0][i]=true; 11 } 12 for(int i=1;i<=n;i++){ 13 for(int j=1;j<=m;j++){ 14 if(p[j-1]!='*'){ 15 if(p[j-1]=='?'||s[i-1]==p[j-1])d[i][j]=d[i-1][j-1]; 16 } 17 else d[i][j]=d[i-1][j]|d[i][j-1]; 18 } 19 } 20 return d[n][m]; 21 } 22 };
多线程
实现一个固定容量的阻塞队列,用生产者消费者测试。
1 package com.venux.train.common.util; 2 3 import java.util.LinkedList; 4 import java.util.Queue; 5 import java.util.concurrent.locks.Condition; 6 import java.util.concurrent.locks.ReentrantLock; 7 8 /** 9 * 一个具有固定大小的阻塞队列,支持线程安全的操作。 10 * 它允许在不发生死锁的情况下添加和移除元素。 11 * 12 * @param <T> 队列中元素的类型 13 */ 14 public class FixedSizeBlockingQueue<T> { 15 private final int capacity;// 队列容量 16 private final Queue<T> queue;// 底层的队列 17 private final ReentrantLock lock;// 用于同步的锁 18 private final Condition notFull;// 队列不满的条件变量,用于阻塞等待队列不满,以便添加元素 19 private final Condition notEmpty;// 队列不空的条件变量,用于阻塞等待队列不空,以便移除元素 20 private volatile boolean isClosed = false; // 用于标记队列是否已关闭 21 22 // 构造函数,初始化队列的容量,以及相关的锁和条件变量 23 public FixedSizeBlockingQueue(int capacity) { 24 this.capacity = capacity; 25 this.queue = new LinkedList<>(); 26 this.lock = new ReentrantLock(); 27 this.notFull = lock.newCondition(); 28 this.notEmpty = lock.newCondition(); 29 } 30 // 将元素放入队列,如果队列已满,则阻塞当前线程,直到队列不满或者队列被关闭 31 public void put(T item) throws InterruptedException { 32 lock.lock(); 33 try { 34 while (queue.size() == capacity || isClosed) { // 检查队列是否已满或已关闭 35 if (isClosed) { 36 throw new IllegalStateException("Queue is closed"); 37 } 38 notFull.await(); 39 } 40 queue.add(item); 41 notEmpty.signal(); 42 } finally { 43 lock.unlock(); 44 } 45 } 46 // 从队列中取出一个元素,如果队列为空,则阻塞当前线程,直到队列不空或者队列被关闭 47 public T take() throws InterruptedException { 48 lock.lock(); 49 try { 50 while (queue.isEmpty()) { 51 notEmpty.await(); 52 } 53 T item = queue.poll(); 54 notFull.signal(); 55 return item; 56 } finally { 57 lock.unlock(); 58 } 59 } 60 // 返回队列中当前元素的数量 61 public int size() { 62 lock.lock(); 63 try { 64 return queue.size(); 65 } finally { 66 lock.unlock(); 67 } 68 } 69 // 关闭队列,不再接受新的元素 70 public void close() { 71 lock.lock(); 72 try { 73 isClosed = true; // 设置关闭标志 74 // 可选:唤醒所有等待的put线程 75 notFull.signalAll(); 76 } finally { 77 lock.unlock(); 78 } 79 } 80 public static void main(String[] args) { 81 FixedSizeBlockingQueue<Integer> queue = new FixedSizeBlockingQueue<>(5); 82 Thread producer = new Thread(() -> { 83 for (int i = 0; i < 10; i++) { 84 try { 85 queue.put(i); 86 System.out.println("Produced: " + i); 87 } catch (InterruptedException e) { 88 e.printStackTrace(); 89 } 90 } 91 }); 92 Thread consumer = new Thread(() -> { 93 for (int i = 0; i < 10; i++) { 94 try { 95 int item = queue.take(); 96 System.out.println("Consumed: " + item); 97 } catch (InterruptedException e) { 98 e.printStackTrace(); 99 } 100 } 101 }); 102 producer.start(); 103 consumer.start(); 104 try { 105 producer.join(); 106 consumer.join(); 107 } catch (InterruptedException e) { 108 e.printStackTrace(); 109 } 110 System.out.println("Queue size: " + queue.size()); 111 queue.close(); 112 System.out.println("Queue closed."); 113 } 114 }
生产者消费者多线程实现, 使用了Java包的BlockingQueue作为阻塞队列。
1 import java.util.concurrent.ArrayBlockingQueue; 2 import java.util.concurrent.BlockingQueue; 3 4 public class ProducerConsumerExample { 5 6 // 创建一个BlockingQueue,容量为10 7 private static final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10); 8 9 // 生产者线程 10 static class Producer implements Runnable { 11 public void run() { 12 int value = 0; 13 while (true) { 14 try { 15 // 生产数据 16 Thread.sleep(1000); // 模拟耗时操作 17 value++; 18 System.out.println("生产者生产了:" + value); 19 20 // 将生产的数据放入队列 21 queue.put(value); 22 23 } catch (InterruptedException e) { 24 Thread.currentThread().interrupt(); 25 } 26 } 27 } 28 } 29 30 // 消费者线程 31 static class Consumer implements Runnable { 32 public void run() { 33 while (true) { 34 try { 35 // 从队列中取出数据 36 Integer value = queue.take(); 37 System.out.println("消费者消费了:" + value); 38 39 // 假设消费者处理数据也需要一些时间 40 Thread.sleep(500); 41 42 } catch (InterruptedException e) { 43 Thread.currentThread().interrupt(); 44 } 45 } 46 } 47 } 48 49 public static void main(String[] args) { 50 // 创建并启动生产者线程 51 Thread producerThread = new Thread(new Producer()); 52 producerThread.start(); 53 54 // 创建并启动消费者线程 55 Thread consumerThread = new Thread(new Consumer()); 56 consumerThread.start(); 57 } 58 }