本专题是分享剑指offer的一些题目,开始刷题计划。
描述
在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]
]
给定 target = 7,返回 true。
给定 target = 3,返回 false。
数据范围:矩阵的长宽满足 0≤�,�≤5000≤n,m≤500 , 矩阵中的值满足 0≤���≤1090≤val≤109
进阶:空间复杂度 �(1)O(1) ,时间复杂度 �(�+�)O(n+m)
示例1
输入:7,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]
返回值 true
说明:存在7,返回true
示例2
输入:1,[[2]]
返回值 false
就是一个从右上角进行查找就可以了,当然也可以从左下角进行查找,这两个操作都是可以的。
代码:
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param target int整型
* @param array int整型vector<vector<>>
* @return bool布尔型
*/
bool Find(int target, vector<vector<int> >& array) {
// write code here
int i = 0;
int j = array[0].size() - 1;
while(i<array.size() && j>=0)
{
if(array[i][j] == target)
{
return true;
}
else if(array[i][j] > target)
{
j--;
}
else
{
i++;
}
}
return false;
}
};
描述
有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。
数据范围:1≤�≤100001≤n≤10000,数组中任意元素的值: 0≤���≤100000≤val≤10000
要求:空间复杂度:�(1)O(1) ,时间复杂度:�(����)O(logn)
这类题目我们对此有三种解法,但是因为解题的效率是不同的,第一种解法就是暴力求解,遍历一遍数组,然后找出最小值,这样的时间复杂度是O(N)。
解法一
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型vector
* @return int整型
*/
int minNumberInRotateArray(vector<int>& nums) {
// write code here
int i = 0;
int n = nums.size();
int ret = nums[0];
while(i < n)
{
if(ret > nums[i])
{
ret = nums[i];
}
i++;
}
return ret;
}
};
虽然这种做法是能通过的,但是不符合我们对题目的要求,题目是要求一个O(logN)的写法,这个是不满足的,解法二是一个类似双指针的做法
解法二
双指针
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型vector
* @return int整型
*/
int minNumberInRotateArray(vector<int>& nums) {
// write code here
int left = 0;
int right = 1;
int n = nums.size();
while(right < n)
{
if(nums[left] > nums[right])
{
return nums[right];
}
right++;
left++;
}
if(nums[0] < nums[n - 1])
{
return nums[0];
}
return 0;
}
};
但是这个是存在随机性的,因为最坏的情况的时候,时间复杂度还是O(N),所以这题的解法如果要达到O(logN)就是一个二分查找,这里分享两种写法。
二分查找
写法一
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型vector
* @return int整型
*/
int minNumberInRotateArray(vector<int>& nums) {
// write code here
int left = 0;
int right = nums.size() - 1;
int mid = 0;
if(nums[left] < nums[right])
{
return nums[left];
}
while(left < right)
{
if(right - left == 1)
{
mid = right;
break;
}
mid = left + (right - left ) / 2;
if(nums[mid] == nums[left] && nums[right] == nums[mid])
{
int ret = nums[left];
for(int i = left + 1; i < right; i++)
{
if(ret > nums[i])
{
ret = nums[i];
}
}
return ret;
}
if(nums[mid] >= nums[left])
{
left = mid;
}
else
{
right = mid;
}
}
return nums[mid];
}
};
但是这个其实感觉也不完全是一个二分查找,因为如果数据全部是一样的时候就不是一个完整的二分查找了,但是如果数据不同的时候就是一个完整的二分查找了。
解法二
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型vector
* @return int整型
*/
int minNumberInRotateArray(vector<int>& nums) {
// write code here
int left = 0;
int right = nums.size() - 1;
while(left < right)
{
if(nums[left] < nums[right])
{
return nums[left];
}
int mid = left + (right - left) / 2;
if(nums[mid] > nums[right])
{
left = mid+1;
}
else if(nums[mid] < nums[right])
{
right = mid;
}
else
{
right--;
//或者写成left++目的其实就是排除一些相同的数字
}
}
return nums[left];
}
};
本人推荐解法二,这个比较是符合二分查找的,但是其实二分查找是有一些模板的,后面会在算法专题更新模板。
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
思路一其实就是可以用双指针的方法,就是从左边和右边一并开始,然后遇到左边找奇数,右边找偶数,进行交换就行了。
思路2
找到奇数然后进行保存,将偶数位置整体往后移动,移动的时候需要注意的一点就是我们之前调整过的奇数是不需要进行调整的,而且调整的位置是当前的奇数开始往前调的。
代码
public class Solution {
public void reOrderArray(int [] array) {
//相对位置不变,稳定性
//插入排序的思想
int m = array.length;
int k = 0;//记录已经摆好位置的奇数的个数
for (int i = 0; i < m; i++) {
if (array[i] % 2 == 1) {
int j = i;
while (j > k) {//j >= k+1
int tmp = array[j];
array[j] = array[j-1];
array[j-1] = tmp;
j--;
}
k++;
}
}
}
}
三题分享完毕,明天继续。