**题目描述:**在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
解决思路:
正常:双循环,挨个查找;【但是这种线性查找方法效率极低】
进阶:根据题面给出的条件(左到右递增,上到下递增),我们可以采取从右上角(或左下角)开始比较这样可以做到一次排除一行或者一列。
比如
1 2 3
2 3 4
4 5 6 //查找5
//右上角:是所在行的最大值,所在列的最小值
//3比5小 ==> 列++
//4比5小 ==> 列++
//6比5大 ==> 行--
在这个过程中要注意避免数组下标越界,所以循环的条件比较重要
class Solution {
public:
bool Find(int target, vector > array) {
int row = array.size();//行
int col = array[0].size();//列
//array[i][j]
int i = 0, j = col - 1;//用右上角的值来比较
while(i < row && j >= 0)
{
//比右上角的值大,行++
if(target > array[i][j]) i++;
//比右上角的值小,列--
else if(target < array[i][j]) j--;
else if(target == array[i][j]) return true;
}
//走到这说明找不到
return false;
}
};
**题目描述:**有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。
解题思路:
正常:1、遍历数组,找最小值【线性查找方法效率太低】;2、双指针,同时比较当前值和下一个值的大小。【时间复杂度与1一样,没有性能提升】
进阶:借鉴二分查找的思想,一个数组经过旋转后,得到的结果可以看作两个新的非降序数组,我们要做的就是找到两个新数组的分割位置,通过不断缩小[left, right]最终确定最小值!
left、right、mid三个下标,
1 2 3 4 5//非降序数组
//情况1
2 3 4 5 1
left mid right
此时a[left]<=a[mid],说明前半段是非降序数组;a[right]
归纳:当a[left]<=a[mid]时,则要搜索后半段
//情况2
5 1 2 3 4//旋转后
left mid right
此时a[left]>a[mid],说明前半段顺序是不对的;a[right]>a[mid],说明后半段是非降序数组,那么对前半段再次进行查找
5 1 2 3 4//旋转后
left mid right
此时a[left]>a[mid],说明前半段顺序是不对的;a[right]>a[mid],说明后半段是非降序数组,那么对前半段再次进行查找
5 1 2 3 4//旋转后
left/mid right
此时left==mid,说明right所指向的就是最小值
归纳:当a[left]>a[mid]时,则要搜索前半段
有一层隐含关系:当(left==mid) > (left+1right) ==> right指向的就是最小值。
非递减排序可能会出现a[left]==a[mid]==a[right]这种情况,我们就无法判定最小值在mid左侧还是右侧,就需要线性遍历了。
1 1 1 0 1//旋转后
left mid right
总的循环条件是rotateArray[left] >= rotateArray[right](能进入循环的肯定是旋转数组!),在进入循环之前mid=0,这样可以保证遇到1 2 2 2(未旋转的数组)这种情况,仍可以判断出来
class Solution {
public:
int minNumberInRotateArray(vector rotateArray) {
if(rotateArray.empty()) return 0;
int left = 0;
int right = rotateArray.size() - 1;
int mid = 0;//如果这个数组是1 2 2 2没有旋转的数组,就是mid
while(rotateArray[left] >= rotateArray[right])//保证该数组是旋转过的数组
{
if(right - left == 1)//两个下标相邻,右索引就是最小值
{
mid = right;
break;
}
mid = (right + left) / 2;
//如果无法判断大小,就线性遍历搜索最小值
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[mid] >= rotateArray[left]) left = mid;//搜索后半段
else right = mid;//搜索前半段
}
return rotateArray[mid];
}
};
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。如1 2 3 4 5 6
划重点:相对位置不变,得到1 3 5 2 4 6
解题思路:
正常:1、改变相对位置。头尾双指针,头指针遇到奇数就++,遇到偶数停下来交换;尾指针遇到偶数就–,遇到奇数就停下来交换。2、不改变相对位置。new两个数组,遍历原数组,一个数组保存奇数,一个数组保存偶数,遍历结束,再把偶数数组的数据添加到奇数数组的后面。
进阶:借鉴插入排序的思想。从头开始遍历,遇到奇数就从当前位置开始从后向前覆盖式地挪动,直到上一个奇数处,留出了一个空位给当前奇数插入。覆盖式挪动,刚好把当前奇数覆盖掉,避免了数据冗余
class Solution {
public:
void reOrderArray(vector &array) {
int k = 0;
for(int i = 0; i < array.size(); i++)
{
if(array[i] & 1)//奇数
{
int tmp = array[i];//保存奇数,后续进行覆盖挪动
int j = i;//从当前下标开始挪动,所以当前下标位置的数据会被覆盖
while(j > k)
{
array[j] = array[j-1];
j--;
}
array[k++] = tmp;
}
}
}
};
题目描述:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
解题思路:
思路一:定义map,使用<数字,次数>的映射关系,最后统计每个字符出现的次数。
思路二:排序,出现次数最多的数字,一定在中间位置。然后检测中间出现的数字出现的次数是否符合要求。
思路三:由于目标数据超过数组长度的一半这个前提条件,那么我们同时去掉两个不同的数字,到最后剩下的一个数就是该数字。如果剩下两个,那么这两个也是一样的,就是结果,在其基础上把最后剩下的一个数字或者两个,用原数组遍历,将数组遍历一遍统计一下数字出现次数进行最终判断。
采用消去法来解决,一个数据与另一个数据不同,则times=0表示已消去
class Solution {
public:
int MoreThanHalfNum_Solution(vector numbers) {
if(numbers.size() == 0) return 0;
int number = numbers[0];
int times = 1;
for(int i = 1; i < numbers.size(); i++)
{
if(times == 0)
{
number = numbers[i];
times = 1;
continue;
}
if(numbers[i] == number)
times++;
else times--;//两个数不同,则抵消
}
//后续还要再确认一下是否是这个数据
int count = 0;
for(int i = 0; i < numbers.size(); i++)
{
if(numbers[i] == number) count++;
}
return count > numbers.size()/2 ? number : 0;
}
};