牛客 剑指offer_编程题 详细题解 (已完结)

文章目录

剑指offer_编程题

【1.二维数组中的查找】

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

【解题思路】
对于每一行使用二分查找,可用lower_bound函数

【AC代码】

class Solution {
   
   
public:
    bool Find(int target, vector<vector<int> > array) {
   
   
        for(auto& v : array) {
   
   
            auto temp = lower_bound(v.begin(), v.end(), target);
            if(temp == v.end()) continue;  //注意要判断是否为end,不然会越界
            if(*temp == target) return true;
        }
        return false;
    }
};

【2.替换空格】

【题目描述】
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

【解题思路】
创建一个副本,遍历源字符串,如果是空格将其替换为"%20",否则将其等于原字符

或者用Java中的自带函数replace

【AC代码】

C++

class Solution {
   
   
public:
	void replaceSpace(char *str,int length) {
   
   
        if(str == NULL) return;
        char temp[110];
        int cnt = -1;
        for(int i = 0; i < length; ++i) {
   
   
            if(str[i] == ' ') {
   
   
                temp[++cnt] = '%';
                temp[++cnt] = '2';
                temp[++cnt] = '0';
            }
            else temp[++cnt] = str[i];
        }
        temp[++cnt] = '\0';  //别忘了‘\0’
        strcpy(str, temp);
	}
};

Java

public class Solution {
   
   
    public String replaceSpace(StringBuffer str) {
   
   
    	return str.toString().replace(" ", "%20");
    }
}

【3.从尾到头打印链表】

【题目描述】
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

【解题思路】
顺序遍历链表,用vector存储其值,最后reverse

【AC代码】

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
   
   
public:
    vector<int> printListFromTailToHead(ListNode* head) {
   
   
        vector<int> v;
        if(head == NULL) return v;
        while(head != NULL) {
   
   
            v.push_back(head -> val);
            head = head -> next;
        }
        reverse(v.begin(), v.end());
        return v;
    }
};

【4.重建二叉树】

【题目描述】
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

【解题思路】
模拟人工重建二叉树方法即可

【AC代码】

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
   
   
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
   
   
        int l = pre.size();
        return rebuild(pre, vin, 0, l - 1, 0, l - 1);
    }
    TreeNode* rebuild(vector<int>& pre, vector<int>& vin, int l1, int r1, int l2, int r2) {
   
   
    //l1,r1表示该子树在pre中的下标,l2,r2表示在vin中的下标
        if(l1 > r1 || l2 > r2) return NULL;
        int index = -1;
        for(int i = l2; i <= r2; ++i) {
   
   
            if(vin[i] == pre[l1]) {
   
   
                index = i; //找到根节点
                break;
            }
        }
        TreeNode* root = new TreeNode(pre[l1]);  //新建根节点
        root -> left = rebuild(pre, vin, l1 + 1, l1 + index - l2, l2, index - 1);
        root -> right = rebuild(pre, vin, l1 + index - l2 + 1, r1, index + 1, r2);
        return root;
    }
};

【5.用两个栈实现队列】

【题目描述】
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

【解题思路】
栈和队列的不同在于pop顺序不同,push顺序相同
第一个栈保存所有的入队元素,在pop时第一个栈所有元素出栈,将所有元素保存至栈二,此时栈二栈顶元素即为队头元素,将其出栈,再将剩余元素全部存入栈一,保持其顺序不变

【AC代码】

class Solution {
   
   
public:
    void push(int node) {
   
   
        stack1.push(node);
    }
    int pop() {
   
   
        while(!stack1.empty()) {
   
   
            stack2.push(stack1.top());
            stack1.pop();
        }
        int u = stack2.top();
        stack2.pop();
        while(!stack2.empty()) {
   
   
            stack1.push(stack2.top());
            stack2.pop();
        }
        return u;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};

【6.旋转数组的最小数字】

【题目描述】
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

【解题思路】
法一:暴力遍历加上一丝剪枝
法二:先sort然后返回开头
法三:二分查找

【AC代码】

暴力

class Solution {
   
   
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
   
   
        if(rotateArray.size() == 0) return 0;
        int pre = -1;
        for(auto& it : rotateArray) {
   
   
            if(it < pre) return it;
            pre = it;
        }
        return rotateArray[0];
    }
};

Sort

class Solution {
   
   
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
   
   
        if(rotateArray.size() == 0) return 0;
        sort(rotateArray.begin(), rotateArray.end());
        return rotateArray[0];
    }
};

二分法

class Solution {
   
   
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
   
   
        if(rotateArray.size() == 0) return 0;
        int left = 0, right = rotateArray.size() - 1;
        int ans = rotateArray[left];
        while(left <= right) {
   
   
            int mid = (left + right) >> 1;
            ans = min(ans, rotateArray[mid]);
            if(rotateArray[mid] >= rotateArray[left]) {
   
   
                left = mid + 1;
                ans = min(ans, rotateArray[mid + 1]);
            }
            if(rotateArray[mid] <= rotateArray[right]) {
   
   
                right = mid - 1;
                ans = min(ans, rotateArray[mid - 1]);
            }
        }
        return ans;
    }
};

【7.斐波那契数列】

【题目描述】
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,n<=39)。

【解题思路】
F(n) = F(n - 1) + F(n - 2)
采用记忆化递归求解,注意结束条件

【AC代码】

class Solution {
   
   
public:
    int F[40];
    int Fibonacci(int n) {
   
   
        if(n == 0) return 0;
        if(n == 1 || n == 2) return 1;
        if(F[n]) return F[n];
        return F[n] = Fibonacci(n - 1) + Fibonacci(n - 2);
    }
};

【8.跳台阶】

【题目描述】
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

