【剑指offer】(1~4)

1:赋值运算符函数

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

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

注意:

  • 返回值类型是否声明为该类型的引用,并在函数结束前返回实例自身的引用(*this)。只有返回一个引用,才可以运行连续赋值。

  • 是否把传入的参数类型声明为常量引用。如果传入的参数不是引用而是实例,那么从形参到实参会调用一次copy构造函数。

  • 是否释放实例自身的内存。如果忘记在分配新内存之前释放自身已有的空间,则程序将出现内存泄漏。

  • 判断传入的实参和当前的实例(*this)是不是同一个实例。如果是同一个,则不进行赋值操作,直接返回。如果事先不判断就进行赋值,那么再释放实例自身内存的时候就会导致严重的问题:当 *this和传入的参数是同一个实例时,一旦释放了自身的内存,传入的参数的内存也同时被释放了,因此也找不到需要赋值的内容了了。

经典解法:

CMyString& CMyString::operator=(const CMyString&str){
    if(str==&this)
        return *this;
    delete []m_pData;
    m_pData=nullptr;
    
    m_pData=new char[strlen(str.m_pData)+1];
    strcpy(m_pData,str.m_pData);
    return *this; 
}

异常安全解法:

在前面的函数中,分配内存之前先用delete释放了实例m_pData的内存。如果此时内存不足导致new char抛出异常,则m_pData将是一个空指针,这样容易导致程序崩溃。

在赋值运算符函数中实现异常安全,方法一,先用new分配新内存,再用delete释放已有的内存;方法二,先创建一个临时的实例,再交换临时实例和原来的实例。

CMyString& CMyString::operator=(const CMyString& str)
{
    if(thies!=&str)
    {
        CMyString strTemp(str);
        
        char* pTemp=strTemp.m_pData;
        strTemp.m_pData=m_pData;
        m_pData=pTemp;
    }
    return *this;
}

2:Singleton模式

题目:设计一个类,我们只能生成该类的一个实例

懒汉式:

class singleton   //实现单例模式的类  
{  
private:  
    singleton(){}  //私有的构造函数  
    static singleton* Instance;  
public:  
    static singleton* GetInstance()  
    {  
        if (Instance == NULL) //判断是否第一调用  
            Instance = new singleton();  
        return Instance;  
    }  
}; 

改进的懒汉式:

class singleton   //实现单例模式的类  
{  
private:  
    singleton(){}  //私有的构造函数  
    static singleton* Instance;  
      
public:  
    static singleton* GetInstance()  
    {  
        if (Instance == NULL) //判断是否第一调用  
        {   
            Lock(); //表示上锁的函数  
            if (Instance == NULL)  
            {  
                Instance = new singleton();  
            }  
            UnLock() //解锁函数  
        }             
        return Instance;  
    }  
};  

饿汉式:

class singleton   //实现单例模式的类  
{  
private:  
    singleton() {}  //私有的构造函数  
      
public:  
    static singleton* GetInstance()  
    {  
        static singleton Instance;  
        return &Instance;  
    }  
}; 

3:数组中重复的数字

题目一:找出数组中重复的数字

在一个长度为n的数组里所有数字都在0~N-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道重复了几次。找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出应该是重复的数字2或者3。

bool duplicate(int numbers[], int length, int* duplication)
{
    if(numbers == nullptr || length <= 0)    //判断给定数组和长度是否符合要求
        return false;
​
    for(int i = 0; i < length; ++i)        //是否满足0~N-1的范围要求
    {
        if(numbers[i] < 0 || numbers[i] > length - 1)
            return false;
    }
​
    for(int i = 0; i < length; ++i)
    {
        while(numbers[i] != i)
        {
            if(numbers[i] == numbers[numbers[i]])
            {
                *duplication = numbers[i];
                return true;
            }
            // 交换numbers[i]和numbers[numbers[i]]             
            int temp = numbers[i];
            numbers[i] = numbers[temp];
            numbers[temp] = temp;
        }
    }
​
    return false;
}
​

思路:依次遍历数组,做值与下标号比较,不相同则交换至该值对应的下标号处,如果下次需要交换的值所对应的下标号已经有等于该下标号的值,说明有重复的数字存在。

题目二:不能修改数组找出重复的数字

在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任一重复的数字,但不能改变输入的数组。

int getDuplication(const int* numbers, int length)
{
    if(numbers == nullptr || length <= 0)
        return -1;
​
    int start = 1;
    int end = length - 1;
    while(end >= start)
    {
        int middle = ((end - start) >> 1) + start;
        int count = countRange(numbers, length, start, middle);
        if(end == start)
        {
            if(count > 1)
                return start;
            else
                break;
        }
​
        if(count > (middle - start + 1))
            end = middle;
        else
            start = middle + 1;
    }
    return -1;
}
​
int countRange(const int* numbers, int length, int start, int end)
{
    if(numbers == nullptr)
        return 0;
​
    int count = 0;
    for(int i = 0; i < length; i++)
        if(numbers[i] >= start && numbers[i] <= end)
            ++count;
    return count;
}

思路:把从1~n的数字从中间的数字m分为两部分,前面一半为1~m,后面一半为m+1~n。如果后面一半为m+1~n。如果1~m的数字的数目超过m,那么这一半的区间里一定包含重复的数字;否则,另一半m+1~n的区间里一定包含重复的数字。可以继续把包含重复数字的区间一分为二,直到找到一个重复的数字。

4:二位数组中的查找

题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

 bool Find(int target, vector<vector<int> > array) {
        int n=array.size();//行
        int m=array[0].size();
        int i=n-1,j=0;
        while(i>=0&&j<m)
        {
            if(array[i][j]==target)
                return 1;
            else if(array[i][j]<target)
                j++;
            else
                i--;
        }
        return 0;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值