《程序员面试金典》习题解答(C/C++)

一.数据结构

1.数组与字符串

1.1  实现一个算法,确定一个字符串的所有字符是否全都不同。假使不允许使用额外的数据结构,又该如何处理?

/*
假设字符集为ASCII字符串,那么字符串至多有256个字符,因为一旦大于256个则必定有两个是相同的
*/
#include<iostream>
#include<string>
using namespace std;

bool isUniqueChars(string str){
    if (str.size() > 256)
        return false;
    bool char_set[256];
    for(int i = 0; i < str.size; ++i) {
        int val = str[i];    //获取字符对应得ASCII码
        if (char_set[val])    //这个字符已经在字符串中出现
            return false;
        char_set[val] = 1;
    }
    return true;
}

/*
假如输入的字符只含有小写字母a到z,则我们只需要用一个int型变量
*/

bool isUniqueChar2(string str) {
    int flag = 0;
    if (str.size() > 26)
        return false;
    for (int i = 0; i < 26; ++i) {
        int val = str[i] - 'a';
        if (flag&(1 << val) > 0)
            return false;
        flag |= (1 << val);
    }
    return true;
}

1.2  用C或C++实现void reverse(char* str)函数,即反转一个null结尾的字符串。

/*假设原地反转,不分配额外空间,下面为C语言实现*/
#include<stdio.h>

void reverse(char* str) {
    char* end = str;    
    char* start = str;
    char tmp;
    if (str) {
        while (*end)
            ++end;
    }
    --end;    /*回退一个字符,因为最后的字符为null*/

    /*从首尾开始交换*/
    while (start < end) {
        tmp = *end;
        *end = *start;
        *start = tmp;
        ++start;
        --end;
    }
}

1.3  给定两个字符串,请编写程序,确定其中一个字符串的字符重新排列后,能否变成另外一个字符串。

/*给定两个字符串,请编写程序,确定其中一个字符串的字符重新排列后,能否变成另外一个字符串。*/
/*假设字符区分大小写,空白字符也考虑在内,以下是两种解法,使用C++*/
#include<iostream>
#include<string>
#include <algorithm>
using namespace std;

/*解法一:排序字符串,将两个字符串进行排序后再进行比较*/
bool permutation1(string s, string t) {
    if (s.size() != t.size())
        return false;
    string s1 = s;
    string t1 = t;
    /*进行排序*/
    sort(s1.begin(), s1.end());    
    sort(t1.begin(), t1.end());
    if (s1 == t1)
        return true;
    return false;
}


/*解法二:比较两个字符串各字符数是否相同*/
bool permutation2(string s, string t) {
    if (s.size() != t.size())
        return false;
    int count[256] = { 0 };
    /*计算s中各个字符出现的次数*/
    for (string::size_type i = 0; i !=s.size(); ++i) {
        int val = (int)s[i];
        count[val]++;
    }
    for (string::size_type j = 0; j != t.size(); ++j) {
        int c = (int) t[j];
        if (--count[c] < 0)
            return false;
    }
    return true;
}

1.4  编写一个方法,将字符串中的空格全部替换成“%20”。假定该字符串尾部有足够的空间存放新增字符,并且知道字符串的真实长度。例如,输入:"Mr Jone Smith",输出:“Mr20%Jone20%Smith”。

/*编写一个方法,将字符串中的空格全部替换成“%20”。假定该字符串尾部有足够的空间存放新增字符,并且知道字符串的真实长度。*/
/*例如,输入:"Mr Jone Smith",输出:“Mr20%Jone20%Smith”。*/

#include<iostream>
#include<string>
using namespace    std;

void replaceSpaces(char* str,int length) {
    int spaceCount = 0;
    for (int i = 0; i < length; ++i) {
        if (str[i] == ' ')
            ++spaceCount;
    }
    int newLength = length + spaceCount * 2;
    str[newLength] = '';
    int p = length - 1;
    int q = newLength - 1;
    while (p < q) {
        if (str[p] != ' ')
            str[q--] = str[p--];
        else
        {
            str[q--] = '0';
            str[q--] = '2';
            str[q--] = '%';
            p--;
        }
    }

}

1.5  利用字符重复出现的次数,编写一个方法,实现基本的字符串压缩功能。比如,字符串"aabcccccaaa"会变成“a2b1c5a3”。若“压缩”后字符串没有变短,则返回原先的字符串。

#include<iostream>
#include<string>
using namespace std;

