计算机算法知识及题目解析

算法精讲与实践

计算机算法知识及题目解析

前言

我创作这篇博客的目的是记录学习技术过程中的笔记。希望通过分享自己的学习经历,能够帮助到那些对相关领域感兴趣或者正在学习的人们。

目录

1.时间复杂度、空间复杂度 最坏情况
2.O(log n) 忽略底数的描述
3.OJ(online judge) 超时时间是1s

O ( n ) O(n) O(n) $ 10^8 $ ------> O ( n 2 ) O(n^2) O(n2) 1 0 4 10^4 104

求解: x 2 = 1 0 8 x^2=10^8 x2=108

x = 1 0 4 x=10^4 x=104

O ( n ) O(n) O(n) $ 10^8 $ ------> O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n)) 1 0 7 10^7 107

求解: 1 0 x ∗ x = 1 0 8 10^x*x=10^8 10xx=108
x ≈ 7 x≈7 x7

4.递归的时间复杂度与空间复杂度
递归的时间复杂度=递归次数*每次递归中的操作次数
递归的空间复杂度=递归深度*每次递归中的空间复杂度

1)设n为具体数字
2)画树形图

5.数列求和公式
等差数列 $\ S_n = n \cdot a_1 + \frac{n \cdot (n - 1)}{2} \cdot d $
等比数列 S n = a 1 ⋅ ( 1 − q n ) 1 − q S_n = \frac{ {a_1 \cdot (1 - q^n)}}{ {1 - q}} Sn=1qa1(1qn)
6.C++内存管理

在这里插入图片描述

7.二分法

前提条件:1)有序 2)无重复元素

区间定义:[left,right]
    1)while(left<=right)
    2)right=middle-1;left=middle+1;
	3)middle=left+(right-left)/2;
8. a + b 2 \frac{ {a+b}}{2} 2a+b —> a + b − a 2 a + \frac{ {b-a}}{2} a+2ba 原因:防止溢出
9.万能库
#include<bits/stdc++.h>
using namespace std;
int main(){
    return 0;
}
10.STL
vector 动态数组
初始化:
vector<int> a;
vector<int> a(10);//值不定
vector<int> a(10,1);//值为1
vector<int> a(b.begin(),b.begin()+3);//值为b中第1个到第3个元素
int b[]={1,2,4,5,9,8};
vector<int> a(b,b+6);
vector<int> a={1,2,4,5,9,8};//容器 列表初始化
方法:
a.front();a.back();a[i];
a.clear();
a.erase(a.begin(),a.begin()+3);a.erase(a.begin()+1);//删除第2个元素  //时间复杂度为O(n)
a.pop_back();a.push_back(5);//时间复杂度为O(1)
a.insert(a.begin()+1,5);a.insert(a.begin(),3,5);a.insert(a.begin()+1,b+3,b+6);a.insert(a.begin()+1,b.begin()+3,b.begin()+6);//时间复杂度为O(n)
a.size();
a.resize(10);a.resize(10,2);
a==b;a>=b;a<=b;a!=b;a<b;a>b;
deque 双端数组 与vector相比
a.pop_front();//时间复杂度为O(1)
a.push_front();//时间复杂度为O(1)
list 双向链表 与vector相比
a.pop_front();//时间复杂度为O(1)
a.push_front();//时间复杂度为O(1)
string 字符串 与vector相比
初始化:
string str;
string str(10,'A');
string str="ABC";
方法:
str.resize(10);str.resize(10,'C');
str.push_back('A');str.append("ABC");str+='A';str+="ABC";//时间复杂度为O(1)
str.insert(2,3,'A');str.insert(2,"ABC");str.insert(str.begin()+2,'A');str.insert(str.begin()+2,3,'A');str.insert(str.begin()+1,str2.begin()+3,str2.begin()+6);
str.erase(2);str.erase(2,1);// 删除从位置 2 开始的 1 个字符
str.erase(str.begin()+2);str.erase(str.begin(),str.begin()+3);
str.find('A');str.find("ABC");//返回子串的首字符位置,或若找不到这种子串则为 std::string::npos
str.substr(2);//提取从位置 2 开始,到字符串末尾的子字符串
str.substr(2,3);//提取从位置 2 开始,长度为 3 的子字符串
a==b;a>=b;a<=b;a!=b;a<b;a>b;//会先按照字典序比较字符串的字符,只有在字符相等的情况下才会考虑字符串的长度
queue 队列 容器适配器
a.push(x);
a.pop();
a.front();
a.back();
stack 栈 容器适配器
a.push(x);
a.pop();
a.top();
set 集合 升序
初始化:
set<int> a;//默认升序
set<int,less<int>> a;//升序
set<int,greater<int>> a;//降序
方法:
a.insert(x);
a.erase(a.begin(),a.begin()+3);a.erase(x);a.erase(a.begin()+1);//删除第二个元素
a.find(x);
a.begin();
a.end();
map 哈希 升序
初始化:
map<int,string> list1;//默认升序
map<int,string,less<int>> list1;//升序
map<int,string,greater<int>> list1;//降序
map<int,string> list2 = {
  
  {1,"java教程"},{2,"c++教程"},{3,"python教程"}};
