① 抛洒物项目具体讲讲
② 项目几个人做的 难点(找创新点 效率)
③ 了解中兴的产业吗
④ 网路的7层模型
⑤ 有没有学习过数字电路
⑥ 薪资要求 最低
⑦ 生活环境介绍
⑧ 想在哪里工作
-项目(web server)
① 讲一下epoll怎么用的
② 项目用的多进程还是多线程 怎么考虑的
③项目难点在哪里(类的设计、优化、解析报文)
④mysql数据库怎么用的
⑤有没有解决tcp沾包问题
思路:用一个变量flag控制线程之间的执行顺序,线程A中判断flag的值,如果==‘A’,轮到该线程打印A,线程B也类似。
#include
/*
直接用一个flag标识就可以
*/
using namespace std;
char flag = 'A';
void printA(){
while(true) {
// flag为A标识B已经打印完了 轮到打印A了
if (flag == 'A') {
cout << "A" << endl;
flag = 'B';
}
}
}
void printB(){
while(true) {
// flag为B标识A已经打印完了 轮到打印B了
if (flag == 'B') {
cout << "B" << endl;
flag = 'C';
}
}
}
int main() {
thread A(printA);
thread B(printB);
//join的作用是当线程执行完之后才返回
//如果后续有需要两个结果才能执行的process 需要先调用join保证任务完成
A.join();
B.join();
}
轮流打印多个字母也一样 开线程用flag控制顺序就好。
// 当前节点的值应该在min_ 和 max_之间
bool dfs(TreeNode* root,long long min_,long long max_) {
if (root == nullptr) {
return true;
}
if (root->val <= min_ || root->val >= max_){
return false;
}
bool l = dfs(root->left,min_,root->val);
bool r = dfs(root->right,root->val,max_);
return l && r;
}
long last = LONG_MIN;
bool isValidBST(TreeNode* root) {
if (root == nullptr) {
return true;
}
bool l = isValidBST(root->left);
if (last < root->val) {
last = root->val;
}else{
return false;
}
bool r = isValidBST(root->right);
return l && r;
}
思路:用一个自建双向链表存储具体的key,value信息(struct Node)。链表前面是最近访问的节点,当访问一个值或者新加入一个值的时候需要将该值移到链表头部。链表尾部是空间不够的时候需要删除的不经常使用的节点。
用一个hash表存储key和对应Node指针,使得当访问一个数据的时候可以达到O(1)的时间。
注:对链表进行插入和删除操作也对应处理hash表。Node中也要存储key的信息,因为在删除尾部节点的时候需要根据key删除对应hash表中的节点。
class LRUCache {
struct Node {
int key,val;
Node *next,*prev;
Node(int key,int value) : key(key),val(value),prev(nullptr),next(nullptr){};
};
unordered_map<int,Node*> cache;
Node* head;
Node* tail;
int capacity;
int size;
public:
LRUCache(int capacity) {
this->capacity = capacity;
size = 0;
head = new Node(0,0);
tail = new Node(0,0);
head->next = tail;
tail->prev = head;
}
int get(int key) {
if (cache.find(key) == cache.end()) {
return -1;
}
Node* cur = cache[key];
MoveToHead(cur);
return cur->val;
}
void put(int key, int value) {
if (cache.find(key) != cache.end()) {
// 如果已经存在 更改该值 并移动到到链表头部
Node *cur = cache[key];
cur->val = value;
MoveToHead(cur);
return;
}
// 不存在
if (size >= capacity) {
// 容量不够 删除一个末尾的节点
// 删除的时候需要知道该节点对应的key是多少 所以链表节点需要存储key的值
cache.erase(tail->prev->key);
DeleteTail();
--size;
}
// 新建一个节点 插入头部
Node* newnode = new Node(key,value);
cache[key] = newnode;
AddToHead(newnode);
++size;
}
void MoveToHead(Node* cur) {
// 删除当前节点s
cur->next->prev = cur->prev;
cur->prev->next = cur->next;
// 插入到头部
AddToHead(cur);
}
void AddToHead(Node* cur) {
cur->next = head->next;
head->next = cur;
cur->prev = head;
cur->next->prev = cur;
}
void DeleteTail() {
tail->prev->prev->next = tail;
tail->prev = tail->prev->prev;
}
};
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache* obj = new LRUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/
class Solution {
int partition(vector<int>& nums,int l,int r) {
int i = rand() % (r - l + 1) + l;//生成[l,r]的随机数 选择随机主元
swap(nums[i],nums[r]);
int pivot = nums[r];
i = l - 1;
for (int j = l;j <= r - 1;++j) {
if (nums[j] <= pivot){
i = i + 1;
swap(nums[i],nums[j]);
}
}
swap(nums[i+1],nums[r]);
return i+1;
}
void quickSort(vector<int>& nums,int l,int r) {
if (l >= r) return;
// 将数据分为两部分 一部分小于pos处的值 另一部分大于pos处的值
int pos = partition(nums,l,r);
quickSort(nums,l,pos-1);// 递归排左半部分
quickSort(nums,pos+1,r);// 递归排右半部分
}
public:
vector<int> sortArray(vector<int>& nums) {
srand((unsigned)time(NULL));
quickSort(nums,0,nums.size() - 1);
return nums;
}
};
class Solution {
void buildHeap(vector<int>& nums,int i,int n) {
while((2 * i + 1) <= n) {
int l = 2 * i + 1;
int r = 2 * i + 2;
int large = i;
if (l <= n && nums[l] > nums[i]) {
large = l;
}
if (r <= n && nums[r] > nums[large]) {
large = r;
}
if(large == i) {
break;
}
swap(nums[large],nums[i]);
i = large;
}
}
void HeapSort(vector<int>& nums) {
// 调整为一个大顶堆
int len = nums.size() - 1;
for (int i = len / 2;i >= 0;--i) {
buildHeap(nums,i,len);
}
// 排序 将最后的值和根节点进行交换 然后调整根节点
for (int i = len;i >= 1;--i) {
swap(nums[i],nums[0]);
len -= 1;
buildHeap(nums,0,len);
}
}
public:
vector<int> sortArray(vector<int>& nums) {
// 堆排序
HeapSort(nums);
return nums;
}
};
class Solution {
vector<int> tmp;
void mergeSort(vector<int>& nums,int left,int right){
if(left >= right) return;
int mid = (right - left) / 2 + left;
mergeSort(nums,left,mid);
mergeSort(nums,mid+1,right);
// 对有序序列进行合并放到数组tmp中 类似合并有序链表的操作
int l = left,r=mid+1,i=left;
while(l<=mid && r<=right){
if(nums[l] <= nums[r]){
tmp[i++] = nums[l++];
}else{
tmp[i++] = nums[r++];
}
}
while(l<=mid) tmp[i++] = nums[l++];
while(r<=right) tmp[i++] = nums[r++];
for(int i=left;i<=right;++i){
nums[i] = tmp[i];
}
}
public:
vector<int> sortArray(vector<int>& nums) {
// 将一个元素视为有序 自底向上不断的合并相邻的有序序列 直到整个序列有序
int n = nums.size();
tmp.resize(n);
mergeSort(nums,0,n-1);
return nums;
}
};
懒汉模式:将构造函数设置为私有的,给外界提供一个getinstance接口,该接口中创建一个局部静态变量返回给外界。由于变量是static的在程序中只有一个,从而达到了只有一个实例的目的。是线程安全的。
饿汉模式:在类中定义一个私有的静态变量,在程序开始的时候就进行初始化。外界直接通过getinstance接口获得该实例对象。
#include
#include
using namespace std;
class lazy_single{ //懒汉模式:用到的时候才初始化实例对象
public:
static lazy_single& getInstance(){
static lazy_single instance; // 调用私有构造函数
return instance;
}
private:
lazy_single() {};
~lazy_single() {};
lazy_single(const lazy_single&);
lazy_single& operator=(const lazy_single&);
};
// -------------------------------------------------
class hungry_single{ // 饿汉模式 程序一开始就创建了实例对象
public:
static hungry_single& getInstance(){
return instance;
}
private:
static hungry_single instance;
private:
hungry_single();
~hungry_single();
hungry_single(const hungry_single&);
hungry_single& operator=(const hungry_single&);
};
hungry_single hungry_single::instance;
思路:类中三个对象,分别是size,capacity和data,data是指向数组的指针,当size等于capacity的时候新建一块数组空间把原来的数据复制过去。
template<typename T>
class MyVector {
private:
T *data;
size_t _capacity;
size_t _size;
void resize(int new_size){
T *newdata = new T[new_size];
for(int i = 0; i < _size; ++i)
newdata[i] = data[i];
delete[] data;
data = newdata;
_capacity = new_size;
}
public:
MyVector(){
data = new T[10];
_capacity = 10;
_size = 0;
}
MyVector(size_t n){
data = new T[n];
_capacity = n;
_size = 0;
}
~MyVector(){
delete[] data;
}
void reserve(size_t n){
if(n > _capacity){
T *newdata = new T[n];
_capacity = n;
for(int i = 0; i < _size; ++i)
newdata[i] = data[i];
data = newdata;
}
}
void push_back(T e){
if(_size == _capacity){
resize(2 * _capacity);
}
data[_size++] = e;
}
T pop_back(T e){
T temp = data[_size];
--_size;
return temp;
}
size_t size(){
return _size;
}
bool empty(){
return _size == 0;
}
T &operator[](int i){
return *(data + i);
}
size_t capacity(){
return _capacity;
}
void clear(){
_size = 0;
}
};
vector中存储自建单向链表链头结点的地址。
#include
#include
using namespace std;
template<typename T1,typename T2>
struce node{
T1 key;
T2 value;
node* next;
node(T1 k,T2 v,node* n):key(k),value(v),next(n){};
}
class Hash{
private:
vector<node*> h;
int size=0;
int capacity = 0;
public:
Hash(){
h.resize(10);
capacity = 10;
}
Hash(int n){
h.resize(n);
capacity = n;
}
~Hash(){
delete[] h;
}
void push(T1 key,T2 value){
int index = key % capacity; // 得到key对应的index
node* e = new node(key,value,nullptr);
node* t = h[index];
if(t == nullptr){ // 如果还没有对应的链表 创建一个
h[index] = e;
++size;
}else{ // 新节点加到链表末尾
while(t->next){
t=t->next;
}
t->next = e;
}
}
T2& get(T1 key){
int index = key % capacity;
node* tmp = h[index];
while(tmp){
if(tmp->key == key){
return tmp->value;
}
tmp=tmp->next;
}
return -1;
}
};
左右下表指针往中间扫描,如果两指针和比target小,左指针往右一步,如果比target大,右指针往左一步。
lass Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int n = numbers.size();
int i = 0,j = n-1,sum;
vector<int> ans;
while(i < j) {
sum = numbers[i] + numbers[j] ;
if (sum == target) {
ans.push_back(i+1);
ans.push_back(j+1);
break;
} else if (sum < target) {
++i;
} else if (sum > target) {
--j;
}
}
return ans;
}
};
解法一:快排 找第n-k小,加入随机化后的时间复杂度是O(n)。
class Solution {
int quickSort(vector<int>& nums,int l,int r, int k) {
// 找枢纽元素
int pos = l + rand() % (r - l + 1);
// swap(nums[pos],nums[r]);
// 将数组中的元素按照枢纽分为两部分 一部分比它大 一部分比它小
// cout << pivot << endl;
int x = nums[pos], i = l - 1;
swap(nums[pos], nums[r]);
for (int j = l; j < r; ++j) {
if (nums[j] <= x) {
swap(nums[++i], nums[j]);
}
}
swap(nums[i + 1], nums[r]);
if (i+1 == k) {
return nums[i+1];
} else {
return i+1 < k ? quickSort(nums,i + 2,r,k) :
quickSort(nums,l,i,k);
}
}
public:
int findKthLargest(vector<int>& nums, int k) {
srand((int)time(0));
int n = nums.size();
return quickSort(nums,0,n-1,n-k);
}
};
解法二:堆
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
priority_queue<int,vector<int>,greater<int>> pq; // 使用小根堆
for (int i=0;i<nums.size();++i) {
if (pq.size() < k) { // 堆中的元素
pq.push(nums[i]);
} else { // 堆中的元素》k个 判断当前元素和堆顶元素的大小
int t = pq.top();
if (t < nums[i]) {
pq.pop();
pq.push(nums[i]);
}
}
}
return pq.top();
}
};
// 数学 n-1个数字删除的下标为x,则其在n个数中的下标为x+m
// f(n,m) = (f(n-1,m) + m) % n;
// 非递归解法
class Solution {
public:
int lastRemaining(int n, int m) {
int res = 0;
for (int i=2;i<=n;++i) {
res = (res+m) % i;
}
return res;
}
};
// 队列模拟
class Solution {
public:
int lastRemaining(int n, int m) {
queue<int> q;
for(int i = 0; i < n; i++) q.push(i);
int cnt = 0;
while(q.size() > 1){
int t = q.front();
q.pop();
cnt++;
if(cnt == m){
cnt = 0;
continue;
}
q.push(t);
}
return q.front();
}
};
针对原数组随机生成一个索引下标,将该索引上的元素添加到新数组中。在原数组中将该元素交换到末尾,表示已经删除,下次生成索引下标的时候范围-1,表示忽略尾部的元素。
void shuffle(int arr[],int length) {
if (arr == nullptr || length == 0) {
return;
}
int rand_index;//每次随机生成的下标
srand((int)time(0));
for (int i=0;i<length;++i) {
// 每次随机生成一个从i到length-1范围内下标,和位置i处的交换
rand_index = i + rand() % (length - i);
swap(arr[i],arr[rand_index]);
}
}