目录
数组简介
java:[], ArrayList
C++: STL , vector,[]
C: 只有[]
理解:输入的数组通常理解为解,我们自己可以排序,查找
注意:C++STL中的vector的一种实现,
(可以给定容器的大小,当满了时,vector扩大一倍,原来的数据复制过去,均摊常数时间)
数组下标是一种特殊的hash...做计数
理解数组与map(key-value)
给数组排序
面试题总体分析
查找和排序——二分查找 / 元素交换 / 排序,中位数 / 归并 / 位运算 / 前缀和的应用
动态规划(数组下标就是状态,数组的值就是该状态下的最优值) 、排列组合
< 选题原则 难度 经典(常见) 新颖 >
一些例题
例1.局部最小值
一个给定的数组(元素互异),每个局部极小值的定义是:1个数比他左右相邻的数(如果存在)都小,求他的一个局部最小值
分析:显然存在,整个数组的最小值就是,O(n) ?
我们规定数组下标a[1...n],并定义a[0]=a[n+1]= ,所以a[1]<a[0] a[n]<a[n+1]
结论:子数组a[x...y] 若a[x]<a[x-1] , a[y] < a[y+1] ,则它包含一个局部极小值
思路:mid=(x+y)/2 二分, 两个子数组a[x...mid] , a[mid+1...y],
01.若a[mid]<a[mid+1] 则子数组a[x...mid]满足a[x-1]>a[x], a[mid]<a[mid+1]
02.若a[mid]>a[mid+1],则子数组a[mid+1...y]满足a[mid]>a[mid+1], a[y]<a[y+1]
复杂度:O(logn)
思考:01.循环有序数组最小值、查找元素x LeetCode(153,154) 循环有序数组例:[4,5,6,7,0,1,2]
02.一个严格单增的数组,查找a[x]==x的位置(二分)
例2.第一个缺失的正整数
给一个数组,找到从1开始第一个不在里面的正整数 LeetCode41
例如:[3,4,-1,1]输出2
int firstMissingPositive(int A[],int n){
for(int i=0;i<n; ){
if(A[i]==i+1){
i++; //A[0...i)=1...i
}else if( (A[i]<=i) || (A[i]>n) || (A[A[i]-1]==A[i]) ){ //3种情况:1.A[i]里面的数在之前1..i中出现过/负数 2.超范围 3.未来 出现重复
A[i]=A[--n]; //删除数组中元素的方法:最后一个数拿过来,长度--
}else{
swap(A[i],A[A[i]-1]); //A[i]放到正确位置
}
}
return n+1;
}
例3.元素间的最大距离
给定一个整数数组(n>1),求把这些整数表示在数轴上,相邻两个数差的最大值 leetcode-164
01.先排序,在遍历一遍看相邻两个数的差值 pass
02.最大值x,最小值y,如果x==y显然答案是0
把数放到n+1个桶,每个桶的大小d=(x-y)/n+1 <共n+1个桶,放n个数,必有一个是空的,鸽巢/抽屉原理>
每个桶的区间是[ y+i*d, y+(i+1)*d ) i=0,1...n
注意:区间是左闭右开,最后一个桶是双闭 最小的数在0号桶,最大的数在n号桶 且第一和最后一个桶非空
中间有空桶,空桶的左右两边肯定有元素,最大间隙出现在一个前面非空桶的最大值和后面非空桶的最小值之间
计算数字r在那个桶内: <取整> 注意r==x 的时候,答案取n
记录每个桶内的最大值,最小值即可,时间复杂度 O(n)
leetcode 164. 最大间距
class Solution {
public:
struct Bucket {
bool used = false;
int minval = INT_MAX;//numeric_limits<int>::max(); // same as INT_MAX
int maxval = INT_MIN;//numeric_limits<int>::min(); // same as INT_MIN
};
int maximumGap(vector<int>& nums) {
int len=nums.size(); if(len<2) return 0;
int mini = *min_element(nums.begin(), nums.end()),
maxi = *max_element(nums.begin(), nums.end());
int bucketsize=max(1,(maxi-mini)/(len-1));
int bucketnum=(maxi-mini)/bucketsize+1;
vector<Bucket> bt(bucketnum);
for(int x:nums){
int index=(x-mini)/bucketsize;
bt[index].used=true;
bt[index].maxval=max(x,bt[index].maxval);
bt[index].minval=min(x,bt[index].minval);
}
int preMax=mini;
int maxgap=-1;
for(Bucket y:bt){
if(y.used){
maxgap=max(maxgap,y.minval-preMax);
preMax=y.maxval;
}
}
return maxgap;
}
};
例4.只出现一次的数
一个数组,所有元素都出现了2次,只有两个数(x、y)只出现了1次,求x、y
思路:所有数字做异或(xor),由于x xor x=0 ,所以最后结果是res=x xor y ,x!=y, res一定非0
取lowbit(res) ,把原数组中的所有数字,按照这位为1和为0 分为2组 在这两组中,一个含x,一个含y(x、y在这两组中各出现1次)
例5.众数问题
找出超过一半的数
分析:众数出现的次数>其他所有数出现的次数之和
每次扔掉两个不同的数,众数不变:1. 扔掉一个众数和一个非众数 2. 扔掉2个非众数
int cnt=0,x;
for(int i=0;i<n;i++){
if(cnt==0) {x=a[i];cnt=1;}
else if(x==a[i]) ++cnt;
else --cnt;
//最后x 即为众数 众数可能不存在 O(n)遍历判断一下
思考:找出数组中出现次数> n*1/k的数 【众数出现的次数>n/2】
提示:保存k-1个数,每来一个数就与他们比较,看是否相同,【查找k-1个数,hash/map】
例6.“前缀和”的应用
给定浮点数组a,求一个数组b,b[i]=a[0]*a[1]*a[2]*...*a[i-1]*a[i+1]*...*a[n-1] ;不能使用除法,不允许再开数组
//先求后缀积
for(int i=n-1; i>=0; i--){
b[i]=a[i]*( (i==n-1)?1:b[i+1] );
}
//顺带求前缀积
for(int i=0,j=1; i<n; j*=a[i++]) b[i]=j*( (i==n-1)?1:b[i+1] );
前缀和性质:a[i]+...+a[j]=sum[j]-sum[i-1];
思考:
01. 求数组中一段连续和,绝对值最小 解:把数组的前缀和数组求出来sum[] ;sum[]排序 ,然后相邻两个数相减,维护min_abs
02. 把一个数组从中间p位置分开,使得a[0]+a[1]+...+a[p-1] 与 a[p]+a[p+1]+a[p+2]+...+a[n-1];差值最小
解:先求出前缀和 以及 总和 ,O(n)时间枚举p,。。。
如果都是非负数,可以采取“两头扫”的方法,和较小的那边多加一个数 【i,pre_sum[0...i] j,suffix_sum[j...n-1]】
总结
利用序:
利用二分查找
利用前缀和
查找,计算,排序
理解数组
map
用数组实现高级数据结构
一般树:并查集
二叉树:堆
抓住简单题:
分治法求逆序对数
有序数组归并
两个有序数组的中位数 leetcode-4
两头扫的方法(2-sum,3-sum)