程序员面试金典(二)||16题

 

目录

面试题 08.04. 幂集

递归乘法

面试题 08.06. 汉诺塔问题

面试题 08.09. 括号

面试题 08.11. 硬币

01背包问题

完全背包问题

多重背包问题 

混合背包问题

背包问题求方案数

面试题 10.02. 变位词组

面试题 10.10. 数字流的秩

不用临时变量交换两数

最大数值

井字游戏


面试题 08.04. 幂集

幂集。编写一种方法,返回某集合的所有子集。集合中不包含重复的元素。
说明:解集不能包含重复的子集。
//1.假设对于level = 3时已经存在状态树,且其叶子节点就是对应的结果
    那么level = 4时,对于leve = 3的每个结果子集,num[3]可以有加入和不加入集合两种选择,即形成leve = 4的状态树。
vector> subsets(vector& nums){
        vector> res;
        vector t;
        dfs(nums,res,t,0,nums.size());
        return res;
    }
    void dfs(vector& nums,vector> &res,vector&t,int level,int n){
        if(level==n){
            res.push_back(t);
            return;
        }
        t.push_back(nums[level]);//加
        dfs(nums,res,t,level+1,nums.size());
        t.pop_back();//不加
        dfs(nums,res,t,level+1,nums.size());
    }
//2.每次从nums中取1个,2个,3个……n个,把这种取得方法模拟出来
vector> subsets(vector& nums) {
        vector> res;
        for(int i=0;i<=nums.size();i++){
            vector t;
            dfs(nums,res,t,i,0);//i表示从nums中取得个数
        }
        return res;
    }
    void dfs(vector& nums,vector>&res,vector &t,int n,int begin){
        if(n==0) {//终止条件
            res.push_back(t);
            return;
        }
        for(int i=begin;i<=nums.size()-n;i++){//取n个意味着最开头那个不能超过size-n;
            t.push_back(nums[i]);
            dfs(nums,res,t,n-1,i+1);//取n个的结果是从n-1个得来的。
            t.pop_back();
        }
    }
//3.直接生成:
/*
先在大容器里面添加一个空容器,然后在nums里面取一个元素加入到当前大容器里面的所有子容器中并将新得到的每个容器添加到大容器中,直到nums的数据取完为止
【1,2,3】
先在大容器里面存一个空容器[].
第一轮:取1
大容器:[] [1];
第二轮:取2
大容器:[] [1] [2] [1,2];
第三轮:取3
大容器:[] [1] [2] [1,2] [3] [1,3] [2,3] [1,2,3] ;
*/
    vector> subsets(vector& nums) {
        vector> v;
        v.push_back({});
        for(int i = 0 ; i < nums.size() ; i++){
             int len=v.size();
            for(int j = 0 ; j < len ; j++){
                vector a = v[j];//获得之前一层的元素,
                a.push_back(nums[i]);//往之前每个元素加上一个数字
                v.push_back(a);
            }
        }
        return v;
    }


递归乘法


递归乘法。 写一个递归函数,不使用 * 运算符, 实现两个正整数的相乘。可以使用加号、减号、位移,但要吝啬一些。
int multiply(int A, int B){
    if(B==0) return 0;
    return A+mutiply(A,B-1);
}

面试题 08.06. 汉诺塔问题


在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:
(1) 每次只能移动一个盘子;
(2) 盘子只能从柱子顶端滑出移到下一根柱子;
(3) 盘子只能叠在比它大的盘子上。
void hanota(vector& A, vector& B, vector& C) {
        move(A,B,C,A.size());//move的含义是把n个盘子从A移到C
    }
    void move(vector& A, vector& B, vector& C,int n){
        if(n==1){
            C.push_back(A.back());
            A.pop_back();
            return;
        }
        move(A,C,B,n-1);//先把n-1个盘子从A移到B
        C.push_back(A.back());A.pop_back();
        move(B,A,C,n-1);//再把n-1个盘子从B移到C
    }


面试题 08.09. 括号


括号。设计一种算法,打印n对括号的所有合法的(例如,开闭一一对应)组合。
说明:解集不能包含重复的子集。
//1.组合出所有的排列然后,一个一个筛选合理性
//2.组合的时候就考虑合理性
vector generateParenthesis(int n) {
        vector res;
        string s;
        int sum=0;
        dfs(res,s,2*n,sum);
        return res;
    }
