《剑指offer》其他类型题

其他类型

本专题中记录《剑指offer》中的一些出现频率较少的类型题,包括位运算,数值运算规律等内容,以及LeetCode中的相似题目。

相关题目列表

index description key words done data
1 赋值运算符 赋值运算符 Y 18-3-19
2 singleton 单例模式 Y 18-3-20
9 斐波那契数列 Fibonacci Y 18-3-21
10 二进制中1的个数 位运算 Y 18-3-21
11 数值的整数次方
12 打印1到最大的n位数
32 从1到n整数中1出现的次数
34 丑数
41_1 和为s的两个数字 two sum Y 18-4-13
41_2 和为s的连续正数序列 类two sum Y 18-4-13
43 n个骰子的点数
44 扑克牌的顺子
46 求1+2+3+...+n
47 不用加减乘除做加法
48 不被继承的类
53 正则表达式匹配
66 矩阵中的路径
67 机器人的运动范围

面试题1: 赋值运算符函数

题目: 如下为类型CMyString的声明,请为该类型添加赋值运算符函数。

class CMyString {
public:
    CMyString(char* pData = NULL);
    CMyString(const CMyString& str);
    ~CMyString(void);
private:
    char* m_pData;
};

题目分析

赋值运算符需要考虑以下几个点:
1、返回值应该为类的引用
2、函数结束return *this;
3、传入参数应该为const 的引用;
4、是否释放实例自身已有的内存;
5、自赋值问题的考虑;

参考代码

#include
#include
#include
//#include<>

using namespace std;

//No.1_赋值运算符函数
//如下为类型CMySting的声明,请为该类型添加赋值运算符函数。
class CMyString
{
public:
    CMyString(char* pData = NULL);  //构造函数
    CMyString(const CMyString& str);    //构造函数
    ~CMyString(void);   //析构函数

    CMyString& operator=(const CMyString& str);

    void Print();   //为测试代码准备

private:
    char* m_pData;
};

//=================================================

//构造函数
CMyString::CMyString(char *pData)
{
    if (pData == NULL)
    {
        m_pData = new char[1];
        m_pData[0] = '\0';
    }
    else
    {
        int length = strlen(pData);
        m_pData = new char[length + 1];
        strcpy(m_pData, pData);
    }
}

//构造函数2
CMyString::CMyString(const CMyString &str)
{
    int length = strlen(str.m_pData);
    m_pData = new char[length + 1];
    strcpy(m_pData, str.m_pData);
}

//析构函数
CMyString::~CMyString()
{
    delete []m_pData;
}

//==========================题目答案============================

/*
//初级程序员
//判断自赋值-->如果不是自赋值则释放*this的内存-->开辟空间进行赋值-->返回*this
CMyString& CMyString::operator=(const CMyString &str)   //赋值运算符应具有构造函数和析构函数两种功能
{
    if (this == &str)   //自赋值
        return *this;
    //先用delete释放内存,然后再用new为新的数据开辟空间,若new char内存不足,则无法正常开辟空间
    //这时,原始的m_pData没有了,也无法更新新的数据,发生异常的情况下CMyString无法保持有效状态,违背了异常安全性原则
    delete []m_pData;
    m_pData = NULL;
    m_pData = new char[strlen(str.m_pData) + 1];
    strcpy(m_pData, str.m_pData);
    return *this;
}
*/

//高级程序员
//先创建一个临时实参,再交换临时实参和原来的实参
CMyString& CMyString::operator=(const CMyString &str)
{
    if (this != &str)   //如果不是自赋值
    {
        CMyString strTemp(str);     //创建临时实参strTemp

        //将局部变量的m_pData指向的地址与需要实例m_pData指向的地址互换
        //这种方法好在:
        //先创建一个临时实例,若不成功,抛出异常,不影响原来的自己
        //若成功,str_Temp是局部变量,在作用域if中,出了if就会自动调用析构函数
        //而我们把str_Temp.m_pData和原来自己的m_pData互换了,这时候析构的就是原来自己的空间
        char* pTemp = strTemp.m_pData;
        strTemp.m_pData = m_pData;
        m_pData = pTemp;

    }
    return *this;
}


//====================测试代码=========================
void CMyString::Print()
{
    cout << m_pData << endl;
}

//测试代码1
void Test1()
{
    cout << "Test1 begin: " << endl;
    char *text = "Hello World";
    CMyString str1(text);
    CMyString str2;
    str2 = str1;

    cout << "The expected result is : " << text << endl;
    cout << "The actual result is : ";
    str2.Print();
    cout << endl;
}

