冒泡排序
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;
}
需要注意
- 返回值类型声明为该类型引用,并在函数结束前返回*this的目的是为了可以连续赋值。注意:this指的是对象的指针,也就是说 * this表示该对象,如果想访问成员变量,可以用this->a或( * this).a,因为返回的类型是对象的引用,也就是对象,所以应该返回 *this而不是this。
- 参数部分是常量引用,常量是因为在该函数中不会改变传入实例的状态,引用是为了少调用一次拷贝构造函数。
- 要在new前先释放自身的内存并指向nullptr,可能出现内存泄露。
- 一定要判断传入的参数和当前的实例(*this)是不是同一个实例,如果是就直接返回就好了,因为如果是一个实例delete了内存,就会造成把本来要赋值的内存也给delete了,有很大问题。
- 特殊情况:当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;
}