手撕代码

冒泡排序

void BubbleSort(vector<int>& vec, int left, int right) {
	for (int i = left; i < right; i++) {//i取到倒数第一个或倒数第二个都行
		for (int j = right - 1; j >= i; j--) {//j从倒数第二个从后往前遍历
			if (vec[j] > vec[j + 1])
				swap(vec[j], vec[j + 1]);
		}
	}
}

快速排序

讲解链接
当基准数选择最左边的数字时,那么就应该先从右边开始搜索;当基准数选择最右边的数字时,那么就应该先从左边开始搜索。不论是从小到大排序还是从大到小排序!快速排序的最差时间复杂度是O(N2),平均时间复杂度为O(NlogN)。

void QuickSort(vector<int>& vec, int left, int right) {
	if (left >= right)
		return;
	int l = left, r = right;
	int base = vec[left];//选取左边的数为基数
	while (l < r) {
		while (l < r && vec[r] >= base)//从右边开始
			r--;
		while (l < r && vec[l] <= base)
			l++;
		if (l < r) {
			int tmp = vec[l];
			vec[l] = vec[r];
			vec[r] = tmp;
		}
	}
	//基数归位
	vec[left] = vec[l];//此时l和r重合
	vec[l] = base;
	QuickSort(vec, left, l - 1);
	QuickSort(vec, l + 1, right);
}

归并排序

归并排序的最差最好时间复杂度都为O(NlogN)。

//合并过程
void Merge(vector<int>& vec, int left, int middle, int right) {
	vector<int> tmp(right - left + 1);//额外创建空间
	int l = left, r = middle + 1, k = 0;
	while (l <= middle && r <= right) {
		if (vec[l] >= vec[r])
			tmp[k++] = vec[r++];
		else
			tmp[k++] = vec[l++];
	}
	while (l <= middle)
		tmp[k++] = vec[l++];
	while (r <= right)
		tmp[k++] = vec[r++];
	for (k = 0, l = left; l <= right; k++, l++)
		vec[l] = tmp[k];
}
//递归划分过程
void MergeSort(vector<int>& vec, int left, int right) {
	if (left >= right)
		return;
	int middle = left + (right - left) / 2;
	MergeSort(vec, left, middle);
	MergeSort(vec, middle + 1, right);
	Merge(vec, left, middle, right);//合并两个有序空间
}

拓扑排序

在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序(英语:Topological sorting)。
(1)每个顶点出现且只出现一次;
(2)若A在序列中排在B的前面,则在图中不存在从B到A的路径。
也可以定义为:拓扑排序是对有向无环图的顶点的一种排序,它使得如果存在一条从顶点A到顶点B的路径,那么在排序中B出现在A的后面。
在这里插入图片描述

int main() {
	vector<vector<int>> vec = { {1,2},{1,3},{1,4},{3,2},{3,5},{4,5},{6,4},{6,5} };
	vector<vector<int>> adjList(7);//邻接表(点数+1),使点的下标和点值对应便于统计
	vector<int> record(7);//入度表(点数+1)
	queue<int> que;
	vector<int> res;
	for (auto v : vec) {
		record[v[1]]++;//求每个点的入度
		adjList[v[0]].push_back(v[1]);
	}
	for (int i = 1; i < record.size(); i++) {
		if (record[i] == 0)
			que.push(i);
	}
	while (!que.empty()) {
		int tmp = que.front();//tmp为record下标
		que.pop();
		res.push_back(tmp);
		for (auto x : adjList[tmp]) {//x为record下标,遍历以tmp为起点的所有点
			record[x]--;
			if (record[x] == 0)
				que.push(x);
		}
	}
	for (auto x : res)
		cout << x << " ";//1 6 3 4 2 5 排序结果不唯一
	return 0;
}

LRU缓存机制

