《算法笔记》第四章

排序

排序算法的思想都比较简单,而且c++可以直接用sort函数进行排序,一般不会直接写排序代码

归根到底就是每轮处理一个数据,n个数据只需处理n-1次即可变得有序

选择排序

最简单的排序,每轮找出一个最值移到相应位置即可,代码

void selectsort(){for(int i=1;i<=n-1;i++){int k=i;for (int j=k;j<=n;j++){if(a[j]>a[k]){k=j;}}if(i!=k){int temp=a[i];a[i]=a[k];a[k]=temp;}
}

插入排序

每轮将一个数据插入已排好的数据中,代码

void insertsort(){for(int i=2;i<=n;i++){int temp=a[i],j=i;while(j>1&&temp<a[j-1]){a[j]=a[j-1];j--;}a[j]=temp;}
}

sort函数

使用前需加上头文件

#include<algorithm>
using namespace std;
sort(首元素地址,尾元素地址的下一个地址,比较函数(非必填));

如不写比较函数,则默认升序

比较函数

bool cmp(int a,int b){return a>b;//可以理解为当a>b时把a放在b前面,这样就实现了降序
}

对结构体排序

定义如下结构体

struct node{int x,y;
}ssd[10];

则比较函数为

bool cmp(node a,node b){return a.x>b.x;//按x值降序
}

如需二级排序,也可以

bool cmp(node a, node b){if(a.x != b.x){return a.x>b.x;}else{return a.y>b.y;}
}

散列

散列对于我来说是一个之前从来没有接触过的全新的算法思想

其核心思想是用空间换时间,用数组的下标来表示数据,代码如下

int hashtable[maxn]={0};
int main(){int n,m,x;scanf("%d%d",n,m);for(int i=0;i<n;i++){scanf("%d",&x);hashtable[x]++;}for(int i=0,i<m,i++){scanf("%d",&x);printf("%d",hashtable[x]);}return 0;
}

这段代码能输出m个数据分别在n个数据中出现的次数

由此,只要将数据的值转换为数组的下标,即可省去将数组遍历一遍的繁琐

除了正常的十进制整数之外,字符也可以通过一定的转化方式转化为哈希数组的下标,这里的思路是,将字符转化为一个整数使得这个整数能唯一地表示这个字符,这里可以根据字符的种类数n定义一个n进制的坐标,比如26个字母就可以将a转化为0,z转化为25,用26进制表示一个字符串。输入时将这个字符串转化为26进制等价的十进制数,这个十进制数就是该字符串的下标,可见这个下标对于特定的字符串是独一无二的。代码如下

int hashfunc(char s[],int len){int id=0;for(int i=0;i<len;i++){id=id*26+(s[i]-'a')//此处注意a必须是单引号,因为单引号才代表字符,双引号代表的是字符串数组}return 0;
}

其实不难发现,这个转化的函数就是把s[i]-'a’转化为26进制。

上面仅考虑了小写字母,如果是一段正常的字符串,那么还有大写字母和数字,只需如法炮制,增大进制数为62即可。

递归

递归的核心思想就是反复调用自身。

递归的一个重要作用是分治,也就是把一个问题划分为若干个小问题,分别解决这些小问题然后合并解就是原问题的解。

最经典的递归函数就是求斐波那契数列了,代码如下

int F(int n){if(n==1||n==2){return 1;}else return F(n-1)+F(n-2);
}

递归函数最重要的两件东西:递归边界和递归式。

递归边界就是最简单问题的解,递归式就是把问题分解为简单问题的方式。

在求斐波那契数列中,F(1)=F(2)=1就是递归边界,F(n)=F(n-1)+F(n-2)就是递归式

运用递归和哈希的思想,还能实现一些其他算法,比如输出n个数的全排列,代码如下

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;}}
}

运用这个全排列思想,还可以优化解决n皇后问题(即把n个皇后放在n*n棋盘上,要求不同行列和对角线),代码如下

