剑指offer之数组(2)

这篇博客探讨了几个经典的算法问题,包括最小的k个数、连续子数组的最大和、栈的压入与弹出操作、数组中出现次数超过一半的数字以及数组中数字出现的次数。通过排序、动态规划、快速选择和摩尔投票法等方法解决这些挑战,提供了解题思路和代码实现。
摘要由CSDN通过智能技术生成

40. 最小的k个数 难度 :简单

思路一:先将数组排序,再选前k个数输出即可。时间复杂度O(N*logN)

思路二:题目只是要求将前k小的数字输出,并没有要求输出的顺序,因此我们可以基于快速排序将数组划分,只要将数组划分为最小的k个数字和其他数字即可。我们采用第二种方法。时间复杂度O(N)。

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if( k >= arr.length)    return arr;
        return quicksort(arr,0,arr.length-1,k);
    }
    public int[] quicksort(int[] arr,int l,int r,int k){
       int mid = arr[(l+r)/2];
       int i = l-1,j = r+1;
       while(i < j){
           do i++;while(arr[i]<mid);
           do j--;while(arr[j]>mid);
           if(i < j){
               int tmp = arr[i];
               arr[i] = arr[j];
               arr[j] = tmp;
           }
       }
           if(i > k)    return quicksort(arr,l,j,k);
           if(i < k)    return quicksort(arr,j+1,r,k);
           return Arrays.copyOf(arr,k);
    }
}

42. 连续子数组的最大和 难度: 简单

本题是典型的动态规划题目,通过遍历一遍数组就可以找到连续子数组的最大和。nums[i] += Math.max(0,nums[i-1])就是将数组的每一位更新为当前遍历到的子数组的最大和,然后res记录总体最大和,遍历结束就得出结果。

class Solution {
    public int maxSubArray(int[] nums) {
      int res = nums[0];
      for(int i = 1; i < nums.length; ++i){
          nums[i] += Math.max(0,nums[i-1]);
          res = Math.max(res,nums[i]);
      }
      return res;
    }
}

31. 栈的压入、弹出操作 难度: 中等

本题的重点是要加入一个辅助栈stack来模拟完成栈的压入操作,每当压入一个元素都要与出栈数组poped的出栈元素比较,如果相等则stack出栈,不相等则继续入栈。stack每出栈一个元素,i下标后移一位,表示出栈。最后若stack为空,则返回true,否则返回false。

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        Stack<Integer> stack = new Stack<>();
        int i = 0,j=0;
        while(j < pushed.length){
            stack.push(pushed[j++]);
            while(!stack.isEmpty() && stack.peek() == popped[i]){
                stack.pop();
                i++;
            }
        }
        return stack.isEmpty();
    }
}

39. 数组中出现次数超过一半的数字 难度: 简单

本题用摩尔投票法,记超过一半的数字为“众数” res,票数为votes。

遍历一遍数组,如果票数为0,那么令res为当前遍历到的数字;票数不为0时,当前数字和res相等,则票数+1,不等则票数-1。最终得到的数字即为众数。

class Solution {
    public int majorityElement(int[] nums) {
        int res = 0, votes = 0;
        for(int n:nums){
            if(votes == 0)  res = n;
            votes += (res == n?1:-1);
        }
        return res;
    }
}

56-I.数组中数字出现的次数 难度:中等

本题的难点在于有两个数字只出现一次,我们都需要找出来。

首先我们将数组中的各个元素相互异或,结果就是这两个数字的异或,我们记为n。将此结果与数字1与,寻找两个数字二进制中不同的位m,接下来通过m将数组分为两组,每组只存在一个只出现一次的数字,分别找出来即可。

class Solution {
    public int[] singleNumbers(int[] nums) {
        int x = 0, y = 0;
        int n = 0, m = 1;
        for(int num:nums){
            n^=num;
        }
        while((m&n) == 0){
            m<<=1;
        }
        for(int num:nums){
            if((num&m) == 0)    x^=num;
            else    y^=num;
        }
        return new int[]{x,y};
    }
}

56-II.数组中数字出现的次数 难度:中等

题目中除了一个数字出现一次外,其他的数字都出现了三次,那么我们求得这个数组中的数字的每一位1的个数,对3求余就得到了我们想要数字的二进制表示,转化为int类型的数即可。

class Solution {
    public int singleNumber(int[] nums) {
        int[] arr = new int[32];
        for(int n : nums){
            int x = 1;
            for(int i = 0;i<32;++i){
                arr[i] += n&1;
                n>>>=1;
            }
        }
        int res = 0,m = 3;
        for(int i = 0;i<32;++i){
            res<<=1;
            res |= arr[31-i]%m;
        }
        return res;
    }
}

57. 和为s的两个数字 难度:简单

本题用双指针就可以求得结果,很简单。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int len = nums.length;
        if(len < 2) return new int[0];
        int[] res = new int[2];
        int l = 0, r = len-1;
        while(l < r){
            if(nums[l] + nums[r] < target)  l++;
            else if(nums[l] + nums[r] > target) r--;
            else{
                res[0] = nums[l];
                res[1] = nums[r];
                return res;
            }
        }
        return res;
    }
}

代码参考LeetCode作者:Krahets

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值