[微软面试100题]61-70

第六十一题:找出整型数组中两个只出现了一次的数(其余都出现了两遍)

思路:凡是涉及到出现了两次,出现了一次的都用XOR。把所有的数都XOR一遍得到一个数tmp,这个数就是要得到的两个数的XOR。tmp肯定不为0,因为这要得的两个数一定不相等。则 使用tmp为1的位可以把原数组分为两组。因为这两个数的这一位一定不同(1^0=1),所以这两个数一定各自落在一个组里,而出现了两次的同一个数也会在同一组里。重新对各个组XOR一编即可找到这个组中唯一出现一次的数了。整个过程遍历了两次数组,复杂度O(2n)=O(n)
void twoSingleNum(const int *a,int cnt){
    int tmp=0;
    for(int i=0;i<cnt;++i)  tmp^=a[i];//比较短的时候放后面
    int mark=1;
    while( (tmp&mark)==0)
        mark<<=1;// mark<<1 has no effect on mark
    int num1=0,num2=0;
    for(int i=0;i<cnt;++i)
        (a[i]&mark)==0 ? num1^=a[i] : num2^=a[i];//must have (), &'s priority is very low.
    cout<<num1<<" "<<num2<<endl;
}

第六十三题:删除字符串中的特定字符

思路:定义两个指针slow和fast,如果fast不是要删的字符则*slow=*fast,++fast,++slow;否则只++fast跳过要删的字符。相当于把字符串复制一遍,只不过跳过了要删的字符。复杂度O(n)
void delChars(char s[],const char *chars){
    int hash[256];
    memset(hash,0,sizeof(int)*256);
    while(*chars!='\0'){
        hash[int(*chars)]=1;
        chars++;
    }
    char *pFast=s,*pSlow=s;
    while(*pFast!='\0'){
        if(hash[int(*pFast)]==0){
            *pSlow=*pFast;
            pSlow++;
        }
        pFast++;//相当于把不删的复制到前面的slow,删的就跳过
    }
    *pSlow='\0';
}

第六十四题:寻找丑数(从小到大打印1500个丑数)