map<int,string> list3 = {pair<int,string> (1,"java教程"),pair<int,string> (2,"c++教程")};
方法:
list1.insert(pair<int,int> (1,15));list1.insert({1,15});
a.erase(a.begin(),a.begin()+3);a.erase(1);a.erase(a.begin()+1);//删除第二个元素
a[1]=6;//如果键 1 不存在,map 容器会自动插入一个新的键值对
a.find(1);
a.begin();
a.end();
priority_queue 优先队列 大顶堆
初始化:
priority_queue<int> a;//默认大顶堆
priority_queue<int,vector<int>,less<int>>	a;//大顶堆
priority_queue<int,vector<int>,greater<int>>	a;//小顶堆
方法:
a.push(x);//O(log(n))
a.pop();//O(log(n))
a.top();

扩展:

1.统计最大前k个元素:k个元素的小顶堆(只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素)

multimap 哈希 升序
方法:
a.count(key);
list1.insert(pair<int,int> (1,15));list1.insert({1,15});
a.erase(a.begin(),a.begin()+3);a.erase(x);//删除键为x的所有元素 
a.erase(a.begin()+1);//删除第二个元素
//和map容器相比,multimap未提供at()成员方法,也没有重载[ ] 运算符
a.find(1);//若存在多个具有相同键的元素,只返回第一个匹配的元素
a.begin();
a.end();
a.equal_range(x);//查找键为 x 的元素范围,返回一个迭代器的pair对象,first成员等价于lower_bound(key)//返回一个迭代器,指向键>=k的第一个元素,second成员等价于upper_bound(key)//返回一个迭代器,指向键>k的第一个元素//lower_bound与upper_bound在有序容器如set、map、multiset、multimap使用
	auto range = a.equal_range(1);
	// 遍历范围内的元素
    for (auto it = range.first; it != range.second; ++it) {
         cout << it->first << ": " << it->second << endl;
    }
multiset 集合 升序
方法:
a.count(x);
a.insert(x);
a.erase(a.begin(),a.begin()+3);a.erase(x);a.erase(a.begin()+1);//删除第二个元素
a.find(x);
a.begin();
a.end();
a.equal_range(x);
pair
pair<string,int> p={"hello",1};
pair<string,int> p("hello",1);
pair<string,int> p = make_pair("hello",1);

p.first; //第一个元素 =hello
p.second; //第二个元素 = 1

//套娃操作 用pair存储3个数据
pair<int, pair<int, int>> p(1,{2,3});

