您的位置:首页 > 其它

必看算法集合_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;  

}  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: