这里是引用 各类算法模板
文章目录
不稳定:快选堆希
排序
冒泡排序
思路:
从小到大,依次选择相邻两数进行比较,前大后小,换位;一趟下列,最大的排在最后
时间复杂度O(n2)
空间复杂度O(1)
选择排序
每趟从待排序中找到最小值放在排好序末尾
for (int i = 0; i < n-1; i++) {
int min = i;
for (int j = i; j < n; j++) {
if (arr[min] > arr[j]) {
min = j;
}
}
if (min != i) {
swap(arr[i], arr[min]);
}
}
插入排序
在有序数列中插入元素
假设前面已排好序,i直接从1开始遍历到结束;i和0-i依次比较
int arr[10] = {9,2,3,0,9,4,7,5,4,2};
int n = sizeof(arr)/4;
for (int i = 1; i < n; i++) {
for (int j = i; j > 0; j--) {
if (arr[j] < arr[j-1]) {
swap(arr[j], arr[j - 1]);
}
}
}
这里是引用高级排序
希尔排序
插入排序改进;缩小增量法,分段插入
1.设置步长,按步长分组,步长 5 2 1;
5 即{9,4}{2,7}{3,5}{0,4}{9,2}
组内排序后{4,9}{2,7}{3,5}{0,4}{2,9}
2即{4,9,2,7,3}{5,0,4,2,9}
组内排序后{2,3,4,7,9}{0,2,4,5,9}
1
int arr[10] = {9,2,3,0,9,4,7,5,4,2};
int n = sizeof(arr)/4;
int main(){
for(int gap = n /2; gap > 0; gap = gap/2 ){//分组,步长依次为5,2,1
for (int i = gap; i < n; i++) {//步长为5时,希望比较第15,取5;26,取6;假设arr[gap]=arr[5]=4 有序,
for (int j = i; j > 0; j=j-gap) {//步长为5时,希望比较第15,取1;26,取2;
if (arr[j] < arr[j-gap]) {
swap(arr[j], arr[j - gap]);
}
}
}
}
时间复杂度大概在O(n^1.3) 左右;比传统要快,但不稳定,比快排、堆排慢
快排
选择一个中心轴,小于它的放右边,大于它的放左边;重复以上三个步骤
int midIndex(int begin, int end);
int arr[] = {5,6,8,8,2,6,4,9,5,7};
int pivot;
int lenth = sizeof(arr)/4;
//int findprivot();
int findprivot(int begin, int end){
pivot = arr[begin];
while(begin < end){
while(begin < end){
if(arr[end] < pivot){
arr[begin] = arr[end];
begin++;
break;
}
else end--;
}
while(begin < end){
if(arr[begin] > pivot){
arr[end] = arr[begin];
end--;
break;
}
else begin++;
}
}
swap(arr[begin],pivot);
return begin;//返回值是基轴
}
void sortquick(int begin, int end){
if(end-begin<=1)return;
int mid = findprivot(begin,end);
sortquick(begin ,mid);
sortquick(mid +1 , end);
}
不稳定,平均复杂度O(nlogn),空间复杂度O(logn)
越有序,时间复杂度越大,所以优化方法之一是随机选取基轴
二分查找
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int left = 0;
int right = n; // 我们定义target在左闭右开的区间里,[left, right)
while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间
int middle = left + ((right - left) >> 1);
if (nums[middle] > target) {
right = middle; // target 在左区间,因为是左闭右开的区间,nums[middle]一定不是我们的目标值,所以right = middle,在[left, middle)中继续寻找目标值
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,在 [middle+1, right)中
} else { // nums[middle] == target
return middle; // 数组中找到目标值的情况,直接返回下标
}
}
return right;
}
};
二叉树定义
struct TreeNode{
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x): val(x),left(NULL),right(NULL){};
}
DFS深度优先算法(递归)
前序遍历
void pretraver(TreeNode *root, vector<int> & rs){
if(root == nullptr)return;
rs.push_back(root->val);
pretraver(root->left);
pretraver(root->right);
}
BFS广度优先遍历(队列)
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);//队列插入push
vector<vector<int>> result;
while (!que.empty()) {
int size = que.size();
vector<int> vec;
// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();//队列的头front
que.pop();
vec.push_back(node->val);
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(vec);
}
return result;
}
};
二叉树的深度
int countDeepth(TreeNode *node){
if(node == nullptr)return 0;
int left = countDeepth(node->left);
int right = countDeepth(node->right);
return 1+max(left,right);
}
二叉树的节点数
int countnodes(TreeNode *node){
if(node == nullptr)return 0;
int left = countnodes(node->left);
int right = countnodes(node->right);
return left+right;
}
二分查找
回溯算法
KMP
数据结构
二叉搜索树
一个节点最多只允许两个子节点,任何节点的键值一定大于其左子树中的每个节点的键值,并小于其右子树中每个结点的键值。从左可以搜索到最小元素,从右可以搜索到最大元素。
二叉排序树BST
左节点的值比根节点小,右比根大
平衡二叉树AVL
每个节点的左右子树深度差不大于1
红黑树
是一个平衡二叉查找树,只不过在每个结点上增加了结点的颜色标志,它能确保没有一条路径会比其它路径长出两倍。主要用来存储有序的数据,如STL中的set,map,效率非常高。
1.每个节点为黑或红,根为黑,子为黑,子(null的地方),(2.5个黑)
2.红节子必黑, (红爸黑子)
3.一节点到所有子孙节点的路径上,黑个数相同(一样的黑)
B树(多路查找)
每个节点至少两个子节点,每个节点上可以多个关键字,包含键和值
B+树
叶子节点互通,内部节点直含键,叶子节点包含全部信息
hashtable
多线程中哈希表保证线程安全,哈希表特别大,桶多的时候怎么加锁
通过哈希函数,将一组数据散列地存储在数组中,每个key 和它的存储位置都有个映射关系,除留取余法
为解决哈希冲突,存储方式一般为数组+链表或者数组加二叉树(红黑树)
哈希表扩容:当装填因子大于0.75时(元素个数/总长度),重新申请长度为原来数组两倍大小的新空间,将旧数据以rehash拷贝过来,
数组实现队列
实现功能
创建,销毁,增删,判空,判满,遍历显示
首first 尾end
1.判空
first == -1
2.判满两种
first = 0 和 end = size-1是满
已经出队,end = first-1
3.入队
三种情况
满则不入
end = size - 1 或 -1 时,再入一个,直接放arr[0],end = 0;
其他end++
4.出队
四种情况
空则不出
只有一个first = end时, 出完置为-1
first = size -1时,出完,将first置为0
其他first++
#define Size 5
template<typename T >
class Queue{
public:
Queue(){
first = end = -1;
}
//~Queue();
bool isempty(){
return first == -1;
}
bool isfull(){
return (first == 0 && end == Size -1 || end == first -1);
}
//template<typename T>
void addmember(T num){//处理数组,成员变量,或者函数传进来
if(isfull()){cout<<"满了,不能加了"<<endl;}
else{
if(end == Size -1 || end==-1){
arr[0] = num;
end = 0;
if(first == -1)first = 0;
}
else{
arr[++end] = num;
}
cout<<"添加 "<<num<<endl;
}
}
int deletemember(){
if(isempty()){cout<<"空了,不能删了"<<endl;exit(0);}
else{
int d = arr[first];
if(first == end){
first = -1;
end = -1;
}
else if(first ==Size -1){
first = 0;
}
else{
first++;
}
cout<<"删除 "<<d<<endl;
return d;
}
}
private:
T arr[Size];
int first;
int end;
};
void main(){
//int arr[] = {1,5,6};
Queue<int> q;
q.addmember(1);
q.addmember(2);
q.addmember(3);
q.addmember(4);
q.addmember(5);
q.deletemember();
q.addmember(6);
q.deletemember();
q.deletemember();
q.deletemember();
q.deletemember();
q.deletemember();
q.deletemember();
/*Queue<char> q;
q.addmember('a');
q.addmember('b');
q.addmember('c');
*/
}
常用的sort排序
bool cmp(int a, int b){
return a>b;
}
sort(s,s+3,cmp);
比如成绩排序
#include<algorithm>;
using namespace std;
struct student{
int id;
char name[10];
float Y;
float S;
float E;
float sum;
};
bool cmp(student a, student b){
if(a.sum > b.sum)return true;
else if(a.sum == b.sum){
if(a.Y > b.Y)return true;
else if(a.Y == b.Y){
if(a.S > b.S)return true;
else if(a.S == b.S){
if(a.E > b.E)return true;
else if(a.E == b.E){
if(a.name < b.name)return true;
}
}
}
}
return false;
}
int main(){
int n = 3;
student s[3];
for(int i = 0; i< 3;i++){
cin>>s[i].id>>s[i].name>>s[i].Y >>s[i].S>>s[i].E;
s[i].sum = s[i].Y + s[i].S + s[i].E;
cout<<s[i].sum<<endl;
}
sort(s,s+3,cmp);
for(int i = 0; i< 3;i++){
cout<<s[i].id<<" "<< s[i].name<<" "<<s[i].sum <<endl;
}
return 0;
}
map自定义类型排序
map <key ,value>
当key为自定义类型 key 为 学生信息,id name ; value是成绩
头文件包含map
map 第三个模板参数指定了用来比较元素的函数对象的对象,使用仿函数需要在类外创建一个结构体或者类对象
1.使用仿函数实现,
#include<map>
class student{
public:
int id;
string name;
};
/*
operator ()仿函数,函数对象
*/
struct cmp{
bool operator ()(student const &a, student const &b){ // operator ()为什么写这个 后面还必须有const,
if(a.id == b.id){
return a.name < b.name;
}
else return a.id < b.id;
}
};
int main(){
map<student,int,cmp> information;// map 第三个模板参数指定了用来比较元素的函数对象的对象
student st1;
st1.id = 1;
st1.name = "abc";
information.insert(pair<student,int>(st1,100));
student st2;
st2.id = 1;
st2.name = "aac";
information.insert(pair<student,int>(st2,102));
student st3;
st3.id = 2;
st3.name = "abc";
information.insert(pair<student,int>(st3,103));
//遍历
map<student,int>::iterator it;
for(it = information.begin(); it != information.end();it++){
cout<<(*it).first.id << " " <<(*it).first.name << " "<< (*it).second <<endl;
}
}
补充仿函数
#include<string>
//函数调用运算符重载 仿函数 operator()
class print{
public:
void operator()(string test){cout<<test;}
};
int main(){
print p;
p("sf");
print()("lixdjif");// 类 + ()是 匿名对象,临时对象,使用完直接释放
}
2.类内重载<, 只能重载< ,改变顺序通过改变判断条件
#include<map>
class student{
public:
int id;
string name;
bool operator< ( student const &a)const{
if(this->id == a.id){
return this->name < a.name;
}
else return this->id < a.id;
}
};
int main(){
map<student,int> information;// map 第三个模板参数指定了用来比较元素的函数对象的对象
student st1;
st1.id = 1;
st1.name = "abc";
information.insert(pair<student,int>(st1,100));
student st2;
st2.id = 1;
st2.name = "aac";
information.insert(pair<student,int>(st2,102));
student st3;
st3.id = 2;
st3.name = "abc";
information.insert(pair<student,int>(st3,103));
//遍历
map<student,int>::iterator it;//map vector 是类模板
for(it = information.begin(); it != information.end();it++ ){
cout<<it->first.id <<" "<< it->first.name<< " "<<it->second<<endl;
}
}
- 以上是两种方法对key排序,对value排序思路,将map中的数压入vector,对vector 使用sort,使用的时候为了保证key有序,写一下cmp
#include<map>
# include<algorithm>
class student{
public:
int id;
string name;
};
//map输入时,必定会比较key,若key是自定义,必须三参加判断条件对象
struct Cmp{
bool operator ()(student const &a, student const &b){ // operator ()为什么写这个 后面还必须有const,
if(a.id == b.id){
return a.name < b.name;
}
else return a.id < b.id;
}
};
/*
bool cmp(pair<student,int> &a,pair<student,int> &b){
if(a.second == b.second){
if(a.first.id = b.first.id){
return a.first.name < b.first.name;
}
else return a.first.id < b.first.id;
}
else return a.second > b.second;
}
*/
//key 有序下,再对value排序
bool cmp(pair<student,int> &a,pair<student,int> &b){
if(a.first.id == b.first.id){
if(a.first.name == b.first.name){
return a.second > b.second;
}
else return a.first.name < b.first.name;
}
else return a.first.id < b.first.id;
}
int main(){
multimap<student,int,Cmp> information;// map 第三个模板参数指定了用来比较元素的函数对象的对象
student st1;
st1.id = 3;
st1.name = "abc";
information.insert(pair<student,int>(st1,60));
student st2;
st2.id = 1;
st2.name = "abc";
information.insert(pair<student,int>(st2,90));
student st3;
st3.id = 1;
st3.name = "abc";
information.insert(pair<student,int>(st3,103));
multimap<student,int>::iterator it;//map vector 是类模板
for(it = information.begin(); it != information.end();it++ ){
cout<<it->first.id <<" "<< it->first.name<< " "<<it->second<<endl;
}
//把map中数据压入vector
vector<pair<student,int>> vec(information.begin(),information.end());
sort(vec.begin(),vec.end(),cmp);
int size=vec.size();
for(int i=0;i<size;i++)
cout<<vec[i].first.id<<" "<<vec[i].first.name<<" "<<vec[i].second<<endl;
return 0;
}
key值可重复,用multimap ,在key有序上,对value排序用以上
如果不管key值,只排value 将cmp更改一下 return a.second > b.second;
ps key 若是内置类型,直接将map装入vector,sort重写cmp
key为自定义,多一个key排序的过程,可以用仿函数 或者 类内重载<,不然map无法插入
4.需要用到const的地方
补充
const 修饰
常量指针和指针常量
“左定值,右定向,const修饰不变量”
const参数传递和函数返回值
传进来的参数在函数体不能被修改;
扩大函数参数接收范围,如果不加,就只能接受非const的实参。
函数的返回值为某个对象时,如果将其声明为const时,多用于操作符的重载
以避免在函数调用时对实参的一次拷贝,提高了效率