剑指offer学习笔记:6.5 发散思维能力

面试题46:求1+2+3+....+n

求1+2+3+....+n,要求不能使用乘除法,不能用for,while,if,else,switch,case等关键字及条件判断语句。
leetcode链接 https://leetcode-cn.com/problems/qiu-12n-lcof/

class Solution {   // 利用&&运算法左子式为false不进行右子式计算的性质
public:
   int sumNums(int n) {
       n && (n += sumNums(n - 1));
       return n;
   }
};

解题思路:由判断条件,本题无法使用递归和循环。
网上看到一神人代码, 定义数组,sizeof大小就是n*(n+1),右移两位实现除2操作。如果是python代码,可以用sum(range(1,n+1))

class Solution {
public:
    int sumNums(int n) {
        bool arr[n][n+1];
        return sizeof(arr)>>1;
    }
};

解法一:从循环做文章,由于循环就是相同的函数重复n次,创建类的对象会调用构造函数。利用这个性质创建n个类的对象即可。类中变量设定为static,这样就完成了对象间变量的继承。

//
// Created by Xue,Lin on 2020/7/7.
//
#ifndef UNTITLED_NSUM_H
#define UNTITLED_NSUM_H
// 利用定义n个类的对象,类的构造函数会被调用n次和static只会被分配一个空间的性质
class Temp{
public:
    Temp() {++N; sum += N; }
    static unsigned int getSum() { return sum; }
private:
    static unsigned int N;
    static unsigned int sum;
};
unsigned int Temp::N = 0;  // 注意static初始化,不初始化好像是不分配空间
unsigned int Temp::sum = 0;
#endif //UNTITLED_NSUM_H
int main() {
    int n = 10;
    Temp* result = new Temp[n];
    delete []result;
    result = NULL;
    cout << Temp::getSum() << endl;  // static函数不需要对象,可以直接通过类名调用
    return 0;
}

解法二:从递归出发,当前无法使用递归的原因是不能使用判断,因此不能终止。利用虚函数,定义基类和策略类对象,定义数组0位为基类对象,其余n-1位为策略类,进行调用,即可实现递归停止。

//
// Created by Xue,Lin on 2020/7/7.
//
#ifndef UNTITLED_NSUMDEC_H
#define UNTITLED_NSUMDEC_H
class A{   // 基类做递归的终止条件
public:
    virtual unsigned int sum(unsigned int n, A* arr[])
    {
        return 0;
    }
};
class B: public A{
public:
    unsigned int sum(unsigned int n, A* arr[])
    {
        // !!是c++中一个运算符,当n不为0,!!n=1,当n=0,!!n=0
        // arr是一个A类实例变量的二元数组,arr[0]指向A,arr[1]指向B
        // 利用虚函数是指针指向实例时哪个类,就调用哪个类函数的特性
        return arr[!!n]->sum(n-1, arr) + n;
    }
};
#endif //UNTITLED_NSUMDEC_H
int main() {
    A* arr[2];
    A a;
    B b;
    arr[0] = &a;
    arr[1] = &b;
    int n = 5;
    cout << b.sum(n, arr) << endl;
    return 0;
}

解法三:利用函数指针,整体思想同解法二。

using namespace std;
typedef unsigned int (*func)(unsigned int);
unsigned int terminator(unsigned int n)
{
    return 0;
}
unsigned int sum(unsigned int n)
{
    func f[2] = {terminator, sum};
    return n + f[!!n](n-1);
}
int main() {
    int n = 10;
    cout << sum(n) << endl;
    return 0;
}

解法四:利用模板类型求解。这个方法需要n最开始为常量,且不能太大。先不研究。
leetcode官方解法
解法一:利用&运算符左值为false不会计算右值的特性,终止递归。

class Solution {
public:
    int sumNums(int n) {
        n && (n += sumNums(n - 1));
        return n;
    }
};

解法二:用加法和位运算来模拟乘法,叫做俄罗斯农民乘法,先不研究了。

面试题47:不用加减乘除做加法

写一个函数,求两个整数之和。要求在函数体内不能使用+,-,*,/ 运算符号
leetcode链接 https://leetcode-cn.com/problems/bu-yong-jia-jian-cheng-chu-zuo-jia-fa-lcof/