丑数是因式全由 2 3 5组成的数。如果逐个因式分解效率爆低。使用 动态规划的思想,后一个丑数肯定是前面丑数的min(2倍 3倍 5倍)且刚好大于前面一个丑数。可以把已经算好的丑数记录在数组中。然后找到前面某个丑数的2倍刚好大于最后一个丑数的,再找3倍、5倍。这三个数的最小者就是下一个丑数。可用三个指针来从前往后寻找。
void generateUglyNum(int n){
    int buf[n+10];
    buf[0]=1;buf[1]=2;buf[2]=3;buf[3]=4;buf[4]=5;
    int *p2=buf,*p3=buf,*p5=buf;
    for(int i=5;i<n;++i){
        while(*p2*2<=buf[i-1])p2++;
        unsigned int c1=*p2*2;
 
        while(*p3*3<=buf[i-1])*p3++;
        unsigned int c2=*p3*3;
 
        while(*p5*5<=buf[i-1])*p5++;
        unsigned int c3=*p5*5;
 
        if(c1<=c2 && c1<=c3)buf[i]=c1;
        if(c2<=c1 && c2<=c3)buf[i]=c2;
        if(c3<=c1 && c3<=c2)buf[i]=c3;
        cout<<i+1<<":"<<buf[i]<<endl;
    }

第六十五题:输出1到最大的N位数

void helper(int n){
    int tmp=0;
    for(int i=0;i<n;++i){
        tmp+=9;
        if(i!=n-1)tmp*=10;
    }
    for(int i=1;i<=tmp;++i){
        cout<<i<<" ";
    }cout<<endl;
}

第六十六题:利用递归翻转一个栈

利用递归可以把栈元素一个一个拿出来,进行某种操作后一个一个放回去。利用这种规律可以先把栈元素拿出来,放回去的时候每个元素都沉到最底。如何沉到最底也是利用递归。
template <class T>
void reverse_stack_recursive(stack<T> &s){
    if(!s.empty()){
        T tmp=s.top();
        s.pop();
        reverse_stack_recursive(s);//从顶到底一个一个拿开
        putToButtom(s,tmp);//从栈最低的元素一个一个放回,但是每次都返回栈底
    }
}
 
template <class T>//这是一种栈的常用操作,先拿出来,再放回去
void putToButtom(stack<T> &s,T &tmp){
    if(s.empty()){
        s.push(tmp);//拿到没有了,就放这个目标元素到栈底
        return;
    }
    T o=s.top();//将栈里的元素一个一个拿开
    s.pop();
    putToButtom(s,tmp);//直到栈为空,放进目标元素
    s.push(o);//再一个一个放回去
}

第六十七题:扑克牌顺序

一开始用了一个麻烦的方法,就是想挑出所有不合法的情况( 尽量避免这种思路,因为不合法的情况会随着思考的深入变得越来越多),最后就是合法的了。但不合法情况的组合比较多,容易漏。
好的方法是用量值来统筹所有情况,把王当做一种可以填充gap的东西,一个王填一个gap。如果可以用的王用完了,或者有两张牌一样的情况就是不合法了。
int has_gap(int a[],int l,int allowGaps){
    for(int i=1;i<5-l;++i){//不同王数for的长度不一,这里用一个参数来传递for的长度就可以实现代码重用,不用写3个for了。
        if(a[i]-a[i-1]==0)return 0;
        allowGaps-=a[i]-a[i-1]-1;
    }
     if(allowGaps<0)return 0;
     return 1;
}
 
int is_sequence_good(int a[]){//14 is the king
    sort(a,a+5);
    if(a[3]==14 && a[4]==14)return has_gap(a,2,2);//double kings
    else if(a[4]==14)return has_gap(a,1,1);
    else return has_gap(a,0,0);
}

第六十七题:打印出n粒骰子的点数和的出现概率

思路:每种搭配出现的概率都是1/6^n,因此问题转移到每种和存在多少种搭配?如3+3=2+4=1+5,两粒骰子的时候和6时就5种情况(2,4 1,5可调转)
使用动态规划非常简单,关键是公式:f(s,n)=f(s-1,n-1)+ f(s-2,n-1)+ f(s-3,n-1)+ f(s-4,n-1)+ f(s-5,n-1)+ f(s-6,n-1),s为n粒骰子的和。
解释:n粒骰子和为s出现的次数,等于n-1粒时,s分别减少1到6时的次数之和。就是概率上的全概率的意思。
void showDicePro(int n){
    #define DICE_NUM 6 //为以后扩展做准备,注意扩展性可以给面试官好印象
    int p[n][DICE_NUM*n+1];
    memset(p,0,sizeof(int)*(DICE_NUM*n+1)*n);
    for(int j=1;j<=DICE_NUM;++j) p[0][j]=1;//初始化1颗骰子的情况
 
    for(int i=1;i<n;++i)
        for(int j=1;j<=DICE_NUM*n;++j)
            for(int k=j-DICE_NUM;k<j;++k)
                if(k>=0) p[i][j]+=p[i-1][k];//n粒的情况DP n-1的
 
    for(int i=1;i<=DICE_NUM*n;++i) cout<<p[n-1][i]<<" ";
    cout<<endl;
}

第六十八题:把数组组成最小的数

思路1:建立一个比较函数判断两个数应该放前放后。从最高位开始比较,如果某一位比较小则放前。如果长度不等,多出来的位与前面的位依次对比,详细看代码。
思路2:此法简单。借用字符串比较两个数的前后顺序。如"ab">"ba"的话,则a放b前,即a<b。由于ab拼接的两个字符串长度一定相等,直接使用string的<即可判断。
PS:1、sort的比较函数的意思就是第一个参数放第二个参数前面的话就true。2、sprintf的用法:可以先用char[]来格式化字符串,然后直接用string的构造函数string(char*)来转化(string没有format函数)。
bool fronter(int a,int b){//思路1
    vector<int> va,vb,tmp;
    while(a!=0){
        tmp.push_back(a%10);
        a/=10;
    }
    while(!tmp.empty()){
        va.push_back(tmp.back());
        tmp.pop_back();
    }
    while(b!=0){
        tmp.push_back(b%10);
        b/=10;
    }
    while(!tmp.empty()){
        vb.push_back(tmp.back());
        tmp.pop_back();
    }
 
    //比较长度
    int lena=va.size();
    int lenb=vb.size();
    int i=0,j=0;
    while(i<lena || j<lenb){
        if(va[i]<vb[j] && i<lena && j<lenb)return true;
        if(va[i]>vb[j] && i<lena && j<lenb)return false;
        if(i==lena && j==lenb)return false;
        if(i==lena && j!=lenb){//前面的放前面就是true
            if(vb[j]>vb[j-lena])return true;//短在前
            else if(vb[j]<vb[j-lena]) return false;
        }//多出来的位与第一位比
        if(i!=lena && j==lenb){
            if(va[i]>va[i-lenb])return false;//短在前
            else if(va[i]<va[i-lenb])return true;
        }
        if(i<lena)++i;
        if(j<lenb)++j;
    }
    return true;
}
 
bool helper(int a,int b){//思路2
    char sa[50],sb[50];
    sprintf(sa,"%d",a);
    sprintf(sb,"%d",b);
    string stra(sa),strb(sb);
    return stra+strb<strb+stra;
}
 
void final(int *num,int n){
    vector<int> a;
    for(int i=0;i<n;++i)a.push_back(num[i]);
    sort(a.begin(),a.end(),helper);
    for(unsigned int i=0;i<a.size();++i){
        cout<<a[i];
    }cout<<endl;
}

第六十九题:旋转数组中的最小数

思路1:直接从左往右扫,碰到右比左小的就是最小数。没有的话第一个就是最小数。
思路2:参照二分查找。如果有旋转,最小数一定在旋转那。
void helper(int *a,int n){
    if(n==2){
        if(a[0]>a[1])cout<<a[1]<<endl;
        else cout<<a[0]<<endl;
        return;
    }
    int mid=n/2;
    if(a[0]>a[mid])helper(a,mid+1);//有旋转
    else if(a[mid]>a[n-1])helper(a+mid,n-mid);//有旋转
    else if(a[0]<a[mid])helper(a,mid+1);//没旋转
    else helper(a+mid,n-mid);//没旋转
}

 

转载于:https://www.cnblogs.com/iyjhabc/archive/2013/03/28/2986020.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值