一、一些常规的算法
1、对于一个字节(8bit) 的无符号整型变量,求其二进制表示中“1”的个数。
要求算法的执行效率尽可能高。
#include<stdio.h>
int count1(int x) //法1
{
int num = 0;
while(x)
{
if(x&1 == 1) num++; //按位与运算,x&1的结果就是取x二进制的最末位,
//即可以判断x是奇数还是偶数
x = x>>1; //右移1位,相当于/2
}
return num;
}
int count2(int x) //法2
{
int num = 0;
while(x)
{
x = x & (x-1); //对于任意整数x,令x=x&(x-1),
//该运算将x的二进制表示的最后一个1变成0
num++;
}
return num;
}
int main()
{
int x, y;
printf("Please input a number:");
scanf("%d", &x);
// y = count1(x);
y = count2(x);
printf("The number of 1 in the binary number is %d", y);
return 0;
}
2、给定一个整数N,N!末尾会有多少个0呢?编写算法计算给定的N!末尾有多少个0?
例如:N=10,N!=3628800,N!末尾有2个0。
#include<stdio.h>
//末尾有多少个0 =》找有多少个5
int count0(int x) //法1
{
int n = 0;
while(x != 0)
{
n = n + x/5;
x = x/5;
}
return n;
}
int count1(int x) //法2:递归 (递归会慢一些)
{
int n = 0, n1 = 0, n2 = 0;
n1 = x/5;
if(n1 > 0)
n2 = count1(n1);
n = n1 + n2;
return n;
}
int main()
{
int x, n = 0;
printf("Please input a number: ");
scanf("%d", &x);
n = count0(x);
// if(x > 0)
// n = count1(x);
printf("The number of 0 in tail is %d", n);
}
//大整数加法,大整数乘法
3、求N!的二进制表示中最低位的1的位置。(跟第2题类似,即找二进制末尾有几个0)
即找2的个数
#include<stdio.h>
int count1(int x) //法1
{
int n = 0;
while(x != 0)
{
n = n + x/2;
x = x/2;
}
return n;
}
int main()
{
int x, n = 0;
printf("Please input a number: ");
scanf("%d", &x);
n = count1(x);
printf("The inverted index of the lowest 1 is %d\n", n+1);
}
4、找出数组中的最大值、最小值
对于一个由N个整数组成的数组,设计算法(程序),求出该数组中的最大值和最小值。
要求尽可能少的元素值比较次数。
你能想到多少种解决方法?给出尽可能多的解决方案,分析每个方案的元素值比较次数。
方法:
1.正常比: 2n-2 全部比较一次
2.奇偶位比较:3n/2 -2 循环一次比较两个数,奇偶位比较
3.二分法: 3n/2 -2
分治法解题的一般步骤:(递归的思想)
(1)分解,将要解决的问题划分成若干规模较小的同类问题;
(2)求解,当子问题划分得足够小时,用较简单的方法解决;
(3)合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。
二分法在求最值上的应用算法思想:
void maxmin2(int A[],int i,int j,int *max,int *min)
//A存放输入的数据,i,j存放数据的范围,初值为0,n-1,*max,*min 存放最大和最小值
{
int mid,max1,max2,min1,min2;
if (j==i)
{
最大和最小值为同一个数;
return;
}
if (j-1==i)
{
将两个数直接比较,求得最大和最小值;
return;
}
mid=(i+j)/2;
求i~mid之间的最大最小值分别为max1,min1;
求mid+1~j之间的最大最小值分别为max2,min2;
比较max1和max2,大的就是最大值;
比较min1和min2,小的就是最小值;
}
#include<stdio.h>
void compare(int *a, int n, int * max, int * min) //2.奇偶位比较:注意需要判断数组长度是奇数还是偶数
{
*min = a[0];
*max = a[1];
int i;
if(n%2 == 0) //数组元素个数为偶数
{
for(i = 2; i < n-1; i = i + 2)
{
if(a[i] < a[i+1])
{
if(a[i] < *min)
*min = a[i];
if(a[i+1] > *max)
*max = a[i+1];
}
else
{
if(a[i+1] < *min)
*min = a[i+1];
if(a[i] > *max)
*max = a[i];
}
}
}
else //数组元素个数为奇数
{
for(i = 2; i < n-1; i = i + 2)
{
if(a[i] < a[i+1])
{
if(a[i] < *min)
*min = a[i];
if(a[i+1] > *max)
*max = a[i+1];
}
else
{
if(a[i+1] < *min)
*min = a[i+1];
if(a[i] > *max)
*max = a[i];
}
}
//奇数在最后会多出一位,单独进行比较
if(a[n-1] < *min)
*min = a[n-1];
if(a[n-1] > *max)
*max = a[n-1];
}
}
void compare1(int *a, int n, int * max, int * min) //1.正常比:所有元素全部比较一次
{
*min = a[0];
*max = a[1];
int i;
for(i = 2; i < n; i++)
{
if(a[i] < *min)
*min = a[i];
if(a[i] > *max)
*max = a[i];
}
}
void search_max_and_min_value(int *array, int index_begin, int index_end, int* max, int* min) //3.二分法
{
//非法输入
if(array == NULL || max == NULL || min == NULL) return;
if(index_begin > index_end) return;
/// the minimum array comparison
if (index_end - index_begin <= 1) //递归出口
{
if (array[index_end] < array[index_begin])
{
*max = array[index_begin];
*min = array[index_end];
}
else
{
*min = array[index_begin];
*max = array[index_end];
}
return;
}
/// divide array to small problem
int middle = index_begin + (index_end - index_begin) / 2;
int max_left, max_right, min_left, min_right;
//递归
search_max_and_min_value(array, index_begin, middle, &max_left, &min_left);
search_max_and_min_value(array, middle + 1, index_end, &max_right, &min_right);
/// conquer the small problem
if (max_left > max_right)
*max = max_left;
else
*max = max_right;
if (min_left < min_right)
*min = min_left;
else
*min = min_right;
return;
}
int main()
{
int a[10] = {3,1,3,1,3,5,8,2,2};
int n = 9;
int max , min;
compare(a, n, &max, &min);
printf("min: %d , max: %d\n", min, max);
compare1(a, n, &max, &min);
printf("min: %d , max: %d\n", min, max);
search_max_and_min_value(a, 0, n-1, &max, &min);
printf("min: %d , max: %d\n", min, max);
}
5、快速找出数组中和为sum的数
快速找出一个数组中所有满足条件的的两个数。
(条件:这两个数的和等于一个给定的值sum.)
为了简化,我们假设这个数组中肯定存在至少一组符合要求的解。
给出尽可能多的解决方法,并分析每种方法的效率。
方法:
1.穷举O(N^2)
2.求两数之和=》已知一个数找另外一个数
key从数组头依次指向数组尾
1)折半查找O(Nlog2N)
2)哈希查找O(N)空间复杂度:S(N)
3.先将数组排序,在用两个指针分别从表头表尾出发,向内边走边查
令i = 0,j = n-1,看arr[i] + arr[j] 是否等于Sum,如果是,i++; j–;继续查找。
如果小于Sum,则i = i + 1;如果大于Sum,则 j = j – 1。
这样只需要在排好序的数组上遍历一次,就可以得到最后的结果,时间复杂度为O(N)。
两步加起来总的时间复杂度O(Nlog2N)
下面利用方法3解决该问题。
#include<iostream>
#include<algorithm>
using namespace std;
int findNumber(int a[], int n, int sum, int number[][2])
{
sort(a, a+n-1);
int i=0, j=n-1, k=0;
while(i < j)
{
while(i<j && a[i]+a[j] == sum)
{
number[k][0] = a[i];
number[k][1] = a[j];
k++;
i++; j--;
}
while(i<j && a[i]+a[j] < sum)
i++;
while(i<j && a[i]+a[j] > sum)
j--;
}
return k;
}
void main()
{
int a[8] = {4,5,1,6,2,7,3,8};
int n = 8, sum = 7, num;
int number[8][2];
int i,j;
for(i = 0; i < n; i++)
for(j = 0; j < 2; j++)
number[i][j] = 0;
num = findNumber(a, n, sum, number);
for(i = 0; i < num; i++)
{
for(j = 0; j < 2; j++)
cout<<number[i][j]<<" ";
cout<<endl;
}
}
6、大整数加法
输入数字n,按顺序打印输出从1到最大的n位十进制数。比如输入3,则打印出1,2,3,一直到最大的3位数999.
要考虑若n很大,我们求最大的n位数用int 或long long 也可能会溢出。
考虑大数问题;
提示:关于大数的表示和存储:用字符数组(取值为数字型字符)来表达大数。
方法:
1.每一次输出+1
2.全排列,递归: 频繁地入栈出栈,占用内存资源。
注意:
数组使用前要初始化
输入n后,我们指定数组的长度为n+1(因为考虑到进位)
思路:
数组从左向右依次+1,若进位,则向右进一位,最后逆序输出。
tips:
C++中未初始化的数组的默认值问题
1.全局数组,未初始化时,默认值都是 0;
2.局部数组,未初始化时,默认值为随机的不确定的值;
3.局部数组,初始化一部分时,未初始化的部分默认值为 0;
#include<iostream.h>
void add1(int a[],int n) //+1运算,将其看作大整数加法,加数为1
{
int i;
int *one = new int[n+1]; //将1也设为数组,其长度为n+1
one[0] = 1; //初始化
for(i = 1; i < n+1; i++)
one[i] = 0;
for(i = 0; i < n; i++) //每一位对应相加
{
a[i] = a[i] + one[i];
if(a[i] >= 10) //进位
{
a[i] = a[i] - 10;
a[i+1]++;
}
}
}
void bigNumber(int a[], int n)
{
int i, j;
while(a[n]==0) //终止条件
{
add1(a, n);
if(a[n] == 0)
{
for(i = n; a[i] == 0; i--); //从右向左,找到第一个不为0的数。
for(j = i; j >= 0; j--) //逆序输出每一位
cout<<a[j];
cout<<" ";
}
}
}
void main()
{
int n = 1, i;
cout<<"n = ";
cin>>n;
int* a = new int[n+1];
for(i = 0; i < n+1; i++) //初始化数组
a[i] = 0;
bigNumber(a, n);
cout<<endl;
}
7.快速找出x在二维数组中的位置
设二维数组B[0…m-1][0…n-1]的数据在行、列方向上都按从小到大的顺序有序,
且x在B中存在。试设计一个算法,找出x在B数组中的位置i, j。要求比较的次数不超过m+n.
思想: 因为数据在行、列方向上都按从小到大的顺序有序,所以可以只比较一列,
在这里我们只比较最后一列,因为是从上向下比,所以该数如果比某一行最后一列小,那么该数一定在该行上。
#include <iostream>
using namespace std;
bool position(int** number, int m, int n, int x, int &p, int &q)
{
int i, j;
bool tag = false;
if (x < number[0][0] || x > number[m-1][n-1]) //非法数
return tag;
else
{
for (i = 0; i < m; i++)
{
if (x == number[i][n-1]) //如果该数就在最后一列上,直接返回
{
p = i; q = n-1;
tag = true;
break;
}
else if (x < number[i][n-1]) //如果比某一行的最后一列小,即找到该行,跳出循环
break;
}
if (x < number[i][n - 1]) //在改行上遍历寻找
{
for (j = 0; j < n-1; j++)
{
if (x == number[i][j])
{
p = i; q = j;
tag = true;
break;
}
}
}
return tag;
}
}
int main()
{
int i, j, m=4, n=4, a = 1;
int x;
bool tag;
int** number = new int* [m];
for (i = 0; i < m; i++)
{
number[i] = new int[n];
for (j = 0; j < n; j++)
{
number[i][j] = a;
a++;
}
}
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
cout.width(3);
cout<<number[i][j]<<" ";
}
cout << endl;
}
cout << endl;
cout << "x = ";
cin >> x;
i = 0; j = 0;
tag = position(number, m, n, x, i, j);
if (tag == true)
cout << "i = " << i << ", j = " << j << endl;
else
cout << "没有找到x" << endl;
}