当pair 结合 sort()函数使用的时候, pair 默认对first升序,当first相同时对second升序(从小到大)。 也可以通过修改cmp函数达到对second排序    
迭代器

迭代器实质上是一个指针,但是,并不是所有的容器的迭代器可以支持加减操作。

能进行算术运算的迭代器只有随机访问迭代器,要求容器元素存储在连续内存空间内;
即vector、string、deque的迭代器是有加减法的;  
而map、set、multimap、multiset、list的迭代器是没有加减法的。他们仅支持++itr、–itr这些操作。

advance函数:将迭代器移动指定的距离

可以用于多种容器,包括 std::vector, std::list, std::deque 等。对于支持随机访问的容器(如 std::vector),std::advance 可以直接通过加法操作来移动迭代器。对于不支持随机访问的容器(如 std::list),std::advance 会通过多次递增或递减操作来达到目标位置。

vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin();
// 向前移动迭代器 2 位
advance(it, 2);
// 向后移动迭代器 1 位
advance(it, -1);
11.自定义排序(后面深入模版再查看原因)

const成员函数:const实际修饰该成员函数隐含的this指针表明在该成员函数中不能对类的任何成员进行修改

set:
//创建castle类
class Castle
{
public:
	Castle(string name, int area)  //提供有参构造函数
	{
		this->m_Name = name;
		this->m_Area = area;
	}
	string m_Name;  //建筑名称
	int m_Area;  //建筑面积
};

struct Castle
{
	Castle(string name, int area) :m_Name(name), m_Area(area) {}

	string m_Name;  //建筑名称
	int m_Area;  //建筑面积
};
//创建降序仿函数(类)
class MyCustom
{
public:
	//重载小括号()
	bool operator()(const Castle& c1, const Castle& c2) const   //注意后面加个const,否则报错  
	{
		if (c1.m_Name != c2.m_Name)
			return c1.m_Name > c2.m_Name;
		else 
			return c1.m_Area > c2.m_Area;   //按建筑面积降序排序
	}
};
    
//在C++中,结构体是特殊的类。
//和类的唯一区别:具有不同的默认访问控制属性。类中默认private,结构体默认public。
//C++为为C语言中的结构体引入了成员函数、访问控制权限、继承、多态等面向对象特性。
struct MyCustom
{
	//重载小括号()
	bool operator()(const Castle& c1, const Castle& c2) const   //注意后面加个const,否则报错 原因:set容器STL内部调用实参为const Date*
	{
		if (c1.m_Name != c2.m_Name)
			return c1.m_Name > c2.m_Name;
		else 
			return c1.m_Area > c2.m_Area;   //按建筑面积降序排序
	}
};


//利用列表初始化,创建对象
Castle c1 = { "餐厅", 235 };
//利用有参构造函数,创建对象
Castle c2("客厅", 666);
//创建set容器,注意尖括号里面的内容,说明创建的是Castle类对象,同时按指定的方法排序
set<Castle, MyCustom> ss;
sort():
//创建castle类
class Castle
{
public:
	Castle(string name, int area)  //提供有参构造函数
	{
		this->m_Name = name;
		this->m_Area = area;
	}
	string m_Name;  //建筑名称
	int m_Area;  //建筑面积
};

struct Castle
{
	Castle(string name, int area) :m_Name(name), m_Area(area) {}

	string m_Name;  //建筑名称
	int m_Area;  //建筑面积
};

//创建降序仿函数(类)
class MyCustom
{
public:
	//重载小括号()
	bool operator()(const Castle& c1, const Castle& c2) const   //注意后面加个const,否则报错  
	{
		if (c1.m_Name != c2.m_Name)
			return c1.m_Name > c2.m_Name;
		else 
			return c1.m_Area > c2.m_Area;   //按建筑面积降序排序
	}
};
    
