必看算法集合_2014找工作准备
2014-04-09 11:00
281 查看
/************************************************************************
* 程序功能:有N个大小不等的自然数(1--N),请将它们由小到大排序。
要求:时间复杂度为O(n),空间复杂度为O(1)。
*
************************************************************************/
void sort(int arr[], int n)
{
int t; /**//*临时变量:空间复杂度O(1)*/
for (int i = 1; i <= n; i++)
{
while(arr[i] != i)
{
t = arr[arr[i]];
arr[arr[i]] = arr[i];
arr[i] = t;
}
}
}
/************************************************************************
* 程序功能:给定能随机 生成整数1到5的函数,写出能随机生成整数1到7的函数
* 原 理:
rand5()*5+rand5(),新的数据范围变成:6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30.并且可以看出来这个25个数出现的可能性是一样的,于是我们可以只用6~26之间的21个数变成1~7这7个数,于是就是要每3个数对应一个数,即:
6,7,8对应1
9,10,11对应2
…………
24,25,26对应7
这种变化对应的方式是(6 - 3)/ 3 = 1,(7 - 3) / 3 = 1,(8-3) / 3 = 1.
************************************************************************/
int rand7()
{
int i;
//直到产生6~26之间的数跳出循环
while((i=rand5()*5+rand5()) > 26) ;
return (i-3)/3;
}
/************************************************************************
* 功 能:《数据结构》KMP算法 ,查找子串的位置
************************************************************************/
void get_next(char*t, int next[ ]){
int t_len=strlen(t);
int i=0; //求解每个next[i]
next[0]=-1; //递推基本条件,然后求解next[i+1]
int j=-1; //向后递推位置下标
/*
next[i]=k =>T0...Tk-1=Ti-k...Ti-1
求解next[i+1]
1> 如果T0..Tk-1Tk=Ti-k...Ti-1Ti=>next[i+1]=k+1=next[i]+1;
2>Tk<>Ti,next[k]=k', 如果Ti=Tk'=>next[i+1]=k'+1=next[k]+1=next[next[i]]+1;
3>依次递推 最后情况next[i+1]=next[0]+1=0,即
*/
while(i<t_len)
{
if(j==-1 ||t[i]==t[j]) //j==-1证明已经与t[0]不匹配了,此时next[i+1]=0
{
i++;
j++;
next[i]=j;
}
else
{
j=next[j];
}
}
}
int KMP(char *s,char *t){
int s_len=strlen(s);
int t_len=strlen(t);
int i=0;
int j=0;
int *next=new int [t_len];
get_next(t,next);
/*
for (int me=0;me<t_len;me++)
cout<<next[me]<<endl;
*/
if( t_len >s_len ) return -1;
while(i < s_len && j<t_len )
{
if(j==-1 || s[i]==t[j])
{
i++;
j++;
}
else
{
j=next[j];
}
}//end while
if( j>=t_len )
return i- t_len;
else
return -1;
}
/************************************************************************
* 程序功能:验证foo(2^31-3)的输出为2,
* 注 意:2^31是异或,不是指数
*
************************************************************************/
int foo(int x)
{
cout<<x<<endl;
cout<<-x<<endl;
return x&-x;
}
/************************************************************************
* 程序功能:n从1开始,每个操作可以选择对n加1或者对n加倍。若想获得整数2013,最少需要多少个操作。
* 输入:整数n,
输出:按上述操作获得n需要的操作个数
* 思路:从输入的整数n开始,若n为奇数,则减1,操作次数加1;否则n减半,操作次数加1.
* 测试例子:op(2013)
* 测试输出:18
* 创 建:2014/3/30
************************************************************************/
#include<cassert>
int op(int n)
{
assert( n>0 );
int time = 0;
if(1 == n)
time = 0;
else
{
if( n & 0x1 == 1)//n是奇数
{
n = n-1;
time += 1;//操作次数+1
}
time += op( n >> 1 ) ;//加上除2后的操作次数
time += 1 ; //除2,操作次数+1
}
return time;
}
/************************************************************************
* 程序功能:n从1开始,每个操作可以选择对n加1或者对n加倍。若想获得整数2013,最少需要多少个操作。
* 输入:整数n,
输出:按上述操作获得n需要的操作个数
* 思路:1、一个新数据结构num,num.d表示当前到达的数,num.time表示到达数的操作次数;辅助结构:队列
2、声明两个变量num one,two,初始数均为2,对应的操作次数为1;
3、one.d加1,one.time加1,当one.d<=FINAL时one进栈,否则输出;
two.d*2,two.time加1,当two.d<=FINAL时two进栈,否则输出;
4、当队列非空时出队列,转入3。
* 测试例子:op(2013)
* 测试输出:18
* 创 建:2014/3/30
************************************************************************/
/******************模拟栈******************/
typedef struct num {
int d;
int time;
} num;
typedef struct queue {
int front, rear, count;
num data[10000000];
} queue;
void enQueue(queue *q, num d)
{
q->data[q->rear ++] = d;
q->count ++;
}
num deQueue(queue *q)
{
num res;
res = q->data[q->front ++];
q->count --;
return res;
}
/*************************************/
void op2 (int FINAL)
{
assert(FINAL>1);
int flag = 0; //结束标志,flag=1,表示增长到输入数FINAL
num bt, one, two, s;
bt.d = 2;//起点从数2开始
bt.time = 1; //2变成1仅需要操作一次
queue *q = (queue *)malloc(sizeof(queue));
q->front = q->rear = q->count = 0;
enQueue(q, bt);
while (q->count > 0)
{
s = deQueue(q);
if (s.d == FINAL)
{ //到达输入的整数FINAL
flag = 1;
printf("%d\n", s.time);
break;
}
one.d = s.d + 1;
one.time = s.time + 1;
if (one.d <= FINAL )
enQueue(q, one);
two.d = s.d * 2;
two.time = s.time + 1;
if (two.d <= FINAL )
enQueue(q, two);
// printf("%d\n", q->count);
}
if (flag == 0)
{
printf("不可能!\n");
}
}
/************************************************************************
* 程序功能:n个数存入数组,模拟洗牌
* 输入:整数数组arr[],数组大小N
输出:一次洗牌操作
* 创 建:2014/3/30
* 说 明:此实现依据《STL源码剖析》random_shuffle的实现
* 思 路:每次迭代中arr[i]与arr[0]~arr[i]的一个随机数交换
* 测 试:int a[]={1,2,3,4,5,6,7};
int N=sizeof(a)/sizeof(a[0]);
shuffleArray_STL(a,N);
************************************************************************/
#include<time.h>
void shuffleArray_STL(int arr[],int N)//《STL源码剖析》random_shuffle的C++实现
{
int i, loc, tmp;
time_t t;
srand((unsigned int)time(&t));
// 洗牌算法
for (i = 1; i < N; i ++) {
loc = rand() % (i + 1); //或者加上 if( loc != i )
tmp = arr[loc];
arr[loc] = arr[i];
arr[i] = tmp;
}
for (i = 0; i < N; i ++)
printf("%d ", arr[i]);
printf("\n");
}
/************************************************************************
* 程序功能:n个数存入数组,模拟洗牌
* 输入:整数数组arr[],数组大小N
输出:一次洗牌操作
* 创 建:2014/3/30
* 说 明:此实现依据《算法导论》5.3节的均匀随机排列,每个排列出现的概率1/n!
* 思 路:每次迭代中arr[i]与arr[i]~arr[N-1]的一个随机数交换,这样第i次迭代后,arr[i]保持不变
int a[]={1,2,3,4,5,6,7};
int N=sizeof(a)/sizeof(a[0]);
shuffleArray_introductionOfAlgorithms(a,N);
************************************************************************/
int rand_ab(int a,int b)//均匀随机生成[a,b]内的一个数
{
time_t t;
srand((unsigned int)time(&t));
return ( a + rand() % ( b-a+1 ) );
}
void shuffleArray_introductionOfAlgorithms(int arr[],int N)
{
int i, loc, tmp;
time_t t;
srand((unsigned int)time(&t));
// 洗牌算法
for (i = 0; i < N; i ++) {
loc = rand_ab(i,N-1); //arr[i]<---->arr[i]~arr[N-1],第i次迭代后,arr[i]保持不变
tmp = arr[loc];
arr[loc] = arr[i];
arr[i] = tmp;
}
for (i = 0; i < N; i ++)
printf("%d ", arr[i]);
printf("\n");
}
/************************************************************************
* 程序功能:写一个程序,要求功能,求出用1、2、5这三个数不同个数组合的和为100的组合数)
* 思 路:这其实就是个数学问题,就是求 x + 2y + 5z = 100解的个数。变化一下,x + 5z = 100 - 2y,这个式子表明 x + 5z只能是偶数,以z为循环变量,有下述规律。
z=0, x=100, 98, 96, ... 0
z=1, x=95, 93, ..., 1
z=2, x=90, 88, ..., 0
z=3, x=85, 83, ..., 1
z=4, x=80, 78, ..., 0
......
z=19, x=5, 3, 1
z=20, x=0
因此,组合总数为100以内的偶数+95以内的奇数+90以内的偶数+...+5以内的奇数+1,
即为:(51+48)+(46+43)+(41+38)+(36+33)+(31+28)+(26+23)+(21+18)+(16+13)+(11+8)+(6+3)+1
第二种方法的循环次数仅为21次。
************************************************************************/
int Sum_Combination1(int nSum)
{
int nCnt = 0;
for (int i32I = 0; i32I <= nSum; i32I += 5)
{
nCnt += (i32I + 2)/2;
}
return nCnt;
}
/************************************************************************
* 程序功能:O(logn)求Fibonacci数列
* 思 路:下面介绍一种时间复杂度是O(logn)的方法。在介绍这种方法之前,先介绍一个数学公式:
{f(n), f(n-1), f(n-1), f(n-2)} ={1, 1, 1,0}n-1
(注:{f(n+1), f(n), f(n), f(n-1)}表示一个矩阵。在矩阵中第一行第一列是f(n+1),第一行第二列是f(n),第二行第一列是f(n),第二行第二列是f(n-1)。)
有了这个公式,要求得f(n),我们只需要求得矩阵{1, 1, 1,0}的n-1次方,因为矩阵{1, 1, 1,0}的n-1次方的结果的第一行第一列就是f(n)。
这个数学公式用数学归纳法不难证明。感兴趣的朋友不妨自己证明一下。
现在的问题转换为求矩阵{1, 1, 1, 0}的乘方。如果简单第从0开始循环,n次方将需要n次运算,并不比前面的方法要快。但我们可以考虑乘方的如下性质:
/ an/2*an/2 n为偶数时
an=
\ a(n-1)/2*a(n-1)/2 n为奇数时
要求得n次方,我们先求得n/2次方,再把n/2的结果平方一下。如果把求n次方的问题看成一个大问题,
把求n/2看成一个较小的问题。这种把大问题分解成一个或多个小问题的思路我们称之为分治法。这样求n次方就只需要logn次运算了。
* 输 入:待求的第n个斐波那契数
* 输 出:第第n个斐波那契数
* 说 明:第n个数: 1 2 3 4 5 6 7 8 ...
斐波那契: 1 2 3 5 8 13
* 调 用:cout<<matrixpower(6).m_00<<endl;
************************************************************************/
struct matrix2by2{
matrix2by2(
long m00 = 0,
long m01 = 0,
long m10 = 0,
long m11 = 0
)
:m_00(m00), m_01(m01), m_10(m10), m_11(m11){}
long m_00;
long m_01;
long m_10;
long m_11;
};
matrix2by2 matrixmultiply(const matrix2by2 &a, const matrix2by2 &b){
return matrix2by2(
a.m_00 * b.m_00 + a.m_01 * b.m_10,
a.m_00 * b.m_01 + a.m_01 * b.m_11,
a.m_10 * b.m_00 + a.m_11 * b.m_10,
a.m_10 * b.m_01 + a.m_11 * b.m_11
);
}
matrix2by2 matrixpower(int n){
matrix2by2 matrix;
if(n == 1)
matrix = matrix2by2(1, 1, 1, 0);
else if(n % 2 == 0){
matrix = matrixpower(n/2);
matrix = matrixmultiply(matrix, matrix);
}
else if(n % 2 == 1){
matrix = matrixpower((n-1)/2);
matrix = matrixmultiply(matrix, matrix);
matrix = matrixmultiply(matrix, matrix2by2(1, 1, 1, 0));
}
return matrix;
}
//更简明的实现
// * 调 用:cout<<power(6).a[0][0]<<endl;
struct matrix //定义2*2的矩阵
{
int a[2][2];
matrix(int aa,int b,int c,int d)
{
a[0][0] = aa;
a[0][1] = b;
a[1][0] = c;
a[1][1] = d;
}
matrix(){};
};
matrix mul(matrix& x,matrix& y) // 矩阵乘法 x = x*y
{
return matrix(x.a[0][0]*y.a[0][0] + x.a[0][1]*y.a[1][0],
x.a[0][0]*y.a[0][1] + x.a[0][1]*y.a[1][1],
x.a[1][0]*y.a[0][0] + x.a[1][1]*y.a[1][0],
x.a[1][0]*y.a[0][1] + x.a[1][1]*y.a[1][1] );
}
matrix power(int n){
matrix mat;
if(n == 1)
mat = matrix(1, 1, 1, 0);
else if(n % 2 == 0){
mat = power(n/2);
mat = mul(mat, mat);
}
else if(n % 2 == 1){
mat = power((n-1)/2);
mat = mul(mat, mat);
mat = mul(mat, matrix(1, 1, 1, 0));
}
return mat;
}
//非递归实现
// * 调 用:cout<<power2(6).a[0][0]<<endl;
matrix power2(int n)
{
//非递归代码:
if(n == 1)
return matrix(1, 1, 1, 0);
matrix result ;
matrix base (1,1,1,0);
bool flag=false;
if(n&1) flag=true;
n = n >> 1;
result = base;
while(n)
{
base = mul(base, base);
if( (n&1) && (n != 1) )
{
base = mul(result, base);
}
n = n >> 1;
}
if(true == flag)
result = mul(result, base);
else
result = base;
return result;
}
/************************************************************************
/*函数功能:转换相对路径为绝对路径,比如:/home/abs/../temp/new/../,输出路径为:/home/temp。
//参考代码:
//调 用:
char a[] = "/home/abs/../temp/new/../";
//char a[] = "/home/abs/temp/new/../";
char b[256];
memset(b, 0, 256);
int nRet = RP2AP(a, b);
if (nRet ==1 )
cout << b << endl;
/************************************************************************/
#include< vector >
#include< string >
int RP2AP(const char* pInStr, char* pOutStr)
{
if (pInStr==NULL || pOutStr==NULL) return 0;
string str = pInStr;
string strTemp;
vector<string> vec_str;
string strOut="";
int nPos1;
int nPos2;
nPos1 = str.find("/", 0);
if (nPos1<0)
{
return -1;
}
while(1)
{
nPos2 = str.find("/", nPos1+1);
if (nPos2>nPos1)
{
strTemp = str.substr(nPos1, nPos2-nPos1);
//如果不是/..,就放入vector里
if (strTemp!="/..")
vec_str.push_back(strTemp);
else//弹出上一个
{
vec_str.reserve(vec_str.size());
vec_str.pop_back();
vec_str.reserve(vec_str.size());
}
nPos1 = nPos2;
}
else
{
break;
}
}
//循环赋值累加
for (int i=0; i<vec_str.size(); i++)
{
strOut +=vec_str[i];
}
//这里用strOut.c_str(),要安全一些,有的环境不这样写编译都不过。
memcpy(pOutStr, strOut.c_str(), strOut.size());
return 1;
}
/*输入两个整数 n 和 m,从数列1,2,3.......n 中 随意取几个数,
使其和等于 m ,要求将其中所有的可能组合列出来.
调 用:
int sum, n;
cout << "请输入你要等于多少的数值sum:" << endl;
cin >> sum;
cout << "请输入你要从1.....n数列中取值的n:" << endl;
cin >> n;
cout << "所有可能的序列,如下:" << endl;
find_factor(sum,n);
*/
#include<list>
list<int>list1;
void find_factor(int sum, int n)
{
// 递归出口
if(n <= 0 || sum <= 0)
return;
// 输出找到的结果
if(sum == n)
{
// 反转list
list1.reverse();
for(list<int>::iterator iter = list1.begin(); iter != list1.end(); iter++)
cout << *iter << " + ";
cout << n << endl;
list1.reverse();
}
list1.push_front(n); //典型的01背包问题
find_factor(sum-n, n-1); //放n,n-1个数填满sum-n
list1.pop_front();
find_factor(sum, n-1); //不放n,n-1个数填满sum
}
/************************************************************************
* 程序功能:不用加减乘除做加法题目:写一个函数,求两个整数之和
* 输入:整数num1,num2
输出:2个数之和
* 思路:5的二进制是101,17的二进制是10001。
把计算分成三步:
第一步各位相加但不计进位,得到的结果是10100(最后一位两个数都是1,相加的结果是二进制的10。
这一步不计进位,因此结果仍然是0);
第二步记下进位。在这个例子中只在最后一位相加时产生一个进位,结果是二进制的10;
第三步把前两步的结果相加,得到的结果是10110,转换成十进制正好是22。
由此可见三步走的策略对二进制也是适用的。
于是把二进制的加法用位运算来替代:
(1)第一步不考虑进位对每一位相加。0加0、1加1的结果都0,0加1、1加0的结果都是1。
这和异或的结果是一样的。对异或而言,0和0、1和1异或的结果是0,而0和1、l和0的异或结果是1。
(2)考虑第二步进位,对0加0、0加1、1加0而言,都不会产生进位,只有1加1时,会向前产生一个进位。
此时我们可以想象成是两个数先做位与运算,然后再向左移动一位。只有两个数都是1的时候, 位与得到
的结果是1,其余都是0。第三步把前两个步骤的结果相加。
(3)第三步相加的过程依然是重复前面两步,直到不产生进位为止。
* 测试例子:Add(5,-6)
* 测试输出:-1
* 创 建:2014/4/4
* 扩 展:用来求两个数的减法
************************************************************************/
int Add (int num1, int num2)
{
int sum,carry;
do
{
sum = num1 ^ num2 ;
carry= (num1 & num2) << 1 ;
num1 = sum;
num2 = carry;
}while ( num2 != 0 );
return num1;
}
//递归实现
int Add2(int a,int b)
{
if( b == 0 )
return a;//没有进位的时候完成运算
int sum,carry;
sum = a ^ b;//完成第一步没有进位的加法运算
carry=(a & b) << 1;//完成第二步进位并且左移运算
return Add(sum,carry);//进行递归,相加
}
/*算法思路:
1、从右到左扫描加数,
2若加数的第i位是1,则针对被加数中从第i位到最左边位,
若第j位是1,则此位置0;否则置1,结束本次循环,即转1。
否则,继续扫描加数的下一位,即转1.
【整个操作可理解为:】从右到左,对加数中的每一个1,对被加数最右端(第0位开始向左)连续的1变0,
原先被加数中为0的最低位置的那一位变1。 */
int Add3(int a, int b)
{
for( int i = 1; i; i <<= 1 )
if( b & i )
for(int j = i; j; j <<= 1)
if(a & j)
a &= ~j;
else
{
a |= j; break;
}
return a ;
}
//直接使用加法实现减法
int Minus(int a,int b)
{
int temp = -b;
return Add(a,temp);
}
/* 思 路:首先取减数的补码,然后相加 */
int Minus2(int a,int b)
{
for(int i = 1; i && ((b & i) ==0 ); i <<= 1)
;
for( i <<= 1; i; i <<=1 )
b ^= i;
return Add(a,b);
}
/* 思 路:
乘法就是将乘数写成(2^0)*k0 + (2^1)*k1 + (2 ^2)*k2 + ... + (2^31)*k31,其中ki为0或1,
然后利用位运算和加法就可以了。
*/
int Mul(int a,int b)
{
int ans = 0;
for(int i = 1; i; i <<= 1, a <<= 1)
if(b & i)
ans += a; //可换成ans= Add( ans,a );
return ans;
}
/*思 路:
除法就是由乘法的过程逆推,依次减掉(如果够减的话)divisor << 31、divisor << 30、... 、
divisor << 2、divisor << 1、divisor(要保证不能溢出)减掉相应数量的除数就在结果加上相应的数量。
*/
int Div(int dividend, int divisor)
{
// assert(divisor != 0)
int sign = 1;
if(dividend < 0 && divisor > 0 || dividend > 0 && divisor < 0)
sign = -1;
unsigned int x = (unsigned int)abs(dividend);
unsigned int y = (unsigned int)abs(divisor);
int bitCnt = sizeof(int) << 3;
int quotient = 0;
int k = bitCnt-1;
while(((1 << k) & y) == 0) k--;
for(int j = bitCnt-1-k; j >= 0; j--)
{
if(x >= (y << j))
{
x -= (y << j);
quotient += (1 << j);
}
}
return sign*quotient;
}
int main()
{
cout << Div(8,-6);
return 0;
}
* 程序功能:有N个大小不等的自然数(1--N),请将它们由小到大排序。
要求:时间复杂度为O(n),空间复杂度为O(1)。
*
************************************************************************/
void sort(int arr[], int n)
{
int t; /**//*临时变量:空间复杂度O(1)*/
for (int i = 1; i <= n; i++)
{
while(arr[i] != i)
{
t = arr[arr[i]];
arr[arr[i]] = arr[i];
arr[i] = t;
}
}
}
/************************************************************************
* 程序功能:给定能随机 生成整数1到5的函数,写出能随机生成整数1到7的函数
* 原 理:
rand5()*5+rand5(),新的数据范围变成:6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30.并且可以看出来这个25个数出现的可能性是一样的,于是我们可以只用6~26之间的21个数变成1~7这7个数,于是就是要每3个数对应一个数,即:
6,7,8对应1
9,10,11对应2
…………
24,25,26对应7
这种变化对应的方式是(6 - 3)/ 3 = 1,(7 - 3) / 3 = 1,(8-3) / 3 = 1.
************************************************************************/
int rand7()
{
int i;
//直到产生6~26之间的数跳出循环
while((i=rand5()*5+rand5()) > 26) ;
return (i-3)/3;
}
/************************************************************************
* 功 能:《数据结构》KMP算法 ,查找子串的位置
************************************************************************/
void get_next(char*t, int next[ ]){
int t_len=strlen(t);
int i=0; //求解每个next[i]
next[0]=-1; //递推基本条件,然后求解next[i+1]
int j=-1; //向后递推位置下标
/*
next[i]=k =>T0...Tk-1=Ti-k...Ti-1
求解next[i+1]
1> 如果T0..Tk-1Tk=Ti-k...Ti-1Ti=>next[i+1]=k+1=next[i]+1;
2>Tk<>Ti,next[k]=k', 如果Ti=Tk'=>next[i+1]=k'+1=next[k]+1=next[next[i]]+1;
3>依次递推 最后情况next[i+1]=next[0]+1=0,即
*/
while(i<t_len)
{
if(j==-1 ||t[i]==t[j]) //j==-1证明已经与t[0]不匹配了,此时next[i+1]=0
{
i++;
j++;
next[i]=j;
}
else
{
j=next[j];
}
}
}
int KMP(char *s,char *t){
int s_len=strlen(s);
int t_len=strlen(t);
int i=0;
int j=0;
int *next=new int [t_len];
get_next(t,next);
/*
for (int me=0;me<t_len;me++)
cout<<next[me]<<endl;
*/
if( t_len >s_len ) return -1;
while(i < s_len && j<t_len )
{
if(j==-1 || s[i]==t[j])
{
i++;
j++;
}
else
{
j=next[j];
}
}//end while
if( j>=t_len )
return i- t_len;
else
return -1;
}
/************************************************************************
* 程序功能:验证foo(2^31-3)的输出为2,
* 注 意:2^31是异或,不是指数
*
************************************************************************/
int foo(int x)
{
cout<<x<<endl;
cout<<-x<<endl;
return x&-x;
}
/************************************************************************
* 程序功能:n从1开始,每个操作可以选择对n加1或者对n加倍。若想获得整数2013,最少需要多少个操作。
* 输入:整数n,
输出:按上述操作获得n需要的操作个数
* 思路:从输入的整数n开始,若n为奇数,则减1,操作次数加1;否则n减半,操作次数加1.
* 测试例子:op(2013)
* 测试输出:18
* 创 建:2014/3/30
************************************************************************/
#include<cassert>
int op(int n)
{
assert( n>0 );
int time = 0;
if(1 == n)
time = 0;
else
{
if( n & 0x1 == 1)//n是奇数
{
n = n-1;
time += 1;//操作次数+1
}
time += op( n >> 1 ) ;//加上除2后的操作次数
time += 1 ; //除2,操作次数+1
}
return time;
}
/************************************************************************
* 程序功能:n从1开始,每个操作可以选择对n加1或者对n加倍。若想获得整数2013,最少需要多少个操作。
* 输入:整数n,
输出:按上述操作获得n需要的操作个数
* 思路:1、一个新数据结构num,num.d表示当前到达的数,num.time表示到达数的操作次数;辅助结构:队列
2、声明两个变量num one,two,初始数均为2,对应的操作次数为1;
3、one.d加1,one.time加1,当one.d<=FINAL时one进栈,否则输出;
two.d*2,two.time加1,当two.d<=FINAL时two进栈,否则输出;
4、当队列非空时出队列,转入3。
* 测试例子:op(2013)
* 测试输出:18
* 创 建:2014/3/30
************************************************************************/
/******************模拟栈******************/
typedef struct num {
int d;
int time;
} num;
typedef struct queue {
int front, rear, count;
num data[10000000];
} queue;
void enQueue(queue *q, num d)
{
q->data[q->rear ++] = d;
q->count ++;
}
num deQueue(queue *q)
{
num res;
res = q->data[q->front ++];
q->count --;
return res;
}
/*************************************/
void op2 (int FINAL)
{
assert(FINAL>1);
int flag = 0; //结束标志,flag=1,表示增长到输入数FINAL
num bt, one, two, s;
bt.d = 2;//起点从数2开始
bt.time = 1; //2变成1仅需要操作一次
queue *q = (queue *)malloc(sizeof(queue));
q->front = q->rear = q->count = 0;
enQueue(q, bt);
while (q->count > 0)
{
s = deQueue(q);
if (s.d == FINAL)
{ //到达输入的整数FINAL
flag = 1;
printf("%d\n", s.time);
break;
}
one.d = s.d + 1;
one.time = s.time + 1;
if (one.d <= FINAL )
enQueue(q, one);
two.d = s.d * 2;
two.time = s.time + 1;
if (two.d <= FINAL )
enQueue(q, two);
// printf("%d\n", q->count);
}
if (flag == 0)
{
printf("不可能!\n");
}
}
/************************************************************************
* 程序功能:n个数存入数组,模拟洗牌
* 输入:整数数组arr[],数组大小N
输出:一次洗牌操作
* 创 建:2014/3/30
* 说 明:此实现依据《STL源码剖析》random_shuffle的实现
* 思 路:每次迭代中arr[i]与arr[0]~arr[i]的一个随机数交换
* 测 试:int a[]={1,2,3,4,5,6,7};
int N=sizeof(a)/sizeof(a[0]);
shuffleArray_STL(a,N);
************************************************************************/
#include<time.h>
void shuffleArray_STL(int arr[],int N)//《STL源码剖析》random_shuffle的C++实现
{
int i, loc, tmp;
time_t t;
srand((unsigned int)time(&t));
// 洗牌算法
for (i = 1; i < N; i ++) {
loc = rand() % (i + 1); //或者加上 if( loc != i )
tmp = arr[loc];
arr[loc] = arr[i];
arr[i] = tmp;
}
for (i = 0; i < N; i ++)
printf("%d ", arr[i]);
printf("\n");
}
/************************************************************************
* 程序功能:n个数存入数组,模拟洗牌
* 输入:整数数组arr[],数组大小N
输出:一次洗牌操作
* 创 建:2014/3/30
* 说 明:此实现依据《算法导论》5.3节的均匀随机排列,每个排列出现的概率1/n!
* 思 路:每次迭代中arr[i]与arr[i]~arr[N-1]的一个随机数交换,这样第i次迭代后,arr[i]保持不变
int a[]={1,2,3,4,5,6,7};
int N=sizeof(a)/sizeof(a[0]);
shuffleArray_introductionOfAlgorithms(a,N);
************************************************************************/
int rand_ab(int a,int b)//均匀随机生成[a,b]内的一个数
{
time_t t;
srand((unsigned int)time(&t));
return ( a + rand() % ( b-a+1 ) );
}
void shuffleArray_introductionOfAlgorithms(int arr[],int N)
{
int i, loc, tmp;
time_t t;
srand((unsigned int)time(&t));
// 洗牌算法
for (i = 0; i < N; i ++) {
loc = rand_ab(i,N-1); //arr[i]<---->arr[i]~arr[N-1],第i次迭代后,arr[i]保持不变
tmp = arr[loc];
arr[loc] = arr[i];
arr[i] = tmp;
}
for (i = 0; i < N; i ++)
printf("%d ", arr[i]);
printf("\n");
}
/************************************************************************
* 程序功能:写一个程序,要求功能,求出用1、2、5这三个数不同个数组合的和为100的组合数)
* 思 路:这其实就是个数学问题,就是求 x + 2y + 5z = 100解的个数。变化一下,x + 5z = 100 - 2y,这个式子表明 x + 5z只能是偶数,以z为循环变量,有下述规律。
z=0, x=100, 98, 96, ... 0
z=1, x=95, 93, ..., 1
z=2, x=90, 88, ..., 0
z=3, x=85, 83, ..., 1
z=4, x=80, 78, ..., 0
......
z=19, x=5, 3, 1
z=20, x=0
因此,组合总数为100以内的偶数+95以内的奇数+90以内的偶数+...+5以内的奇数+1,
即为:(51+48)+(46+43)+(41+38)+(36+33)+(31+28)+(26+23)+(21+18)+(16+13)+(11+8)+(6+3)+1
第二种方法的循环次数仅为21次。
************************************************************************/
int Sum_Combination1(int nSum)
{
int nCnt = 0;
for (int i32I = 0; i32I <= nSum; i32I += 5)
{
nCnt += (i32I + 2)/2;
}
return nCnt;
}
/************************************************************************
* 程序功能:O(logn)求Fibonacci数列
* 思 路:下面介绍一种时间复杂度是O(logn)的方法。在介绍这种方法之前,先介绍一个数学公式:
{f(n), f(n-1), f(n-1), f(n-2)} ={1, 1, 1,0}n-1
(注:{f(n+1), f(n), f(n), f(n-1)}表示一个矩阵。在矩阵中第一行第一列是f(n+1),第一行第二列是f(n),第二行第一列是f(n),第二行第二列是f(n-1)。)
有了这个公式,要求得f(n),我们只需要求得矩阵{1, 1, 1,0}的n-1次方,因为矩阵{1, 1, 1,0}的n-1次方的结果的第一行第一列就是f(n)。
这个数学公式用数学归纳法不难证明。感兴趣的朋友不妨自己证明一下。
现在的问题转换为求矩阵{1, 1, 1, 0}的乘方。如果简单第从0开始循环,n次方将需要n次运算,并不比前面的方法要快。但我们可以考虑乘方的如下性质:
/ an/2*an/2 n为偶数时
an=
\ a(n-1)/2*a(n-1)/2 n为奇数时
要求得n次方,我们先求得n/2次方,再把n/2的结果平方一下。如果把求n次方的问题看成一个大问题,
把求n/2看成一个较小的问题。这种把大问题分解成一个或多个小问题的思路我们称之为分治法。这样求n次方就只需要logn次运算了。
* 输 入:待求的第n个斐波那契数
* 输 出:第第n个斐波那契数
* 说 明:第n个数: 1 2 3 4 5 6 7 8 ...
斐波那契: 1 2 3 5 8 13
* 调 用:cout<<matrixpower(6).m_00<<endl;
************************************************************************/
struct matrix2by2{
matrix2by2(
long m00 = 0,
long m01 = 0,
long m10 = 0,
long m11 = 0
)
:m_00(m00), m_01(m01), m_10(m10), m_11(m11){}
long m_00;
long m_01;
long m_10;
long m_11;
};
matrix2by2 matrixmultiply(const matrix2by2 &a, const matrix2by2 &b){
return matrix2by2(
a.m_00 * b.m_00 + a.m_01 * b.m_10,
a.m_00 * b.m_01 + a.m_01 * b.m_11,
a.m_10 * b.m_00 + a.m_11 * b.m_10,
a.m_10 * b.m_01 + a.m_11 * b.m_11
);
}
matrix2by2 matrixpower(int n){
matrix2by2 matrix;
if(n == 1)
matrix = matrix2by2(1, 1, 1, 0);
else if(n % 2 == 0){
matrix = matrixpower(n/2);
matrix = matrixmultiply(matrix, matrix);
}
else if(n % 2 == 1){
matrix = matrixpower((n-1)/2);
matrix = matrixmultiply(matrix, matrix);
matrix = matrixmultiply(matrix, matrix2by2(1, 1, 1, 0));
}
return matrix;
}
//更简明的实现
// * 调 用:cout<<power(6).a[0][0]<<endl;
struct matrix //定义2*2的矩阵
{
int a[2][2];
matrix(int aa,int b,int c,int d)
{
a[0][0] = aa;
a[0][1] = b;
a[1][0] = c;
a[1][1] = d;
}
matrix(){};
};
matrix mul(matrix& x,matrix& y) // 矩阵乘法 x = x*y
{
return matrix(x.a[0][0]*y.a[0][0] + x.a[0][1]*y.a[1][0],
x.a[0][0]*y.a[0][1] + x.a[0][1]*y.a[1][1],
x.a[1][0]*y.a[0][0] + x.a[1][1]*y.a[1][0],
x.a[1][0]*y.a[0][1] + x.a[1][1]*y.a[1][1] );
}
matrix power(int n){
matrix mat;
if(n == 1)
mat = matrix(1, 1, 1, 0);
else if(n % 2 == 0){
mat = power(n/2);
mat = mul(mat, mat);
}
else if(n % 2 == 1){
mat = power((n-1)/2);
mat = mul(mat, mat);
mat = mul(mat, matrix(1, 1, 1, 0));
}
return mat;
}
//非递归实现
// * 调 用:cout<<power2(6).a[0][0]<<endl;
matrix power2(int n)
{
//非递归代码:
if(n == 1)
return matrix(1, 1, 1, 0);
matrix result ;
matrix base (1,1,1,0);
bool flag=false;
if(n&1) flag=true;
n = n >> 1;
result = base;
while(n)
{
base = mul(base, base);
if( (n&1) && (n != 1) )
{
base = mul(result, base);
}
n = n >> 1;
}
if(true == flag)
result = mul(result, base);
else
result = base;
return result;
}
/************************************************************************
/*函数功能:转换相对路径为绝对路径,比如:/home/abs/../temp/new/../,输出路径为:/home/temp。
//参考代码:
//调 用:
char a[] = "/home/abs/../temp/new/../";
//char a[] = "/home/abs/temp/new/../";
char b[256];
memset(b, 0, 256);
int nRet = RP2AP(a, b);
if (nRet ==1 )
cout << b << endl;
/************************************************************************/
#include< vector >
#include< string >
int RP2AP(const char* pInStr, char* pOutStr)
{
if (pInStr==NULL || pOutStr==NULL) return 0;
string str = pInStr;
string strTemp;
vector<string> vec_str;
string strOut="";
int nPos1;
int nPos2;
nPos1 = str.find("/", 0);
if (nPos1<0)
{
return -1;
}
while(1)
{
nPos2 = str.find("/", nPos1+1);
if (nPos2>nPos1)
{
strTemp = str.substr(nPos1, nPos2-nPos1);
//如果不是/..,就放入vector里
if (strTemp!="/..")
vec_str.push_back(strTemp);
else//弹出上一个
{
vec_str.reserve(vec_str.size());
vec_str.pop_back();
vec_str.reserve(vec_str.size());
}
nPos1 = nPos2;
}
else
{
break;
}
}
//循环赋值累加
for (int i=0; i<vec_str.size(); i++)
{
strOut +=vec_str[i];
}
//这里用strOut.c_str(),要安全一些,有的环境不这样写编译都不过。
memcpy(pOutStr, strOut.c_str(), strOut.size());
return 1;
}
/*输入两个整数 n 和 m,从数列1,2,3.......n 中 随意取几个数,
使其和等于 m ,要求将其中所有的可能组合列出来.
调 用:
int sum, n;
cout << "请输入你要等于多少的数值sum:" << endl;
cin >> sum;
cout << "请输入你要从1.....n数列中取值的n:" << endl;
cin >> n;
cout << "所有可能的序列,如下:" << endl;
find_factor(sum,n);
*/
#include<list>
list<int>list1;
void find_factor(int sum, int n)
{
// 递归出口
if(n <= 0 || sum <= 0)
return;
// 输出找到的结果
if(sum == n)
{
// 反转list
list1.reverse();
for(list<int>::iterator iter = list1.begin(); iter != list1.end(); iter++)
cout << *iter << " + ";
cout << n << endl;
list1.reverse();
}
list1.push_front(n); //典型的01背包问题
find_factor(sum-n, n-1); //放n,n-1个数填满sum-n
list1.pop_front();
find_factor(sum, n-1); //不放n,n-1个数填满sum
}
/************************************************************************
* 程序功能:不用加减乘除做加法题目:写一个函数,求两个整数之和
* 输入:整数num1,num2
输出:2个数之和
* 思路:5的二进制是101,17的二进制是10001。
把计算分成三步:
第一步各位相加但不计进位,得到的结果是10100(最后一位两个数都是1,相加的结果是二进制的10。
这一步不计进位,因此结果仍然是0);
第二步记下进位。在这个例子中只在最后一位相加时产生一个进位,结果是二进制的10;
第三步把前两步的结果相加,得到的结果是10110,转换成十进制正好是22。
由此可见三步走的策略对二进制也是适用的。
于是把二进制的加法用位运算来替代:
(1)第一步不考虑进位对每一位相加。0加0、1加1的结果都0,0加1、1加0的结果都是1。
这和异或的结果是一样的。对异或而言,0和0、1和1异或的结果是0,而0和1、l和0的异或结果是1。
(2)考虑第二步进位,对0加0、0加1、1加0而言,都不会产生进位,只有1加1时,会向前产生一个进位。
此时我们可以想象成是两个数先做位与运算,然后再向左移动一位。只有两个数都是1的时候, 位与得到
的结果是1,其余都是0。第三步把前两个步骤的结果相加。
(3)第三步相加的过程依然是重复前面两步,直到不产生进位为止。
* 测试例子:Add(5,-6)
* 测试输出:-1
* 创 建:2014/4/4
* 扩 展:用来求两个数的减法
************************************************************************/
int Add (int num1, int num2)
{
int sum,carry;
do
{
sum = num1 ^ num2 ;
carry= (num1 & num2) << 1 ;
num1 = sum;
num2 = carry;
}while ( num2 != 0 );
return num1;
}
//递归实现
int Add2(int a,int b)
{
if( b == 0 )
return a;//没有进位的时候完成运算
int sum,carry;
sum = a ^ b;//完成第一步没有进位的加法运算
carry=(a & b) << 1;//完成第二步进位并且左移运算
return Add(sum,carry);//进行递归,相加
}
/*算法思路:
1、从右到左扫描加数,
2若加数的第i位是1,则针对被加数中从第i位到最左边位,
若第j位是1,则此位置0;否则置1,结束本次循环,即转1。
否则,继续扫描加数的下一位,即转1.
【整个操作可理解为:】从右到左,对加数中的每一个1,对被加数最右端(第0位开始向左)连续的1变0,
原先被加数中为0的最低位置的那一位变1。 */
int Add3(int a, int b)
{
for( int i = 1; i; i <<= 1 )
if( b & i )
for(int j = i; j; j <<= 1)
if(a & j)
a &= ~j;
else
{
a |= j; break;
}
return a ;
}
//直接使用加法实现减法
int Minus(int a,int b)
{
int temp = -b;
return Add(a,temp);
}
/* 思 路:首先取减数的补码,然后相加 */
int Minus2(int a,int b)
{
for(int i = 1; i && ((b & i) ==0 ); i <<= 1)
;
for( i <<= 1; i; i <<=1 )
b ^= i;
return Add(a,b);
}
/* 思 路:
乘法就是将乘数写成(2^0)*k0 + (2^1)*k1 + (2 ^2)*k2 + ... + (2^31)*k31,其中ki为0或1,
然后利用位运算和加法就可以了。
*/
int Mul(int a,int b)
{
int ans = 0;
for(int i = 1; i; i <<= 1, a <<= 1)
if(b & i)
ans += a; //可换成ans= Add( ans,a );
return ans;
}
/*思 路:
除法就是由乘法的过程逆推,依次减掉(如果够减的话)divisor << 31、divisor << 30、... 、
divisor << 2、divisor << 1、divisor(要保证不能溢出)减掉相应数量的除数就在结果加上相应的数量。
*/
int Div(int dividend, int divisor)
{
// assert(divisor != 0)
int sign = 1;
if(dividend < 0 && divisor > 0 || dividend > 0 && divisor < 0)
sign = -1;
unsigned int x = (unsigned int)abs(dividend);
unsigned int y = (unsigned int)abs(divisor);
int bitCnt = sizeof(int) << 3;
int quotient = 0;
int k = bitCnt-1;
while(((1 << k) & y) == 0) k--;
for(int j = bitCnt-1-k; j >= 0; j--)
{
if(x >= (y << j))
{
x -= (y << j);
quotient += (1 << j);
}
}
return sign*quotient;
}
int main()
{
cout << Div(8,-6);
return 0;
}
相关文章推荐
- 2014找工作总结 - 机会往往是留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 【算法题目集锦】为找工作做准备的
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人
- 2014找工作总结-机会往往留给有准备的人