剑指offer—其他

题目代号: 剑指 Offer 15. 二进制中1的个数

题目描述:

请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。其中负数用补码表示。

例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。

测试用例:

示例 1:

输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 ‘1’。
示例 2:

输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 ‘1’。
示例 3:

输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 ‘1’。

我的分析:

按位来与1进行“与”运算

代码:

package 剑指offer;

import java.util.Scanner;

/**
 * @program: day01
 * @description:
 * @author: Mr.Wang
 * @create: 2021-04-12 20:52
 **/
public class offer15 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()){
            int n = sc.nextInt();
            System.out.println(hammingWeight(n));
        }
    }

    public static int hammingWeight(int n) {
        int flag = 1;
        int count = 0;
        while (flag != 0){
            if( (n & flag) != 0){//说明这一位是可以的
                count++;
            }
            flag = flag << 1;//不管这一位可不可以,都要左移看看下一位的
        }
        return count;
    }
}

题目代号: 剑指 Offer 40. 最小的k个数

题目描述:

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

测试用例:

示例 1:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:

输入:arr = [0,1,2,1], k = 1
输出:[0]

我的分析:

方法一:用排序,然后遍历

方法二:大顶堆(java自带小顶堆,改变一下里面的方法)

代码:

//方法一:
class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        Arrays.sort(arr);
        int[] res = new int[k];
        for(int i = 0; i < k;i++){
            res[i] = arr[i];
        }
        return res;
    }
}
//方法二:

    // queue.peek();拿出第一个看一看
    // queue.offer(a);把元素a放进队尾
    // queue.poll();把第一个移除
    public int[] getLeastNumbers(int[] arr, int k) {
        if(arr.length == 0 || k == 0){
            return new int[0];
        }
        Queue<Integer> queue = new PriorityQueue<>((a,b) -> (b-a));
        for(int num : arr){
            if(queue.size() < k){//把前k个全放进去,并且形成一个大顶堆
                queue.offer(num);
            }else if(num < queue.peek()){
                queue.poll();
                queue.offer(num);
            }
        }
        
        int[] res = new int[k];
        int i = 0;
        for (int num : queue){
            res[i++] = num;
        }
        return res;

    }

题目代号: 剑指 Offer 43. 1~n 整数中 1 出现的次数

题目描述:
输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。

例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。

测试用例:

示例 1:

输入:n = 12
输出:5
示例 2:

输入:n = 13
输出:6

我的分析:

这种题只能总结一下规律,让我们来探究一下

最高位是不是1,这点很关键,那我们就分两种情况吧

情况1:n = 1324, high = 1,front = 1000,last = 324
这样就能把数分为
1~999
1000~1324

》1~999这个范围的1的个数是f(front-1)
》1000~1324这个范围1的个数又能分为两个部分
—>千分位是1的个数:234 +1(last+1),注意这里只看千分位,不看其他位
—>其他位是1 的个数:即324中出现1的个数f(last)

所以加起来就是f(front-1) + last +1 + f(last)

情况2:n = 3234,high = 3,front = 1000,last = 234
这样就要把数字范围分为
1~999
1000~1999
2000~2999
3000~3234

在1~999中,个数为f(front-1)
在1000~1999中分为两种情况
——千分位为1的个数:front,注意这里只看千分位
——其他位为1的个数:即是999中出现1的个数,f(front-1)
2000~2999中,个数是f(front-1)
3000~3234中,个数是f(last)

所以加起来的个数是 high * f(front - 1) + f(last) + front

代码:

package 剑指offer;

import java.util.Scanner;

/**
 * @program: day01
 * @description:
 * @author: Mr.Wang
 * @create: 2021-04-13 13:51
 **/
public class offer43 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()){
            int n = sc.nextInt();
            System.out.println(countDigitOne(n));
        }

    }
    public static int countDigitOne(int n){
        if(n <= 0){
            return 0;
        }
        String s = String.valueOf(n);//将这个数先转化成字符串
        int high = s.charAt(0) - '0';//将这个单个字符转化成整数类型
        int front = (int)Math.pow(10,s.length()-1);//在计算最大的基数是1000,还是10000,还是100
        int last = n - high * front;
        if(high == 1){//主要思路就是去除掉最高位,这样就一位一位的减少,递归速度也会很快的
            return countDigitOne(front-1) + countDigitOne(last) + last + 1;
        }else {
            return high * countDigitOne(front - 1) + countDigitOne(last) + front;
        }
    }
}

题目代号: 剑指 Offer 49. 丑数

题目描述:

我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。

测试用例:

