必须干掉这10道,面试100%遇到!(四)
0X08 TOPK问题
TOPK问题真的非常经典,通常问的有最小的K个数,寻找第K大都是TOPK这种问题,这里就用力扣215寻找数组第K大元素作为板子。
详细分析:一文拿捏TOPK
TOPK的问题解决思路有很多,如果优化的冒泡或者简单选择排序,时间复杂度为O(nk),使用优化的堆排序为O(n+klogn),不过掌握快排的变形就可以应付大体上的所有问题了(面试官要是让你手写堆排序那真是有点难为你了)。
快排每次确定一个数pivot位置,将数分成两部分:左面的都比这个数pivot小,右面的都比这个数pivot大,这样就可以根据这个k去判断刚好在pivot位置,还是左侧还是右侧?可以压缩空间迭代去调用递归最终求出结果。
很多人为了更快过测试样例将这个pivot不选第一个随机选择(为了和刁钻的测试样例作斗争),不过这里我就选第一个作为pivot了,代码可以参考:
class Solution {
public int findKthLargest(int[] nums, int k) {
quickSort(nums,0,nums.length-1,k);
return nums[nums.length-k];
}
private void quickSort(int[] nums,int start,int end,int k) {
if(start>end)
return;
int left=start;
int right=end;
int number=nums[start];
while (left<right){
while (number<=nums[right]&&left<right){
right--;
}
nums[left]=nums[right];
while (number>=nums[left]&&left<right){
left++;
}
nums[right]=nums[left];
}
nums[left]=number;
int num=end-left+1;
if(num==k)//找到k就终止
return;
if(num>k){
quickSort(nums,left+1,end,k);
}else {
quickSort(nums,start,left-1,k-num);
}
}
}
0X09 无重复的最长子串(数组)
这个问题可能是个字符串也可能是数组,但是道理一致,无重复字符的最长子串和最长无重复子数组本质一致。
题目要求为:给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
分析:
此题就是给一个字符串让你找出最长没有重复的一个子串。要搞清子串和子序列的区别:
子串:是连续的,可以看成原串的一部分截取。
子序列:不一定是连续的,但是要保证各个元素之间相对位置不变。
那么我们如何处理呢?
暴力查找,暴力查找当然是可以的,但是复杂度过高这里就不进行讲解了。这里选择的思路是滑动窗口,滑动窗口,就是用一个区间从左往右,右侧先进行试探,找到区间无重复最大值,当有重复时左侧再往右侧移动一直到没重复,然后重复进行到最后。在整个过程中找到最大子串即可。
具体实现时候可以用数组替代哈希表会快很多:
class Solution {
public int lengthOfLongestSubstring(String s) {
int a[]=new int[128];
int max=0;//记录最大
int l=0;//left 用i 当成right,当有重复左就往右
for(int i=0;i<s.length();i++)
{
a[s.charAt(i)]++;
while (a[s.charAt(i)]>1) {
a[s.charAt(l++)]--;
}
if(i-l+1>max)
max=i-l+1;
}
return max;
}
}
0X10 排序
不会真的有人以为用个Arrays.sort()就完事了吧,手写排序还是很高频的,像冒泡、插入这些简单的大家相比都会,像堆排序、希尔、基数排序等考察也不多,比较高频的就是快排了,这里额外奖励一个也很高频的归并排序,两个都是典型分治算法,也可以将快排和前面的TOPK问题比较一番。
排序详细的十大排序都有详细讲过,大家可以自行参考:程序员必知必会十大排序
快排:
具体实现:
public void quicksort(int [] a,int left,int right)
{
int low=left;
int high=right;
//下面两句的顺序一定不能混,否则会产生数组越界!!!very important!!!
if(low>high)//作为判断是否截止条件
return;
int k=a[low];//额外空间k,取最左侧的一个作为衡量,最后要求左侧都比它小,右侧都比它大。
while(low<high)//这一轮要求把左侧小于a[low],右侧大于a[low]。
{
while(low<high&&a[high]>=k)//右侧找到第一个小于k的停止
{
high--;
}
//这样就找到第一个比它小的了
a[low]=a[high];//放到low位置
while(low<high&&a[low]<=k)//在low往右找到第一个大于k的,放到右侧a[high]位置
{
low++;
}
a[high]=a[low];
}
a[low]=k;//赋值然后左右递归分治求之
quicksort(a, left, low-1);
quicksort(a, low+1, right);
}
归并排序:
实现代码为:
private static void mergesort(int[] array, int left, int right) {
int mid=(left+right)/2;
if(left<right)
{
mergesort(array, left, mid);
mergesort(array, mid+1, right);
merge(array, left,mid, right);
}
}
private static void merge(int[] array, int l, int mid, int r) {
int lindex=l;int rindex=mid+1;
int team[]=new int[r-l+1];
int teamindex=0;
while (lindex<=mid&&rindex<=r) {//先左右比较合并
if(array[lindex]<=array[rindex])
{
team[teamindex++]=array[lindex++];
}
else {
team[teamindex++]=array[rindex++];
}
}
while(lindex<=mid)//当一个越界后剩余按序列添加即可
{
team[teamindex++]=array[lindex++];
}
while(rindex<=r)
{
team[teamindex++]=array[rindex++];
}
for(int i=0;i<teamindex;i++)
{
array[l+i]=team[i];
}
}
结语
好了,今天给大家分享的10个问题,是真的在面试中非常非常高频,我敢说平均每两次面试就得遇到这里面的其中一个题(毫不夸张)!
虽说题海很深学不完,但是学过缓存的都知道要把热点数据放缓存,考过试的都知道要把必考点掌握……这十个问题已经送到嘴边。
文章转自公众号:bigsai