//在C++中,结构体是特殊的类。
//和类的唯一区别:具有不同的默认访问控制属性。类中默认private,结构体默认public。
//C++为为C语言中的结构体引入了成员函数、访问控制权限、继承、多态等面向对象特性。
struct MyCustom
{
	//重载小括号()
	bool operator()(const Castle& c1, const Castle& c2) const   //注意后面加个const,否则报错 原因:set容器STL内部调用实参为const Date*, 而
	{
		if (c1.m_Name != c2.m_Name)
			return c1.m_Name > c2.m_Name;
		else 
			return c1.m_Area > c2.m_Area;   //按建筑面积降序排序
	}
};

static bool cmp(const Castle& c1, const Castle& c2) {
	if (c1.m_Name != c2.m_Name)
		return c1.m_Name > c2.m_Name;
	else
		return c1.m_Area > c2.m_Area;   //按建筑面积降序排序
}



vector<Castle> ss;
//sort函数的第三个参数可以是函数对象(重载函数调用操作符(operator())来实现可调用操作的类的对象)
MyCustom mycustom;
sort(ss.begin(), ss.end(), mycustom);
//sort函数的第三个参数可以是函数指针(函数名也是函数地址)
sort(ss.begin(), ss.end(), cmp);
12.this指针
class Date
{
public:
	//Print编译器处理前
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
 
	//Print编译器处理后
	void Print(Data* const this) //注意const修饰,指针不能修改,指向的内容可以修改
	{
		cout << this->_year << "-" << this->_month << "-" << this > _day << endl;
	}
 
	//Init编译器处理前
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//Init编译器处理后
	void Init(Date* const this, int year, int month, int day)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};
int main()
{
	Date d1;
	Date d2;
	Date d3;
	d1.Init(2022, 10, 17);
	d2.Init(2023, 10, 18);
 
	//编译器实参改变
	d1.Init(&d1, 2022, 10, 17);
	d2.Init(&d2, 2023, 10, 18);
 
	d1.Print();
	d2.Print();
 
	//编译器实参改变
	d1.Print(&d1);
	d2.Print(&d2);
 
	return 0;
}
13.const修饰
1)const修饰变量:
const char *p = "hello";  // 非const指针,
                           // const数据
						  // 可以指向别的变量,但不可以通过修改所指向的变量的值

char * const p = "hello";   // const指针,
                            // 非const数据
							// 可以修改所指向变量的值,但不可以指向别的变量

const char * const p = "hello";  // const指针,
                                 // const数据
								 // 不可以修改所指向变量的值,也不可以指向别的变量
技巧:
const往右看,忽视数据类型,p代表指针内容不可改变,*p代表指针所指向的内容不可改变
2)const修饰函数:
const char* func();//函数声明
char* str = func();// error 
const char* str = func(); //right
权限问题:——涉及指针或引用时,而普通变量之间主要是赋值关系。

权限不能放大(松),只能缩小(紧)或不变。

class Date
{
public:
	//构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Printf()
	{
		cout << _year << "年" << _mon th << "月" << _day << "日" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
 
void Func(const Date& d)
{
	d.Printf();
}
 
int main()
{
	Date d1(2023, 11, 1);
	d1.Printf();
	Date d2(2023, 11, 2);
	Func(d2);//error
	return 0;
}

在这里插入图片描述

14.algorithm
sort函数 默认升序
1)数组
int arr[]={3,1,4,1,5,9,2,6,5,3,5};
int n=sizeof(arr)/sizeof(int);
sort(arr,arr+n);
2)vector容器  
vector<int> arr={3,1,4,1,5,9,2,6,5,3,5};
sort(arr.begin(),arr.end());
reverse函数 仅适用于容器,不适用于数组
vector<int> arr={3,1,4,1,5,9,2,6,5,3,5};
reverse(arr.begin(),arr.end());

"整体反转+局部反转"法