class LRUCache {
private:
    struct LinkNode {
        int k, val;
        LinkNode* pre;
        LinkNode* next;
        LinkNode():k(0), val(0), pre(nullptr), next(nullptr){};
        LinkNode(int k, int val): k(k), val(val), pre(nullptr), next(nullptr){};
    };
    int _capacity;
    int _size;
    LinkNode* head;
    LinkNode* tail;
    unordered_map<int, LinkNode*> ump;
public:
    LRUCache(int capacity) {
        _capacity = capacity;
        _size = 0;
        head = new LinkNode();
        tail = new LinkNode();
        head->next = tail;
        tail->pre = head;
    }
    
    int get(int key) {
        if(ump.count(key) == 0)
            return -1;
        LinkNode* node = ump[key];
        move2Head(node);
        return node->val;
    }
    
    void put(int key, int value) {
        if(ump.count(key) == 1) {
            ump[key]->val = value;
            move2Head(ump[key]);
        }
        else {
            LinkNode* node = new LinkNode(key, value);
            ump.insert({key, node});
            add2Head(node);
            _size++;
            if(_size > _capacity) {
                LinkNode* del = removeTail();
                ump.erase(del->k);
                delete del;
                _size--;
            }
        }
    }
    void add2Head(LinkNode* node) {
        head->next->pre = node;
        node->next = head->next;
        head->next = node;
        node->pre = head;
    }
    void move2Head(LinkNode* node) {
        node->pre->next = node->next;
        node->next->pre = node->pre;
        add2Head(node);
    }
    LinkNode* removeTail() {
        LinkNode* node = tail->pre;
        node->pre->next = node->next;
        node->next->pre = node->pre;
        return node;
    }
};

01背包

讲解

int test_2wei_bag_problem(int bagWeight, vector<int>& weight, vector<int> value) {
	//声明dp数组:dp[i][j]表示从下标为0-i的物品里任意选取,放进容量为j的背包里,背包所装物品的最大价值
	vector<vector<int>> dp(weight.size() + 1, vector<int>(bagWeight + 1, 0));
	//初始化数组
	//for (int j = weight[0]; j <= bagWeight; j++)
	//	dp[0][j] = value[0];
	for (int j = bagWeight; j >= weight[0]; j--)	//倒序遍历
		dp[0][j] = dp[0][j - weight[0]] + value[0];	//下标为0的物品状态为“取”,背包里物品价值才最大
	//根据状态方程更新dp数组
	for (int i = 1; i < weight.size(); i++) {		//先遍历物品下标
		for (int j = 0; j <= bagWeight; j++) {		//再遍历背包重量(从0或1开始都可以)
			if (j < weight[i])
				dp[i][j] = dp[i - 1][j];			//取不了第i个
			else
				dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
		}
	}
	return dp[weight.size() - 1][bagWeight];
}
void main() {
	vector<int> weight = { 1,3,4 };
	vector<int> value = { 15,20,30 };
	int result = test_2wei_bag_problem(4, weight, value);
	cout << result << endl;
}

线程安全的单例模式

#include <iostream>
#include <mutex>
using namespace std;
mutex mt;
class Singleton {
private:
	Singleton(){};//对外禁用构造函数
	Singleton(const Singleton&){};//对外禁用拷贝构造函数
	Singleton& operator=(const Singleton&){};//对外禁用拷贝复制函数
	static Singleton* instance;
public:
	static Singleton* getInstance() {
		//只有在指针为空时存在线程安全的问题,而锁调度会耗费资源影响性能,
		//为提高效率,再加一层判断,若指针为空则加锁,若不为空则不用加锁
		if (instance = NULL) {
			mt.lock();
			if (instance = NULL) {
				instance = new Singleton();
			}
			mt.unlock();
		}
		return instance;
	}
	static void deleteInstance() {
		if (instance != NULL) {
			delete instance;
			instance = NULL;
		}
	}
};
Singleton* Singleton::instance = NULL;
int main() {
	Singleton* s1 = Singleton::getInstance();
	Singleton::deleteInstance();
	return 0;
}

手写string类