void dfs(vector& res,string& str,int n,int &sum){
        if(sum<0){
            return;
        }
        if(n==0){
            if(sum==0)
                res.push_back(str);
            return;
        }
        str+='(';sum++;
        dfs(res,str,n-1,sum);
        str.pop_back();sum--;//回溯
        str+=')';sum--;
        dfs(res,str,n-1,sum); 
        str.pop_back();sum++;//回溯
    }

//2.1优化
vector res;
vector generateParenthesis(int n) {
        backtrack("",0,0,n);
        return res;
    }
    void backtrack(string temp,int left,int right,int n)
    {
        if(temp.length()==n*2)
        {
            res.push_back(temp);
            return ;
        }
        if(left             backtrack(temp+'(',left+1,right,n);
        if(right             backtrack(temp+')',left,right+1,n);
    }


面试题 08.11. 硬币


硬币。给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007)
思路:其实就是背包问题,有m(m=4)种物品,背包容量为n
int waysToChange(int n) {
        //首先要意识到dp[i]=dp[i-1]+dp[i-5]+dp[i-10]+dp[i-25]是不对的。
        //因为dp[6]=1+5或者6个1,这两种,而dp[1]=1,dp[5]=2,显然不对。
        int coin[4]={1,5,10,25};
        vector dp(n+1,0); 
        dp[0]=1;
        for(int i=0;i<4;i++){
            for(int j=coin[i];j<=n;j++){
                dp[j]=(dp[j]+dp[j-coin[i]])%1000000007;
            }
        }
        return dp[n];
    }


01背包问题


有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 ii件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
//原始完整:
cin>>n>>m;
vector v(n,0);
vector w(n,0);
vector>  f(n,vector(m+1,0));//最大价值
for(int i=0;i     cin>>v[i]>>w[i];
for(int i=0;i     for(int j=0;j<=m;j++){
        f[i][j]=f[i-1][j];//不装第i件,或者说装不下第i件。
        if(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);//装第i种物品
    }
}
cout<

//改进,变成一维
cin>>n>>m;
vector v(n,0);
vector w(n,0);
vector f(m,0);//最大价值
for(int i=0;i     for(int j=m;j>=0;j--){//倒序
        if(j>=v[i]) f[j]=max(f[j],f[j-v[i]]+w[i]);
    }
}
cout<                         

for(int i=0;i     for(int j=m;j>=v[j];j--){
         f[j]=max(f[j],f[j-v[i]]+w[i]);
    }
}

完全背包问题


有 NN 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 ii 种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
cin>>n>>m;
vector v(n,0);
vector w(n,0);
vector>  f(m+1,0);//最大价值
for(int i=0;i     for(int j=v[i];j<=m;j++){
        f[j]=max(f[j]),f[j-v[i]]+w[i]);
    }
}
cout<

多重背包问题 


有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
法1:思路:其实是01背包的性质。
      cin>>n>>m;
      vector v(n+1,0);
        vector w(n+1,0);
        vector s(n+1,0);
      vector f(m+1,0);
        for(int i=1;i<=n;i++)
            cin>>v[i]>>w[i]>>s[i];
      for(int i=0;i     for(int j=m;j>=0;i--){
        for(int k=0;k<=s&&k*v[i]<=j;k++){
            f[j]=max(f[j],f[j-k*v[i]]+k*w[i]);
        }
    }
      }
cout<

法2:二进制优化,二进制转化为01背包。
cin>>n>>m;
vector V,W;
for(int i=0;i     int v,w,s;
    cin>>v>>w>>s;
    for(int k=1;k<=s;k*=2){//以下转化为01背包问题
        V.push_back(k*v);
        W.push_back(k*w);
        s-=k;
    }
    if(s>0){
        V.push_back(s*v);
        W.push_back(s*w);
    }
}
vector f(m+1,0);
for(int i=0;i     for(int j=m;j>=V[i];j--){
        f[j]=max(f[j],f[j-V[i])+W[i]);
    }
}
cout<


混合背包问题


有 N 种物品和一个容量是 VV的背包。
物品一共有三类:
• 第一类物品只能用1次(01背包);
• 第二类物品可以用无限次(完全背包);
• 第三类物品最多只能用 si次(多重背包);
每种体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
• si=−1 表示第 ii 种物品只能用1次;
• si=0 表示第 ii 种物品可以用无限次;
• si>0 表示第 ii 种物品可以使用 si 次;

//用结构体写法
struct BAG{
    int kind;
    int v,w;
}
int main(){
    vector bag;
    cin>>n>>m;
    vector f(m+1,0);
    for(int i=0;i         int v,w,s;
        cin>>v>>w>>s;
        if(s==0)
            bag.push_back({0,v,w});
        else if(s==-1){
            bag.push_back({-1,v,w});
        }
        else {//二进制转换
            for(int k=1;k<=s;k*=2){
                bag.push_back({-1,k*v,k*w});
                s-=k;
            }
            if(s>0) 
                bag.push_back({-1,s*v,s*w});
        }
    }
    //最后按01背包和完全背包分别处理。
    for(int i=0;i         if(bag[i].kind<0){
            for(int j=m;j>=bag[i].v;j--)
                f[j]=max(f[j],f[j-bag[i].v]+bag[i].w);
        }
        else{
            for(int j=bag[i].v;j<=m;j++){
                f[j]=max(f[j],f[j-bag[i].v]+bag[i].w);
            }
        }
    }
    cout< }


背包问题求方案数


有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出 最优选法的方案数。注意答案可能很大,请输出答案模 10e9+7的结果。
输入格式
第一行两个整数,N,V用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 ii 件物品的体积和价值。
cin>>n>>m;
vector v(n,0),w(m,0);
vector f(m+1,0);//最大价值
vector g(m+1,0);//最大价值时的方案数
g[0]=1;//不选也算一种方案。
for(int i=0;i     cin>>v[i]>>w[i];
}
for(int i=0;i     for(int j=m;j>=v[i];j--){
        int t=max(f[j],f[j-v[i]]+w[i]);
        int s=0;
        if(t==f[j]) s=g[j];
        if(t==f[j-v[i]]+w[i]) s+=g[j-v[i]];
        s%=1000000007;
        f[j]=t;//最大价值
        g[j]=s;//最大价值时的方案数
    }
}
int max0;
for(int i=0;i<=m;i++) maxw=max(maxw,f[i]);//找出最大价值
int cnt=0;
for(int i=0;i<=m;i++){
    if(f[i]==maxw){
        cnt+=g[i];
        cnt%=1000000007;
    }
}
cout<


面试题 10.02. 变位词组


编写一种方法,对字符串数组进行排序,将所有变位词组合在一起。变位词是指字母相同,但排列不同的字符串。
输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]