输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。

我的分析:

我对2 3 5 分别形成3个下标,依次去×相应的倍数

每次都把最小值放进去

谁放了,就相应的下标自加1,这样就能保证放进去的每一个值都是2 3 5 的倍数

代码:

public int nthUglyNumber(int n){
        int[] res = new int[n];
        res[0] = 1;
        int a = 0,b = 0,c = 0;//分别是×2 ×3 ×5的下标
        for(int i = 1;i < n;i++){//一个一个存呗,就取res[n-1]就行了
            int h2 = res[a] * 2;int h3 = res[b] * 3;int h5 = res[c] * 5;//这样做的好处是每一个数都是2或3或5的倍数
            res[i] = Math.min(h5,Math.min(h2,h3));
            if(res[i] == h2) a++;
            if(res[i] == h3) b++;
            if(res[i] == h5) c++;
        }
        return res[n-1];
    }

题目代号: 剑指 Offer 57. 和为s的两个数字

题目描述:

输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

测试用例:

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]
示例 2:

输入:nums = [10,26,30,31,47,60], target = 40
输出:[10,30] 或者 [30,10]

我的分析:

左右两个指针嘛,相加看看和目标target的比较
如果相加很大,那就调小点
如果相加很小,那就调大点
如果相加相等,那就不调了

代码:

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

题目代号: 剑指 Offer 57 - II. 和为s的连续正数序列

题目描述:

输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。

序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

测试用例:

示例 1:

输入:target = 9
输出:[[2,3,4],[4,5]]
示例 2:

输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]

我的分析:

是这样的,我们是在找一个滑动窗口,两个指针都从数的左侧来开始,然后就形成一个滑动窗口,看这个窗口里面的和与目标值进行比较

sum > target low++
sum < target high++
sum == target 把窗口里面的值都放进新建的数组中

最后一步有个链表转数组的过程哈
因为只能先把数组加入到链表中,再把链表转数组

代码:

public int[][] findContinuousSequence(int target) {
        int low = 1,high = 2;
        int sum = (low + high)*(high-low+1)/2;
        List<int[]> RES = new ArrayList<int[]>();
        while (low < high && low < (sum+1)/2){
            sum = (low + high)*(high-low+1)/2;

            if(sum < target){
                high++;
            }else if(sum > target){
                low++;
            }else {
                int[] res = new int[high - low +1];
                for(int i = low;i <= high;i++){
                    res[i-low] = i;
                }
                RES.add(res);
                low++;
            }
        }
        return RES.toArray(new int[RES.size()][]);//得新创建一个二维数组,列表转化成新数组
    }

题目代号: 剑指 Offer 61. 扑克牌中的顺子

题目描述:
从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。

数组长度为 5
数组的数取值为 [0, 13] .

测试用例:
示例 1:

输入: [1,2,3,4,5]
输出: True

示例 2:

输入: [0,0,1,2,5]
输出: True

我的分析:

首先对这个数组排序
第二步统计有几个0
第三步把相等的返回false

当遍历结束的时候,最骚的操作来了,就看最大值 - 最小值 < 5,只要这个满足,那就肯定能形成顺子了,好好想想

代码:

class Solution {
   public boolean isStraight(int[] nums) {
        Arrays.sort(nums);
        int idx0 = 0;//统计0的个数
        for(int i = 0;i < 4;i++){
            if(nums[i] == 0) idx0++;//先把0搞完
            else if(nums[i] == nums[i+1]) return false;//再把相等的避开

        }
        return nums[4] - nums[idx0] < 5;
        //既然里面0的个数也统计了,相等的也排除了,那肯定最大值 - 最小值 < 5即可形成顺子(里面的数很容易填充的)


    }
}

题目代号: 剑指 Offer 62. 圆圈中最后剩下的数字

题目描述:

0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

测试用例:

示例 1:

输入: n = 5, m = 3
输出: 3
示例 2:

输入: n = 10, m = 17
输出: 2

我的分析:

其实我们就只管下标即可,拿0 1 2 3 4为例吧m = 3
在这里插入图片描述

代码:

class Solution {
    public int lastRemaining(int n, int m) {
        int ans = 0;
        // 最后一轮剩下2个数,所以从2开始反推
        // 就是在找最后剩下的那个数在每一次删除其他数的时候的下标
        for (int i = 2; i <= n; i++) {
            ans = (ans + m) % i;
        }
        return ans;
    }
}

题目代号: 剑指 Offer 41. 数据流中的中位数

题目描述:

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。

测试用例:

示例 1:

