[微软面试100题]5160

第五十一题:输出所有和为n的连续正数序列
方法1:从1遍历到n/2,算出所有sum,如果sum刚好等于n则输出,效率极低。
方法2:用一个变量b标志连续序列的开头,e表示结尾。如果序列的sum<n,则e++;如果sum>n,则b++。复杂度O(n)
void constSumSequence(int n){
    int b=1,e=1,sum=1,bound=n/2+1;
    while(e<=bound){
        if(sum==n){
            for(int k=b;k<=e;++k)cout<<k<<" ";
            cout<<endl;
            sum+=(e+1-b);
            b++;
            e++;
        }else if(sum>n){
            sum-=b;
            b++;
        }else{
            e++;
            sum+=e;
        }
    }
}

第五十三题:输入字符串,打印字符串所含字符的所有组合

以第一个字符为分组。每组还没到最后两个字符时,往下递归。到最后两个时,先打印一次,调换了再打印一次。
把所有字符一次放到字符串第一位,依次进行上面算法
如a组abc  acb, b组bac bca  c组cba  cab
//将第i个字符与后面的字符串对掉
void swapchar(char *c,int i,int n){
    char tmp=c[i];
    for(int k=i+1;k<n;++k){
        c[k-1]=c[k];
    }
    c[n-1]=tmp;
}
 
void helper(char *c,int i,int n){
    if(i<n-2){//还没到最后两个字符时,swap
 
        char tmp[n+1];
        for(int k=0;k<n;++k)tmp[k]=c[k];
        tmp[n]='\0';
 
        helper(c,i+1,n);
        swapchar(tmp,i,n);
        helper(tmp,i+1,n);
    }//到最后两个,打印一个,对调后再打印一次
    cout<<c<<endl;
    swapchar(c,i,n);
    cout<<c<<endl;
}
 
//把每一个字符依次放到第一
void final(char *c,int n){
    for(int i=0;i<n;++i){
        char tmp[n+1];
        for(int k=0;k<n;++k)tmp[k]=c[k];
        tmp[n]='\0';
        char firstChar=c[i];
        tmp[i]=c[0];
        tmp[0]=firstChar;
        helper(tmp,1,n);
    }
}

第五十四题:调整数组位置使奇数放在数组前部,偶数后部。复杂度为O(n)

记录一个index,然后从前往后扫描数组,扫到一个奇数就把奇数和index上的数对调,然后index++。
不知道为什么网上这一题其他人的答案都好复杂什么从两边同时扫。。。明显简单问题复杂化了吧?
void helper(int *num,int n){
    int i=0;
    for(int j=0;j<n;++j){
        if(num[j]%2!=0){
            int tmp=num[i];
            num[i]=num[j];
            num[j]=tmp;
            i++;
        }
    }
}

第五十六题:最长公共子串

定义f(m, n)为Xm和Yn之间最长的子序列的长度
若f(m-1,n-1)已知,那么这时候检查Xm和Yn
1.如果 xm == xn,那么有,说明最后一位应该加入公共子序列,所以其长度 f(m,n) = f(m-1,n-1)+1
2.如果 ym != yn,那么公共子序列是: 
X(m-1)与 Y(n)的最长公共子序列 
X(m)与 Y(n-1)的最长公共子序列
之间较长的那个
则f(m, n) = max{ f(m-1, n), f(m, n-1) }
PS:需要用连个FOR循环,将表从左上往右下遍历。最后右下就是结果
 
int max(int a,int b){
    if(a>=b)return a;
    else return b;
}
int helper(int **f,int i,int j){
    if(f[i][j]==-1){
        int a,b;
        if(f[i-1][j]!=-1)a=f[i-1][j];
        else a=helper(f,i-1,j);
        if(f[i][j-1]!=-1)b=f[i][j-1];
        else b=helper(f,i,j-1);
        f[i][j]=max(a,b);
        return f[i][j];
    }else{
        return f[i][j];
    }
}
 
int final(char *c1,char *c2,int n1,int n2){
    int* f[n1+1];
    for(int i=0;i<=n1;++i){
        f[i]=new int[n2+1];
    }
    for(int i=0;i<=n1;++i){
        for(int j=0;j<=n2;++j) f[i][j]=-1;
    }
    for(int i=0;i<=n1;++i){
        f[i][0]=0;
    }
    for(int i=0;i<=n2;++i){
        f[0][i]=0;
    }
    for(int i=1;i<=n1;++i){
        for(int j=1;j<=n2;++j){
            if(c1[i-1]==c2[j-1]){
                f[i][j]=helper(f,i-1,j-1)+1;
            }else helper(f,i,j);
        }
    }
    return f[n1][n2];
}

第五十七题:用两栈实现队列

一盏用来存放,一盏用来弹出。栈具有翻转顺序的性质,两个栈就刚刚是顺序的队列性质了。
 
第五十九题:设计不能被继承的类
方法1:把构造函数和析构函数声明为私有。使用静态函数来new和delete来新建。

方法2:

class Helper{
    friend class noSon;//这里其实应该用模板T代替noson,但codeblock不知为何不支持
private:
    Helper(){}
    ~Helper(){}
};
 
class noSon:virtual public Helper{//使用虚继承是希望子类直接调用最底层父类的构造函数。从而继承noson的子类调用Help的私有构造函数
public:                                          //这样noson就不能被继承。如果没有virtual,则子类会正常调用noson的共有构造函数。
    noSon(){cout<<"noSon is created!";}//而noson在调用helper的构造函数(因为是友元),从而可以使用。
};

虚继承:普通继承时,子类只可以调用直接父类的构造函数。而虚继承时,子类可以调用所有父类的构造函数详细看源代码

虚继承时为了解决多继承的“二义性”问题。 例如B继承A,C也继承A,D继承B,C。则D含有2个A的实例。调用了2次A的构造函数。
更严重的是造成“二义性”,就是通过D调用A的函数或者成员时,由于有2个A实例,到底调用哪个?而使用虚继承时则只有一个A实例

第六十题:O(1)时间删除单链表节点

传统方法是从链表头节点开始遍历,找到需要删除的节点的前一个节点,然后把这个节点与要删的后一个节点相连。
新的方法是,把要删的后一个节点的值复制给要删的节点,然后删除后一个节点。这样就不需要遍历了。如要删最后一个节点则按传统方法,总体的平均复杂度也是O(1)
原文地址:https://www.cnblogs.com/iyjhabc/p/2986030.html