手撕vector
class myvector{
public:
int cap = 99999;//数组的可用空间
int num_size;
int* vec = new int[cap];
myvector(){
this->num_size = 0;
}
~myvector(){
}
void push_back(int val){
if(num_size<cap)
this->vec[num_size++] = val;
//扩容
else{
this->cap = this->cap*2;
int* new_vec = new int[this->cap];
for(int i=0; i<num_size; i++){
new_vec[i] = this->vec[i];
}
delete[] this->vec;
this->vec = new_vec;
this->vec[num_size++] = val;
}
}
void pop_back(){
if(num_size>0)
this->num_size--;
return -1;
}
int back(){
if(num_size>0)
return this->vec[num_size-1];
return -1;
}
}
手撕strcpy
需考虑内存重叠问题,复制前判断字符串是否包含
void strcpy1(char* a,const char* b)
{
if (a == NULL || b == NULL)
return;
char* temp = a;
int len = strlen(b)+ 1;
if (a > b && a < b + len){
temp = temp + len - 1;
b = b + len - 1;
while (len--)
*temp--=*b--;
}
else {
while (len--)
*temp++ = *b++;
}
}
手撕string类
class MyString {
public:
MyString(const char* str = nullptr); //普通构造函数
MyString(const MyString& other); //拷贝构造函数
~MyString(void); //析构函数
MyString& operator=(const MyString& other); //赋值函数
private:
char* m_data; //用于保存字符串
};
//普通构造函数
MyString::MyString(const char* str) {
if (str == nullptr) {
m_data = new char[1];
*m_data = '\0';
} else {
int length = strlen(str);
m_data = new char[length + 1];
strcpy(m_data, str);
}
}
//析构函数
MyString::~MyString(void) {
delete[] m_data; //或delete m_data
}
//拷贝构造函数
MyString::MyString(const MyString& other) //输入参数为const
{
int length = strlen(other.m_data);
m_data = new char[length + 1];
strcpy(m_data, other.m_data);
}
//赋值函数
MyString& MyString::operator=(const MyString& other) { //输入参数为const型
if (this == &other) return *this;
delete[] m_data; //释放原有内存空间
int length = strlen(other.m_data);
m_data = new char[length + 1];
strcpy(m_data, other.m_data);
return *this; //返回本对象应用
}
手撕shared_ptr
a、当创建智能指针类的新对象时,初始化指针,并将引用计数设置为1;
b、当能智能指针类对象作为另一个对象的副本时,拷贝构造函数复制副本的指向辅助类对象的指针,并增加辅助类对象对基础类对象的引用计数(加1);
c、使用赋值操作符对一个智能指针类对象进行赋值时,处理复杂一点:先使左操作数的引用计数减 1(为何减 1:因为指针已经指向别的地方),如果减1后引用计数为 0,则释放指针所指对象内存。然后增加右操作数所指对象的引用计数(为何增加:因为此时做操作数指向对象即右操作数指向对象);
d、完成析构函数:调用析构函数时,析构函数先使引用计数减 1,如果减至 0 则 delete 对象。
template<class T>
class SmartPtr{
public:
SmartPtr(T* ptr = NULL): _ptr(ptr), _pcount(new int(1)) {} //构造函数
SmartPtr(const SmartPtr& s): _ptr(s.ptr), _pcount(s._pcount){ //拷贝构造函数
*(_pcount)++; //用指针进行count操作能够方便控制不同智能指针对象的计数值
}
SmartPtr<T>& operator=(const SmartPtr& s){ //重载 = ,赋值函数
if(this != &s){ //检测自我赋值
if(--(*(this->_pcount)) == 0){
delete this->_ptr;
delete this->_pcount;
}
_ptr = s._ptr;
_pcount = s._pcount;
*(_pcount)++;
}
return *this;
}
T& operator*(){
return *(this->_ptr);
}
T* operator->(){
return this->_ptr;
}
~SmartPtr(){ //析构函数要进行判定,count为0才会delete,delete之后避免野指针要赋值nullptr
--(*(this->_pcount));
if(this->_pcount == 0){
delete _ptr;
_ptr = nullptr;
delete _pcount;
_pcount = nullptr;
}
}
private:
T* _ptr;
int* _pcount;
};
手撕unique_ptr
class HasPtr{
friend ostream& operator<<(ostream&, const HasPtr&);
public:
//构造函数
HasPtr(const string &s = string()):ps(new string(s))
{cout<< "HasPtr(const string &s = string())"<<endl;}
HasPtr(HasPtr&& rhs) noexcept :ps(rhs.ps){rhs.ps = nullptr; cout<< "HasPtr(HasPtr&&)"<<endl;}
//拷贝构造函数
HasPtr& operator=(HasPtr &&p){
if(this != &rhs){
if(ps)
delete ps;
ps = rhs.ps;
rhs.ps = nullptr;
}
}
HasPtr(const HasPtr&) = delete; //拷贝构造与赋值不能使用
HasPtr& operator=(const HasPtr&) = delete;
//析构函数
~HasPtr(){
if(ps)
delete ps;
};
private:
string *ps;
};
手撕单例模式
懒汉式(懒汉式原来会有安全问题,c++11解决了多线程安全问题)
class CSingleton
{
private:
CSingleton(){ cout << "单例对象创建!" << endl; };
CSingleton(const CSingleton &);
CSingleton& operator=(const CSingleton &);
~CSingleton(){ cout << "单例对象销毁!" << endl; };
public:
static CSingleton * getInstance()
{
static CSingleton myInstance;
return &myInstance;
}
};
饿汉式
class CSingleton
{
private:
CSingleton(){ cout << "单例对象创建!" << endl; };
CSingleton(const CSingleton &);
CSingleton& operator=(const CSingleton &);
~CSingleton(){ cout << "单例对象销毁!" << endl; };
static CSingleton myInstance; // 单例对象在这里!
public:
static CSingleton* getInstance()
{
return &myInstance;
}
};
手撕生产者消费者
#define PRODUCER_NUM 5 //生产者数目
#define CONSUMER_NUM 5 //消费者数目
#define POOL_SIZE 11 //缓冲池大小
int pool[POOL_SIZE]; //缓冲区
int head=0; //缓冲池读取指针
int rear=0; //缓冲池写入指针
sem_t room_sem; //同步信号信号量,表示缓冲区有可用空间
sem_t product_sem; //同步信号量,表示缓冲区有可用产品
pthread_mutex_t mutex;
void producer_fun(void *arg)
{
while (1)
{
sleep(1);
sem_wait(&room_sem);
pthread_mutex_lock(&mutex);
//生产者往缓冲池中写入数据
pool[rear] = 1;
rear = (rear + 1) % POOL_SIZE;
printf("producer %d write to pool\n", (int)arg);
printf("pool size is %d\n",(rear-head+POOL_SIZE)%POOL_SIZE);
pthread_mutex_unlock(&mutex);
sem_post(&product_sem);
}
}
void consumer_fun(void *arg)
{
while (1)
{
int data;
sleep(10);
sem_wait(&product_sem);
pthread_mutex_lock(&mutex);
//消费者从缓冲池读取数据
data = pool[head];
head = (head + 1) % POOL_SIZE;
printf("consumer %d read from pool\n", (int)arg);
printf("pool size is %d\n",(rear-head+POOL_SIZE)%POOL_SIZE);
pthread_mutex_unlock(&mutex);
sem_post(&room_sem);
}
}
int main()
{
pthread_t producer_id[PRODUCER_NUM];
pthread_t consumer_id[CONSUMER_NUM];
pthread_mutex_init(&mutex, NULL); //初始化互斥量
int ret = sem_init(&room_sem, 0, POOL_SIZE-1); //初始化信号量room_sem为缓冲池大小
ret = sem_init(&product_sem, 0, 0); //初始化信号量product_sem为0,开始时缓冲池中没有数据
for (int i = 0; i < PRODUCER_NUM; i++)
{
//创建生产者线程
ret =pthread_create(&producer_id[i], NULL, producer_fun, (void*)i);
//创建消费者线程
ret = pthread_create(&consumer_id[i], NULL, consumer_fun, (void*)i);
}
}
手撕读写锁
手撕线程安全的队列
template<typename T> class ThreadSafe_Queue
{
private:
mutable mutex m_mut;
queue<T> m_queue;
condition_variable m_data_cond;
public:
ThreadSafe_Queue() {}
ThreadSafe_Queue(const ThreadSafe_Queue&) = delete;
void push(T data)
{
lock_guard<mutex> lg(m_mut);
m_queue.push(data);
m_data_cond.notify_one();
}
void WaitPop(T&t)
{
unique_lock<mutex> ul(m_mut);
m_data_cond.wait(ul, [this] {return !m_queue.empty(); });
t = m_queue.front();
m_queue.pop();
}
bool TryPop(T &t)
{
lock_guard<mutex> lg(m_mut);
if (m_queue.empty())
return false;
t = m_queue.front();
m_queue.pop();
return true;
}
bool IsEmpty()
{
lock_guard<mutex> lg(m_mut);
return m_queue.empty();
}
};
手撕bitset、bitset接口
struct Bitset
{
unsigned a[1600];
void reset(){
memset(a,0,sizeof(a));
}
Bitset(){
reset();
}
void flip(int x){
a[x>>5]^=1<<(x&31);
}
void set(int x){
a[x>>5]|=1<<(x&31);
}
void reset(int x){
a[x>>5]&=~(1<<(x&31));
}
int test(int x){
return (a[x>>5]>>(x&31))&1;
}
Bitset operator ~()const{
Bitset ret;
for(int i=0;i<1600;i++)ret.a[i]=~a[i];
return ret;
}
Bitset operator &(const Bitset &b)const{
Bitset ret;
for(int i=0;i<1600;i++)ret.a[i]=a[i]&b.a[i];
return ret;
}
Bitset operator |(const Bitset &b)const{
Bitset ret;
for(int i=0;i<1600;i++)ret.a[i]=a[i]|b.a[i];
return ret;
}
Bitset operator ^(const Bitset &b)const{
Bitset ret;
for(int i=0;i<1600;i++)ret.a[i]=a[i]^b.a[i];
return ret;
}
Bitset operator <<(const int t)const{
Bitset ret;
unsigned last=0;
int high=t>>5,low=t&31;
for(int i=0;i+high<1600;i++){
ret.a[i+high]=last|(a[i]<<low);
if(low)last=a[i]>>(32-low);
}
return ret;
}
Bitset operator >>(const int t)const{
Bitset ret;
unsigned last=0;
int high=t>>5,low=t&31;
for(int i=1600-1;i>=high;i--){
ret.a[i-high]=last|(a[i]>>low);
if(low)last=a[i]<<(32-low);
}
return ret;
}
vector<int> ones()const{
vector<int> ret;
for(int i=0;i<1600;i++){
unsigned tmp=a[i];
while(tmp){
short t=__builtin_ctz(tmp);
ret.pb((i<<5)|t);
tmp^=1u<<t;
}
}
return ret;
}
}use,trans,cur;
手撕布隆过滤器
class BloomFilterDemo {
//设置的位集大小
private int defaultSize = 16 << 24;
//最大索引位置
private int maxIndex = defaultSize - 1;
//建立一个二进制位集
private BitSet bits = new BitSet(defaultSize);
/*** 添加元素的方法*/
public void add(String key) {
int bloomCode[] = getCode(key);
for (int i = 0; i < 8; i++) {
bits.set(bloomCode[i]); // 这里影响添加的元素大小
}
}
/** 随机数生成器 */
private int[] getCode(String key) {
int[] randomArr = new int[8];
for (int i = 0; i < 8; i++) {
randomArr[i] = hash(key, i);
}
return randomArr;
}
/*** 自己定义的hash算法(一定要用&运算)
* @param key 输入的key值
* @param n 自定义的参数,用来传入不同参数生成不同的随机数*/
private int hash(String key, int n) {
int result = key.hashCode() * n;
return result & maxIndex;
}
/** 判断一个元素是否在表中*/
public boolean exits(String key) {
int keyCode[] = getCode(key);
if (bits.get(keyCode[0]) && bits.get(keyCode[1])
&& bits.get(keyCode[2]) && bits.get(keyCode[3])
&& bits.get(keyCode[4]) && bits.get(keyCode[5])
&& bits.get(keyCode[6]) && bits.get(keyCode[7])) {
return true;
}
return false;
}
}
public class BloomFilter {
public static void main(String[] args) {
BloomFilterDemo bf = new BloomFilterDemo();
Long aBeginTime = System.currentTimeMillis();// 记录BeginTime
for (int i = 0; i < 100000; i++) {
bf.add("Java" + i);
}
Long aEndTime = System.currentTimeMillis();// 记录EndTime
System.out.println("Insert Time-->" + (aEndTime - aBeginTime));
Long lBeginTime = System.currentTimeMillis();// 记录BeginTime
System.out.println(bf.exits("Java" + 99139));
Long lEndTime = System.currentTimeMillis();// 记录EndTime
System.out.println("Search Time--->" + (lEndTime - lBeginTime));
}
}
355. 设计推特
对于操作 3 和操作 4,我们只需要用一个哈希表存储,即可实现插入和删除的时间复杂度都为 O(1)O(1)。
对于操作 1 和操作 2,由于操作 2 要知道此用户关注的人和用户自己发出的最近十条推文,因此我们可以考虑对每个用户用链表存储发送的推文。每次创建推文的时候我们在链表头插入,这样能保证链表里存储的推文的时间是从最近到最久的。那么对于操作 2,问题其实就等价于有若干个有序的链表,我们需要找到它们合起来最近的十条推文。由于链表里存储的数据都是有序的,所以我们将这些链表进行线性归并即可得到最近的十条推文。这个操作与 23. 合并K个排序链表 基本等同。