@[TOC]
「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战」。
警句
少而好学,如日出之阳;壮而好学,如日中之光;志而好学,如炳烛之光。——刘向
题目
只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 说明: 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗? 示例 1: 输入: [2,2,1] 输出: 1 示例 2: 输入: [4,1,2,1,2] 输出: 4
我第一反应就是直接使用hash来做,但是题目有说明,这样的话就不好了。能解题但是不是最优解。
于是我又想到了使用一个指针,一开始这个指针获取第一个数字,然后去扫描判断这个数字有没有重复的,如果有,这个指针代表的数字重置,然后再去判断下一个,知道扫描完毕,如果没有重复的那么指针指向的值为没有重复值,但是问题在于这个指针重置的值怎么设置,这个值不能是我们列表里面有的东西,所以,问题在这。
后来我想起来昨天用的位运算,没错直接用异或不就ok了。
class Solution {
public int singleNumber(int[] nums) {
int flag = 0;
for (int num : nums) {
flag ^= num;
}
return flag;
}
}
移动0
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 示例: 输入: [0,1,0,3,12] 输出: [1,3,12,0,0]
这个是简单的双指针问题嘛
class Solution {
public void moveZeroes(int[] nums) {
for(int i=0,j=0;j<nums.length;j++){
if(nums[j] !=0){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
i++;
}
}
}
}
机器人的运动范围【剑指offer13】
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子? 示例 1: 输入:m = 2, n = 3, k = 1 输出:3 示例 2: 输入:m = 3, n = 1, k = 0 输出:1
第一反应,看到机器人先想到的是DP,一个一个方格子走嘛。但是这边不仅仅是找那个路的走法,同时我们要找到 那个 x +y 坐标<=k 的,所以这里涉及到搜索问题。并且这里的x.y是坐标拆分之后相加 就像那个例子。
但是关于这个题目的话,问的是能够到达多少个格子,没说要走什么路,所以这里又变了,变成了一个迷宫问题,从0,0出发,上下左右,之和大于k的就是墙,并且我们需要的是所有的可以走的路,所以我们这边就可以使用BFS,当然也可以用DFS.但是这里要更简单,因为只是一个中档题,直接暴力搜索就可以,不需要像先前的问题一样
class Solution {
public int movingCount(int m, int n, int k) {
if (k == 0) {
return 1;
}
boolean[][] juzheng = new boolean[m][n];
int ans = 1;
juzheng[0][0] = true;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if ((i == 0 && j == 0) || getsplitsum(i) + getsplitsum(j) > k) {
continue;
}
// 边界判断
if (i - 1 >= 0) {
juzheng[i][j] |= juzheng[i - 1][j];
}
if (j - 1 >= 0) {
juzheng[i][j] |= juzheng[i][j - 1];
}
ans += juzheng[i][j] ? 1 : 0;
}
}
return ans;
}
public int getsplitsum(int x) {
int res = 0;
while (x != 0) {
res += x % 10;
x /= 10;
}
return res;
}
}
一维数组的动态求和
给你一个数组 nums 。数组「动态和」的计算公式为:runningSum[i] = sum(nums[0]…nums[i]) 。
请返回 nums 的动态和。
示例 1:
输入:nums = [1,2,3,4] 输出:[1,3,6,10] 解释:动态和计算过程为 [1, 1+2, 1+2+3, 1+2+3+4] 。
来换换脑子,来个弱智题。
class Solution {
public int[] runningSum(int[] nums) {
for(int i=1;i<nums.length;i++){
nums[i] = nums[i]+nums[i-1];
}
return nums;
}
}
剑指offer 63 滑动窗口的最大值
给定一个数组 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]
这个乍一看,好像很简单,很快我们就想到了思路,于是我们这样做
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
List<Integer> flag = new ArrayList<>();
for(int i=0;i<nums.length;i++){
if(i+k-1>=nums.length){
break;
}else {
flag.add(max(nums,i,i+k-1));
}
}
return flag.stream().mapToInt(Integer::intValue).toArray();
}
public int max(int[] nums,int left,int right){
int max = nums[left];
for(int i=left;i<=right;i++){
if(max<nums[i]){
max = nums[i];
}
}
return max;
}
}
于是我们取得了这样的战绩
所以我们这边其实还涉及到优化的问题。
优化
前面是我们从左到右扫描了一遍,同时每次扫描一遍我们都需要求最大值,所以这个复杂度直接飙升,就是个暴力解法。但是我们其实发现我们在窗口滑动的过程当中发现,一个数字可以在A窗口也可以在B窗口,如果我们假设有个数字3是A窗口的最大值,并且3在B窗口,由于是从左到右滑动,所以我们其实只需要比较3后面的数字大不大(在B窗口当中)也就是说,如果在当前添加的元素当中的值,比上一个滑动窗口的最大值还小,那就不需要再比较了(两个窗口的交集中)此外这里优化了一下那个返回值的长度,先前是直接使用arrary然后转化消耗贼大。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length==0)
{
return new int[0];
}
int[] res=new int[nums.length-k+1];
res[0]=Integer.MIN_VALUE;
for(int j=0;j<k;j++){
res[0]=Math.max(res[0],nums[j]);
}
for(int i=1;i<res.length;i++){
if(nums[i+k-1]>=res[i-1])
res[i]=nums[i+k-1];
else
{
if(nums[i-1]!=res[i-1]){
res[i]=res[i-1];
}
else{
res[i]=Integer.MIN_VALUE;
for(int j=i;j<k+i;j++){
res[i]=Math.max(res[i],nums[j]);
}
}
}
}
return res;
}
}