//测试代码2--自赋值
void Test2()
{
    cout << "Text2 begin: " << endl;
    char *text = "Hello World";
    CMyString str1(text);
    CMyString str2;
    str1 = str1;

    cout << "The expected result is : " << text << endl;
    cout << "The actual result is : ";
    str1.Print();
    cout << endl;
}

//测试代码3--连续赋值
void Text3()
{
    cout << "Text3 begin: " << endl;
    char *text = "Hello World";
    CMyString str1(text);
    CMyString str2, str3;
    str2 = str3 = str1;

    cout << "The expected result is : " << text << endl;
    cout << "The actual result is : ";
    str2.Print();
    cout << endl;
}


int main()
{
    Test1();
    Test1();
    Test1();

    return 0;
}

面试题2: 实现Singleton模式

题目:使用C++实现单例模式

题目分析

单例模式是最经典的设计模式,还是需要掌握的。
单例模式需要满足两个条件:
1、保证一个类只能创建一个实例;
2、提过对该实例的全局访问点。

单例模式的应用:

1、日志类,一个应用往往只对应一个日志实例;
2、配置类,应用的配置集中管理,并提供全局访问;
3、管理器,比如windows系统的任务管理器就是一个例子,总是只有一个管理器实例;
4、共享资源类,加载资源需要较长时间,使用单例可以避免重复加载资源,并访问多个共享资源。

参考代码

/* lazy Singleton */

class Singleton
{
public:
    /* 提供static的Instance()方法,作为全局访问点;
     * 如果有现成的实例,则直接返回;
     *如果没有,则将新生成的实例保存到私有的static属性中
    */
     /* Instance()返回的是实例的引用而不是指针,如果是指针有被外部调用者delete的风险。
      * 直到Instance()方法被访问,才会生成实例,这种特性被称为延迟初始化(Lazy Singleton)
     */
    /* 但是这种方式不是线程安全的,比如有线程A和B
     * 都通过了instance==nullptr判断,则两个线程会分配创建新实例,这样单例模式就被打破了。
    */
    static Singleton& Instance() {
        if (instance == nullptr)
            instance = new Singleton;
        return *instance;
    }

private:
    //构造函数与拷贝构造函数,拷贝赋值运算符都声明为私有方法,这样杜绝从外部生成新的实例
    Singleton();
    ~Singleton();
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);

private:
    static Singleton* instance;
};
//实现文件中
Singleton* Singleton::instance = 0;


/* Eager Singleton */
/* 这种模式在程序开始就完成了实例的创建。与Lazy相反;
 * 因为在main函数之前初始化,所以没有线程安全的问题;
 * 但是潜在的问题在于no-local static对象(函数外的static对象)
 * 在不同编译单元中的初始化顺序是未定义的。
 * 如果在初始化完成之前调用了Instance()方法就会返回一个未定义的实例。
 * 也就是说不能保证先完成初始化。
*/
class Singleton
{
public:
    static Singleton& Instance() {
        return instance;
    }
private:
    Singleton();
    ~Singleton();
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);

private:
    static Singleton instance;
};


/* Meyers Singleton*/
/* 下面是effective C++中提出的一种模式,使用local static对象
 * 实现了当第一次访问Instance()方法时才创建实例。
*/
class Singleton
{
public:
    static Singleton& Instance() {
        static Singleton instance;
        return instance;
    }
private:
    Singleton();
    ~Singleton();
    Singleton(const Singleton&);
    Singleton& operator=(const Singleton&);

};

面试题9:斐波那契数列

题目1: 写一个函数,输入n,求斐波那契数列的第n项。

题目分析

本题为直接考查斐波那契数列,可以利用多种方式解答。

参考代码

//Fibonacci数列
#include
using namespace std;

//递归方式,包含大量重复计算
int Fibonacci_Recursive(int n)
{
    if (n < 0)
        return -1;
    if (n == 0)
        return 0;
    else if (n == 1)
        return 1;
    else
        return Fibonacci_Recursive(n - 1) + Fibonacci_Recursive(n - 2);
}

//循环方式
int Fibonacci_Iterative(int n)
{
    int result[2] = {0, 1};
    if (n < 2)
        return result[n];

    int i = 0;
    int j = 1;
    int FibN = 0;
    for (int index = 2; index <= n; ++index)    //相当于两个指针不断向后移动
    {
        FibN = i + j;
        i = j;
        j = FibN;
    }

    return FibN;
}

int main()
{
    cout << Fibonacci_Recursive(6) << endl;
    cout << Fibonacci_Iterative(6) << endl;

    return 0;
}

题目2:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级台阶总共有多少种跳法。

题目分析

本题为Fibonacci的典型应用。参考代码如上。

相似题目

