Happy剑指offer:第2章题目笔记

概念题

定义一个空的类,里面没有任何成员变量和成员函数。对该类型求sizeof,会得到的结果是1。因为虽然没有成员变量和成员函数,没有有用信息,但是声明实例的时候,它必须在内存之中占用一定的空间,空间的大小由编译器决定,Visual Studio其分配空间为1。哪怕类内定义了构造函数和析构函数,这个结果仍然不会改变,仍然会是1。因为在实例需要初始化或者析构的时候,只需要调用这两个函数,即取得这两个函数的地址即可。这两个函数不会为实例增添任何内容。

在上面的例子之中,如果把析构函数构造成虚函数,那么结果会发生变化。这是因为编译器一旦发现类之中有虚函数,那么它一定会生成一个指向虚函数的虚函数表。在虚函数表之中,生成一个指向虚函数表的指针,指针的大小随系统的位数决定。32位系统指针占用4个字节,求sizeof之后,结果为4,64为系统占用8个字节,最终结果为8                  。

判断题1

涉及到的一个问题是类内赋值构造函数的传值问题。如果允许复制构造函数传值,就会在赋值构造函数内部递归一般的进行对复制构造函数的调用,最终必然导致栈溢出。

如果仔细观察就会发现,如果要是调用                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               复制构造函数,赋值构造函数的输入参数必然是引用形式。

举例;

A(const A &other)

面试题1

自己在定义一个类的时候,对构造函数,仍然会有一点点的不确定。

尝试了一把运算符重载。感觉还好,注意的是

1. 输入为引用,输出为引用

2. 对于指针,注意分配内存和释放内存。

内存泄露指的是在定义指针之后为其动态分配了内存,但是在使用完毕之后并未释放该内存。导致该内存一直被占用。其实说白了就是该内存空间使用完毕之后未回收。

//offer1
class CMyString
{
public:
    CMyString(char* pData = NULL);//这个应该只能算是声明吧
    CMyString(const CMyString& str);
    ~CMyString(void);
    CMyString& operator =(const CMyString &str);
private:
    char* m_pData;
};
CMyString& CMyString::operator = (const CMyString &str)
{
    if (this == &str)
        return *this;

    delete[]m_pData;
    m_pData == NULL;

    m_pData = new char[strlen(str.m_pData) + 1];
    strcpy(m_pData, str.m_pData);
    
    return *this;
}

面试题2

singleton模式:即单例模式。数学与逻辑学中,singleton定义为“有且仅有一个元素的集合”

如果一个类和singleton模式相关联,那么毫无疑问,这是一个特殊类,因为这种类只有一个对象,所以称为单例,单个例子嘛。一般这种单例非常容易被外界访问。

优点:节约系统资源。

生成实例的方法是静态方法。

//offer2 实现singleton模式
class Singleton
{
public:
    static Singleton& GetInstance()
    {
        static Singleton Instance;
        return Instance;
    }
private:
    Singleton();
    Singleton(const Singleton& other);
    ~Singleton();
    Singleton& operator=(const Singleton& other);
};

面试题3

注意int* matrix只能是代表一维数组。思路知道之后这道题突然间变得好简单。

//offer3
bool Find(int* matrix, int rows, int columns, int number)
{
    bool IsFound = false;
    if (matrix != NULL && rows > 0 && columns > 0)
    {
        int row = rows-1;
        int column = 0;
        while (row >= 0 && column < columns)
        {
            if (matrix[row*columns + column] == number)
            {
                IsFound = true;
                break;
            }
            else
            {
                if (matrix[row*columns + column] < number)
                {
                    ++column;
                }
                else
                {
                    --row;
                }
            }
        }
    }
    return IsFound;
}

判断题2:

两个字符串均定义为char str[]类型的时候,判断两个字符串是否相等,需要判定两个是不是指向相同的内存空间。这个问题十分重要。并非内容一样就可以的。另一种:char* str就不存在这个问题。它们定义的时候会自动指向对应的内存地址的。

面试题4:

