最近把之前刷过的剑指offer题目总结一下,先列举一部分,待更新……
剑指 Offer 03. 数组中重复的数字
题意:
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
思路:
假设当前值为x,我们将其放在下标为x的位置,也就是令nums[x] = x;如果在交换前发现nums[x]=x,即已经存在x这个数了就找到重复的数字了。
时间复杂度:O(n) 空间复杂度:O(1)
代码:
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
if(nums.empty()) return -1;
for(int i = 0; i < nums.size(); ++i)
{
if(nums[i] == i) continue;
if(nums[i] == nums[nums[i]]) return nums[i];
else swap(nums[i], nums[nums[i]]);
}
return -1;
}
};
//https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/
剑指 Offer 04. 二维数组中的查找
题意:
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
思路:
假设我们从右上角开始遍历,每次可以向左或者向右走一步,就会发现向左值就会变小,向下值就会变大,这不就是二叉搜索树吗?所以我们从右上角开始遍历就可以了
时间复杂度:O(N) 空间复杂度:O(1)
代码:
class Solution
{
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target)
{
int n = matrix.size();
if(!n) return false;
int m = matrix[0].size();
if(!m) return false;
int row = 0, col = m - 1;
while(row < n && col >= 0)
{
if(matrix[row][col] == target) return true;
else if(matrix[row][col] > target) --col;
else ++row;
}
return false;
}
};
//https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/
//右上角开始遍历,往左变小,往右变大,相当于二叉搜索树,遍历即可。
剑指 Offer 07. 重建二叉树
题意:
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
思路:
对于前序遍历的第一个结点一定是二叉树的根节点,那么中序遍历中根节点之前的结点一定都是左子树。所以我们记录一下中序遍历中每个数出现的位置,然后将中序遍历划分成两个序列,即左子树,根结点,右子树。然后我们对左右子树同样的方法处理即可。
时间复杂度:O(N) 空间复杂度O(N)
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
unordered_map<int, int> mp;
TreeNode* build(vector<int>& preorder, vector<int>& inorder, int L1, int R1, int L2, int R2)
{
if(L1 > R1 || L2 > R2) return NULL;
TreeNode *head = new TreeNode(preorder[L1]);
int pos = mp[preorder[L1]];
head->left = build(preorder, inorder, L1 + 1, L1 + pos - L2, L2, pos - 1);
head->right = build(preorder, inorder, R1 - R2 + pos + 1, R1, pos + 1, R2);
return head;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
for(int i = 0; i < inorder.size(); ++i) mp[inorder[i]] = i;
return build(preorder, inorder, 0, preorder.size() - 1, 0, inorder.size() - 1);
}
};
//https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/
//先序第一个点是根节点,找到中序中根节点,左边是左子树,右边是右子树,递归分治
剑指 Offer 09. 用两个栈实现队列
题意:
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数
appendTail
和deleteHead
,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead
操作返回-1
)
思路:
首先对于入队列操作,我们将其入栈S1。对于出队操作,我们将栈S2的元素直接出栈,如果栈为空就将栈S1的元素取出来放入栈S2中,这样顺序正好是对的。
时间复杂度:O(N) 空间复杂度:O(N)
代码:
class CQueue
{
public:
stack<int> s1, s2;
CQueue()
{
while(!s1.empty()) s1.pop();
while(!s2.empty()) s2.pop();
}
void appendTail(int value)
{
s1.push(value);
}
int deleteHead()
{
if(s2.empty() && s1.empty()) return -1;
int x;
if(!s2.empty()) x = s2.top(), s2.pop();
else
{
while(!s1.empty())
{
s2.push(s1.top());
s1.pop();
}
x = s2.top();
s2.pop();
}
return x;
}
};
/**
* Your CQueue object will be instantiated and called as such:
* CQueue* obj = new CQueue();
* obj->appendTail(value);
* int param_2 = obj->deleteHead();
*/
//题目链接:https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/
剑指 Offer 11. 旋转数组的最小数字
题意:
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组
[3,4,5,1,2]
为[1,2,3,4,5]
的一个旋转,该数组的最小值为1。
思路:
显然这题要用到二分查找,比较当前区间右端点与mid位置的值大小,如果大于的话就让r=mid;如果小于的话另l = mid + 1;否则相等的话说明有重复元素就另r–。
时间复杂度:O(logN) 空间复杂度:O(1)
代码:
class Solution {
public:
int minArray(vector<int>& numbers)
{
int x = numbers[0], y = numbers[numbers.size() - 1], ans = x;
if(x < y) ans = x;
else
{
int l = 0, r = numbers.size() - 1, mid;
while(l <= r)
{
mid = (l + r) >> 1;
if(numbers[r] > numbers[mid]) r = mid;
else if(numbers[mid] > numbers[r]) l = mid + 1;
else --r;
}
ans = numbers[l];
}
return ans;
}
};
//题目链接:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/
剑指 Offer 14- II. 剪绳子 II
题意:
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m - 1] 。请问 k[0]k[1]…*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
思路:
通过对几个数的尝试我们不难发现一些规律,就是尽可能将其划分成3,这样得到的乘积最大。
时间复杂度:O(N) 空间复杂度O(1)
代码:
class Solution {
public:
int mod = 1e9 + 7;
long long res = 1;
int cuttingRope(int n) {
if(n < 3) res = 1;
else if(n == 3) res = 2;
else if(n % 3 == 0)
{
res = 1;
for(int i = 1; i <= n / 3; ++i)
{
res = res * 3 % mod;
}
}
else if(n % 3 == 1)