本题与LeetCode中的70. Climbing Stairs完全一致,参考代码见:
LeetCode 70 code
还可以在牛客网 剑指offer上完成对本题的练习。

面试题10: 二进制中1的个数

题目:请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。

题目分析

本题为典型的位运算应用题。可以记住如下规律:
将一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变为0。 通过这个规律可以得到鲁棒性最强的代码。

参考代码

#include

using namespace std;

//通过将n不断右移与1相与判断,但是这样对于有符号数中的负数会陷入死循环
int NumberOf1_Solution1(int n)
{
    int count = 0;
    while (n)
    {
        if (n & 1)
            count ++;
        n = n >> 1;
    }
    return count;
}

//通过将1不断左移1->10->100... 与n相与计算结果,但是干循环的次数由于while循环条件,等于二进制的位数,即32.
int NumberOf1_Solution2(int n)
{
    int count = 0;
    unsigned int i = 1;
    while (i)
    {
        if (n & i)
            count ++;
        i = i << 1;
    }
    return count;
}

//整数中有几个1就只需要循环几次
int NumberOf1_Solution3(int n)
{
    int count = 0;
    while (n)
    {
        count++;
        n = n & (n - 1);
    }
    return count;
}

int main()
{
    cout << NumberOf1_Solution1(9) << endl;
    //cout << NumberOf1_Solution1(-9) << endl;    //死循环

    cout << NumberOf1_Solution2(9) << endl;
    cout << NumberOf1_Solution2(-9) << endl;

    cout << NumberOf1_Solution3(9) << endl;
    cout << NumberOf1_Solution3(-9) << endl;

    return 0;
}

相似题目

本题与LeetCode中338. Counting Bits类似,只是将本题扩展了。参考代码见:
LeetCode 338 code
还可以在牛客网 剑指offer上完成对本题的练习。

面试题41_1: 和为s的两个数字

题目: 输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s, 输出任意一对即可。

题目分析

由于是已排序的递增数组,本题可以采用夹逼的方式,用双指针实现。

参考代码

#include
#include
using namespace std;

class Solution {
public:
    vector result;
    vector FindNumbersWithSum(vector array,int sum) {
        if (array.size() <= 0)
            return result;
        int small = 0;
        int big = array.size() - 1;
        while (small < big){
            if (array[small] + array[big] == sum){
                result.push_back(array[small]);
                result.push_back(array[big]);
                break;
            }
            else if (array[small] + array[big] > sum){
                big--;
            }
            else
                small++;
        }
        return result;
    }
};

int main()
{
    vector data = {1,2,3,4,5,6,7,8};
    Solution solu;
    vector result = solu.FindNumbersWithSum(data, 11);
    for (int i = 0; i < result.size(); ++i)
    {
        cout << result[i] << " ";
    }

    return 0;
}

相似题目

本题与LeetCode中的1. Two Sum完全一致。参考代码见:
LeetCode 1 code

还可以在牛客网 剑指offer上完成对本题的练习。

面试题41_2: 和为s的连续正数序列

题目: 输入一个正数s,打印出所有和为s的连续正整数序列(至少含有两个数)。例如输入15,由于1+2+3+4+5=4+5+6=7+8+15,所以打印结果就为这三个序列。

题目分析

根据41_1的解题经验,我们可以采用两个数small和big分别表示序列中的最小值和最大值,当序列的和大于target时,可以增大small,当序列和小于target时,可以增大big。
由于要求最少有两个数,所以big一直增大到(1+target)/2。

参考代码

#include
#include
using namespace std;

class Solution {
public:
    vector> result;
    vector > FindContinuousSequence(int sum) {
        int small = 1;
        int big = 2;
        int Cursum = 3;
        int middle = (1+sum)/2;
        while (small < middle){
            Cursum = (small + big)*(big - small +1)/2;
            if (Cursum < sum)
                ++big;
            if (Cursum == sum){
                vector temp;
                for (int i = small; i <= big; ++i){
                    temp.push_back(i);
                }
                result.push_back(temp);
                ++small;    //此句很重要,不可缺少,要不然无法完成while循环
            }
            if (Cursum > sum)
                ++small;
        }
        return result;
    }
};

int main()
{
    Solution solu;
    vector> result;
    result = solu.FindContinuousSequence(15);
    for (int i = 0; i < result.size(); ++i)
    {
        for (int j = 0; j < result[i].size(); ++j)
        {
            cout << result[i][j] << " ";
        }
        cout << endl;
    }

    return 0;
}

相似题目

可以在牛客网 剑指offer上完成对本题的练习。

【参考】
[1] 《剑指offer》
欢迎转载,转载请注明出处wenmingxing 《剑指offer》其他类型题

你可能感兴趣的:(《剑指offer》其他类型题)