【数据结构与算法】程序员面试金典 1.1-1.9

面试题 01.01. 判定字符是否唯一

class Solution {
public:
    bool isUnique(string astr) {
        // 方法一:使用set
        // set<char> setChar(astr.cbegin(),astr.cend());
        // if(setChar.size() == astr.size())
        //     return true;
        // return false;

        // 方法二:bool数组
        // vector<bool> vbool(26,false);
        // for(auto s : astr){
        //     int index = s-'a';
        //     if(!vbool[index]){
        //         vbool[index] = true;
        //     }else{
        //         return false;
        //     }
        // }
        // return true;

        // 方法三:位运算
        /*基于位运算的方法:
我们可以使用一个int类型的变量(下文用mark表示)来代替长度为26的bool数组。假设这个变量占26个bit(在多数语言中,这个值一般不止26),那么我们可以把它看成000...00(26个0),这26个bit对应着26个字符,对于一个字符c,检查对应下标的bit值即可判断是否重复。那么难点在于如何检查?这里我们可以通过位运算来完成。首先计算出字符char离'a'这个字符的距离,即我们要位移的距离,用move_bit表示,那么使用左移运算符1 << move_bit则可以得到对应下标为1,其余下标为0的数,如字符char = 'c',则得到的数为000...00100,将这个数跟mark做与运算,由于这个数只有一个位为1,其他位为0,那么与运算的结果中,其他位肯定是0,而对应的下标位是否0则取决于之前这个字符有没有出现过,若出现过则被标记为1,那么与运算的结果就不为0;若之前没有出现过,则对应位的与运算的结果也是0,那么整个结果也为0。对于没有出现过的字符,我们用或运算mark | (1 << move_bit)将对应下标位的值置为1。*/
        int mark = 0;
        int move_bit = 0;
        for(auto s : astr){
            move_bit = s - 'a';
            if(mark & (1 << move_bit)){
                //如果与完之后还得1,说明该位已经有1了,重复了!
                return false;
            }else{
                //说明该位之前不是1,应该把该位置1
                mark |= (1 << move_bit);
            }
        }
        return true;
    }
};

面试题 01.02. 判定是否互为字符重排

class Solution {
public:
    bool CheckPermutation(string s1, string s2) {
        /*定义如下:1两字符串字符个数相等 2每个字符出现次数相同*/
        if(s1.size() != s2.size()){
            return false;
        }
        if(s1.size() == 0){
            return true;
        }
        //此时,两者非空,且字符个数相等
        vector<int> vi(26,0);
        int index = 0;
        for(auto char1 : s1){
            index = char1 - 'a';
            ++ vi[index];            
        }
        for(auto char2 : s2){
            index = char2 - 'a';
            if(vi[index] > 0){
                -- vi[index];
            }else{
                return false;
            }
        }
        for(auto i : vi){
            if(i != 0)
                return false;
        }
        return true;
    }
};

面试题 01.03. URL化

class Solution {
public:
    string replaceSpaces(string S, int length) {
        int flag = S.size() - 1;
        for(int i=length-1;i>=0;--i){
            if(S[i] == ' '){
                S[flag--] = '0';
                S[flag--] = '2';
                S[flag--] = '%';
            }else{
                S[flag--] = S[i];                
            }
        }
        if(flag >= 0){
            S = S.substr(flag+1, S.size()-1);
        }
        return S;
    }
};

面试题 01.04. 回文排列

class Solution {
public:
    bool canPermutePalindrome(string s) {
        //每个字符出现的次数为偶数, 或者有且只有一个字符出现的次数为奇数时, 是回文的排列; 否则不是
        bitset<128> bs;
        for(auto c : s){
            bs.flip(c);
        }
        return bs.count() == 1 || bs.none();
    }
};

面试题 01.05. 一次编辑

45%,100%