swap函数
1)数组
int arr[]={3,1,4,1,5,9,2,6,5,3,5};
swap(arr[i],arr[j]);
2)vector容器
vector<int> arr={3,1,4,1,5,9,2,6,5,3,5};
swap(arr[i],arr[j]);
3)变量
swap(a,b);
find函数
1)数组
int arr[]={1,3,5,7,9};
int n=sizeof(arr)/sizeof(int);
int* p=find(arr,arr+n,5);
p!=arr+n
2)vector容器
vector<int> vec={1,3,5,7,9};
auto iter=find(vec.begin(),vec.end(),5);
iter!=vec.end();
cmath
fabs(double x);//绝对值
floor(double x);//向下取整		
ceil(double x);//向上取整
log(double x);//以e为底的对数
sin(double x);cos(double x);tan(double x);//参数为弧度
asin(double x);acos(double x);atan(double x);//返回的结果是以弧度为单位的角度值
round(double x);//四舍五入仅保留到整数位,保留小数位可先乘再除
__gcd(a,b);//最大公因数
a*b/__gcd(a,b);//最小公倍数
常量
const double PI=atan(1.0)*4;//精确的PI值
const int INF=(1<<30)-1;//避免大数+大数溢出
字符串与char* 字符指针、数字的相互转化
1)字符串转化字符指针
str.c_str();
2)字符指针转化数字
atoi(char_p);
atol(char_p);
atof(char_p);    
3)字符串转化数字
stoi(str);
stol(str);
stoll(str);
stof(str);  
stod(str);
stoll(str, nullptr, 2);//将二进制字符串转换为长长整型
4)数字转化字符串
to_string(num);
5)字符指针转化字符串
char* charPtr = "Hello, World!";
string str(charPtr);
string str;
str = charPtr;
全排列
1)vector
do{
    
}
while(next_permutation(nums.begin(),nums.end()));
2)数组
do{
    
}
while(next_permutation(arr,arr+n));
3)字符串
do{
    
}
while(next_permutation(str.begin(),str.end()));
printf函数
1)%md 	变量超过m位则保持原样输出
2)输出string类型
string s = "Hello World";
printf("%s", s.c_str()); 
3)格式
%[标志][最小宽度][.精度][类型长度]类型。
d o(不输出前缀0) u x/X(不输出前缀0x) f/lf  e/E  c s p 
- 左对齐 +输出符号 space 为正空格为负负号 0 前面为0
scanf函数
1)除了%c,scanf函数对%d,%s等其他格式把空白符和换行符视为结束
2)多行字符串输入
    2
    Apple is red.
    Banana is yellow.
    
    方法一:
    scanf("%d",&a);//cin>>a;
	getchar();
	getline(cin,s1);//以换行符结束
	getline(cin,s2);

	方法二:
	scanf("%d\n",&a);
    getline(cin,s1);
	getline(cin,s2);
3)读取到文件结尾(Window系统Dos平台Ctrl+Z代表文件结束符)
    while(scanf("%d",&n)!=EOF){
        ...
    }
sizeof函数
sizeof(arr);//数组大小
sizeof(str);//字符串对象大小  通常包含多个成员变量即不是简单的个数乘以类型字节数
sizeof(vec);//vector对象大小 通常包含多个成员变量即不是简单的个数乘以类型字节数
数据结构定义
链表
    struct ListNode{
        int val;
        ListNode* next;
        ListNode(int x):val(x),next(NULL){}
    };
二叉树
    struct TreeNode{
        int val;
        TreeNode* left;
        TreeNode* right;
        TreeNode(int x):val(x),left(NULL),right(NULL){}
    }; 
lower_bound与upper_bound函数——时间复杂度 O ( l o g ( n ) ) O(log(n)) O(log(n)),内部实现基于二分查找
lower_bound//用于在有序容器查找第一个大于或等于给定值的元素位置。如果没有找到,它将返回指向容器末尾的迭代器(或指针)。左闭右开区间 不要求不重复
upper_bound//用于在有序容器查找第一个大于给定值的元素位置。如果没有找到,它将返回指向容器末尾的迭代器(或指针)。左闭右开区间不要求不重复

