• 《剑指offer》习题解答(C/C++)


    1.二维数组中的查找

    /*
        题目:在一个二维数组中,没一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。
        请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
    */
    #include<stdio.h>
    #include<string.h>
    
    //    从右上角开始比较
    
    bool Find(int *matrix, int rows, int columns, int number) {
        bool found = false;
        if (matrix != NULL && rows > 0 && columns > 0) {
            int row = 0;
            int column = columns - 1;
            while (row < rows&&column >= 0) {
                if (matrix[row*columns + column] == number) {
                    found = true;
                    break;
                }
                else if (matrix[row*columns + column ]> number)
                    column--;
                else
                    row++;
            }
        }
        return found;
    }
    
    //    从左下角开始比较
    bool Find_2(int *arr, int rows, int columns, int number) {
        bool find = false;
        if (arr != NULL&&rows > 0 && columns > 0) {
            int row = rows - 1;
            int column = 0;
            while (row >=0 && column <= columns - 1) {
                if (arr[row*columns + column] == number) {
                    find = true;
                    break;
                }
                else if (arr[row*columns+column] < number)
                    column++;
                else
                    row--;
            }
        }
        return find;
    }
    
    int main() {
        int arr [4][4]= {
            {1, 2, 8, 9},
            {2,4 ,9 ,12},
            {4, 7, 10, 13},
            {6, 8, 11, 15}
        };
        Find_2(*arr, 4, 4, 7);
        return 0;
    }

     2.字符串

      C/C++中的每个字符串都以’’结尾。为了节省空间,C/C++经常把常量字符串放到一个单独的内存区域。当几个指针赋值给相同的常量字符串时,它们实际会指向相同的地址空间。例如:

    #include<stdio.h>
    int main() {
        char str1[] = "Hello World";
        char str2[] = "Hello world";
    
        char *str3 = "Hello world";
        char *str4 = "Hello world";
        if (str1 == str2)
            printf("str1 and str2 are same. 
    ");
        else
            printf("str1 and str2 are not same.
    ");
        if (str3 == str4)
            printf("str3 and str4 are same.
    ");
        else
            printf("str3 and str4 are not same.
    ");
        return 0;
    }

    输出如下:

    str1 and str2 are not same.
    str3 and str4 are same.

      

      题目:请实现一个函数,把字符串中的每个空格替换成”%20”。例如输入“We are happy.”,则输出为”We%20are%20%20happy.”。实现代码如下:

    #include<stdio.h>
    //    lenght 为字符数组string的总容量
    void ReplaceSpace(char string[], int length) {
        if (string == NULL&&length <= 0)
            return;
        //    originalLenght为字符串string的实际长度
        int originalLenght = 0;
        int numOfBlank = 0;
        int i = 0;
        while (string[i] != '') {
            originalLenght++;
            if (string[i] == ' ')
                numOfBlank++;
            i++;
        }
        int newLenght = originalLenght + numOfBlank * 2;
        if (newLenght <= originalLenght)
            return;
        
        int indexOfOriginal = originalLenght;
        int indexOfNew = newLenght;
        while (indexOfOriginal >= 0 && indexOfNew > indexOfOriginal) {
            if (string[indexOfOriginal] == ' ') {
                string[indexOfNew--] = '0';
                string[indexOfNew--] = '2';
                string[indexOfNew--] = '%';
            }
            else
                string[indexOfNew--] = string[indexOfOriginal];
            indexOfOriginal--;
        }
    }
    
    int main() {
        char string[] = "We are happy.";
        int lenght = sizeof(string) / sizeof(char);
        ReplaceSpace(string, lenght);
        int j = 0;
        while (string[j] != '') {
            printf("%c", string[j]);
            j++;
        }
    printf("
    ");
        return 0;
    }

    输出结果:

    We%20are%20happy.

      

      相关题目:有两个排序的数组A1和A2,内存在A1末尾有足够的空余空间容纳A2。请实现一个函数,把A2中的所有数字插入到A1中并且所有的数字是排序的。实现代码如下:

    #include<stdio.h>
    // len1为数组arr1的有效数字的长度
    void unionArray(int arr1[], int len1, int arr2[], int len2) {
        //    arr1_all是啊让人整个数组的空间长度
        int arr1_all = len1 + len2;
        if (len2 ==0)
            return;
        int index_1 = len1-1;
        int index_2 = arr1_all-1;
        int index_3 = len2-1;
        while (index_2 > index_1) {
            if (arr1[index_1] > arr2[index_3]) {
                arr1[index_2--] = arr1[index_1];
                index_1--;
            }
            else
            {
                arr1[index_2--] = arr2[index_3];
                index_3--;
            }
    
        }
        
    }
    
    int main() {
        int arr1[9] = { 1,2,3,4 };
        int arr2[5] = { 5,6,7,8,9 };
        unionArray(arr1, 4, arr2, 5);
        int len = sizeof(arr1) / sizeof(int);
        int i = 0;
        while (i < len) {
            printf("%d", arr1[i]);
            ++i;
        }
        printf("
    ");
        return 0;
    }

    输出结果:

    123456789

     3.链表

    //    单向链表的节点定义
    struct ListNode
    {
        int m_nValue;
        ListNode *m_pNext;
    };
    
    //    往链表的末尾添加一个节点
    void AddToTail(ListNode** pHead, int value) {
        ListNode*    pNew = new ListNode();    //    定义一个新节点指针并为它分配内存
        pNew->m_nValue = value;
        pNew->m_pNext = NULL;
    
        //    如果链表为空,则新添加的节点为头节点,使头指针pHead指向该节点
        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 = NULL;
        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;
            }    
        }
    }

    题目:输入一个链表的头节点,从尾到头打印出每个节点的值。

    //    题目:输入一个链表的头节点,从尾到头打印出每个节点的值。
    //    方法一:从头到尾遍历链表,把节点依次放进一个栈中,当遍历完整个链表后,再从栈顶开始输出节点的值
    void PrintListReversingly_Interatively(ListNode *pHead) {
        std::stack<ListNode*> nodes;
        
        ListNode *pNode = pHead;
        while (pNode != NULL) {
            nodes.push(pNode);
            pNode = pNode->m_pNext;
        }
        while (!nodes.empty()) {
            pNode = nodes.top();
            printf("%d
    ", pNode->m_nValue);
            nodes.pop();
        }
    }
    
    //    方法二:利用递归。每次访问一个节点时,先输出它后面的节点
    void PrintListReversingly_Recursively(ListNode* pHead) {
        if (pHead != NULL) {
            if (pHead->m_pNext != NULL) {
                PrintListReversingly_Recursively(pHead->m_pNext);
            }
            printf("%d
    ", pHead->m_nValue);
        }
    }
    
    void haha(ListNode** pHead) {
        while ((*pHead)->m_pNext != NULL)
        {
            printf("%d
    ", (*pHead)->m_nValue);
            (*pHead) = (*pHead)->m_pNext;
        }
    
    }

     4.树

      题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果都不含重复的数字。

    #include<stdio.h>
    #include<iostream>
    
    //    二叉树结点定义
    struct  BinaryTreeNode
    {
        int        m_nvalue;
        BinaryTreeNode*        m_pLeft;
        BinaryTreeNode*        m_pRight;
    };
    
    //    函数声明
    BinaryTreeNode* ConstructCore(int* startPreorder, int* endPreorder,
        int* startInorder, int* endInorder);
    
    //    用递归的方法构建左右子树和根节点
    
    BinaryTreeNode* Construct(int *preorder, int *inorder, int length) {
        if (preorder == NULL || inorder == NULL || length <= 0) 
        {
            return NULL;
        }
        return ConstructCore(preorder,preorder+length-1,inorder,inorder+length-1);
    }
    BinaryTreeNode* ConstructCore(int* startPreorder, int* endPreorder, 
                                    int* startInorder, int* endInorder) 
    {
        //    前序遍历第一个数字即为根节点的值
        int rootValue = startPreorder[0];
        BinaryTreeNode* root = new BinaryTreeNode();
        root->m_nvalue = rootValue;
        root->m_pLeft = NULL;
        root->m_pRight = NULL;
        if (startPreorder == endPreorder)
        {
            if (startInorder == endInorder&&*startPreorder == *startPreorder)
                return root;
            else
                throw std::exception("Invalid input");
        }
        //    在中序遍历中找到根节点的值
        int* rootInorder = startPreorder;
        while (*rootInorder != rootValue && rootInorder <= endInorder)
        {
            ++rootInorder;
        }
        if (*rootInorder != rootValue&&rootInorder == endInorder)
            throw std::exception("Invalid input");
    
        int leftLength = rootInorder - startInorder;
        int* leftPreorderEnd = startInorder + leftLength;
        if (leftLength > 0) {
            //    构建左子树
            root->m_pLeft = ConstructCore(startPreorder + 1, endPreorder, 
                startInorder, leftPreorderEnd - 1);
        }
        if (leftLength < endInorder - startInorder)
        {
            //    构建右子树
            root->m_pRight = ConstructCore(leftPreorderEnd+1,endPreorder,
                rootInorder+1,endInorder);
        }
    }

    5.栈和队列

      题目:用两个栈实现一个队列。队列的声明如下,请实现它的两个函数appendTail和deleteHead,分别完成在队列尾部插入节点和在队列头部删除节点的功能。

    //    队列的声明
    template <typename T> class CQueue
    {
    public:
        CQueue(void);
        ~CQueue(void);
    
        // 在队列末尾添加一个结点
        void appendTail(const T& node);
    
        // 删除队列的头结点
        T deleteHead();
    
    private:
        stack<T> stack1;
        stack<T> stack2;
    };
    
    template <typename T> CQueue<T>::CQueue(void)
    {
    }
    
    template <typename T> CQueue<T>::~CQueue(void)
    {
    }

      接下来定义两个函数:

    //    往第一个栈中插入元素
    template<typename T> void CQueue<T>::appendTail(const T& element)
    {
        stack1.push(element);
    }
    
    //    删除最先插入的元素
    template<typename T> T CQueue<T>::deleteHead()
    {
        //    如果第二个栈为空,把栈一的元素依次弹出插入栈二
        if (stack2.size() <= 0)
        {
            while (stack1.size()>0)
            {
                T& data = stack1.top();
                stack1.pop();
                stack2.push(data);
            }
        }
    
        if (stack2.size() == 0)
            throw new exception("queue is empty");
    
        //    依次弹出并返回栈二栈顶元素
        T head = stack2.top();
        stack2.pop();
    
        return head;
    }

     6.查找和排序

      题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。

    //    利用二分查找法
    
    #include<stdio.h>
    #include<exception>
    int MinInOrder(int* numbers, int index1, int index2);
    
    int Min(int* numbers, int length)
    {
        if (numbers == NULL || length <= 0)
            throw new std::exception("Invalid parameters");
    
        int index1 = 0;
        int index2 = length - 1;
        int indexMid = index1;
        while (numbers[index1] >= numbers[index2])
        {
            // 如果index1和index2指向相邻的两个数,
            // 则index1指向第一个递增子数组的最后一个数字,
            // index2指向第二个子数组的第一个数字,也就是数组中的最小数字
            if (index2 - index1 == 1)
            {
                indexMid = index2;
                break;
            }
    
            // 如果下标为index1、index2和indexMid指向的三个数字相等,
            // 则只能顺序查找
            indexMid = (index1 + index2) / 2;
            if (numbers[index1] == numbers[index2] && numbers[indexMid] == numbers[index1])
                return MinInOrder(numbers, index1, index2);
    
            // 缩小查找范围
            if (numbers[indexMid] >= numbers[index1])
                index1 = indexMid;
            else if (numbers[indexMid] <= numbers[index2])
                index2 = indexMid;
        }
    
        return numbers[indexMid];
    }
    
    int MinInOrder(int* numbers, int index1, int index2)
    {
        int result = numbers[index1];
        for (int i = index1 + 1; i <= index2; ++i)
        {
            if (result > numbers[i])
                result = numbers[i];
        }
    
        return result;
    }
    
    // ====================测试代码====================
    void Test(int* numbers, int length, int expected)
    {
        int result = 0;
        try
        {
            result = Min(numbers, length);
    
            for (int i = 0; i < length; ++i)
                printf("%d ", numbers[i]);
    
            if (result == expected)
                printf("	passed
    ");
            else
                printf("	failed
    ");
        }
        catch (...)
        {
            if (numbers == NULL)
                printf("Test passed.
    ");
            else
                printf("Test failed.
    ");
        }
    }
    
    int main()
    {
        // 典型输入,单调升序的数组的一个旋转
        int array1[] = { 3, 4, 5, 1, 2 };
        Test(array1, sizeof(array1) / sizeof(int), 1);
    
        // 有重复数字,并且重复的数字刚好的最小的数字
        int array2[] = { 3, 4, 5, 1, 1, 2 };
        Test(array2, sizeof(array2) / sizeof(int), 1);
    
        // 有重复数字,但重复的数字不是第一个数字和最后一个数字
        int array3[] = { 3, 4, 5, 1, 2, 2 };
        Test(array3, sizeof(array3) / sizeof(int), 1);
    
        // 有重复的数字,并且重复的数字刚好是第一个数字和最后一个数字
        int array4[] = { 1, 0, 1, 1, 1 };
        Test(array4, sizeof(array4) / sizeof(int), 0);
    
        // 单调升序数组,旋转0个元素,也就是单调升序数组本身
        int array5[] = { 1, 2, 3, 4, 5 };
        Test(array5, sizeof(array5) / sizeof(int), 1);
    
        // 数组中只有一个数字
        int array6[] = { 2 };
        Test(array6, sizeof(array6) / sizeof(int), 2);
    
        // 输入NULL
        Test(NULL, 0, 0);
    
        return 0;
    }

    7.递归和循环

      递归虽然有简洁的优点,但是缺点也是显著的。由于递归式函数调用自身,而函数的调用是由空间和时间消耗的,所以递归的效率不如循环。

      题目一:写一个函数,输入n,求斐波那契(Fibonacci)数列的第n项。斐波那契数列定义如下:

      F(0)=0,F(1)=1, F(n)=F(n-1)+F(n-2)(n>=2

      解答代码:

    #include<stdio.h>
    #include <cassert>
    
    // ====================方法1:递归====================
    //    时间复杂度以n的指数方式递增
    
    long long Fibonacci_Solution1(unsigned int n)
    {
        if (n <= 0)
            return 0;
    
        if (n == 1)
            return 1;
    
        return Fibonacci_Solution1(n - 1) + Fibonacci_Solution1(n - 2);
    }
    
    // ====================方法2:循环====================
    //    时间复杂度为O(n)
    
    long long Fibonacci_Solution2(unsigned n)
    {
        int result[2] = { 0, 1 };
        if (n < 2)
            return result[n];
    
        long long  fibNMinusOne = 1;
        long long  fibNMinusTwo = 0;
        long long  fibN = 0;
        for (unsigned int i = 2; i <= n; ++i)
        {
            fibN = fibNMinusOne + fibNMinusTwo;
    
            fibNMinusTwo = fibNMinusOne;
            fibNMinusOne = fibN;
        }
    
        return fibN;
    }
    
    // ====================方法3:基于矩阵乘法====================
    //    时间复杂度为O(logn)
    
    struct Matrix2By2
    {
        Matrix2By2
        (
            long long m00 = 0,
            long long m01 = 0,
            long long m10 = 0,
            long long m11 = 0
        )
            :m_00(m00), m_01(m01), m_10(m10), m_11(m11)
        {
        }
    
        long long m_00;
        long long m_01;
        long long m_10;
        long long m_11;
    };
    
    Matrix2By2 MatrixMultiply
    (
        const Matrix2By2& matrix1,
        const Matrix2By2& matrix2
    )
    {
        return Matrix2By2(
            matrix1.m_00 * matrix2.m_00 + matrix1.m_01 * matrix2.m_10,
            matrix1.m_00 * matrix2.m_01 + matrix1.m_01 * matrix2.m_11,
            matrix1.m_10 * matrix2.m_00 + matrix1.m_11 * matrix2.m_10,
            matrix1.m_10 * matrix2.m_01 + matrix1.m_11 * matrix2.m_11);
    }
    
    Matrix2By2 MatrixPower(unsigned int n)
    {
        assert(n > 0);
    
        Matrix2By2 matrix;
        if (n == 1)
        {
            matrix = Matrix2By2(1, 1, 1, 0);
        }
        else if (n % 2 == 0)
        {
            matrix = MatrixPower(n / 2);
            matrix = MatrixMultiply(matrix, matrix);
        }
        else if (n % 2 == 1)
        {
            matrix = MatrixPower((n - 1) / 2);
            matrix = MatrixMultiply(matrix, matrix);
            matrix = MatrixMultiply(matrix, Matrix2By2(1, 1, 1, 0));
        }
    
        return matrix;
    }
    
    long long Fibonacci_Solution3(unsigned int n)
    {
        int result[2] = { 0, 1 };
        if (n < 2)
            return result[n];
    
        Matrix2By2 PowerNMinus2 = MatrixPower(n - 1);
        return PowerNMinus2.m_00;
    }
    
    // ====================测试代码====================
    void Test(int n, int expected)
    {
        if (Fibonacci_Solution1(n) == expected)
            printf("Test for %d in solution1 passed.
    ", n);
        else
            printf("Test for %d in solution1 failed.
    ", n);
    
        if (Fibonacci_Solution2(n) == expected)
            printf("Test for %d in solution2 passed.
    ", n);
        else
            printf("Test for %d in solution2 failed.
    ", n);
    
        if (Fibonacci_Solution3(n) == expected)
            printf("Test for %d in solution3 passed.
    ", n);
        else
            printf("Test for %d in solution3 failed.
    ", n);
    }
    
    int main()
    {
        Test(0, 0);
        Test(1, 1);
        Test(2, 1);
        Test(3, 2);
        Test(4, 3);
        Test(5, 5);
        Test(6, 8);
        Test(7, 13);
        Test(8, 21);
        Test(9, 34);
        Test(10, 55);
    
        Test(40, 102334155);
    
        return 0;
    }

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

      其实这道题的解答就是求上面的斐波那契数列。

    8.位计算

      位运算是把数字用二进制表示之后,对每一位上0或者1的运算。

      题目:请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。例如把9表示成二进制是1001,有2位是1.因此如果输入为9,输出为2.

    /*考虑到有负数的情况,如果只是对输入进行简单的右移,那么负数的高位会自动的添加1则可能会进入死循环*/
    
    /*常规解法:
        不进行右移,先把输入与标志位进行与判断最低位是不是1,再把标志位进行左移判断次低位是不是1
      依次类推直到最高位,即可计算出1的个数
    */
    
    int NumberOf1(int n){
        int count=0;
        unsigned int flag=1;
        while(flag){
            if(n&flag)
                count++;
            flag<<1;
        }
        return count;
    
    }
    
    /*进阶解法:
        可进行论证把一个数减去1,再和原来数做与运算,会把该整数最右边一个1变成0。例如1100,减去1后为1011,两数与后为1000。
    */
    int NumberOf2(int n){
        int count=0;
        while(n){
            ++count;
            n=(n-1)&n;
        }
        return count;
    }
  • 相关阅读:
    序列化二叉树
    把二叉树打印成多行
    按之字形顺序打印二叉树
    对称的二叉树
    二叉树的下一个节点
    java ee项目用gradle依赖打包
    spring cloud gateway 拦截request Body
    oauth2学习
    docker 应用
    gradle spring 配置解释
  • 原文地址:https://www.cnblogs.com/maluning/p/8016379.html
Copyright © 2020-2023  润新知