《剑指offer第二版》十

1. 数组中数字出现的次数

(1)题目描述

在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

示例 1:
输入:nums = [3,4,3,3]
输出:4

(2)题目分析

本题优先想到的解法是哈希表,通过统计每个数字的次数,然后找到次数为1的那个数即可,但是哈希表需要用到额外的空间。由于除了出现一次的数字外,其他数字都出现了3次,因此这些数字的每一位之和肯定是3的倍数,这里通过对二进制的每一位进行相加,最后将每一位不是3的倍数的进行拼接,即可得到出现一次的数字。

(3)代码

package swordOffer.day10;

import java.util.HashMap;
import java.util.Map;

/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-04-11 11:29
 * @desc
 */
public class t51 {
    /**
     * 二进制相加
     *
     * @param nums
     * @return
     */
    public static int singleNumber(int[] nums) {
        int ans = 0;
        //int 32位
        for (int i = 0; i < 32; i++) {
            int count = 0;
            for (int num : nums) {
                if ((num & (1 << i)) != 0) {
                    count++;
                }
            }
            if (count % 3 != 0) {
                ans += 1 << i;
            }
        }
        return ans;
    }

    /**
     * 哈希表
     *
     * @param nums
     * @return
     */
    public static int getNum(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
        }
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            if (entry.getValue() == 1) {
                return entry.getKey();
            }
        }
        return 0;
    }

    public static void main(String[] args) {
        int[] nums = {3, 4, 3, 3};
        System.out.println(getNum(nums));
    }
}

2. 队列的最大值

(1)题目描述

请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
示例 1:
输入:
[“MaxQueue”,“push_back”,“push_back”,“max_value”,“pop_front”,“max_value”]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]

(2)题目分析

要得到队列的最大值,本题可通过维护一个单调的双端队列来实现,每当有数字入队时,与双端队列的尾数进行比较,如果大于尾数,则将双端队列中小于该数的数字出队,如果大于等于尾数,则将该数入队尾。每当有数字出队时,与双端队列的队头进行比较,如果等于队头,则将双端队列的队头出队。则双端队列的队头即为队列的最大值。

(3)代码

package swordOffer.day10;

import java.util.LinkedList;

/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-04-10 14:00
 * @desc
 */
public class t48 {
    LinkedList<Integer> queue;
    LinkedList<Integer> dequeue;

    public t48() {
        this.queue = new LinkedList<>();
        this.dequeue = new LinkedList<>();
    }

    public int max_value() {
        return dequeue.isEmpty() ? -1 : dequeue.peek();
    }

    public void push_back(int value) {
        queue.offer(value);
        while (!dequeue.isEmpty() && value > dequeue.peekLast()) {
            dequeue.pollLast();
        }
        dequeue.offerLast(value);
    }

    public int pop_front() {
        if (queue.isEmpty()) {
            return -1;
        }
        int value = queue.poll();
        if (!dequeue.isEmpty() && value == dequeue.peek()) {
            dequeue.pollFirst();
        }
        return value;
    }

    public static void main(String[] args) {
        t48 queue = new t48();
        queue.push_back(1);
        System.out.println(queue.max_value());
        queue.push_back(2);
        System.out.println(queue.max_value());
        queue.pop_front();
        System.out.println(queue.max_value());
    }
}

3. 滑动窗口的最大值

(1)题目描述

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:

  滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

(2)题目分析

要计算滑动窗口的最大值,刚好与上题的队列的最大值相似,只需要维护一个辅助的单调双端队列,弄明白了上题的解法,本题就很容易求解。

(3)代码

package swordOffer.day10;

import java.util.LinkedList;

/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-04-11 10:27
 * @desc
 */
public class t50 {
    LinkedList<Integer> dequeue = new LinkedList<>();
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0 || nums.length < k) {
            return new int[0];
        }
        int[] res = new int[nums.length - k + 1];
        for (int i = 0; i < k; i++) {
            offer_back(nums[i]);
        }
        res[0] = max_value();
        for (int i = k; i < nums.length; i++) {
            poll_front(nums[i - k]);
            offer_back(nums[i]);
            res[i - k + 1] = max_value();
        }
        return res;
    }

    public int max_value() {
        return dequeue.isEmpty() ? -1 : dequeue.peek();
    }

    public void offer_back(int value) {

        while (!dequeue.isEmpty() && value > dequeue.peekLast()) {
            dequeue.pollLast();
        }
        dequeue.offerLast(value);
    }

    public void poll_front(int value) {

        if (!dequeue.isEmpty() && value == dequeue.peek()) {
            dequeue.pollFirst();
        }
    }

    public static void main(String[] args) {
        int[] nums = {-7, -8, 7, 5, 7, 1, 6, 0};
        t50 tt = new t50();
        int[] res = tt.maxSlidingWindow(nums, 4);
        for (int i : res) {
            System.out.print(i + " ");
        }
    }
}

4. 和为s的连续正数序列

(1)题目描述

输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

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

(2)题目分析

由于需要求解和为s的连续正数序列,因此本题可以采用动态的滑动窗口来实现,通过设置左右指针,每当窗口内的数字之和小于target时,则右指针右移一位,当窗口内的数字之和大于target时,则左指针右移一位。如果相等,则左右指针均右移一位。最后返回所有和为s的序列即可。

(3)代码

package swordOffer.day10;

import java.util.ArrayList;
import java.util.List;

/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-04-10 20:18
 * @desc
 */
public class t49 {
    public static int[][] findContinuousSequence(int target) {
        int i = 1; // 滑动窗口的左边界
        int j = 1; // 滑动窗口的右边界
        int sum = 0; // 滑动窗口中数字的和
        List<int[]> res = new ArrayList<>();

        while (i <= target / 2) {
            if (sum < target) {
                // 右边界向右移动
                sum += j;
                j++;
            } else if (sum > target) {
                // 左边界向右移动
                sum -= i;
                

                i++;
            } else {
                // 记录结果
                int[] arr = new int[j - i];
                for (int k = i; k < j; k++) {
                    arr[k - i] = k;
                }
                res.add(arr);
                // 左边界向右移动
                sum -= i;
                i++;
            }
        }
        return res.toArray(new int[res.size()][]);
    }

    public static void main(String[] args) {

        int[][] res = findContinuousSequence(15);
        for (int[] nums : res) {
            for (int i : nums) {
                System.out.print(i + " ");
            }
            System.out.println();
        }
    }
}

5. 求1到n的数字之和

(1)题目描述

求 1+2+…+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

示例 1:
输入: n = 3
输出: 6

(2)题目分析

由于题目的限制,本题可通过递归来实现,但是由于本题不允许使用判断条件,因此这里可以利用&&的短路机制来跳出递归。

(3)代码

package swordOffer.day10;

/**
 * @author chengzhengda
 * @version 1.0
 * @date 2020-04-10 13:34
 * @desc
 */
public class t47 {
    public static int sumNums(int n) {
        int sum = n;
        boolean b = (n > 0) && ((sum += sumNums(n - 1)) > 0);
        return sum;
    }

    public static void main(String[] args) {
        System.out.println(sumNums(9));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值