废话不多说直接切入正题
一.递归
1.递归分为数学公式递归和枚举递归(大问题化小问题)。所谓递归,顾名思义,既有递,也有归。我所理解的递就是递推式,能够一次次的调用自身函数直到最后一次;归就是递归边界,即达到边界后不再使用函数,然后出结果。
2.递归与循环的区别
递归与循环看着有点相似,但实现起来却有很大的差别。比如阶乘,最大公约数,这些能用循环求出来的也可以用递归写出来。但是那些递归的题目一般都不能用循环写出来,往往不能得到想要的答案。它俩都是通过控制变量达到边界从而输出答案,但是递归往往更难找到何递何归,而且在递归函数中往往多次调用自身函数,而且很难想出来。
3.本周所遇到的关于递归的题型和应用
(写一点核心代码或者只写出递推式再加强一下自己的理解)
(1)蛇形方阵
void fill( int number, int begin, int size)
{ int i, row = begin, col = begin;
if (size == 0) return;
if (size == 1) { p[begin][begin] = number; return; }
p[row][col] = number; ++number;
for (i=0; i<size-1; ++i)
{ ++row; p[row][col] = number; ++number; }
for (i=0; i<size-1; ++i)
{ ++col; p[row][col] = number; ++number; }
for (i=0; i<size-1; ++i)
{ --row; p[row][col] = number; ++number; }
for (i=0; i<size-2; ++i)
{ --col; p[row][col] = number; ++number; }
fill( number, begin+1, size-2 );
}
(2)字符串的全排列(三种方法)
第一种只适用于全排列排列1~n
int n,p[11],hashTable[11]={false};
void generateP(int index)
{ if(index(==n+1)
{for(int i=1;i<=n;i++)
printf("%d",p[i]);
printf("\n");return;}
for(int x=1;x<=n;x++)
{if(hashTable[x]==false)
{p[index]=x;
hashTable[x]=true;
generateP(index+1);
hashTable[x]=false;}//已经处理完的要还原!!!
}}
第二种适应于任何排列
void pw(char str[],int k)
{
int i;
if(k==strlen[str])cout<<stl<<endl;
else for(int i=k;i<strlen(str);++i)
{
swap(str,k,i);pw(str,k+1);
swap(str,k,i);//回归原始!!!
}
}
第三种用到一个函数 next_permutation(注意不能用while,否则第一种情况便不会输出)
int main(){
int a[10]={1,2,3};
do{
printf("%d%d%d\n",a[0],a[1],a[2])
}while(next_permutation(a,a+3));
}
(3)斐波那契数列
int f(int n)
{
if(n==0||n==1)return 1;
else return f(n-1)+f(n-2);
}
(4)上楼梯(和斐波那契数列类似)
上到第n层的办法数=(n-1)层的情况+(n-2)层的情况
(5)n皇后问题
在本问题中,将递归与回溯结合,我现在只能单纯的理解为,当枚举递归时,枚举次数太多导致程序运行的慢,而且只能在这一种情况递归完之后,才能检验本情况的正确性。而回宿便可以在每种情况的过程中便可以提前检测前面的这些数据是否合适,从而判断需不需要继续下写这种情况。如果前面没问题,就能继续递归。如果此情况行不通的话,那么就不需要继续向下了,而是向前返回一次数据,然后再继续换另一个数据尝试,可能这就是回溯的意思吧。(我的语言简直太粗糙了,这就是我目前理解到的意思。)
(那就直接上和回溯联系后的代码吧,加强一遍我的理解)
void generateP(int index)
{ if(index==n+1){count++;return;}
for(int x=1;x<=n;x++)
{
if(hashTable[x]==false)
bool flag=true;
for(int pre=1;pre<index;pre++)
{
if(abs(index-pre)==abs(x-p[pre]))
{
flag=false;break;//两个皇后在一个对角线上,冲突
}
} if(flag)
{ f[index]=x;hashTable[x]=true;
generateP(index+1);hashTable[x]=false;}
}
}
(6)阶乘 (这个太熟悉就不写了)
二、STL
详细的用法书上也都有,一搜也全都是,就不一一详细的说每个的用法了,我就简单总结一下从算法笔记上学到的吧,简单说说他们的特别之处和一些注意事项,并理清一下我脑袋中凌乱的知识点
1、vector
在上一篇博客中详细总结了它的用法,长度能够根据需要而随时改变,能避免普通数组超内存的情况。(还可以用来以邻接表的方式存储图,我还没有学到这个)
2、set
它的好处就是将容器内的元素自动按递增的顺序排序并且自动去重,方便了许多
它不能用下标访问,只能用迭代器访问
3、 string
可以通过下标和迭代器访问,和vector一样都能对迭代器进行加减某个数字 (str.begin()+3)
两个string数组可以直接相加拼接起来
4、map
能将任何基本类型映射到各种基本类型,第一个可以称为键,每个键只能在map中出现一次,第二个可称为值。例如遇到位数特别多的数字,就可以用map把这些数字当成字符串建立string至int的映射(不能是char,char不能作为键值)
可以用下标和迭代器访问
5、queue
队列:先进先出
front()和back()分别访问队首和队尾(使用前最好用empty()判断是否为空)
6、priority_queue
优先队列:本质是堆,队首元素永远都是优先级最大的
只能通过top()访问队首元素,在此之前也需要empty()判断是否为空
也可以把优先级最小的放队首(less <int>表示数字大的优先级大,greater<int>表示数字小的优先级大),因此只需如下定义
priority_queue<int,vector<int>,greater<int>>q;
可以解决一些贪心问题
7、stack
栈:后进先出
top()获得栈顶元素,pop()删除栈顶元素(注:栈顶元素是最后进的)
可以模拟实现一些递归,防止程序对栈的内存的限制而导致程序运行出错
8、一些可以直接用的常用函数(需要头文件<algorithm>)
max(),min(),abs(),swap() (这些都太熟悉了,不多说)
我学到的新函数:
(1)reverse:可以将int和string类型的数组反转
reverse(a,a+4):将a[0]到a[3]反转
reverse(str.begin()+2,str.begin()+6):对str[2]~[5]反转
(2)next_permutation:给出一个排序在全排列的下一个序列
(注意用do{}while不能用while,否则第一种情况便不会输出)代码上面有
(3)fill:吧数组或容器中某一段区间赋为某个相同的值
fill(a,a+5,5) ;a[0]~[4]全部赋值为5
四、心得体会
费老说这是一条不归路 我既然选择了就不会轻言放弃 最近工作好忙 但同时督促我更要抓紧时间有空就看看ACM算法 我要慢慢把时间调整利用好 平衡好工作和学习 抓住碎片化的时间
如果一有时间就做点代码题看资料的时间就少了,但只看资料不实践也不可以,经常纠结于此,我要抓紧找到那个平衡点想好今后该如何分配好自己的时间
马上就要网课了 下周见不到可ai可qin的费老了 我要继续加大马力学习啊证明我自己 绝不能在网课时间松懈 加油加油