zoukankan      html  css  js  c++  java
  • 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] != '')
        {
            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 << "	";
        }
    }

    递归方式

    //从尾到头打印链表,递归方式
    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 << "	";
    }
    //面试题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);
    }
  • 相关阅读:
    使用 ant-design/pro-table
    cross-env 根据环境打包
    React 生成图片验证码组件使用
    一些常用的命令行
    react-grid-layout
    vsCode 常用快捷键(mac 版)
    mac 使用命令行,对远程服务器进行文件更新
    原生js 平滑滚动到页面的某个位置
    html2canvas 导出包含滚动条的内容
    react 中的 PureComponent
  • 原文地址:https://www.cnblogs.com/chengxuyuanxiaowang/p/4242619.html
Copyright © 2011-2022 走看看