void generateP(int index){if(index==n+1){bool flag=true;for(int i=1;i<=n;i++){for(int j=i+1;j<=n;j++){if(abs(i-j)==abs(P[i]-P[j])){flag=false;}}}if(flag){count++;}return;}for(int x=1;x<=n;x++){if(hashtable[x]==false){P[index]=x;hashtable[x]=true;generateP(index+1);hashtable[x]=false;}}
}

可以发现,这段代码其实和之前求全排列代码就是上半部分有改变,上半部分其实就是递归边界,全排列的边界是index=n+1就输出,而n皇后其实就是在index=n+1的基础上多判断了一步对角线是否相等。

这个算法还可以进行一点优化,不用把全排列都列出来再判断,可以回溯之前已经摆下的棋子判断是否冲突(即是否在同一条对角线),如果冲突那就下一个,这样可以省去一些无用的计算,代码如下

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){P[index]=x;hashtable[x]=true;generateP(index+1);hashtable[x]=false;}}}
}

递归算是我学习以来比较难理解的一块内容了,代码虽然勉强能看懂,其实中间有很多细节是要靠自己细细体会的,慢慢来吧。

贪心算法

贪心算法主要用来解决求最优的问题,而很多情况下我们无法一下子实现全局最优,于是就可以考虑在当前状态下实现局部最优,然后达到全局最优。

简单的贪心算法比如给一串数字输出最大(小)数等等,这些难度都不大。

还有一类区间不相交的问题,即从一堆给定区间中找出尽可能多的不相交区间。

解决这个问题的思路是首先考虑有无区间包含,若有区间包含了另一个区间则将其舍去,因为小区间可以腾出更多的空间。其次只需按照左端点从大到小依次选下去即可(按右端点从小到大也行),核心代码如下

struct Inteval{int x,y;
}I[maxn];
bool bmp(Inteval a,Inteval b){if(a.x!=b.x){return a.x>b.x;}else return a.y<b.y;
}
int main(){int n;while(scanf("%d",&n),n!=0){for(int i=0;i<n;i++){scanf("%d%d",&I[i].x,&I[i].y);}sort(I,I+n,cmp);int count=1,last=I[0].x;for(int i=1;i<=n;i++){if(I[i].y<=last){last=I[i].x;count++;}}printf("%d",count);}return 0;
}

二分

二分的算法其实不仅仅是用在计算机上,平时生活中也经常用到二分的思想。

在面对未知的情况时,折中似乎是一种最有效的方法。不停地折中,逼近正确答案的概率也就越大。尤其是在面对有序数列时,二分就能知道答案存在的大致区间然后缩小这个区间,不用一个个寻找。

代码如下

while(left<=right){mid=(left+right)/2;if(a[mid]==x){return mid;}else if(a[mid]>x){right=mid-1;}else{left=mid+1;}
}
return -1;//查找失败

这是最简单的二分查找,在实际的二分查找中经常会遇到输出第一个满足某条件的位置,通用代码如下

while(left<right){//如果left和right相等,意味着找到唯一位置mid=(left+right)/2;if(条件){right=mid;}else{left=mid+1;}
}
return left;

这段代码能输出第一个满足该条件的位置,并且该条件必须在序列中从左到右先不满足后满足如果不是的话取反即可。

除了查找之外,二分法还有其他的作用,比如估计无理数的近似值

以2\sqrt{2}2​为例,定义一个函数f(x)=x2f(x)=x^2f(x)=x2,而2\sqrt{2}2​的值又必定在1和2之间,所以只需找出一个逼近2\sqrt{2}2​的值(这里设精度为10−510^{-5}10−5),初始的二分区间为[1,2],只要将区间缩小到10−510^{-5}10−5即可,代码如下

double f(double x){return x*x;
}
double calSqrt(){double left=1,right=2,mid;while(right-left>1e-5){mid=(right+left)/2;if(f(mid)>2){right=mid;}else{left=mid;}}return mid;
}

再推广一下,可以用该方法解决任何求f(x)=0方程解的代码

