1.选择排序
数组A[n],从0开始到n,进行n趟操作,每次选出最小的数放在该趟数区间第一位。比如:第一次的区间是[0,n],第二次是[1,n]。
2.插入排序
数组A[n],令i从1开始枚举,进行n-1趟操作。每次将第i个数插入到[0,i-1]的区间中。
3.sort()函数
sort函数的使用必须加上头文件
#include<algorithm>
using namespace std;
sort()就是用来排序的函数,它根据具体情形使用不同的排序方法,效率较高。
使用方式:
sort(首元素地址(必填),尾元素地址的下一个地址(必填),比较函数(非必填));
如若不写比较函数,则默认进行递增排序。
比较函数用cmp来实现,eg:
bool cmp(int a,int b){
return a>b;//当a>b时,把a放在b前面
}
注意:strcmp(str1,str2);当str1的字典序小于str2时返回一个负数(不一定是-1),当str1的字典序等于str2返回0,当str1的字典序大于str2时返回一个正数(不一定是1)。
4.散列hash
“将元素通过一个函数转换为整数,使得该整数可以尽量唯一地代表这个元素。”
5.二分法
每次折中查找,直到找到目标。
int binarySeach(int a[],int left, int right,int x)
{
int mid;
while(left <= right)
{
mid = (right + left) / 2;
//当right值大于整形一半时可能会溢出,使用mid =(right+left)/2+left代替;
if(a[mid] == x) return mid;
else if(a[mid]<x)
left = mid +1;
else
right = mid -1;
}
return -1;
}
二分法多数用于求有序数列,或单调函数中 一个满足某条件的值的位置,注意区间的开闭要求
二分法的拓展:用于求根号2的近似值
核心:当要求精度为1e-5时,则while的进入条件为right-left>精度即可。退出条件根据不同的题目要求不同。
6.快速幂(二分幂)
a的b次方,快速幂基于以下事实:
快速幂的递归写法:
typedef long long LL;
LL binaryPow(LL a,LL b,LL m){
if(b == 0) return 1;
if(b % 2 == 1) return a * binaryPow(a, b-1,m) %m;
//奇数
else{//偶数
LL mul = binaryPow(a,b/2,m);
return mul *mul % m;
}
}
7.two pointers
利用问题本身与序列的特性,使用两个下标i、j对序列进行扫描,以较低的复杂度解决问题。
较经典的运用:
给定一个递增的正整数序列和一个正整数M,求序列中的两个不同位置的数a和数b,使得他们的和恰好为M,输出所有满足条件的方案。
若是二重循环枚举的话,当数组N在10的5次方是规模是不可承受的。
for(int i =0;i<n;i++){
for(int j = i+1;j<n;j++){
if(a[i]+a[j] ==m){
...
}
}
}
而i和j的大小变化有规律,结果中i最小的解j最大,所以优化后,i = a[0],j= a[n],
如果a[i]+a[j]<m,则i++,i值最小的解还没有找到。
如果a[i]+a[j] == m,则说明找到了一组解,并且在区间[i+1,j-1]可能还存在解。
如果a[i]+a[j]>m,则j–。
while(i<j){
if(a[i]+a[j]==m){
printf("%d %d\n",i,j);
i++;
j--;
}else if(a[i]+a[j] == m){
i++;
}else {
j--;
}
}`在这里插入代码片`
8.归并排序——2-路归并排序
原理:将序列两两分组,将序列归并为n/2个组,组内单独排序;然后将这些组再两两归并,生成n/4个组,组内再单独排序,以此内推,只到只剩下一个组为止。
//将数组a的[L1,R1]与[L2,R2]区间合并为有序区间(此处L2= R1+1)
const int maxn = 100;
void merge(int a[],int L1,int R1,int L2,int R2){
int i =L1,j=L2;
int temp[maxn],index = 0;
while(i <= R1 && j <=R2){
if(a[i] <= a[j]){
temp[index++] = a[i++];
}else{
temp[index++] =a[j++];
}
}
while(i<=R1) temp[index++] = a[i++];
while(j<=R2) temp[index++] = a[j++];
for(i =0;i<index;i++){
a[L1+i] = temp[i];
}
}
递归写法:
//将数组a当前区间[left,right]进行归并排序
void mergeSort(int a[],int left ,int right){
if(left < right){
int mid = (left+right) / 2;
mergeSort(a,left,mid);//递归,将左子区间归并排序
mergeSort(a,mid+1,right);
merge(a,left,mid,mid+1,right);//将左子区间和右子区间合并
}
}
非递归写法:
因为每次组内元素个数上限都是2的幂次,每次执行合并的次数为上一次的2倍。
以下代码中数组从1开始
void mergeSort(int a[]){
for(int step = 2;step / 2 <=n;step *=2){
//取出每组数目的值
for(int i = 1;i<n;i += step)
int mid = i +step/2-1;//左子区间元素个数为step/2
if(mid +1 <= n){//右子区间存在元素则合并
merge(a,i,mid,mid+1,min(i+step-1,n);
}
}
}
}
也可使用sort函数替代merge函数
void mergeSort(int a[]){
for(int step = 2;step / 2 <=n;step *=2){
//取出每组数目的值
for(int i = 1;i<n;i += step){
sort(a+i,a+min(i+step,n+1));
}
}
9.快速排序
调整序列中的元素,使当前序列最左端的元素在调整后满足左端所有元素均不超过该元素、右侧所有元素均大于该元素。
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]作为主元,而是随机选一个作为主元
C语言中产生随机数的函数,需要添加stdlib.h头文件和time.h头文件。在main函数第一句写上
srand((unsigned)time(NULL));
用于初始化随机种子。在需要使用随机数的地方使用rand()函数。想要限定随机数产生范围,使用rand() % a+b;表示范围在[a-1,b];
int randPartition(int a[],int left, int right){
int p = (round(1.0*rand()/RAND_MAX*(right - left)+left;
swap(a[p],a[left]);
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;
}