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));
}
}