double f(double x){return 函数;
}
double calSqrt(){double left=1,right=2,mid;while(right-left>精度){mid=(right+left)/2;if(f(mid)>0){//若f(x)递减,则改为<0right=mid;}else{left=mid;}}return mid;
}

可以得出二分可以用来解决一类答案在一个单调区间内的问题,设置好精度和关系,不停地二分尝试即可。

快速幂

求高次幂经常会溢出而且要进行多步循环,所以可以用二分的思想把幂运算拆分,即a2n=an∗ana^{2n}=a^n*a^na2n=an∗an,a2n+1=a2n∗aa^{2n+1}=a^{2n}*aa2n+1=a2n∗a,运用这个思路就可以用递归的思想求高次幂,代码如下(计算aba^bab%m)

long long binarypow(long long a,long long b,long long m){if(b==0)return-1;if(b%2==1)return a*binarypow(a,b-1,m)%m;else{long long mul=binarypow(a,b/2,m);return mul*mul%m;}
}

除了递归的思路之外,还可以用迭代的思想计算。

具体思路是把b看成二进制数,然后就可以把aba^bab拆分成若干a2ka^{2k}a2k的乘积,具体代码如下

long long binarypow(long long a,long long b,long long m){long long ans=1;whlie(b>0){if(b&1){//b的末尾是否为1,即b%2=1ans=ans*a%m;}a=a*a%m;b>>=1;//将b右移一位,即b=b/2}return ans;
}

two pointers

这个不是具体的算法,可以说是一种算法思想吧,当用一个循环变量枚举比较复杂时,改用两个循环变量,可以大大降低算法复杂度

具体的例子就是,从有序数列中找出两个数使其和为M,常规的逻辑当然是双层循环一个个试,但这样当数列很大时O(n2)的复杂度就不太方便,而运用two pointers的思想则能大大优化,代码如下

while(i<j){if(a[i]+a[j]==M){printf("%d%d",i,j);i++;j--;}else if(a[i]+a[j]>M){j--;}else{i++;}
}

归并排序

归并排序的思想是先把无序序列拆成若干个2个数的小序列,然后在小序列里面各自排序,排好后两两合并小序列继续排序,直到合并成一个序列。

快速排序

运用two pointers方法,从序列的两头将大于某个值的数放到一边,小于的放到另一边,遍历整个序列即可,代码如下。

int partition(int a[],int left,int right){int temp=a[left];while(left<right){while(left<right && a[right]>temp){right--;}a[left]=a[right];while(left<right && a[left]<=temp){left++;}a[right]=a[left];}a[left]=temp;return left;
}
void quicksort(int a[],int left,int right){if(left<right){int pos=partition(a,left,right);quicksort(a,left,pos-1);//对左边区间排序quicksort(a,pos+1,right);//对右边区间排序}
}

这个算法还可以用于判断a[left]是数组中第几大的数

其他技巧

  1. 在一些算法中(尤其是一些递归算法中),前面计算的结果可能在后面也用的上,这时就可以先把这些数据输出或者存储起来,等到后面再需要用到的时候就不用重新计算了,以递归法求斐波那契数列为例,越到后面计算量会变得很大,这时候来一个数组存储之前的数就能大大降低计算量
  2. 如果算法比较复杂容易超时,可以在本地运行出结果然后直接在题目里面输出,也可以在本地先小规模地试一下找找规律。

《算法笔记》第四章笔记相关推荐

  1. 《学习JavaScript数据结构与算法》 第四章笔记 栈

    文章目录 前言 一.栈? 二.构建两种栈的大致步骤 三.创建基于数组的栈 创建class Stack 定义用于操作栈的方法 使用栈 四.创建基于对象的栈 创建class Stack 定义用于操作栈的方 ...

  2. 《Go语言圣经》学习笔记 第四章 复合数据类型

    <Go语言圣经>学习笔记 第四章 复合数据类型 目录 数组 Slice Map 结构体 JSON 文本和HTML模板 注:学习<Go语言圣经>笔记,PDF点击下载,建议看书. ...

  3. 机器学习实战 基于_机器学习实战:基于Scikit-Learn和TensorFlow—第四章笔记

    机器学习实战:基于Scikit-Learn和TensorFlow---第四章笔记 一.学习目标 之前用了那么多的机器学习的模型,但是它们各自的训练算法在很大程度上还是一个黑匣子,我们对对系统内部的实现 ...

  4. 《Linux内核设计与实现》 第八周读书笔记 第四章 进程调度

    20135307 张嘉琪 第八周读书笔记 第四章 进程调度 调度程序负责决定将哪个进程投入运行,何时运行以及运行多长时间,进程调度程序可看做在可运行态进程之间分配有限的处理器时间资源的内核子系统.只有 ...

  5. 计算机网络(第7版)谢希仁著 学习笔记 第四章网络层

    计算机网络(第7版)谢希仁著 学习笔记 第四章网络层 第四章 网络层 4.3划分子网和构造超网 p134 4.3.1划分子网 4.3.2使用子网时分组的转发 4.3.3无分类编址CIDR(构建超网) ...

  6. 机器人导论(第四版)学习笔记——第四章

    机器人导论(第四版)学习笔记--第四章 4.1 引言 4.2 解的存在性 4.3 当n<6时操作臂子空间的描述 4.4 代数解法和几何解法 4.5 简化成多项式的代数解法 4.6 三轴相交的Pi ...

  7. 深入理解 C 指针阅读笔记 -- 第四章

    Chapter4.h #ifndef __CHAPTER_4_ #define __CHAPTER_4_/*<深入理解C指针>学习笔记 -- 第四章*//*指针数组 -- 意思就是这是一个 ...

  8. 管理系统中计算机应用第四章重点,管理系统中计算机应用课堂笔记第四章(4)...

    管理系统中计算机应用课堂笔记第四章(4) 分类:自考 | 更新时间:2016-07-08| 来源:转载 这个分析和抽象工作可分以下三步进行: 5.2.1数据流程图的绘制 数据流程图既是对原系统进行分析 ...

  9. Android群英传笔记——第四章:ListView使用技巧

    Android群英传笔记--第四章:ListView使用技巧 近期也是比較迷茫.可是有一点点还是要坚持的,就是学习了.近期离职了,今天也是继续温习第四章ListView,也拖了事实上也挺久的了,lis ...

  10. [云炬创业基础笔记] 第四章测试17

    [云炬创业基础笔记] 第四章测试7

最新文章

  1. 获取GridView隐藏列值
  2. 想和产品大咖一对一沟通吗?
  3. linux 查看ntp版本号_如何让Linux时间与internet时间同步(CentOS)?
  4. jetbrains从入门到卸载 (前言) 为什么要jetbrains
  5. python进程和线程
  6. 【经典回放】多种语言系列数据结构算法:二叉树(JavaScript版)
  7. Docker容器中常见的十种误区
  8. matlab潮流计算编程教学,潮流计算 程序_牛拉法潮流计算程序_matlab潮流计算教程...
  9. CodeBlock 常用快捷键
  10. 青龙面板 抖音极速版
  11. 如何使用 CSS 使表格居中(快速指南)
  12. adf输稿器是什么_送稿器是什么
  13. 转帖:经济危机来临时的上海MM生活
  14. 解决img撑大父盒子
  15. 合作开发收费系统——临时表解决用户重复登录问题
  16. Flutter 淡入淡出效果
  17. Java练习题-09
  18. 【软件测试】思维开拓—用软件测试的思维测试QQ好友是在线或者离线
  19. 南邮 OJ 1573 雷曼兔
  20. 手工卡纸做机器人_【机器人手工】幼儿园创意手工制作机器人,小小科学家从机器人开始...

热门文章

  1. MySQL核心技术(持续更新)
  2. 白鹭引擎学习笔记(二)
  3. MySQL 系统表空间ibdata:我也不想当一个死肥宅
  4. android wifi 无法连接电脑连接,没有数据线,Wifi也能连接Android真机开发调试!彻底解决“无法识别的USB设备”等数据线连接问题!...
  5. Linux字体相关文件存放的目录位置
  6. 市场调研报告-全球与中国充气帆船市场现状及未来发展趋势
  7. android底部导航切换实现(md)
  8. 对统驭科目和特别总账标志的理解
  9. 详解EC11编码器示波器波形图
  10. Can't update table 'psxt_user' in stored function/trigger because it is already used by statement wh