class CMyString {
public:
	CMyString(const char* str = 0);//构造函数
	CMyString(const CMyString& str);//拷贝构造函数
	CMyString& operator=(const CMyString& str);//拷贝赋值函数
	~CMyString();//析构函数
private:
	char* m_data;
};
CMyString::CMyString(const char* str) {
	if (str) {
		m_data = new char[strlen(str) + 1];
		strcpy(m_data, str);
	}
	else {
		m_data = new char[1];
		*m_data = '\0';
	}
}
CMyString::CMyString(const CMyString& str) {
	m_data = new char[strlen(str.m_data) + 1];
	strcpy(m_data, str.m_data);
}
CMyString& CMyString::operator=(const CMyString& str) {
	if (this != &str) {
		delete[]m_data;
		m_data = nullptr;
		m_data = new char[strlen(str.m_data) + 1];
		strcpy(m_data, str.m_data);
		
		//防止分配内存失败导致异常
		//CMyString strTmp(str);
		//char* pTmp = strTmp.m_data;
		//strTmp.m_data = m_data;
		//m_data = pTmp;
	}
	return *this;
}
CMyString::~CMyString() {
	delete[]m_data;
	m_data = NULL;
}

需要注意

  1. 返回值类型声明为该类型引用,并在函数结束前返回*this的目的是为了可以连续赋值。注意:this指的是对象的指针,也就是说 * this表示该对象,如果想访问成员变量,可以用this->a或( * this).a,因为返回的类型是对象的引用,也就是对象,所以应该返回 *this而不是this。
  2. 参数部分是常量引用,常量是因为在该函数中不会改变传入实例的状态,引用是为了少调用一次拷贝构造函数。
  3. 要在new前先释放自身的内存并指向nullptr,可能出现内存泄露。
  4. 一定要判断传入的参数和当前的实例(*this)是不是同一个实例,如果是就直接返回就好了,因为如果是一个实例delete了内存,就会造成把本来要赋值的内存也给delete了,有很大问题。
  5. 特殊情况:当new char因为内存不足导致抛出异常时,实例中的m_pData其实已经被删除了,无法复原,所以考虑用一个临时对象,在拷贝构造函数中new,这样,当if结束时,因为临时变量的缘故会自动调用析构函数,释放strTemp的内存,而因为strTemp.m_pData指向的内存就是实例之前m_pData的内存,其实也就是释放了原来的内存。

多线程交替打印奇偶数

mutex mt;
const int max_num = 10;
int num1, num2;
//使用锁交替打印------------------begin---------------------
void odd1() {
	while (1) {
		mt.lock();//互斥锁锁住了num1这一临界资源
		if (num1 >= max_num) {
			mt.unlock();
			break;
		}
		else if (num1 % 2 == 0) {
			++num1;
			cout << "互斥锁实现-奇数线程 " << num1 << endl;
		}
		mt.unlock();
	}
}
void eve1() {
	while (1) {
		mt.lock();//互斥锁锁住了num1这一临界资源
		if (num1 >= max_num) {
			mt.unlock();
			break;
		}
		else if (num1 % 2 == 1) {
			++num1;
			cout << "互斥锁实现-偶数线程 " << num1 << endl;
		}
		mt.unlock();
	}
}
//使用锁交替打印-------------------end----------------------

//使用计数交替打印------------------begin---------------------
void odd2() {
	while (1) {
		if (num2 >= max_num) {
			break;
		}
		else if (num2 % 2 == 1) {
			cout << "计数实现-奇数线程 " << num2 << endl;
			++num2;
		}
	}
}
void eve2() {
	while (1) {
		if (num2 > max_num) {//该处不应该加=否则不打印最后一个偶数
			break;
		}
		else if (num2 % 2 == 0) {
			cout << "计数实现-偶数线程 " << num2 << endl;
			++num2;
		}
	}
}
//使用计数交替打印-------------------end----------------------
int main() {
	num1 = 0, num2 = 1;
	thread th1(odd1);
	thread th2(eve1);
	th1.join();
	th2.join();

	thread th3(odd2);
	thread th4(eve2);
	th3.join();
	th4.join();

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值