算法中琐碎知识点(排序、哈希散列、进制转化、C++)
一、进制转化
- 对于一个P进制想要转化为Q进制的数,需要分成两步:
1. 将P进制数x转化为十进制数y;
int y = 0, product = 1;//product在循环中会不断乘P,得到1, p,p^1.....
while(x != 0){
y += (x % 10) * product;
x /= 10;
product *= P;
}
2. 将十进制数y转化为Q进制z;
- 采用除基取余法,所谓基是指进制Q,每次将数除以Q,然后将得到的余数作为低位存储,而商继续除以Q,最后当商为0时,将所有位从高到低输出就可以得到z;
int z[40], num = 0;
do{
z[num++] = y % Q;
y /= Q;
}while(y != 0);
- 这样将z数组从高位z[num-1]到低位z[0]即为Q进制z,进制转换完成。
二、各种排序
- 需要一个判断每一次排序后是否一致的函数:
bool isSame(int A[], int B[]){
for(int i = 0; i < n; i++){
if(A[i] != B[i]) return false;
}
return true;
}
- 需要一个进行下一轮后输出结果的函数
void showArray(int A[]){
for(int i = 0; i < n; i++){
if(i != 0) printf(" ");
printf("%d", A[i]);
}
printf("\n");
}
- 需要一个每一步排序判断的函数,即与具体的算法进行结合;
bool insertSort(){
bool flag = false;
for(int i = 1; i < n; i++){
if(i != 1 && isSame(tempOri, change)){
flag = true;
}
int temp = tempOri[i], j = i;
while(j > 0 && temp < tempOri[j-1]){
temp[j] = temp[j-1];
j--;
}
if(flag == true){
return true;
}
}
return false;
}
1. 选择排序
- 对于一个序列A[N],进行N趟排序,第i次将i~N中选出最小的元素,然后将其与A[i]位置的元素进行交换;
void seleteSort(){
for(int i = 0; i < n; i++){
int k = i;
for(int j = i; i < n; i++){
if(A[j] < A[k]){
k = j;
}
}
int temp = A[i];
A[i] = A[k];
A[k] = temp;
}
}
2. 插入排序
- 默认将A[N]序列的前方1~i-1位为有序,然后将第i位插入到其中,具体实现方法为,临时使用一个变量,然后存储A[i],变量j = i,然后从i-1个元素开始比较,如果大于它,将第i-1位覆盖掉A[i], 说明第i为可以往前插入,然后j--;
int A[Maxn], n;//数组元素个数为n,下标从1~n
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;
}
}
3. 2路-归并排序
- 原理是通过每次将两组有序序列再进行排序,最后进行递归处理即可,首先将左边的进行递归排序,然后是右边,最后进行合并即可
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(int i = 0; i < index; i++){
A[L1 + i] = temp[i];
}
}
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);
}
}
4. 快速排序
- 每次将主元Left上的元素放到它的基位上,也就是左边元素都小于它,右边元素都大于它, 关键是它的Partition函数,使用temp接收数组左边第一个元素,然后分别使用指针指向数组的第一个元素和最后一个元素,结束标志是左边指针和右边指针重合时,首先对右边进行查找到第一个小于temp的, 然后将该值赋到左边第一个值处,左边同理,最后将temp值放入left终止处,返回left位置;
- 最终函数关键是第一次返回的主元位置,将数组划分为两个部分,然后递归继续划分
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 qiuckSort(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);
}
}
三、哈希散列(key是整数)
1. 插入
- 直接将输入的数作为数组下标来对这个数的性质进行统计,用空间换时间,一般这样处理每个数不会超过10的5次方
- 散列就将一个元素是通过一个函数转化为一个整数,使得该整数尽量唯一的代表这个元素。
- 使用一个v数组进行存储转换后的下标,hash数组用于记录是否插入到散列中;
vector<int> v(n), hash(m);
for(int i = 0; i < n; i++){
scanf("%d", &v[i]);
int size = 0;
while(size < m){
if(hash[(v[i] + size*size) % m] != 0){
size++;
}else{
hash[(v[i] + size*size) % m] = 1;
v[i] = (v[i] + size*size) % m;
break;
}
}
if(size >= m) v[i] = -1;
}
2. 查找
- 查找的时候,可以根据上面插入时候的hash表,每次进行查找,方法基本一致,统计查找次数,有一点区别是,这个时候size的界限是小于等于m,而不是小于m
- 同时,如果查到到hash值为空,也应该停止查找;
3. 常用的散列函数:
- 直接定址法
- 平方取中法
- 除留余数法(常用)
- 公式:H(key) = key % mod, 一般将mod取成Tsize, mod一般是个素数。
bool isPrime(int n){
if(n <= 1) return false;
int sqr = (int)sqrt(n);
for(int i = 2; i <= sqr; i++){
if(n % i == 0) return false;
}
return true;
}
4. 除留余数法常用解决冲突的方法
- 线性探测法,如果被占用,那么就加一。
- 平方探测法,当位置被占有时,将k等于1^2, 2^2, 3^2,进行查找,如果k在[0, Tsize)都找不到位置,那么当k>=Tsize也找不到位置;
- 链地址法,直接将余数相同的值,放入一个链表中;
作者:睿晞
身处这个阶段的时候,一定要好好珍惜,这是我们唯一能做的,求学,钻研,为人,处事,交友……无一不是如此。
劝君莫惜金缕衣,劝君惜取少年时。花开堪折直须折,莫待无花空折枝。
曾有一个业界大牛说过这样一段话,送给大家:
“华人在计算机视觉领域的研究水平越来越高,这是非常振奋人心的事。我们中国错过了工业革命,错过了电气革命,信息革命也只是跟随状态。但人工智能的革命,我们跟世界上的领先国家是并肩往前跑的。能身处这个时代浪潮之中,做一番伟大的事业,经常激动的夜不能寐。”
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.