string compress(string str) {
    if (str.empty())
        return NULL;
    string str1;
    char last = str[0];
    int count = 1;
    for (string::size_type i = 1; i < str.size(); ++i) {
        if (str[i] == last)
            ++count;
        else {
            str1.push_back(last);
            str1+=to_string(count);
            last = str[i];
            count = 1;
        }
    }
    /*因为最后一组重复字符还未放入压缩字符串中就退出循环,所以还要更新最后一组字符*/
    str1.push_back(last);
    str1+=to_string(count);

    return str1.size() < str.size() ? str1: str;
 }

1.6  给定一幅由M*N矩阵表示的图像,其中每个像素的大小为4字节,编写一个方法,将图像旋转90度。不占用额外内存空间能否做到?

/*假设顺时针旋转,*/
#include<iostream>
using namespace std;

void rotate(int** matrix, int n) {
    for (int layer = 0; layer < n / 2; ++layer) {
        int first = layer;
        int last = n - 1 - layer;
        for (int i = first; i < last; ++i) {
            int offset = i - first;
            //存储上边
            int top = matrix[first][i];

            //左到上
            matrix[first][i] = matrix[last - offset][first];

            //下到左
            matrix[last - offset][first] = matrix[last][last - offset];

            //右到下
            matrix[last][last - offset] = matrix[i][last];

            //上到右
            matrix[i][last = top;
        }
    }
}

 1.7  编写一个算法,若M*N的矩阵中某个元素为0,则将其所在的行与列清零。

void setZeros(vector<vector<int> > matrix) {
    bool *row= new bool[matrix.size()];
    bool *column= new bool[matrix[0].size()];

    //为保险起见首先初始化为false
    for (int i = 0; i < matrix.size(); ++i) {
        for (int j = 0; j < matrix[0].size(); ++j) {
            row[i] = false;
            column[j] = false;
        }
    }
    //记录值为0的元素所在的行的索引和列的索引
    for (int i = 0; i < matrix.size(); ++i) {
        for (int j = 0; j < matrix[0].size(); ++j) {
            if (matrix[i][j] == 0)
            {
                row[i] = true;
                column[j] = true;
            }
        }
    }

    //若行i或列j有个元素为0,则matrix[i][j]置为0
    for (int i = 0; i < matrix.size(); ++i) {
        for (int j = 0; j < matrix[0].size(); ++j) {
            if(row[i]||column[j])
                matrix[i][j] = 0;
        }
    }
    
}

1.8  假定有一个方法isSubstring,可检查一个单词是否为其他字符串的子串。给定两个字符串s1和s2,请编写代码检查s2是否为s1旋转而成,要求只能调用一次isSubstring。(比如,waterbottle是erbottlewat旋转后的字符串)

/*
*假设s2由s1旋转而成,那么我们可以找出旋转点在哪。例如,若以wat旋转waterbottle,就会得到erbottlewat。
*我们把字符串s1切分为两部分x和y,并将它们重新组合成s2.
*s1=xy=waterbottle
*x=wat
*yerbottle
*s2=yx=erbottlewat
*我们需要确认的是有没有办法将s1切分成xy=s1,yx=s2。不论xy的分割点在何处,xy都是xyxy的子串。也即s2是s1s1的子串
*/
#include<iostream>
using namespace std;

bool isRotation(string s1, string s2) {
    int len = s1.size();
    //检查s1和s2是否等长且不为空
    if (len == s1.size() && len > 0) {
        //凭借s1和s1,放入新字符串中
        string s1s1 = s1 + s1;
        return isSubstring(s1s1, s2);
    }
    return false;
}

2.链表

2.1  编写代码,移除为排序链表中的重复节点。进阶:如果不得使用临时缓冲区,该怎么解决?

#include<iostream>
#include<map>
using namespace std;

//链表节点定义
struct Node {
    Node* next = NULL;
    int data;
};

/*解法一:采用散列表来存储节点的值是否出现过,若重复,则删除该节点。*/
void deleteDups1(Node *phead) {
    map<int, bool> imap;
    Node* previous = phead;
    Node* p = phead;

    while (p != NULL) {
        if (imap[p->data])
        {
            previous->next =p->next;
        }
        else {
            imap[p->data] = true;
            previous = p;

        }
        p = p->next;
    }
}

/*解法二:进阶不使用额外缓冲区,使用两个指针来迭代:current迭代访问整个列表,runner用于检查后续的节点是否重复*/
void deleteDups2(Node* phead) {
    if (phead == NULL)
        return;
    Node* current = phead;
    while (current != NULL) {
        Node* runner = current;
        while (runner->next != NULL) {
            if (current->data == runner->next->data) {
                current->next = runner->next->next;
            }
            else {
                runner = runner->next;
            }
            current = current->next;
        }
    }
}
原文地址:https://www.cnblogs.com/maluning/p/8721321.html