冲算法笔试题!【数组】

  • 计算机语言和开发平台日新月异,但万变不离其宗的是那些算法和理论。
  • 算法和理论永远是程序员最重要的内功,秃头也得学呀

记录部分 当时的思路或者学习其他优秀作者的思路,也帮自己重新梳理逻辑,争取加深印象,加油 (^-^)V

下一个排序

在这里插入图片描述
思路:从右往左,找到一个比他前面大的数,比如1,3,2。我们找到这个数3符合要求。
此时意味着:3之后的序列包括3都是最大的数了。

在以上例子中:3前面还有1,我们在3之后的序列包括3中挑选一个大于1的最小数,和1调换位置
如果3之前没有数,那么说明这个数已经是最大序列,只需直接将他升序排序。

class Solution {
    public void nextPermutation(int[] nums) {
        if(nums.length<=1) return;
        //找到比上一个数大的数
        int i ;
        for ( i = nums.length-1; i > 0; i--) {
        	//寻找一个数,使得比他前面的数大。如果没有则这个序列已经是最大序列
            if(nums[i] > nums[i-1]){
                int index = i;
                for (int j = i; j < nums.length; j++) {
                    if(nums[j]>nums[i-1] && nums[j]<nums[index]){
                        index = j;
                    }
                }
                int t = nums[index];
                nums[index] = nums[i-1];
                nums[i-1] = t;
                break;
            }
        }
        Arrays.sort(nums,i,nums.length);
    }
}

总持续时间可被60整除的歌曲

在这里插入图片描述

刚遇到这道题,给我的第一感觉:太简单了吧!

对的,如果要做出来结果来是非常的简单,你可以用暴力双重循环列出所有2个数的组合和,然后对60取模。结果就是:
在这里插入图片描述

这是一道数组题,时刻提醒我们运用数组解决问题

暴力法的时间复杂度为O(n2),暴力法的时间花在越往后的数字,重复取模的次数会越来越多,因为所有数的取模结果只有60种,可以考虑用一个长度60的数组存储所有取模结果再计算结果,时间复杂度 为O(n)。

  • 我先想到的是。从数组尾巴n=time[tail]%60出发,每次对count+=res[60-n]计算,就是算法1,但是结果不是特别令人满意,3ms,击败60%,于是参考别人的代码;
  • 优化:题目给了一个例子[60,60,60],他们取模都是0,且也需要取模为0的数配对。假设n个数都是取模为0或30的数,那他们对count操作次数就是n-1次。
    如果一次知道取模为0的个数为(假设)i,那么他们配对个数就是i*(i-1)/2。只需要计算一次。其他情况通用适用,于是有解法2。
//解法1
public int numPairsDivisibleBy60(int[] time) {
	int[]res=new int[60];
    int count=0;
    for (int i = time.length-1; i >=0; i--) {
        int left=time[i]%60;
        if(left!=0)
            count+=res[60-left];
        else count+=res[0];
        res[left]++;
    }
    return count;
}
//解法2
public int numPairsDivisibleBy60(int[] time) {
        int[]res=new int[60];
        int count=0;
        for(int i:time){
            res[i%60]++;
        }
        count+=(res[0]*(res[0]-1))/2;
        count+=(res[30]*(res[30]-1))/2;
        int i=1,j=59;
        while (i<j){
            count+=res[i++]*res[j--];
        }
        return count;
    }

滑动窗口中位数

在这里插入图片描述
分析 根据题意,要找出滑动窗口中位数,我们可以令窗口中的部分数据有序,得到中位数,主要难点就是避免频繁的对窗口数据进行排序。
解题 每次窗口滑动一格,就会有一个数据排除,一个数据进入窗口,可以利用二分算法先找到被排除数据,再用快速排序思想快速定位进入窗口的数。

public static double[] medianSlidingWindow(int[] nums, int k) {
		//存储结果
        double res[] = new double[nums.length - k + 1];
        //储存窗口数据
        int win[] = new int[k];
        for (int i = 0; i < k; i++) {
            win[i] = nums[i];
        }
        Arrays.sort(win);
        //第一个中位数
        res[0] = getCenter(win);
        //利用二分算法和快速排序维护滑动窗口
        for (int i = k; i < nums.length; i++) {
            int target = nums[i-k];
            int index = getTargetIndex(win,target,0,k-1);
            while (index > 0 && win[index-1]>nums[i]){
                win[index] = win[index-1];
                index--;
            }
            while (index < k-1 && win[index+1]<nums[i]){
                win[index] = win[index+1];
                index++;
            }
            win[index] = nums[i];
            res[i-k+1] = getCenter(win);
        }
        return res;
    }
    //获取中位数
    public static double getCenter(int[] nums){
        int center = (nums.length-1)/2;
        return nums.length%2==0 ? (nums[center+1]/2.0 + nums[center]/2.0) : nums[center];
    }
	//二分算法找到目标数据下标
    public static int getTargetIndex(int[]win,int target,int i,int j){
        int index = (i+j)/2;
        if (win[index]==target){
            return index;
        }
        if(win[index] > target){
            return getTargetIndex(win,target,i,index-1);
        }else {
            return getTargetIndex(win,target,index+1,j);
        }
    }