//offer4
void ReplaceBlank(char str[], int length)
{
    if (str == NULL || length < 0)
        return;

    int LengthOriginal = strlen(str);
    int LengthNew = LengthOriginal;
    int i = 0;
    while (str[i] != '\0')
    {
        if (str[i] == ' ')
        {
            LengthNew += 2;
        }
        ++i;
    }

    int IndexOri = LengthOriginal;
    int IndexNew = LengthNew;

    while (IndexOri >= 0 && IndexNew > IndexOri)//IndexNew>IndexOri如果前面一大片都没有空格的话,那么这个判断可以节约很多时间
    {
        if (str[IndexOri] == ' ')
        {
            str[IndexNew--] = '0';
            str[IndexNew--] = '2';
            str[IndexNew--] = '%';
        }
        else
            str[IndexNew--] = str[IndexOri];
        IndexOri--;
    }
}

面试题4附加题

void InerstNumber(int* arr1, int length1,int* arr2, int length2,int length)
{
    if (arr1 == NULL || length < 0)
        return;
    
    if (arr2 == NULL)
    {
        arr2 = arr1;
        return;
    }

    if (length1 + length2>length)
        return;

    int index1 = length1-1;
    int index2 = length2-1;
    int index3 = length1 + length2-1;

    while (index1>=0&&index2>=0&&index3 >=0 )
    {
        if (arr1[index1] > arr2[index2])
        {
            arr2[index3--] = arr1[index1--];
        }
        else if (arr1[index1] < arr2[index2])
        {
            arr2[index3--] = arr2[index2--];
        }
        else
        {
            arr2[index3--] = arr2[index2--];
            arr2[index3--] = arr1[index1--];
        }
    }
    if (index2 < 0)
    {
        while (index1>=0)
            arr2[index3--] = arr1[index1--];
    }
}

在链表的尾部添加结点

void AddToTail(ListNode **pHead, int value)
{

    ListNode* pNew = new ListNode();
    pNew->m_nValue = value;
    pNew->m_pNext = NULL;

    if (*pHead == NULL)
    {
        *pHead = pNew;
    }
    else
    {
        ListNode* pNode = *pHead;
        while (pNode->m_pNext != NULL)
            pNode = pNode->m_pNext;
        pNode->m_pNext = pNew;
    }
}

删除一个节点

void RemoveNode(ListNode** pHead, int value)
{
    if (pHead == NULL || *pHead == NULL)
        return;

    ListNode* pToBeDeleted;

    if ((*pHead)->m_nValue == value)
    {
        pToBeDeleted = *pHead;
        *pHead = (*pHead)->m_pNext;
    }
    else
    {
        ListNode* pNode = *pHead;
        while (pNode->m_pNext != NULL&&pNode->m_pNext->m_nValue != value)
            pNode = pNode->m_pNext;
        if (pNode->m_pNext != NULL&&pNode->m_pNext->m_nValue == value){
            pToBeDeleted = pNode->m_pNext;
            pNode->m_pNext = pNode->m_pNext->m_pNext;
        }
    }
    if (pToBeDeleted != NULL)
    {
        delete pToBeDeleted;
        pToBeDeleted = NULL;
    }
}

面试题5从尾到头打印链表。这道题可以在栈的帮助下用迭代的方式实现。或者使用递归方式实现。

迭代方式:

void PrintListNode(ListNode* pHead)
{
    if (pHead == NULL)
        return;

    ListNode* pNode = pHead;
    stack<ListNode*> nodes;

    while (pNode != NULL)
    {
        nodes.push(pNode);
        pNode = pNode->m_pNext;
    }

    while (!nodes.empty())
    {
        pNode = nodes.top();
        nodes.pop();
        cout << pNode->m_nValue << "\t";
    }
}

递归方式

//从尾到头打印链表,递归方式
void PrintListNode_Recursively(ListNode* pHead)
{
    ListNode* pNode = pHead;

    if (pNode != NULL)
    {
        if (pNode->m_pNext != NULL)
        {
            PrintListNode_Recursively(pNode->m_pNext);
        }
    }
    cout << pNode->m_nValue << "\t";
}

 

//面试题6 重建二叉树