思路1循环对比,每两个元素之间对比是否是变位词。复杂度n^2.超时
思路2:先用sort函数对每个字符串排序,然后用unordered_map来记录相同的字符串的下标
vector> groupAnagrams(vector& strs) {
    vector> res;
    unrdered_map> hash;//记录下标
    for(int i=0;i         string tmp=strs[i];
        sort(tmp.begin(),tmp.end());
        hash[tmp].push_back(i);
    }
    vector t;
    for(auto it:hash){
        auto index=it.second;//用index记录,不用每次循环都计算
        for(auto num:index){
            t.push_back(strs[num]);
        }
        res.push_back(t);
    }
    return res;
}
//改进
vector> groupAnagrams(vector& strs) {
    vector> res;
    unrdered_map> hash;//直接记录字符串
    for(auto str:strs){
        string tmp=str;
        sort(tmp.begin(),tmp.end());
        hash[tmp].push_back(str);
    }
    for(auto it:hash){
        res.push_back(it.second);
    }
    return res;
}

面试题 10.10. 数字流的秩


假设你正在读取一串整数。每隔一段时间,你希望能找出数字 x 的秩(小于或等于 x 的值的个数)。请实现数据结构和算法来支持这些操作,也就是说:
实现 track(int x) 方法,每读入一个数字都会调用该方法;
实现 getRankOfNumber(int x) 方法,返回小于或等于 x 的值的个数。x <= 50000