输入:
[“MedianFinder”,“addNum”,“addNum”,“findMedian”,“addNum”,“findMedian”]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]
示例 2:

输入:
[“MedianFinder”,“addNum”,“findMedian”,“addNum”,“findMedian”]
[[],[2],[],[3],[]]
输出:[null,null,2.00000,null,2.50000]

我的分析:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这个图对应的是
大顶堆 arr : 50 45 40 20 25 35 30 10 15
小顶堆 arr : 10 20 15 25 50 30 40 35 45

因此我们这道题就一个大顶堆(里面存放小数,只满足堆顶最大)和一个小顶堆(里面存放大数,只满足堆顶最小)

代码:

private Queue<Integer> min;//小顶堆,里面都存放的是大数(升序)
    private Queue<Integer> max;//大顶堆,里面都存放的是小数
    public MedianFinder() {
        min = new PriorityQueue<>();
        max = new PriorityQueue<>((x, y) -> (y - x));
    }
    /*
    咱们说一下存放的原则,当两边的个数相等的时候,就存放在小顶堆(但为了防止这个数太小了,要让它进大顶堆里面,替换出最大数来)
                        当两边个数不等的时候,就存放在大顶堆(但为了防止这个数太大了,要让它进小顶堆里面,替换出最小数来)
     */
    public void addNum(int num) {  //从数据流中添加一个整数到数据结构中
        if(max.size() == min.size()){
            max.offer(num);
            min.offer(max.poll());

        }else {
            min.offer(num);
            max.offer(min.poll());
        }
    }

    public double findMedian() {  //返回目前所有元素的中位数
        return min.size() == max.size() ? (min.peek()+max.peek())/2.0 : min.peek();
    }
		//这两种情况是功能一样的
		max = new PriorityQueue<>((x, y) -> (y - x));

        max = new PriorityQueue<>(new Comparator<Integer>(){
            public int compare(Integer o1,Integer o2){
                return o2-o1;
            }
        });

题目代号: 121. 买卖股票的最佳时机

题目描述:

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

测试用例:

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

我的分析:

买的时候,就找最低点就好了
卖的时候只要当前这个值比买的时候的值大的很多,不就可以了嘛

代码:

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        int buy1 = prices[0], sell1 = 0;
     
        for (int i = 1; i < n; i++) {
             buy1 = Math.min(buy1, prices[i]);
             sell1 = Math.max(sell1, prices[i] - buy1);    
        }
         return sell1;
    }
}

题目代号: 123. 买卖股票的最佳时机 III

题目描述:

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

测试用例:

示例 1:

输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。

示例 2:

输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这个情况下, 没有交易完成, 所以最大利润为 0。

我的分析:

主要看第二次买的时候,要以第一次卖的时刻赚的钱为基准,先降下去,然后再升上来

代码:

class Solution {
    public static int maxProfit(int[] prices){
        int buy1 = prices[0],sell1 = 0;//buy1就是数组里面的最小值,sell1是当前遍历值与买的值的差值
        int buy2 = prices[0],sell2 = 0;
        for(int i = 1;i < prices.length;i++){
            buy1 = Math.min(buy1,prices[i]);
            sell1 = Math.max(sell1,prices[i] - buy1);
            buy2 = Math.min(buy2,prices[i] - sell1);
            //这个地方我给你好好说说,好家伙,我终于明白了,
            // 既然你第一次的买卖已经结束了,那就知道自己挣了多少钱了,
            // 那我第二次买的时候以第一次的基础降下去,但我第二次卖的时候又升上去了,所以还是得到了总的
            sell2 = Math.max(sell2,prices[i] - buy2);
        }
        return sell2;
    }
}

题目代号: 剑指 Offer 44. 数字序列中某一位的数字

题目描述:

数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。

请写一个函数,求任意第n位对应的数字。

测试用例:

示例 1:

输入:n = 3
输出:3
示例 2:

输入:n = 11
输出:0

我的分析:

在这里插入图片描述

代码:

int digit = 1;//位数,两位数,三位数,四位数。。。
        long start = 1;//每个digit位数的起始数字 1 10 100 1000 。。。
        long count = 9;
        while (n > count) { // 1.确定了n所在数字的位数,记为digit
            n -= count;
            digit += 1;
            start *= 10;
            count = digit * start * 9;
        }
        long num = start + (n - 1) / digit; // 2.确定了n所在的数字
        return Long.toString(num).charAt((n - 1) % digit) - '0'; // 3.(n - 1) % digit知道了num中的哪一位(先转为字符串,方便提取数字,再转为数字)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值