盛水最多的容器

在这里插入图片描述
题意已经很明确了,就是找到两个柱子和X轴围成最大的面积。

第一思路肯定是蛮力法穷举所有可能的面积取最大值,思考如何改进。

变量: 设置指针i,j分别从左右端开始扫描,高度分别为l,r,最大面积max为(j-i)*min(l,r)

思考: 如果数组的值(柱高)是随机分布,那么从数组两端开始更可能先获取最大面积,之后过滤不可能的值。

步骤:
在这里插入图片描述

//开始
i=0 ;     j=8;
l=height[i]	;	r=height[j];
max=1*8=8;
//选择柱子低的一方指针,往中间移动,直到height[i]>l
l=height[1]=8
//计算面积是否更大
7*7=49>max;
max=49
//现在是r<l了,右边指针往中间移动,直到j=6
8*6<49
继续移动

index=2,3,4,5,的柱子都比 左边最高的l 或者右边最高的r 短,忽略,从而得到最大值49,省略了不必要的计算。

public int maxArea(int[] height) {
        int i=0,j=height.length-1,l=0,r=0,max=0;
        while(i<j){
            l=height[i];
            r=height[j];
            if(l<=r){
                max=Math.max(max,(j-i)*l);
                while (i<height.length&&height[i]<=l)i++;
            }else {
                max=Math.max(max,(j-i)*r);
                while (j>=0&&height[j]<=r)j--;
            }
        }
        return max;
    }

数组中出现超过一半的数

在这里插入图片描述
按照一打一的思想,如果一个数n大于总数的一半,一对一消掉后,肯定还剩至少一个n。

为了证明n出现次数大于一半,在进行一轮循环计算n的个数。

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        int cur = array[0];
        int times = 1;
        for(int i = 1 ;i < array.length ; i++){
            if(cur == array[i]){
                times++;
            }else{
                times -- ;
            }
            if(times == 0){
                cur = array[i];
                times = 1;
            }
        }
        times = 0;
        for(int i = 0;i<array.length;i++){
            if(array[i] == cur){
                times ++ ;
            }
        }
        if(times>array.length/2){
           return cur; 
        }
        return 0;
    }
}

最长无重复子串(int)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
问题可以转换成如何根据数据快速获取它的下标。
我们可以想到hashmap,和数组。
此处不建议使用HashMap,n的长度已经限定在10万,可以采用数组直接计算物理位置的特性来快速获取目标数下标,而hashmap需要通过hashcode()、链表、红黑树 等路径去寻找目标数,才能获取下标。

public int maxLength (int[] arr) {
        // write code here
        int[] lastPos = new int[100005];
        int len = arr.length;
        int start = 0;
        int res = 0;
        for(int i =0;i<len;i++){
            int now = arr[i];
            start = Math.max(start,lastPos[now]);
            res = Math.max(res,i-start+1);
            lastPos[now] = i+1;
        }
         
        return res;
    }

最大子数组累加和问题

在这里插入图片描述

问题切入点:如果前人留下了正资产,我继承,否则我不继承。

//分别存储 当前最大值`cur`和最大值`max`
 public int maxsumofSubarray (int[] arr) {
        // write code here
        int max = 0,cur = 0;
        for(int i= 0; i<arr.length;i++){
            cur+=arr[i];
            if(cur < 0){
                cur = 0;
            }else if(cur > max){
                max = cur;
            }
        }
        return max;
    }

第K大的数

在这里插入图片描述
快排改造:

public int findKth(int[] a, int n, int K) {
        // write code here
        quickSort(a,K,0,n-1);
        return a[n-K];
    }
    public void quickSort(int[] a,int k , int l ,int r){
        if(l>=r)return;
        int t = a[l];
        int i = l, j = r;
        while(i<j){
            while(i<j && a[j]>=t)j--;
            a[i] = a[j];
            while(i<j && a[i]<=t)i++;
            a[j] = a[i];
        }
        a[i] = t;
        if(i==a.length-k)return;
 
        if(i>a.length-k)
            quickSort(a,k,l,i);
        else
            quickSort(a,k,i+1,r);
    }

最小的K个数

问题切入点:选择排序

    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        int f=0;
        	ArrayList<Integer> s= new ArrayList<Integer>();
        if (k>input.length)
            return s;
        	for (int i =0 ; i<k ; i++) {
        		for (int j=i;j<input.length;j++) {
        			if (input[i]> input[j])
        			{
        				f=input[i];
        				input[i]=input[j];
        				input[j]=f;
        			}
        		}
        		s.add(input[i]);
        	}
        	return s;
    }

排序

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
这里使用快速排序,平均时间复杂度为n log n

public int[] MySort (int[] arr) {
        if(arr.length<=1){
            return arr;
        }
        quick(arr,0,arr.length-1);
        return arr;
    }
    public void quick(int [] args,int n,int m ){
        if(n>=m)return;
        int base=args[n],i=n,j=m;
        while (i<j){
            while (i<j && args[j]>=base){
                j--;
            }
            args[i]=args[j];
            while (i<j && args[i]<=base){
                i++;
            }
            args[j]=args[i];
        }
        args[i]=base;

        quick(args,n,i-1);
        quick(args,i+1,m);
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值