1)数组
int arr[] = {10, 20, 30, 40, 50};
int size = sizeof(arr)/sizeof(int);
auto lowerIt = lower_bound(arr, arr + size, 30);
if (lowerIt != arr + size) {
	cout << "找到元素:" << *lowerIt << endl;
} else {
	cout << "未找到元素" << endl;
}
2)vector容器  
vector<int> vec = {10, 20, 30, 40, 50};
auto lowerIt = lower_bound(vec.begin(), vec.end(), 30);
if (lowerIt != vec.end()) {
	cout << "找到元素:" << *lowerIt << endl;
} else {
	cout << "未找到元素" << endl;
}
15.数组
1)数组传参:
一维数组:
	void test(int arr[]);
	void test(int arr[10]);
二维数组:
	void test(int arr[][5]);
	void test(int arr[3][5]);
2)赋值
局部变量一维数组、二维数组={0},其他元素默认赋值为0。
3)字符串 
string s[11];//字符串数组可以像二维数组那样访问
16.列表初始化和构造函数传参

1)容器可初始化列表

string hex_16[16]={"0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111"};

2)new

Rectangle* p_rect=new Rectangle{5.0,6.0};//列表初始化
Rectangle* p_rect=new Rectangle(5.0,6.0);//有参构造函数
Rectangle* p_rect=new Rectangle;//无参构造函数

delete p_rect;
17.常识

1)1不是质数也不是合数。

2)1和任何数互质,其他情况下最大公因数为1就互质。

3)闰年要么可被4整除,但不可被100整除;要么可被400整除。

4)真因子:除了它本身以外的因子,包括1在内。

5)完全平方数:一个能表示成某个整数的平方的形式的数。

6)VS快速弹出智能提示:Ctrl+J。

7)32位系统下基本数据类型规模

类型					   规模        输出格式符
int/long				10^9		%d
long long				10^18		%lld
unsigned long long		10^20		%llu
float								%f
double								%lf

8)蓝桥杯最大栈空间为256MB,即最大可以开10^7左右的数组空间。

9)set与vetor的相互转化

vector转set:
vector<int> nums1;
unordered_set<int> nums_set(nums1.begin(), nums1.end());

set转vector:
unordered_set<int> nums2;
vector<int> nums_vector(nums2.begin(), nums2.end());

10)验算:

输入规模的最小项、最大项;
输入规模的奇偶项;
输入值的负数、输入值的零;
输入值的无规律排列;
根据输入规模最大值决定是否开拓新空间;

11)排序算法时间复杂度

冒泡排序: O ( n 2 ) O(n^2) O(n2)

快速排序: O ( n ∗ l o g ( n ) ) O(n*log(n)) O(nlog(n))

12)进阶时间复杂度计算

看每一个元素被操作的次数,每个元素在滑动窗口进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 $2 × n 也就是 也就是 也就是O(n)$

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int result = INT32_MAX;
        int sum = 0; // 滑动窗口数值之和
        int i = 0; // 滑动窗口起始位置
        int subLength = 0; // 滑动窗口的长度
        for (int j = 0; j < nums.size(); j++) {
            sum += nums[j];
            // 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
            while (sum >= s) {
                subLength = (j - i + 1); // 取子序列的长度
                result = result < subLength ? result : subLength;
                sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
        return result == INT32_MAX ? 0 : result;
    }
};

13)二维动态数组的定义与赋值

vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
res[i][j];

14)数组与链表的特性对比

在这里插入图片描述

15)单链表统一所有节点的删除操作和增加操作——设置一个虚拟头结点

head = dummyNode->next;

在这里插入图片描述

16)C++11基于范围的for循环——可以遍历支持迭代器的集合,如std::vector、std::list等,也可以遍历支持下标操作的集合,如std::arra

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

早日退休!!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值