Day1-977有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
解法一:直接.sort
//这个解法没啥可说的
class Solution {
public int[] sortedSquares(int[] nums) {
int[] ans=new int[nums.length];
for(int i=0;i<nums.length;i++){
ans[i]=nums[i]*nums[i];
}
Arrays.sort(ans);
return ans;
}
}
解法二:利用原数组是有序,平方后正数保持非递减,负数部分平方后变为非递增。然后把两个部分看成两个数组,进行归并排序。
有一点值得注意:这是两个排序顺序不同的有序数组,所以为了保证归并成功,一个要从头开始遍历,另一个要从尾向前遍历。
class Solution {
public int[] sortedSquares(int[] nums) {
//1.找出正负的分界点
int n = nums.length;
int negative = -1;
for (int i = 0; i < n; ++i) {
if (nums[i] < 0) {
negative = i;
} else {
break;
}
}
int[] ans = new int[n];
int index = 0, i = negative, j = negative + 1;
while (i >= 0 || j < n) {
if (i < 0) {
ans[index] = nums[j] * nums[j];
++j;
} else if (j == n) {
ans[index] = nums[i] * nums[i];
--i;
} else if (nums[i] * nums[i] < nums[j] * nums[j]) {
ans[index] = nums[i] * nums[i];
--i;
} else {
ans[index] = nums[j] * nums[j];
++j;
}
++index;
}
return ans;
}
}
Day2-轮转数组
2.1
给你一个数组,将数组中的元素向右轮转 k
个位置,其中 k
是非负数。
解法一:暴力解法,依次遍历原数组,将原数组下标为i的元素放入新数组下标为(i+k)%n的元素内。
class Solution {
public void rotate(int[] nums, int k) {
int n=nums.length;
int[] co=new int[n];
for(int i=0;i<n;i++){
co[(i+k)%n]=nums[i];
}
for(int i=0;i<n;i++){
nums[i]=co[i];
}
}
}
解法二:运用一个常识:可以通过先将数组全部反转(即reverse操作),然后将头部k个元素内部继续反转,后n-k元素内部反转。由此可以实现将数组元素向右轮转k个位置的效果。
class Solution {
public void rotate(int[] nums, int k) {
k %= nums.length;
reverse(nums, 0, nums.length - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.length - 1);
}
public void reverse(int[] nums, int start, int end) {
while (start < end) {
int temp = nums[start];
nums[start] = nums[end];
nums[end] = temp;
start += 1;
end -= 1;
}
}
}
2.2
删除排序数组中的重复项
算法思想:快慢指针。用slow指针维护一个“新的空的”数组,用fast指针遍历原数组。只有当原数组中slow和fast所指向元素值不同时,才会将fast指向的元素的值赋给slow维护的新数组。
class Solution {
public int removeDuplicates(int[] nums) {
int slow=0;
for(int fast=1;fast<nums.length;fast++){
if(nums[slow]!=nums[fast]){
nums[++slow]=nums[fast];
}
}
return ++slow;
}
}
day3-买卖股票的最佳时机 II
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
解法一:动态规划
本人第一次解动态规划的题目,这道题的解法给我的感觉有点类似于递归和微分方程解应用题揉在一起的感觉。会了实际上没啥可讲的,直接cv。
用dp[i][0]表示第i天手里没有股票这个状态下已有的最大利润;
dp[i][1]表示第i天手里有股票这个状态下已有的最大利润;
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int[][] dp = new int[n][2];
dp[0][0] = 0;
dp[0][1] = -prices[0];
for (int i = 1; i < n; ++i) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
}
return dp[n - 1][0];
}
}
解法二:贪心算法
简言之就是追涨杀跌(#doge),反正未来走势都知道了,明天比今天高就今天买明天出,只要我不亏,永远是最赚的。。。。简单粗暴。。。
class Solution {
public int maxProfit(int[] prices) {
int sum=0;
for(int i=1;i<prices.length;i++){
if(prices[i-1]<prices[i]){
sum+=prices[i]-prices[i-1];
}
}
return sum;
}
}
day4
4.1-只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
解法一:先给数组排序,排序后遍历整个数组。
class Solution {
public int singleNumber(int[] nums) {
Arrays.sort(nums);
for(int i=0;i<nums.length-1;i++){
if(nums[i+1]==nums[i]){
i++;
}else{
return nums[i];
}
}
//特殊情况,当唯一一个的数字在最后一个时。
return nums[nums.length-1];
}
}
解法二:运用异或运算的性质
class Solution {
public int singleNumber(int[] nums) {
int sum=0;
for(int i=0;i<nums.length;i++){
sum^=nums[i];
}
return sum;
}
}
4.2-两个数组的交集 II
给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。
解法一:利用哈希表不重复的特性,建立一个HashMap。
key为数组长度小的数组的元素值
vaule为对应key在数组中出现的次数
建立好这个map后,遍历另一个数组,每访问一个元素便查看其在map中vaule值,大于0说明该元素是重复的元素便将其记录在一个新的数组里面,然后v减一,再然后检查v值是否等于0,等于0则要将其从map中去除,不等与0需要更新一下value的值(v减一了)。
===> <===
getOrDefault(num, 0):查询map中key为num的v,map中有num则返回其对应的v,没有则返回0.
代码中getOrDefault(num, 0)+1配合map.put(num,count)完成了对数组长度小的数组中元素各个元素的计数,且封装到map中。很有借鉴意义
===> <===
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
if (nums1.length > nums2.length) {
return intersect(nums2, nums1);
}
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int num : nums1) {
int count = map.getOrDefault(num, 0) + 1;
map.put(num, count);
}
int[] intersection = new int[nums1.length];
int index = 0;
for (int num : nums2) {
int count = map.getOrDefault(num, 0);
if (count > 0) {
intersection[index++] = num;
count--;
if (count > 0) {
map.put(num, count);
} else {
map.remove(num);
}
}
}
return Arrays.copyOfRange(intersection, 0, index);
}
}
day5
T_66 加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
解法一:很简单,借鉴机组进位的知识,设一个flag标记即可
纯自己写。。。
public class Solution {
public int[] plusOne(int[] digits) {
int n = digits.length - 1;
boolean flag = true;
for (int i = n; i >= 0; i--) {
if (flag) {
digits[i]++;
if (digits[i] == 10) {
flag = true;
digits[i]=0;
} else {
flag = false;
}
} else {
break;
}
}
if (digits[0] == 0) {
int[] result = new int[digits.length + 1];
for (int i = 0; i < result.length; i++) {
if (i == 0) {
result[i] = 1;
} else {
result[i] = 0;
}
}
return result;
}
return digits;
}
}
day6
T_2210 统计数组中峰和谷的数量
给你一个下标从 0 开始的整数数组 nums 。如果两侧距 i 最近的不相等邻居的值均小于 nums[i] ,则下标 i 是 nums 中,某个峰的一部分。类似地,如果两侧距 i 最近的不相等邻居的值均大于 nums[i] ,则下标 i 是 nums 中某个谷的一部分。对于相邻下标 i 和 j ,如果 nums[i] == nums[j] , 则认为这两下标属于 同一个 峰或谷。
注意,要使某个下标所做峰或谷的一部分,那么它左右两侧必须 都 存在不相等邻居。
返回 nums 中峰和谷的数量。
解法一:暴力解就完事了
class Solution {
public int countHillValley(int[] nums) {
int count=0;
for (int i = 1; i < nums.length-1; i++) {
int[] closeDiff = getCloseDiff(nums, i);
if(closeDiff[0]==-1 ||closeDiff[1]==-1){
continue;
}
if((nums[i]<nums[closeDiff[0]] && nums[i]<nums[closeDiff[1]])||(nums[i]>nums[closeDiff[0]] && nums[i]>nums[closeDiff[1]])){
count++;
}
if(closeDiff[1]!=(i+1)){
i=closeDiff[1]-1;
}
}
return count;
}
public static int[] getCloseDiff(int[] nums, int index) {
int key = nums[index];
int[] result = {-1, -1};
for (int i = index - 1; i >= 0; i--) {
if (key != nums[i]) {
result[0] = i;
break;
}
}
for (int i = index + 1; i < nums.length; i++) {
if (key != nums[i]) {
result[1] = i;
break;
}
}
return result;
}
}
T_1 两数之和
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
解法一:我就不写都会想到的两层for循环的暴力解法了,那种是“往后看”,官方给的题解是“往前看”。
算法思想:1.创建一个哈希表,键值对为:元素值-下标
2.遍历整个数组,遍历当前元素时先检查,哈希表中是否有key为target-nums[i]的一对。有则直接返回当前i和表中key对应的v。
3.没有则把当前元素加入哈希表
--->tips:按照这种思路当遇到target=1+1/2+2/3+3这种情况,并不会和哈希表k唯一冲突。因为找到解会直接return。
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer,Integer> map=new HashMap<Integer,Integer>();
for(int i=0;i<nums.length;i++){
if(map.containsKey(target-nums[i])){
return new int[]{map.get(target-nums[i]),i};
}
map.put(nums[i],i);
}
return new int[0];
}
}
day7
这天其实写题目的,写的全是dp算法,写了3道,妈的,然后没有保存,直接就没了。心态炸裂,以后补更。
day8
T_48 旋转图像
解法一:暴力解法
算法思想:没啥好说的找规律,找出等式a[j][n-1-j]=a[i][j]即可
class Solution {
public void rotate(int[][] matrix) {
int col=matrix.length;
int row=col;
int[][] asit=new int[row][col];
for(int i=0;i<row;i++){
for(int j=0;j<col;j++){
asit[i][j]=matrix[i][j];
}
}
for(int i=0;i<row;i++){
for(int j=0;j<col;j++){
matrix[j][col-i-1]=asit[i][j];
}
}
}
}
解法二:分解法
算法思想:把选择分成两部=水平反转+沿主对角线反转
class Solution {
public void rotate(int[][] matrix) {
int row = matrix.length;
//1.水平反转
for (int i = 0; i < row / 2; i++) {
for (int j = 0; j < row; j++) {
int temp;
temp = matrix[row - i - 1][j];
matrix[row - i - 1][j] = matrix[i][j];
matrix[i][j] = temp;
}
}
//2.主对角线反转
for (int i = 1; i < row; i++) {
for (int j = 0; j < i; j++) {
int temp;
temp=matrix[i][j];
matrix[i][j]=matrix[j][i];
matrix[j][i]=temp;
}
}
}
}
day9
T_344 反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s
的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
class Solution {
public void reverseString(char[] s) {
int n = s.length;
char temp;
for (int i = 0; i < n / 2; i++) {
temp=s[i];
s[i]=s[n-1-i];
s[n-1-i]=temp;
}
}
}
T_7 整数反转
给你一个 32 位的有符号整数 x
,返回将 x
中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1]
,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)。
解法一:这题关键有两步
1.如何实现整数反转
主要就是用sum=sum*10+res实现累加
2反转数进行判断是否溢出.
判断就是在这一轮对sum进行更新前先记录值,然后更新后用新的逆着解num,如果逆着能和原来的一样就没有溢出。
class Solution {
public int reverse(int x) {
int res = 0;
int sum = 0;
while (x != 0) {
res = x % 10;
int temp = sum;
sum = sum * 10 + res;
if ((sum - res) / 10 != temp) {
return 0;
}
x = x / 10;
}
return sum;
}
}
day10
T_242 有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
解法一:。。用了前几天刚学的HashMap统计,结果答案直接给我调接口
class Solution {
public boolean isAnagram(String s, String t) {
char[] chs1 = s.toCharArray();
char[] chs2 = t.toCharArray();
if(chs1.length!= chs2.length){
return false;
}
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
for (int i = 0; i < chs1.length; i++) {
map.put(chs1[i], map.getOrDefault(chs1[i], 0) + 1);
}
for (int i = 0; i < chs2.length; i++) {
Integer count = map.getOrDefault(chs2[i],0);
if (count==0) {
return false;
} else {
count--;
map.put(chs2[i],count);
}
}
return true;
}
}
解法二:接口法
class Solution {
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
char[] str1 = s.toCharArray();
char[] str2 = t.toCharArray();
Arrays.sort(str1);
Arrays.sort(str2);
return Arrays.equals(str1, str2);
}
}
T_125 验证回文串
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 。
解法一:按照步骤写就行了
写这题发现了Java中一个有趣的接口。StringBuffer,这个可以支持字符串reverse。String可以先变成StringBuffer然后直接.reverse
class Solution {
public boolean isPalindrome(String s) {
//1.小写
String lows = s.toLowerCase();
//2.除去非字母数字的字符
char[] chs1 = lows.toCharArray();
int slow=0;
for(int fast=0;fast< chs1.length;fast++){
char key=chs1[fast];
if((key>='a' && key<='z')||(key>='0' && key<='9')){
chs1[slow++]=chs1[fast];
}
}
//3.逆置后比较
for(int i=0;i<slow/2;i++){
if(chs1[i]!=chs1[slow-1-i]){
return false;
}
}
return true;
}
}