刷题总结
本文最后更新于:1 年前
算法刷题(思路,错误点,数据结构知识点)
个人总结与整理
零钱兑换问题:
【动态规划,递归】 Coins change
1 |
|
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-change
1 |
|
采用递归解题时,常见的缺陷是:函数调用的参数是通过栈空间来传递的,在调用过程中会占用线程的栈资源。而递归调用,只有走到最后的结束点后函数才能依次退出,而未到达最后的结束点之前,占用的栈空间一直没有释放,如果递归调用次数过多,就可能导致占用的栈资源超过线程的最大值,从而导致栈溢出,导致程序的异常退出;
而大部分的递归问题,都可以通过栈实现递归转化为非递归
DFS深度优先搜索,在二叉树的题目中,其实与二叉树的前序遍历是一致的
当结合栈的运用时,要注意左子树后入栈(后进先出)
散列函数Hash与回溯算法
典型问题:
- N-Queen puzzle
n*n取n
对于每行列举1~n确定位于哪一列即为N×N量级
研究棋盘,有(X,Y表示行,列):
abs(x1-x2)=abs(y1-y2) 表示两个点之间在同一对角线上的关系
由此作为判断能否进一步列举的条件
主要的思路是,先分析好一个子问题再演化成子问题集的合并
回溯模板
hashtable[index]=true;
dfs(index+1,res);
HashTable[index]=false;二分查找
若二分上界超过int型数据范围的一般,在查询较靠后的数据时可能导致
mid=(left+right)/2 结果溢出,一般改用:mid= left + (right-left)/2快速幂:要点
- 幂的分解,奇偶数次项对应不同的分解方式,并利用递归
快速排序与随机选择
分析快速排序的时间复杂度
由此,如何高效的在每次递归使用快速排序开始前选好pivot能够带来优化,于是研究随机选择与two pointers思想结合
- tips algorithm中的sort函数运用的就是快速排序,如果能用sort尽量就直接用利于提高效率减少代码量
最大公约数与最小公倍数
gcd(a,b)
1
2
3
4int gcd(int a, int b)
{
return !b ? a : gcd(b, a % b);
}
- lcm(a,b)= (a/gcd(a,b))*b [括号只是为了更明显的区分计算步骤,先除以最大公约数防止计算溢出]
1
return a/gcd(a,b)*b
动态规划 two pointers
- 动态规划模板:
素数的判断
- 算法的依据 :
- 设 k为n的约数(在 1至n-1之间)
- n%k=0 , k * (n / k ) == n; n/k与k都小于n
- 且能满足一个小于sqrt(n)另一个大于,因此只需要在2~sqrt(n)的向下取整范围内进行素数的判断即可常用于素数表的建立,以及寻找某数值范围内所有的素数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#include <cstdio>
#include <math.h>
//基本的素数判断算法
bool isPrime(int n)
{
int sqr = (int)sqrt(1.0 * n);
//对开根号的结果向下取整,sqrt函数的参数为浮点数
if (n <= 1)
return false;
else
{
for (int i = 2; i <= sqrt; i++)
{
if (n % i == 0)
return false;
}
}
return true;
}
筛法求素数表 n数量级为以下
引入数学公式写法出错- 依据,前面的数,其倍数若在需求的范围内,那他的倍数一定不是素数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35#include <cstdio>
#include <math.h>
//该算法求的是100内的素数
const int maxn = 101;
int prime[maxn];
int pNum = 0;
bool p[maxn] = {0};
void findPrime()
{ //在[2,maxn)区间遍历判断
for (int i = 2; i < maxn; i++)
{
if (p[i] == false) //若i是素数
{
prime[pNum++] = i;
//将该素数存进表中
for (int j = i + i; j < maxn; j += i)
{ //筛去所有j的倍数
p[j] = true;
}
}
}
//题2: 先制表,再输出第i个素数,数组空间的大小尽量设置的比需求大一些
}
int main()
{
findPrime();
for (int i = 0; i < pNum; i++)
{
printf("%d ", prime[i]);
}
return 0;
}
质因子分解
依据,结合素数判断部分的知识得出结论
对一个正整数n来说,若在它[2,n]范围内有质因子,则质因子的分布情况无非两种
全部在sqrt(n)左侧即<=sqrt(n)
一个质因子>sqrt(n),其余全部<=sqrt(n)
[若有两个以上大于根号n,则p1*p2>n不符合实际,因此最多只有一个在右侧]
算法 PAT A1059
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75#include <cstdio>
#include <math.h>
const int maxn = 100010;
int prime[maxn];
int pNum = 0;
bool p[maxn] = {0};
void findPrime()
{ //在[2,maxn)区间遍历判断
for (int i = 2; i < maxn; i++)
{
if (p[i] == false) //若i是素数
{
prime[pNum++] = i;
//将该素数存进表中
for (int j = i + i; j < maxn; j += i)
{ //筛去所有j的倍数
p[j] = true;
}
}
}
}
struct factor
{
int x, cnt;
//x is factor,cnt present numbers of factor
} fac[10];
int main()
{
findPrime(); //打表
int n, num = 0;
scanf("%d", &n);
if (n == 1)
printf("1=1");
else
{
printf("%d=", n);
int sqr = (int)sqrt(1.0 * n);
for (int i = 0; i < pNum && prime[i] <= sqr; i++)
{
if (n % prime[i] == 0) //若当前数为n的质因子
{
fac[num].x = prime[i];
fac[num].cnt = 0;
while (n % prime[i] == 0)
{
fac[num].cnt++;
n /= prime[i];
}
num++;
}
if (n == 1)
break;
}
if (n != 1)
{
fac[num].x = n;
fac[num++].cnt = 1;
//存在>sqrt(n)的质因子
}
//print
for (int i = 0; i < num; i++)
{
if (i > 0)
printf("*");
printf("%d", fac[i].x);
if (fac[i].cnt > 1)
{
printf("^%d", fac[i].cnt);
}
}
}
return 0;
}优化:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#include <iostream>
#include <cmath>
using namespace std;
int main(void)
{
int n = 0;
cin >> n;
cout << n << "=";
int sqrtn = sqrt(n), count = 0;
for (int i = 2;i <= sqrtn && n > 1;++i) {
if (count) cout << "*";
for (count = 0;!(n % i) && n > 1;n /= i, ++count);
if (count >= 1) {
cout << i;
if (count > 1) cout << "^" << count;
}
}
if (!count) cout << n;
return 0;
}
大数运算问题
C++ STL 标准模板库
vector 可变长数组
- 二维数组定义用法:
- vector
vi[100];如此定义仅二维上为可变长,一维为定长 100;
- vector
- vector
> vi 一维和二维都是变长数组
- vector
- 访问数组
- 下标访问 vi[0]
- 二维数组定义用法:
迭代器(iterator)访问 vector
::iterator it=vi.begin(); it指向数组起始地址,类似指针,可通过*it来访问向量组中的元素
1
vi[i]=*(vi.begin()+i)=*(it+i);
end()函数并不指的是尾元素地址而是其下一地址,左闭右开这么理解,该地址不存储任何元素
支持自加自减操作;it++,++it
拓展变形:1
2
3for(vector<int>:: iterator it=vi.begin();it!=vi.end();it++){
printf("%d ",*it);
}size()函数返回的是unsigned类型,但一般用%d取整型结果问题不大
vector用处:对于不确定输入样本的按照一定格式输出的问题,利用vector来存储输入的结果再遍历输出;邻接表
set 集合
排序题
解题思路基本都是差不多的
涉及存储量大的数据或者说以结构体的形式来进行比较
例题: A1012
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int length = 1000000;
struct student
{
int id;
int rank[4]; //0-3:A,C,M,E
} stu[2005];
int ans[length][4] = {0}; //保存每个id下每个成绩对应的排名
int now;
bool cmp(student a, student b)
{ //cmp只能带两个参数
return a.rank[now] > b.rank[now];
}
int main()
{
int n, m;
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++)
{
scanf("%d %d %d %d", &stu[i].id, &stu[i].rank[1], &stu[i].rank[2], &stu[i].rank[3]);
stu[i].rank[0] = round((stu[i].rank[3] + stu[i].rank[1] + stu[i].rank[2]) / 3.0) + 0.5;
}
for (now = 0; now < 4; now++)
{ //依次排序
sort(stu, stu + n, cmp);
//第一名先设置好
ans[stu[0].id][now] = 1;
for (int j = 1; j < n; j++)
{
if (stu[j].rank[now] == stu[j - 1].rank[now])
{
//则排名一样
ans[stu[j].id][now] = ans[stu[j - 1].id][now];
}
else
{
ans[stu[j].id][now] = j + 1;
}
}
}
char map[4] = {'A', 'C', 'M', 'E'};
int query;
for (int i = 0; i < m; i++)
{
scanf("%d", &query);
if (ans[query][0] == 0)
{
printf("N/A\n");
}
else
{
int k = 0;
for (int j = 1; j < 4; j++)
{ //选出最小的数值,若大小相同则输出优先级最高的字母
if (ans[query][j] < ans[query][k])
{
k = j;
}
}
printf("%d %c\n", ans[query][k], map[k]);
}
}
return 0;
}首先不要被大量的数据吓到了,要把题目理解清除,找到排序的对应要求,拆分成多个步骤来解题循序渐进
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!