class Solution {
public:
    bool oneEditAway(string first, string second) {

        // 长度相差最多为1,否则必然不符合要求
        int fsize = first.size();
        int ssize = second.size();
        if(abs(fsize - ssize) > 1){
            return false;
        }

        // 如果两个字符串长度相等,且仅有0个或1个字符不同,符合要求
        if(abs(fsize - ssize) == 0){
            int flag = 0;
            for(int i=0;i<fsize;++i){
                if(first[i]!=second[i]){
                    ++flag;
                }
                if(flag >= 2){
                    return false;
                }
            }
            return true;
        }

        // 如果两个字符串长度相差为1,有两种情况(1)first更长,就看second添一个字符能否和first相同(2)first更短,就看second删一个字符能否和first相同
        if(fsize - ssize == 1){
            auto fbegin = first.cbegin(),fend = first.cend();
            auto sbegin = second.cbegin(),send = second.cend();
            while(fbegin != fend){
                if(*fbegin == *sbegin){
                    ++fbegin;
                    ++sbegin;
                }else{
                    ++fbegin;
                }
            }
            if(sbegin == send){
                return true;
            }
            return false;
        }

        if(fsize - ssize == -1){
            auto fbegin = first.cbegin(),fend = first.cend();
            auto sbegin = second.cbegin(),send = second.cend();
            while(sbegin != send){
                if(*fbegin == *sbegin){
                    ++fbegin;
                    ++sbegin;
                }else{
                    ++sbegin;
                }
            }
            if(fbegin == fend){
                return true;
            }
            return false;
        }
        return false;//随便的,反正到不了这一步
    }
};

其实插入字符选项和删除字符选项,是可以合并的:

class Solution {
public:
    bool oneEditAway(string first, string second) {

        // 长度相差最多为1,否则必然不符合要求
        int fsize = first.size();
        int ssize = second.size();
        if(abs(fsize - ssize) > 1){
            return false;
        }

        // 如果两个字符串长度相等,且仅有0个或1个字符不同,符合要求
        if(abs(fsize - ssize) == 0){
            int flag = 0;
            for(int i=0;i<fsize;++i){
                if(first[i]!=second[i]){
                    ++flag;
                }
                if(flag >= 2){
                    return false;
                }
            }
            return true;
        }

        // 如果两个字符串长度相差为1,有两种情况(1)first更长,就看second添一个字符能否和first相同(2)first更短,就看second删一个字符能否和first相同
        if(fsize - ssize == 1){
            return isEdited(first.begin(),first.end(),second.begin(),second.end());     
        }
        if(fsize - ssize == -1){
            return isEdited(second.begin(),second.end(),first.begin(),first.end());
        }
         return false;
    }
    
    bool isEdited(string::iterator longbegin,string::iterator longend,string::iterator shortbegin,string::iterator shortend){
            while(longbegin != longend){
                if(*longbegin == *shortbegin){
                    ++longbegin;
                    ++shortbegin;
                }else{
                    ++longbegin;
                }
            }
            if(shortbegin == shortend){
                return true;
            }
            return false;
        }
};

利用双指针法,从两侧夹逼,涉及3个变量,1个i从左往右检查,另外两个是j和k,因为字符串的长度可能不同

class Solution {
public:
    bool oneEditAway(string first, string second) {
        if(first == second){
            return true;
        }

        const int lengthfirst = first.size();
        const int lengthsecond = second.size();

        if(abs(lengthfirst - lengthsecond)>1){
            return false;
        }

        int i=0, j=lengthfirst-1, k=lengthsecond-1;
        while(i<lengthfirst && i<lengthsecond && first[i] == second[i]){
            ++i;
        }
        while(j>=0 && k>=0 && first[j] == second[k]){//这里要检查j和k是否等于0
            --j;
            --k;
        }
        return j-i<1 && k-i<1;
    }
};

面试题 01.06. 字符串压缩
s += to_string(num)s = s + to_string(num)是不一样的。前者是接在s后面,后者是新建一个临时s对象,复制右边的结果,相当耗时,应该选择前者写法。

