<算法入门>_基础入门篇一
目录
一、 二维数组中的查找
方法一:
方法二:
二、旋转数组的最小数字
方法一:
方法二:
方法三:
三、调整数组奇偶顺序
方法一:
方法二:
延展题:
一、 二维数组中的查找
二维数组中的查找_牛客题霸_牛客网 (nowcoder.com)
1.在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递 增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]]
给定 target = 7,返回 true。
给定 target = 3,返回 false。
数据范围:矩阵的长宽满足 0 \le n,m \le 5000≤n,m≤500 , 矩阵中的值满足 0 \le val \le 10^90≤val≤109
进阶:空间复杂度 O(1)O(1) ,时间复杂度 O(n+m)O(n+m)
方法一:
BF:很容易想到遍历完整个数组,依依对比是否有我们需要查找的数。
时间复杂度:O(N)
class Solution {
public:
bool Find(int target, vector<vector<int> > array) {
for (int i = 0; i < array.size(); ++i)
{
for (int j = 0; j < array[i].size(); ++j)
{
if (target == array[i][j])
{
return true;
}
}
}
return false;
}
};
方法二:
方法一没有使用题目给我们的二维数组的特性每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递 增的顺序排序。我们想办法去利用这一条件,来优化我们的程序。题目的要求是查找数字,我们可以理解为查找的本质是排除,讲不符合条件的数据排除,剩下的就是我们需要查找的数据。
我们从右上角开始查找,由条件得:右上角的数据是所在行的最大值,所在列的最小值,将右上角的值 x 和待查找的数据 target 进行比较,如果 x < target 则排除所在行,因为x是这一行上的最大值,如果 x > target 则排除所在列,因为 x 是这一列上的最小值。
class Solution {
public:
bool Find(int target, vector<vector<int>> array) {
int i = 0;
int j = array[0].size() - 1;
while (i < array.size() && j >= 0)
{
if (array[i][j] < target)
{
++i;
}
else if (array[i][j] > target)
{
--j;
}
else
{
return true;
}
}
return false;
}
};
二、旋转数组的最小数字
旋转数组的最小数字_牛客题霸_牛客网 (nowcoder.com)
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋 转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的 所有元素都大于0,若数组大小为0,请返回0。
要求:空间复杂度:O(1)O(1) ,时间复杂度:O(logn)O(logn)
输入:
[3,4,5,1,2]返回值:
1
方法一:
BF:在不考虑题目给出的条件下,很容易想到暴力遍历求解,将数组遍历一遍,找出最小值。
时间复杂度O(N)
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
int min = rotateArray[0];
for (int i = 1; i < rotateArray.size(); ++i)
{
if (rotateArray[i] < min)
{
min = rotateArray[i];
}
}
return min;
}
};
方法二:
因为数组的旋转过的,且原数组是非递减的,所以我们可以从第一个元素开始,前后两个元素x1、x2 进行比较,如果是x1 <= x2, 就继续比较,如果遇到 x1 > x2,就返回 x2.
时间复杂度O(N)
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
if (rotateArray.empty())
{
return 0;
}
int i = 0;
for (i = 0; i < rotateArray.size() - 1; ++i)
{
if (rotateArray[i] > rotateArray[i + 1])
{
return rotateArray[i + 1];
}
}
return rotateArray[0];
}
};
方法三:
方法二虽然较方法一稍有优化,但本质都是线性查找。时间复杂度都是O(N)
这里我们需要的是查找数组arr最小值,其本质就是排除,我们需要找到一个能够迅速排除其他元素的方法,我们从题目中知道这个数组是旋转的,且旋转前是非递减的,那么我们就可以用二分法的思想:旋转后的数组分为两部分,且两部分都是非递减的,我们就需要确定最小值的区间,我们用left记录一个区间最左边的下标,right记录一个区间最右边的下标,mid记录一个区间中间的下标。
arr[left] < arr[mid] 说明中间值在第一部分,即最小值在后半区间;
arr[left] > arr[mid] 说明中间值在第二部分,最小值在前半区间;
特殊情况:arr[left] == arr[mid] == arr[right], 这时无法判断出最小值在哪一个区间,可以针对这一种情况用线性遍历找最小值;
通过下图分析,当left和right相邻时返回right,即为所求最小值
时间复杂度:O(logN)
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
int left = 0;
int right = rotateArray.size() - 1;
int mid = 0;
while (rotateArray[left] >= rotateArray[right])
{
if (right - left == 1)
{
mid = right;
break;
}
mid = left + ((right - left) >> 1);
if (rotateArray[left] == rotateArray[mid] &&
rotateArray[right] == rotateArray[mid])
{
int min = rotateArray[left];
for (int i = left + 1; i < right; ++i)
{
if (rotateArray[i] < min)
{
min = rotateArray[i];
}
}
return min;
}
if (rotateArray[left] > rotateArray[mid])
{
right = mid;
}
else
{
left = mid;
}
}
return rotateArray[mid];
}
};
三、调整数组奇偶顺序
调整数组顺序使奇数位于偶数前面_牛客题霸_牛客网 (nowcoder.com)
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位 于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
方法一:
很容易想到,再创建一个n的数组,将奇数和偶数依次放进去;
class Solution {
public:
void reOrderArray(vector<int> &array) {
vector<int> v;
for (int i = 0; i < array.size(); ++i)
{
if (array[i] & 1)
{
v.push_back(array[i]);
}
}
for (int i = 0; i < array.size(); ++i)
{
if (!(array[i] & 1))
{
v.push_back(array[i]);
}
}
array = v;
}
};
方法二:
我们可以使用排序的方法,在不额外开辟空间的条件下进行数据挪动,我们需要两个变量来进行记录,一个 x 记录偶数的开始位置,一个 y 记录下一个奇数的位置,从0位置开始,y 找到一个奇数,就将x 到 y之间的数据进行挪动覆盖,再将y的值填到空的位置上去;
class Solution {
public:
void reOrderArray(vector<int> &array) {
int k = 0;
for (int i = 0; i < array.size(); ++i)
{
if (array[i] & 1)
{
int tmp = array[i];
int j = i;
while (k < j)
{
array[j] = array[j - 1];
--j;
}
array[k++] = tmp;
}
}
}
};
延展题:
如果可以改变奇偶顺序,只是将奇数放在数组的前面,偶数放在数组的后面,我们可以使用双指针的方法,fisrt 指针从前面找偶数,end 指针从结尾找奇数;
1 #include <iostream>
2 using namespace std;
3
4 void reArray(int* a, int n)
5 {
6 int left = 0;
7 int right = n - 1;
8
9 while (left < right)
10 {
11 while (left < right && !(a[right] & 1))
12 {
13 --right;
14 }
15
16 while (left < right && (a[left] & 1))
17 {
18 ++left;
19 }
20
21 if (left < right)
22 {
23 swap(a[left], a[right]);
24 }
25 }
26
27 }
28
29 int main()
30 {
31 int a[] = {1,2,3,4,5};
32 reArray(a, sizeof(a)/sizeof(int));
33
34 for (int i = 0; i < 5; ++i)
35 {
36 cout << a[i] << " ";
37 }
38
39 cout << endl;
40
41 return 0;
42 }