class Solution {
public:
   int add(int a, int b) {
       // 因为不能用加减乘除,只能用位运算
       while(b != 0)
       {
           cout << "a: " << a << " b: " << b << endl;
           int sum = a ^ b;
           int car = ((unsigned int)(a & b) << 1);  // 这里注意要转unsigned,不然负数会有问题
           a = sum;
           b = car;
       }>
       return a;
   }
};

解题思路:找到加法和二进制位运算之间的关系。注意负数。
先考虑两个数都是正数,譬如3+5
3的二进制是011,5的二进制是101,按位相加,不记进位,相加为110,进位为010,再次进行重复操作,不记进位100,进位100,相加不记进位000,记进位1000,最终结果为1000=8。按位相加,不记进位可以用位异或实现。进位可以用位与后左移一位实现。终止条件为进位为0。
考虑一正一负。-5和3
-5的二进制表示,在计算机中,数字的二进制是用补码表示的,正数的补码就是本身,负数的补码是绝对值取反+1,再加上符号位即可。5的8位二进制表示0000101,取反1111010,+1得1111011,加符号位11111011。3的二进制表示00000011,相加11111110,去符号位-1,得11111101,取反1000010,结果为-2。
两个负数,同样用负数补码进行操作,同上。

面试题48:不能被继承的类

用c++设计一个不能被继承的类 这道题没有平台有链接

// Created by Xue,Lin on 2020/7/7.
//
#ifndef UNTITLED_PRIVATECLASS_H
#define UNTITLED_PRIVATECLASS_H
class A{
   friend class B;
private:
   A() {};
   ~A() {};
};
class B:virtual public A
{
public:
   B() {};
   ~B() {};
};
#endif //UNTITLED_PRIVATECLASS_H

解题思路:
思路一:常规思路。把构造函数和析构函数定义为私有函数。定义共有静态函数来获取和释放类的实例。但是这样我们只能获得堆上的实例,不能获得栈上的实例

//
// Created by Xue,Lin on 2020/7/7.
//
#ifndef UNTITLED_PRIVATECLASS_H
#define UNTITLED_PRIVATECLASS_H
class A{
private:
    A() {};
    ~A() {};
public:
    static A* getA(){ A *a = new A(); return a; }
    static void deleteA(A *a) {delete a;}
};
#endif //UNTITLED_PRIVATECLASS_H

思路二:使用虚拟继承。利用友元类可以调用基类的私有构造和析构函数,但是其余类不行的特性。 参考链接 https://blog.csdn.net/zhouwei1221q/article/details/45741701?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.nonecase

// Created by Xue,Lin on 2020/7/7.
//
#ifndef UNTITLED_PRIVATECLASS_H
#define UNTITLED_PRIVATECLASS_H
class A{
    friend class B;
private:
    A() {};
    ~A() {};
};
class B:virtual public A
{
public:
    B() {};
    ~B() {};
};
#endif //UNTITLED_PRIVATECLASS_H

【c++拾遗】c++中友元
参考链接 https://www.cnblogs.com/Libinkai/p/10622473.html

友元函数:友元函数是在类中使用关键字friend修饰的非成员函数
1)友元普通函数:友元函数是一个普通的函数,友元普通函数在实现时,不需要类名的限定;在调用时,也不需要由实例来调用。在类中定义的友元函数其实就是普通函数,而不是类的成员。
2)友元成员函数:友元函数是其它类的成员函数。必须先定义包含成员函数的类(比如说A),再在另外一个类(比如说B)中将该成员函数声明为友元函数。此时虽然这个友元函数是A的成员函数,该友元函数仍然称为非成员函数(对于B来说)

友元类:1)友元关系是不能传递的,如A是B的友元,B是C的友元,但A不是C的友元 2)友元关系是单向的,A是B的友元,A可以访问B的私有属性,反之不成立 3) 友元关系是不被继承的,A是B的友元,但A的派生类不是B的友元

【c++拾遗】虚继承
参考链接 https://blog.csdn.net/longlovefilm/article/details/80558879
如果是普通继承关系,类D中就有了两份类A的成员。定义虚拟继承,就只有一份。

继承关系

你可能感兴趣的:(剑指offer学习笔记:6.5 发散思维能力)