思路一:(暴力)插入时排下序,读取时二分法
class StreamRank {
public:
    StreamRank() {
        
    }
    
    void track(int x) {
        
        num.push_back(x);
        sort(num.begin(),num.end());
    }
    
    int getRankOfNumber(int x) {
        if(num.empty()) return 0;
        int left=0,right=num.size()-1;
        while(left<=right){
            int mid=left+(right-left)/2;
            if(num[mid]<=x) left=mid+1;
            else right=mid-1;
        }
        return left;
    }
private:
    vector num;
};
思路二:树状数组
class StreamRank {
private:
    vector a;public:
public:
    StreamRank() {
        for(int i=0;i<50001;i++) a.push_back(0);
    }
    
    void track(int x) {//构建树状数组
        ++x;//防止x=0;
        for(int i=x;i<=50001;i+=(i&(-i)){
            a[i]+=1;
        }
    }
    
    int getRankOfNumber(int x) {
        ++x;
        int sum=0;
        for(int i=x;i>=0;i-=(i&(-i)){
            sum+=a[i];
        }
        return sum;
    }
};
class StreamRank {
//法3:map解决
private:
    map hash;
public:
    StreamRank() {
        
    }
    
    void track(int x) {
        hash[x]++;
    }
    
    int getRankOfNumber(int x) {
        int sum=0;
        for(auto it=hash.begin();it!=hash.end();it++){
            if(it->first<=x)
                sum+=it->second;
        }
        return sum;
    }
};
//法4:自己构建树
class StreamRank{
    struct TreeNode{
        int sum;
        int val;
        TreeNode *left;
        TreeNode *right;
        TreeNode(int x):val(x),sum(1),left(NULL),right(NULL) {}
    };
private:
    TreeNode *root;
public:
    StreamRank() {
           root=NULL; 
    }
    void track(int x) {//构建树
        if(root){
            TreeNode* cur=root;
            while(cur){
                if(x<=cur->val){
                    cur->sum++;
                    if(x==cur->val) break;//想等的话直接sum++之后就可以break了
                    if(cur->left)
                        cur=cur->left;
                    else {
                        cur->left=new TreeNode(x);
                        break;//建好之后直接break
                    }
                }
                else{
                    if(cur->right)
                        cur=cur->right;
                    else {
                        cur->right=new TreeNode(x);
                        break;
                    }
                }
            }
        }
        else {
            root=new TreeNode(x);
            return;//建好之后直接return;
        }
    }
    int getRankOfNumber(int x) {//查询树
        int res=0;
        if(root){
            TreeNode* cur=root;
            while(cur){
                if(xval){
                    cur=cur->left;
                }
                else if(s>cur-val){
                    res+=cur->sum;
                    cur=cur->right;
                }
                else{
                    res+=cur->sum;
                    break;
                }
            }
        }
        return res;
    }
}


不用临时变量交换两数


编写一个函数,不用临时变量,直接交换numbers = [a, b]中a与b的值。

vector swapNumbers(vector& numbers) {
        //法1:
        //return {numbers[1],numsber[0]};
        //法2:
        //numbers[1]=numbers[0]-numbers[1]+(numbers[0]=numbers[1]);
        //return numbers;
        //法3:用加法
        // numbers[0]=numbers[1]-numbers[0];
        // numbers[1]=numbers[1]-numbers[0];
        // numbers[0]=numbers[1]+numbers[0];
        // return numbers;
        //法4:位运算
/*
  a ^ b = c
  c ^ b = a
  a ^ c = b
  */
        numbers[0]^=numbers[1];
        numbers[1]^=numbers[0];
        numbers[0]^=numbers[1];
        return numbers;
    }
阶乘尾数
设计一个算法,算出 n 阶乘有多少个尾随零。
class Solution {
public:
    int trailingZeroes(int n) {
        //纯数学问题,其实就是计算n!所有因子中尾数含5、0的个数。
        // 因为n!中的零全部是5和2的倍数贡献的,由于因子为2的个数大于5的,所以,只需计算其中有多少个5的倍数即可。
        // int cnt=0;
        // for(int i=1;i<=n;i++){
        //     int num=i;
        //     while(num%5==0){
        //         cnt++;
        //         num/=5;
        //     }
        // }
        // return cnt;
        
        
        //改进
        int cnt=0;
        while(n>=5){
            n/=5;
            cnt+=n;
        }
        return cnt;
    }
};


最大数值


编写一个方法,找出两个数字a和b中最大的那一个。不得使用if-else或其他比较运算符。
//1.数学思维 最大值为(|a-b|+a+b)/2.
//为了防止a+b超出范围,用long
long c=a,d=b;
return (int)((fabs(c-d)+c+d)/2)
//2.位运算
int c=a/2-b/2;//除以2为了防止超出int
int k=c>>31;//获得符号正为0,负为-1;
return (k+1)*a-k*b;//k为0时,得到啊;k为-1时得到b


井字游戏


设计一个算法,判断玩家是否赢了井字游戏。输入是一个 N x N 的数组棋盘,由字符" ","X"和"O"组成,其中字符" "代表一个空位。
以下是井字游戏的规则:
    • 玩家轮流将字符放入空位(" ")中。
    • 第一个玩家总是放字符"O",且第二个玩家总是放字符"X"。
    • "X"和"O"只允许放置在空位中,不允许对已放有字符的位置进行填充。
    • 当有N个相同(且非空)的字符填充任何行、列或对角线时,游戏结束,对应该字符的玩家获胜。
    • 当所有位置非空时,也算为游戏结束。
    • 如果游戏结束,玩家不允许再放置字符。
如果游戏存在获胜者,就返回该游戏的获胜者使用的字符("X"或"O");如果游戏以平局结束,则返回 "Draw";如果仍会有行动(游戏未结束),则返回 "Pending"。
string tictactoe(vector& board) {
        //1.纯暴力
        /*if(board.empty()) return "DRAW";
        int n=board.size();
        for(int i=0;i             if(Row(board[i])=='O') return "O";
            if(Row(board[i])=='X') return "X";
            if(Col(board,i)=='O') return "O";
            if(Col(board,i)=='X') return "X";
            if(corner(board)=='O') return "O";
            if(corner(board)=='X') return "X";
            if(rcorner(board)=='O') return "O";
            if(rcorner(board)=='X') return "X";
        }
        if(has0(board)) return "Pending";
        return "Draw";*/
        //2.求和判断
        bool isfull=true;
        int n=board.size();
        int sum_row=0,sum_col=0,dia_left=0,dia_right=0;
        for(int i=0;i             sum_row=0,sum_col=0;
            dia_left += board[i][i];        //主对角相加
            dia_right += board[i][n-1-i];  //副对角相加
            for(int j=0;j                 if(board[i][j]==' ') isfull=false;
                sum_row+=board[i][j];
                sum_col+=board[j][i];
            }
            if(sum_row==n*'O'||sum_col==n*'O') return "O";
            if(sum_row==n*'X'||sum_col==n*'X') return "X";
        }
        if(dia_left==n*'O'||dia_right==n*'O') return "O";//对角线的判断放到for循环外面
        if(dia_left==n*'X'||dia_right==n*'X') return "X";
        if(isfull) return "Draw";
        else return "Pending"; 
    }
    char Row(string str){
            int n=str.size();
            for(int i=1;i                 if(str[i]!=str[0])
                    return 'N';
            }
            return str[0];
        }
        char Col(vector& board,int j){
            int n=board.size();
            for(int i=0;i                 if(board[i][j]!=board[0][j])
                    return 'N';
            }
            return board[0][j];
        }
        char corner(vector& board){
            int n=board.size();
            for(int i=0;i                 if(board[i][i]!=board[0][0])
                    return 'N';
            }
            return board[0][0];
        }
        char rcorner(vector& board){
            int n=board.size();
            for(int i=0;i                 if(board[i][n-i-1]!=board[0][n-1])
                    return 'N';
            }
            return board[0][n-1];
        }
        bool has0(vector& board){
            int n=board.size();
            for(int i=0;i                 for(int j=0;j                     if(board[i][j]==' ')
                        return true;
                }
            }
            return false;
        }
 

你可能感兴趣的:(数据结构与算法,算法)