牛客网剑指offer数组题目总结(共13道)
1、二维数组中的查找(剑指1)
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
class Solution {
public:
bool Find(int target, vector<vector<int> > array) {
if(array.empty()) return false;
int i=0,j=array[0].size()-1;
while(i<array.size()&&j>=0){
if(array[i][j]==target) return true;
else if(array[i][j]<target) i++;
else j--;
}
return false;
}
};
2、调整数组顺序使奇数位于偶数前面(剑指13)
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
法一:额外申请一个vector先保存奇数再保存偶数。
class Solution {
public:
void reOrderArray(vector<int> &array) {
vector<int> temp;
int i=0;
for(i=0;i<array.size();i++){
if(array[i]%2==1) temp.push_back(array[i]);
}
for(i=0;i<array.size();i++){
if(array[i]%2==0) temp.push_back(array[i]);
}
for(i=0;i<temp.size();i++)
array[i]=temp[i];
}
};
法二:借助插入排序的思想可以保证稳定性。
class Solution {
public:
void reOrderArray(vector<int> &array) {
int i=0,k=0,j=0;
for(i=0;i<array.size();i++){
if(array[i]%2==1){
int j=i;
while(j>k){
int temp=array[j];
array[j]=array[j-1];
array[j-1]=temp;
j--;
}
k++;
}
}
}
};
3、顺时针打印矩阵(剑指19)
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
vector<int> ans;
if(matrix.empty()) return ans;
int row1=0,row2=matrix.size()-1,col1=0,col2=matrix[0].size()-1;
int cnt=matrix.size()*matrix[0].size();
int x=0,y=0;
for(x=0,y=0;cnt>0;cnt--){
ans.push_back(matrix[x][y]);
if(x==row1){
if(y<col2) y++;
else if(y==col2) x++;
continue;
}
if(y==col2){
if(x<row2) x++;
else if(x==row2) y--;
continue;
}
if(x==row2){
if(y>col1) y--;
else if(y==col1) x--;
continue;
}
if(y==col1){
if(x>row1+1) x--;
else if(x==row1+1)
{
y++;
row1++;
row2--;
col1++;
col2--;
}
continue;
}
}
return ans;
}
};
4、数组中出现次数超过一半的数字 (剑指28,简单题)
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
int size=numbers.size();
map<int,int> m;
for(int i=0;i<numbers.size();i++){
m[numbers[i]]++;
if(m[numbers[i]]>size/2) return numbers[i];
}
return 0;
}
};
5、最小的k个数 (剑指29)
multiset指定排序方式greater实现最大堆。
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> ans;
if(k<=0||k>input.size()) return ans;
multiset<int,greater<int>> m;
multiset<int,greater<int>>::iterator it;
for(int i=0;i<input.size();i++){
if(m.size()<k){
m.insert(input[i]);
}
else{
it=m.begin();
if(input[i]<*it){
m.erase(it);
m.insert(input[i]);
}
}
}
for(it=m.begin();it!=m.end();it++)
ans.push_back(*it);
return ans;
}
};
6、把数组排成最小的数 (剑指32)
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323
class Solution {
public:
static bool cmp(int a,int b){
string a1=to_string(a);
string b1=to_string(b);
return a1+b1<b1+a1;
}
string PrintMinNumber(vector<int> numbers) {
string ans="";
sort(numbers.begin(),numbers.end(),cmp);
for(int i=0;i<numbers.size();i++){
string s=to_string(numbers[i]);
ans+=s;
}
return ans;
}
};
7、数组在排序数组中出现的次数(剑指37)
巧妙使用二分
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
return find(data,k+0.5)-find(data,k-0.5);
}
int find(vector<int> data,double k){
int l=0,r=data.size()-1;
while(l<=r){
int mid=(l+r)/2;
if(data[mid]<k) l=mid+1;
else r=mid-1;
}
return r;
}
};
8、数组中只出现一次的数字(剑指40)
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
解题思路:所有数抑或,得到的是只出现一次的数字的抑或,找到这个数字中为1的一个位置,说明在此位置两个数的位不同,将所有数按照该位为1或0分为两类,在两类中数字相抑或,可以得到这两个数字。
class Solution {
public:
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
int len=data.size();
if(len<2) return;
int temp=0;
for(int i=0;i<data.size();i++)
temp^=data[i];
int index=0;
while((temp&1)==0&&index<32){
temp=temp>>1;
index++;
}
for(int i=0;i<data.size();i++){
if(judge(data[i],index))
*num1^=data[i];
else *num2^=data[i];
}
}
bool judge(int i,int index){
i=i>>index;
return i&1;
}
};
9、和为S的两个数字(剑指42)
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
对应每个测试案例,输出两个数,小的先输出。
两边找:
class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
vector<int> ans;
if(array.empty()) return ans;
int i=0,j=array.size()-1;
while(i<j){
if(array[i]+array[j]==sum){
ans.push_back(array[i]);
ans.push_back(array[j]);
break;
}
else if(array[i]+array[j]>sum) j--;
else i++;
}
return ans;
}
};
10、数组中重复的数字(剑指50,同leetcode287)
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。
例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
class Solution {
public:
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
bool duplicate(int numbers[], int length, int* duplication) {
for(int i=0;i<length;i++){
int index=numbers[i]%length;
if(numbers[index]>=length){
*duplication=index;
return true;
}
numbers[index]+=length;
}
return false;
}
};
11、构建乘积数组(剑指51 leetcode238与此相同)
给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]A[1]...A[i-1]A[i+1]...A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * ... * A[n-1],B[n-1] = A[0] * A[1] * ... * A[n-2];)
class Solution {
public:
vector<int> multiply(const vector<int>& A) {
int len=A.size(),i;
vector<int> B(len,1);
if(len<=1) return B;
B[0]=1;
for(i=1;i<len;i++)
B[i]*=B[i-1]*A[i-1];
int temp=1;
for(i=len-2;i>=0;i--){
temp*=A[i+1];
B[i]*=temp;
}
return B;
}
};
12、滑动窗口的最大值(剑指64,同leetcode239)
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
解法:借助双端队列
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int> ans;
deque<int> q;
for(int i=0;i<num.size();i++){
while(!q.empty()&&num[q.back()]<num[i]) q.pop_back();
while(!q.empty()&&i-q.front()+1>size) q.pop_front();
q.push_back(i);
if(size!=0&&i>=size-1) ans.push_back(num[q.front()]);
}
return ans;
}
};
13、机器人的运动范围(剑指66)
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7=18。但是,它不能进入方格(35,38),因为3+5+3+8=19。请问该机器人能够达到多少个格子?
class Solution {
public:
int movingCount(int threshold, int rows, int cols)
{
if(threshold<=0||rows<=0||cols<=0) return 0;
vector <vector<bool>> visited(rows,vector<bool> (cols,false));
int ans=Count(threshold,rows,cols,0,0,visited);
return ans;
}
int Count(int threshold, int rows, int cols,int x,int y,vector <vector<bool>> &visited){
int count=0;
if(x>=0&&x<rows&&y>=0&&y<cols&&!visited[x][y]&&judge(threshold,x,y)){
visited[x][y]=true;
count=1+Count(threshold,rows,cols,x+1,y,visited)
+Count(threshold,rows,cols,x,y+1,visited)
+Count(threshold,rows,cols,x-1,y,visited)
+Count(threshold,rows,cols,x,y-1,visited);
}
return count;
}
bool judge(int threshold, int x, int y){
int temp=0;
while(x!=0){
temp+=x%10;
x/=10;
}
while(y!=0){
temp+=y%10;
y/=10;
}
return temp<=threshold;
}
};