class Solution {
public:
    string compressString(string S) {
        //检查字符串内容合法性
        for(auto c : S){
            if(c>='a' && c<='z' || c>='A' && c<='Z'){
                continue;
            }
            return "";
        }

        //进行初步逻辑判断
        if(S.size() <= 2){
            return S;
        }
        
        string comstring;
        int num = 0;
        for(int i=1; i<S.size(); ++i){
            if(S[i-1] == S[i]){
                ++num;
            }else{
                ++num;
                comstring += S[i-1] + to_string(num);
                num = 0;
            }
        }
        
        ++num;
        comstring += S[S.size()-1] + to_string(num);
        
        return (comstring.size() > S.size())? S : comstring;
    }
};

面试题 01.07. 旋转矩阵
这个n平方,n平方的方法反而100%,100%

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int len = matrix.size();
        auto matrixfb = matrix;
        for(int i=0;i<len;++i){
            for(int j=0;j<len;++j){
                matrixfb[j][len-i-1] = matrix[i][j];
            }
        }
        matrix = matrixfb;
        return;
    }
};

下面这个靠旋转推导出的关系:
反倒56%,100%

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n = matrix.size();
        int tmp;
        for(int row=0;row<n/2;++row){
            for(int col=0;col<(n+1)/2;++col){
                tmp = matrix[row][col];
                matrix[row][col] = matrix[n-col-1][row];
                matrix[n-col-1][row] = matrix[n-row-1][n-col-1];
                matrix[n-row-1][n-col-1] = matrix[col][n-row-1];
                matrix[col][n-row-1] = tmp;
            }
        }
    }
};

先上下翻转,再沿主对角线翻转:

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n = matrix.size();
		// 上下翻转
        for(int i=0;i<n/2;++i){
            for(int j=0;j<n;++j){
                swap(matrix[i][j],matrix[n-i-1][j]);
            }
        }
        // 沿主对角线翻转
        for(int i=0;i<n;++i){
            for(int j=0;j<i;++j){
                swap(matrix[i][j],matrix[j][i]);
            }
        }
    }
};

面试题 01.08. 零矩阵
利用bool数组:

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int row = matrix.size();
        int col = matrix[0].size();
        if(row == 0 || col == 0){
            return;
        }

        vector<bool> vboolrow(row,false);
        vector<bool> vboolcol(col,false);

        for(int i=0;i<row;++i){
            for(int j=0;j<col;++j){
                if(matrix[i][j] == 0){
                    vboolrow[i] = true;
                    vboolcol[j] = true;
                }
            }
        }

        for(int i=0;i<row;++i){
            for(int j=0;j<col;++j){
                if(vboolrow[i] == true || vboolcol[j] == true){
                    matrix[i][j] = 0;
                }
            }
        }
    }
};

利用set:

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        unordered_set<int> row_set, col_set;
        size_t row = matrix.size(), col = matrix[0].size();
        for (size_t i = 0; i < row; ++i)
            for (size_t j = 0; j < col; ++j)
                if (matrix[i][j] == 0) {
                    row_set.insert(i);
                    col_set.insert(j);
                }
        for (const auto& row_idx : row_set)
            matrix[row_idx].assign(col, 0);
            // for (size_t i = 0; i < col; ++i)
            //     matrix[row_idx][i] = 0;
        for (const auto& col_idx : col_set) {
            for (size_t i = 0; i < row; ++i)
                matrix[i][col_idx] = 0;
        }
    }
};

面试题 01.09. 字符串轮转

class Solution {
public:
    bool isFlipedString(string s1, string s2) {
        int ss1 = s1.size(),ss2 = s2.size();
        if(ss1 != ss2){
            return false;
        }
        // return (s2+s2).find(s1)!= -1;
        return (s1+s1).find(s2)!= -1;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值