BinaryTreeNode* Construct(int* PreOrder, int* Inorder, int length)
{
    if (PreOrder == NULL || Inorder == NULL || length <= 0)
        return NULL;
    else
        return ConstructCore(PreOrder, PreOrder + length - 1, Inorder, Inorder + length - 1);
}

BinaryTreeNode* ConstructCore(int* PreOrderStart, int* PreOrderEnd, int* InOrderStart, int*InOrderEnd)
{
    
    int RootValue = PreOrderStart[0];
    BinaryTreeNode* Root = new BinaryTreeNode();
    Root->m_nValue = RootValue;
    Root->m_pLeft = Root->m_pRight = NULL;

    if (PreOrderEnd == PreOrderStart)
    {
        if (InOrderStart == InOrderEnd&&InOrderStart[0] == RootValue)
            return Root;
        if (InOrderStart == InOrderEnd&&InOrderStart[0] != RootValue)
            throw exception("Invalid Input");
    }

    //构建左右子树
    int* PreLeftRoot = PreOrderStart + 1;
    int LeftTreeLength = 0;
    int* InRoot = InOrderStart;
    
    while (InRoot!=InOrderEnd&&*InRoot != RootValue)
    {
        InRoot++;
        LeftTreeLength++;
    }

    if (InRoot == InOrderEnd&&*InRoot != RootValue)
        throw exception("Invalid Input");

    if (LeftTreeLength > 0)
    {
        Root->m_pLeft = ConstructCore(PreOrderStart + 1, PreOrderStart + LeftTreeLength, InOrderStart, InOrderStart + LeftTreeLength - 1);
    }

    if (LeftTreeLength < InOrderEnd - InOrderStart)
    {
        Root->m_pRight = ConstructCore(PreOrderStart + LeftTreeLength + 1, PreOrderEnd, InRoot + 1, InOrderEnd);
    }
}
//用两个堆栈做成一个队列
template<typename T>class CQueue
{
public:
    CQueue();
    ~CQueue(void);
    void AppendTail(const T& node);
    T& DeleteHead();
private:
    stack<T> stack1;
    stack<T> stack2;
};

template<typename T> void CQueue::AppendTail(const T& node)
{
    stack1.push(node);
}

template<typename T> T& CQueue::DeleteHead()
{
    if (stack2.empty())
    {
        if (!stack1.empty())
        {
            while (!stack1.empty())
            {
                T& node = stack1.top();
                stack1.pop();
                stack2.push(node);
            }
        }
        else
            throw exception("Invalid Input");
    }
    T node2 = stack2.top();
    stack2.pop();
    return node2;
}

用两个队列实现一个堆栈的方法和上面类似。只要充分利用队列先进先出和堆栈后进先出的特点即可。

template<typename T>T CStack::DeleteHead(const T& node)
{
    if (q1.empty())
    {
        while (!q2.empty())
        {
            T& node = q2.top();
            q2.pop();
            if (!q2.empty())
                q1.push(node);
        }
    }
    else
    {
        while (!q1.empty())
        {
            T& node = q1.top();
            q1.pop();
            if (!q1.empty())
                q2.push(node);
        }
    }
}
//快速排序
void swap(int &a, int &b)
{
    int temp;
    temp = a;
    a = b;
    b = temp;
}
int Partition(int data[], int length, int start, int end)
{
    if (data == NULL || length <= 0 || start < 0 || end >= length)
        throw exception("Invalid Input");
    
    int index = (start + end) / 2;
    int small = start - 1;

    swap(data[index], data[end]);
    for (int index = start; index < end; ++index)
    {
        if (data[index] < data[end])
        {
            ++small;
            if (small != index)
                swap(data[small], data[index]);
        }
    }
    ++small;
    swap(data[small], data[end]);
    return small;
}

void QuickSort(int data[], int length, int start, int end)
{
    if (start == end)
        return;
    int index = Partition(data, length, start, end);
    if (index>start)
        QuickSort(data, length, start, index - 1);
    if (index < end)
        QuickSort(data, length, index + 1, end);
}
posted @ 2015-01-22 20:12  程序员小王  阅读(122)  评论(0编辑  收藏  举报