【解题思路】
动态规划,考虑假设当前已经跳了n阶台阶,那么可以从n - 1跳一阶到达n,也可以从n - 2跳两阶到达n,故递推式为斐波那契递推式

【AC代码】

class Solution {
   
   
public:
    int F[100];
    int jumpFloor(int number) {
   
   
        if(number == 0 ||number == 1) return 1;
        if(F[number]) return F[number];
        return F[number] = jumpFloor(number - 1) + jumpFloor(number - 2);
    }
};

【9.变态跳台阶】

【题目描述】
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

【解题思路】
和上一题一样,考虑当前跳到了n阶台阶,可以从0 - (n - 1)分别跳(n) -(1)次,所以有
F(n) = F(0) + F(1) + F(2) + … + F(n - 1)
F(n + 1) = F(0) + F(1) + F(2) + … + F(n)
得到F(n + 1) = 2 * F(n)
得到F(n) = 2(n-1)

【AC代码】

class Solution {
   
   
public:
    int jumpFloorII(int number) {
   
   
        if(number == 0 ||number == 1) return 1;
        return pow(2, number - 1);
    }
};

【10.矩形覆盖】

【题目描述】
我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

【解题思路】
还是斐波那契数列,依旧是动态规划,假设当前已经覆盖好了2 * n的矩形,那么现在的状态可以由2 * (n - 1)铺一块2 * 1,也可以由2 * (n - 2)铺两块2 * 1得来,所以F[n] = F[n - 1] + F[n - 2]

这题有坑的地方是0的时候答案是0

【AC代码】

class Solution {
   
   
public:
    int F[100];
    int rectCover(int number) {
   
   
        if(number == 0) return 0;
        return solve(number);
    }
    int solve(int n) {
   
   
        if(n == 0 || n == 1) return 1;
        if(F[n]) return F[n];
        return F[n] = solve(n - 1) + solve(n - 2);
    }
};

【11.二进制中1的个数】

【题目描述】
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

【解题思路】
法一:Java将int转成二进制字符后统计1的个数
法二:调用Java中的>>>运算符
法三:找规律

法三:
将n表示为二进制后,若其不为0,那么至少含有一个1,那么n - 1就会将n中最右边的1变成0,然后将从右至该位的所有0变为1,此时n & (n - 1)就可以将该位变为0

例如:
n = 12 = 1100
n - 1 = 11 = 1011
n = n & (n - 1) = 1000 = 8
n - 1 = 7 = 0111
n & (n - 1) = 0000 = 0

【AC代码】

暴力

public class Solution {
   
   
    public int NumberOf1(int n) {
   
   
        char[] s = Integer.toBinaryString(n).toCharArray();
        int len = s.length, ans = 0;
        for(int i = 0; i < len; ++i) {
   
   
            if(s[i] == '1') ++ans;
        }
        return ans;
    }
};

>>>

public class Solution {
   
   
    public int NumberOf1(int n) {
   
   
        int ans = 0;
        while(n != 0) {
   
   
            ans += (n & 1);
            n >>>= 1;
        }
        return ans;
    }
};

规律

class Solution {
   
   
public:
     int  NumberOf1(int n) {
   
   
         int ans = 0;
         while(n) {
   
   
             ++ans;
             n &= (n - 1);
         }
         return ans;
     }
};

【12.数值的整数次方】

【题目描述】
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

保证base和exponent不同时为0

【解题思路】
考虑用快速幂,如果不理解快速幂可以看下面这张图

在这里插入图片描述
注意指数可能是负数

【AC代码】

class Solution {
   
   
public:
    double Power(double base, int exponent) {
   
   
        if(base == 0) return 0.0;
        double res = 1.0;
        int n = abs(exponent);
        while(n) {
   
   
            if(n & 1) res *= base;
            base *= base;
            n >>= 1;
        }
        return exponent > 0 ? res : 1 / res;
    }
};

【13.调整数组顺序使其位于偶数前面】

【题目描述】
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

【解题思路】
法一:创立一个临时数组,遍历array两次,第一次找奇数,第二次找偶数
法二:借鉴插入排序,每遇到一个奇数就将其与前面的偶数交换

法一牺牲空间,节省时间,法二节省空间,消耗时间

【AC代码】

创建临时数组

class Solution {
   
   
public:
    void reOrderArray(vector<int> &array) {
   
   
        vector<int> temp;
        for(auto& it : array) {
   
   
            if(it & 1) temp.push_back(it);
        }
        for(auto& it : array) {
   
   
            if(it & 1) continue;
            temp.push_back(it);
        }
        array = temp;
    }
};

借鉴插入排序

class Solution {
   
   
public:
    void reOrderArray(vector<int> &array) {
   
   
        int l = array.size();
        for(int i = 0; i < l; ++i) {
   
   
            if(array[i] & 1) {
   
   
                int j = i;
                while(j > 0 && (array[j - 1] & 1) == 0) {
   
   
                    swap(array[j], array[j - 1]);
                    --j;
                }
            }
        }
    }
};

【14.链表倒数第k个节点】

【题目描述】
输入一个链表,输出该链表中倒数第k个结点。

【解题思路】
可以用vector或stack存放所有的链表节点,或者先求得链表总长度,重新遍历

【AC代码】

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
   
   
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
   
   
        int cnt = 1;
        ListNode* pHead = pListHead;
        while(pHead != NULL) {
   
   
            pHead = pHead -> next;
            ++cnt;
        }
        if(k >= cnt) return NULL;
        for(int i = 1; i < cnt - k; ++i) pListHead = pListHead -> next;
        return pListHead;
    }
};

【15.反转链表】

【题目描述】
输入一个链表,反转链表后,输出新链表的表头。

【解题思路】
模拟即可

可以画个图模拟一下

【AC代码】

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
   
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值