小浩算法-java题解
原文链接
https://www.geekxh.com/0.0.%E5%AD%A6%E4%B9%A0%E9%A1%BB%E7%9F%A5/01.html
这里是把这个大佬的题解用java写一下做个记录
数组系列
两个数组的交集(350)
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]
方法一:map映射
两组数记录下每组数中每位数的次数,再加入另一组数中比较,如果另一组数组的元素在map中的v,也就是次数大于0,做记录,如果次数=0除去这个数。注意要减次数。
官方题解比较美观,贴出来。
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
if(nums1.length>nums2.length)
return intersect(nums2,nums1); //保证nums1永远是那个最小的
Map<Integer,Integer> map=new HashMap<Integer,Integer>();
for (int num : nums1)
{
int count=map.getOrDefault(num,0)+1; //如果出现过出现次数+1,如果没出现过为1
map.put(num,count);
}
int [] intersection=new int[nums1.length];//记录结果的,最大长度是nums1的长度
int index=0;
for(int num:nums2)
{
int count=map.getOrDefault(num,0);
if(count>0)
{
intersection[index++]=num;
count--;//记录完记得减次数
if(count>0)//减完还是大于0更新map
{
map.put(num,count);
}else//=0说明该移除了
{
map.remove(num);
}
}
}
return Arrays.copyOfRange(intersection,0,index);
}
}
方法二:排序后双指针对比,碰到一样的记录,不一样的根据大小往后推,直到两边都遍历完,用一个ans数组存储结果。
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
if(nums1.length>nums2.length)
return intersect(nums2,nums1);
int ans[]=new int[nums1.length];
Arrays.sort(nums1);
Arrays.sort(nums2);
int i=0,j=0,k=0;
while(i<nums1.length && j<nums2.length)
{
if(nums1[i]==nums2[j])
{
ans[k++]=nums1[i];
i++;j++;
}else if(nums1[i]>nums2[j])
{
j++;
}else i++;
}
return Arrays.copyOfRange(ans, 0, k);
}
}
最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,则返回""
示例1:
输入: ["flower","flow","flight"]
输出: "fl"
解题思路:横向比较,假设第一个就是答案,向后遍历,遍历时判断这两个的公共前缀,如果出现了空的就直接返回“”,否则继续向后。
参考:https://leetcode-cn.com/problems/longest-common-prefix/solution/hua-jie-suan-fa-14-zui-chang-gong-gong-qian-zhui-b/
class Solution {
public String longestCommonPrefix(String[] strs) {
if(strs.length==0) return "";
String prefix=strs[0];
for(int i=1;i<strs.length;i++)
{
int j=0;
for(;j<prefix.length() && j<strs[i].length();j++)//j是要小于两个才是公共前缀
{
if(prefix.charAt(j)!=strs[i].charAt(j))
break;
}
prefix=prefix.substring(0,j);
if(prefix.equals("")) return "";
}
return prefix;
}
}
第122题:买卖股票的最佳时机 II
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。注意你不能在买入股票前卖出股票。
示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
贪心:只算正数的
class Solution {
public int maxProfit(int[] prices) {
int ans=0;
int tem_ans=0;
for(int i=1;i<prices.length;i++)
{
tem_ans=prices[i]-prices[i-1];
if(tem_ans>0)
ans+=tem_ans;
}
return ans;
}
}
题目189: 旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
解题思路:反转数组,7654321,再分别反转 k%7,得到答案。
class Solution {
public void rotate(int[] nums, int k) {
reverse(nums,0,nums.length-1);
k%=nums.length; //如果k的长度大于数组长度,那么只计算取余之后的,因为=的部分会不变。
reverse(nums,0,k-1);
reverse(nums,k,nums.length-1);
}
public void reverse(int [] nums,int strat,int end)
{
while(strat<end)
{
int tem=nums[strat];
nums[strat]=nums[end];
nums[end]=tem;
end--; strat++;
}
}
}
题目27:移除元素
给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
解题思路:修改后必定是长度少于原来的,我们直接修改。
class Solution {
public int removeElement(int[] nums, int val) {
int k=0; //这个就是真正的索引
for(int i=0;i<nums.length;i++)
{
if(nums[i]!=val)
{
nums[k]=nums[i];
k++;
}
}
return k;
}
}
类似题目:删除排序数组中的重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次。
返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
解题思路:和上面的类似,稍作修改,还是用一个真正的索引来确定位置,然后进行判断。
class Solution {
public int removeDuplicates(int[] nums) {
if(nums.length==0)return 0;
int index=1;
for(int i=1;i<nums.length;i++)
{
if(nums[i]!=nums[i-1])
{
nums[index]=nums[i];
index++;
}
}
return index;
}
}
删除排序数组中的重复项 II
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定 nums = [1,1,1,2,2,3],
函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。
你不需要考虑数组中超出新长度后面的元素。
解题思路:已经排好序,我们直接看第三个是不是和第一个一样,不一样我们更新数组,一样就不更新
class Solution {
public int removeDuplicates(int[] nums) {
int i=0;
for(int n:nums)
if(i<2|| n!=nums[i-2])
nums[i++]=n;
return i;
}
}
第66题:加一
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
解题思路:从后往前看,普通情况就是加上之后不会进位直接输出即可,特别情况下进位,然后输出即可,再更特别情况下要一直进位,还要在前面补个一。
贴个神仙思路:https://leetcode-cn.com/problems/plus-one/solution/java-shu-xue-jie-ti-by-yhhzw/
class Solution {
public int[] plusOne(int[] digits) {
for(int i=digits.length-1;i>-1;i--)
{
digits[i]++;
digits[i]=digits[i]%10;
if(digits[i]!=0) return digits;
}
digits=new int[digits.length+1]; //如果是更特别的情况长度+1
digits[0]=1;//第一个是1,后面全是0
return digits;
}
}
第1题:两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
解题思路:可以暴力法直接遍历,这里用哈希表
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> map=new HashMap<Integer,Integer>();
for(int i=0;i<nums.length;i++)
map.put(nums[i],i);
for(int i=0;i<nums.length;i++)
{
int com=target-nums[i];
if(map.containsKey(com) && map.get(com)!=i) //如果是两个一样的保证是不是一个
{
return new int[]{i,map.get(com)};
}
}
return new int[]{-1,-1};
}
}
第15题:三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
解题思路: 排序,然后固定第一个,再用双指针判断后面,大于就右移,小于就左移,得到结果后再固定第二个,以此类推。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res=new ArrayList();
for(int i=0;i<nums.length;i++)
{
int target=0-nums[i]; //这个是双指针要判断的目标值
int l=i+1;
int r=nums.length-1;
if(nums[i]>0) break;//如果大于0那么后面必定大于0直接结束
if(i==0 || nums[i]!=nums[i-1]) //这个是去重
{
while(l<r)
{
if(nums[l]+nums[r]==target)
{
res.add(Arrays.asList(nums[i],nums[l],nums[r]));
while(l<r && nums[l]==nums[l+1]) l++;//这个也是去重,下面的也是
while(l<r && nums[r]==nums[r-1]) r--;
l++;r--;
}else if(nums[l]+nums[r]>target)//说明需要右移
r--;
else
l++;
}
}
}
return res;
}
}
第6题:Z 字形变换
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。比如输入字符串为 **“LEETCODEISHIRING” **行数为 3 时,排列如下:
L C I R
E T O E S I I G
E D H N
解题思路:
https://www.geekxh.com/1.0.%E6%95%B0%E7%BB%84%E7%B3%BB%E5%88%97/009.html#_01%E3%80%81%E9%A2%98%E7%9B%AE%E7%A4%BA%E4%BE%8B
https://leetcode-cn.com/problems/zigzag-conversion/solution/zzi-xing-bian-huan-by-jyd/
图解更清晰一点。
这里贴第二个的
class Solution {
public String convert(String s, int numRows) {
if(numRows<2) return s;
List<StringBuilder> rows=new ArrayList<StringBuilder>();
for(int i=0;i<numRows;i++) rows.add(new StringBuilder());
int i=0,flag=-1;
for(char c:s.toCharArray())
{
rows.get(i).append(c);
if(i== 0 || i==numRows-1) flag=-flag;
i+=flag;
}
StringBuilder res=new StringBuilder();
for(StringBuilder row:rows) res.append(row);
return res.toString();
}
}