2024-06-15 合并数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
解题思路:把num2中的元素按照顺序插入num1中就可以了
解题方法:因为nums1是排好序的,所以比较适合插入排序法,核心就是找到合适的位置,将该位置之后的值全部后移一位,并插入值。
复杂度:O(N+M)
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
for (int i=0;i<n;i++){
int j = m-1;
while (j!=-1 && nums2[i]<nums1[j]){
j--;
}
System.arraycopy(nums1,j+1,nums1,j+2,nums1.length-j-2);
nums1[j+1] = nums2[i];
m++;
}
}
}
2024-06-16 原地移除数组
给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素。元素的顺序可能发生改变。然后返回 nums
中与 val
不同的元素的数量。
假设 nums
中不等于 val
的元素数量为 k
,要通过此题,您需要执行以下操作:
- 更改
nums
数组,使nums
的前k
个元素包含不等于val
的元素。nums
的其余元素和nums
的大小并不重要。 - 返回
k
。
示例 1:
输入:nums = [3,2,2,3], val = 3 输出:2, nums = [2,2,_,_] 解释:你的函数函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。 你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。
解题思路:快慢指针的概念。
解题方法:还是由begin记录当前数组不为val的数量,其实只要通过正常去遍历,只要当前“ i ”位置不是val那就放在当前begin位置,begin++就可以了,就不需要考虑使用一个临时变量来存储val的值了,因为题目不关心。
public int removeElement(int[] nums,int val){
int begin = 0;
for(int i=0;i<nums.length;i++){
if (nums[i]!=val){
nums[begin++] = nums[i];
}
}
return begin;
}
2024-06-17 删除数组中重复项
给你一个 非严格递增排列 的数组 nums
,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums
中唯一元素的个数。
示例 1:
输入:nums = [1,1,2] 输出:2, nums = [1,2,_] 解释:函数应该返回新的长度 不需要考虑数组中超出新长度后面的元素。
解题思路1:快慢指针,保证数组中慢指针在内之前的元素不重复,最终返回慢指针索引位置+1。
解题方法1:通过慢指针快指针索引元素比较,选择下一个不重复的元素覆盖到慢指针的下一个位置,并将慢指针右移一位。
时间复杂度:O(N)
空间复杂度:O(1)
public static int removeDuplicates(int[] nums) {
int i=0;
int next=1;
while (next<nums.length) {
if (nums[i]==nums[next]){
next++;
}else{
nums[++i]=nums[next];
}
}
return i+1;
}
2024-06-18 删除有序数组中的重复项 II
给你一个有序数组 nums
,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例 1:
输入:nums = [1,1,1,2,2,3] 输出:5, nums = [1,1,2,2,3] 解释:函数应返回新长度 length =5
, 并且原数组的前五个元素被修改为1, 1, 2, 2, 3
。 不需要考虑数组中超出新长度后面的元素。
解题思路1:快慢指针,保证慢指针索引之前元素符合标准,快指针选择未重复两次的元素。
解题方法2:前两位必定成立,从第三位数开始判断,如果快指针位置的值 != 慢指针位置-2的值,说明快指针指向的值符合条件,赋值到慢指针的位置,并右移一位。
时间复杂度:O(n)
空间复杂度:O(1)
int begin=2;
for(int i=2;i<nums.length;i++){
if (nums[i]!=nums[begin-2]){
nums[begin++]=nums[i];
}
}
return begin;
2024-06-19 多数元素
给定一个大小为 n
的数组 nums
,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋
的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:nums = [3,2,3] 输出:3
解题思路1:通过Map计数。
时间复杂度:O(n) / O(2*n)
空间复杂度:O(n)
public int majorityElement(int[] nums) {
Map<Integer,Integer> map = new HashMap(nums.length/2);
for(Integer i : nums){
if(map.containsKey(i)){
map.put(i,map.get(i)+1);
}else{
map.put(i,1);
}
}
for(Integer i : map.keySet()){
if(map.get(i)>nums.length/2) return i;
}
return -1;
}
解题思路2:排序,取中间的元素
解题方法2:数组无序采用选择排序
时间复杂度:O(n2)
空间复杂度:O(1)
public int majorityElement(int[] nums) {
for(int i=0;i<nums.length;i++){
int min=0;
for(int j=i+1;j<nums.length;j++){
if(nums[min]>nums[j]){
min=j;
}
}
int temp = nums[min];
nums[min] = nums[i];
nums[i] = temp;
}
return nums[nums.length/2];
}
解题方法3:对数组遍历,记录值出现的次数,如果值相同数量+1否则-1,如果次数为零则记录新值,最终返回的值就是那个超过半数的值了。
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int majorityElement(int[] nums) {
int count=0;
int e=0;
for(int i=0;i<nums.length;i++){
if(count==0){
e = nums[i];
}
count += e==nums[i]?1:-1;
}
return e;
}
}
2024-06-20 轮转数组
给定一个整数数组 nums
,将数组中的元素向右轮转 k
个位置,其中 k
是非负数。
示例 1:
输入: nums = [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]
解题思路1:额外数组存储后k位数
时间复杂度:O(n) / O(3*n)
空间复杂度:O(n)
public void rotate(int[] nums, int k) {
if(nums == null || nums.length == 0 || nums.length == 1) return;
k = k % nums.length;
int[] newNums = new int[k];
System.arraycopy(nums,nums.length-k,newNums,0,k);
System.arraycopy(nums,0,nums,k,nums.length-k);
System.arraycopy(newNums,0,nums,0,k);
}
2024-06-21 买卖股票的最佳时机
给定一个数组 prices
,它的第 i
个元素 prices[i]
表示一支给定股票第 i
天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
。
示例 1:
输入:[7,1,5,3,6,4] 输出:5 解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
解题思路:记录最小值,与最大利润
时间复杂度:O(n)
空间复杂度:O(1)
if(prices==null || prices.length==0) return 0;
int min = prices[0];
int maxMony = 0;
for (int i=1;i<prices.length;i++){
min = Math.min(min,prices[i]);
maxMony = Math.max(maxMony,prices[i]-min);
}
return maxMony;
一样的思路,做了判断,避免了不必要的赋值,所以更快:
public int maxProfit(int[] prices) {
int minPrice = prices[0];
int maxPrice = 0;
for(int i=0;i<prices.length;i++){
if(minPrice>prices[i]){
minPrice=prices[i];
}else {
maxPrice=nums[i]-minPrice;
}
}
return maxPrice;
}
2024-06-22 买卖股票的最佳时机 II
给你一个整数数组 prices
,其中 prices[i]
表示某支股票第 i
天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
示例 1:
输入:prices = [7,1,5,3,6,4] 输出:7 解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3。 最大总利润为 4 + 3 = 7 。
解题思路:假设有上帝视角,只要有利润就都算上
解题方法:如果后一天 减 前一天大于0,则累加
时间复杂度:O(n)
空间复杂的:O(1)
public int maxProfit(int[] prices) {
int maxPrice=0;
for(int i=0;i<prices.length-1;i++){
if(prices[i+1]>prices[i]){
maxPrice+=prices[i+1]-prices[i];
}
}
return maxPrice;
}
2024-06-23 跳跃游戏
给你一个非负整数数组 nums
,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标,如果可以,返回 true
;否则,返回 false
。
示例 1:
输入:nums = [2,3,1,1,4] 输出:true 解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
解题思路:贪心算法,计算能够到达的最大距离,如果达到或者超过了,直接返回true
时间复杂度:O(n)
空间复杂度:O(1)
public boolean canJump(int[] nums) {
if(nums.length==0 || nums.length==1) return true;
int maxIndex=0;
for(int i=0;i<nums.length;i++){
if(i>maxIndex) return false;
maxIndex=Math.max(maxIndex,nums[i]+i);
if(maxIndex>=nums.length-1) return true;
}
return false;
}
2024-06-24 跳跃游戏 II
给定一个长度为 n
的 0 索引整数数组 nums
。初始位置为 nums[0]
。
每个元素 nums[i]
表示从索引 i
向前跳转的最大长度。换句话说,如果你在 nums[i]
处,你可以跳转到任意 nums[i + j]
处:
0 <= j <= nums[i]
i + j < n
返回到达 nums[n - 1]
的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]
。
示例 1:
输入: nums = [2,3,1,1,4] 输出: 2 解释: 跳到最后一个位置的最小跳跃数是2
。 从下标为 0 跳到下标为 1 的位置,跳1
步,然后跳3
步到达数组的最后一个位置。
解题思路:并使用贪心算法计算出每次到达界限前可以达到的最远距离,当跳到了这次最远的界限,跳跃的次数+1,并更新当前最远的界限。
时间复杂的:O(n)
空间复杂度:O(1)
public int jump(int[] nums) {
if(nums==null) return -1;
if(nums.length==0 || nums.length==1) return 0;
int jumps=0;
int currentTemps=0;
int max=0;
for(int i=0;i<nums.length;i++){
max=Math.max(max,nums[i]+i);
if(currentTemps==i){
jumps++;
currentTemps=max;
if(currentTemps>=nums.length-1){
return jumps;
}
}
}
return -1;
}
2024-06-25 O(1) 时间插入、删除和获取随机元素
实现RandomizedSet
类:
RandomizedSet()
初始化RandomizedSet
对象bool insert(int val)
当元素val
不存在时,向集合中插入该项,并返回true
;否则,返回false
。bool remove(int val)
当元素val
存在时,从集合中移除该项,并返回true
;否则,返回false
。int getRandom()
随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。每个元素应该有 相同的概率 被返回。
你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1)
。
示例:
输入 ["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"] [[], [1], [2], [2], [], [1], [2], []] 输出 [null, true, false, true, 2, true, false, 2] 解释 RandomizedSet randomizedSet = new RandomizedSet(); randomizedSet.insert(1); // 向集合中插入 1 。返回 true 表示 1 被成功地插入。 randomizedSet.remove(2); // 返回 false ,表示集合中不存在 2 。 randomizedSet.insert(2); // 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。 randomizedSet.getRandom(); // getRandom 应随机返回 1 或 2 。 randomizedSet.remove(1); // 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。 randomizedSet.insert(2); // 2 已在集合中,所以返回 false 。 randomizedSet.getRandom(); // 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。
解题思路:
- insert:通过HashMap直接判断值是否存在、不存在存储值所在ArrayList的下标。
- remove:通过HashMap直接判断值是否存在,如果存在,将list最后一个值覆盖到要删除的元素上,并更改hashmap中的索引。
时间复杂度:O(1)
空间复杂的:O(n)
private HashMap<Integer,Integer> hashmap;
private List<Integer> list;
private Random random;
//construct
public RandomizedSet(){
hashmap=new HashMap<>();
list=new ArrayList<>();
random = new Random();
}
//insert
public boolean insert(int val){
if(hashmap.containsKey(val)) return false;
hashmap.put(val,list.size());
list.add(list.size(),val);
return true;
}
//remove
public boolean remove(int val){
if(!hashmap.containsKey(val)) return false;
int index=hashmap.get(val);
int lastNum=list.get(list.size()-1);
list.set(index,lastNum);
list.remove(list.size()-1);
hashmap.put(lastNum,index);
hashmap.remove(val);
return true;
}
//getRandom
public int getRandom(){
int index=random.nextInt(list.size());
return list.get(index);
}
2024-06-26 H 指数
给你一个整数数组 citations
,其中 citations[i]
表示研究者的第 i
篇论文被引用的次数。计算并返回该研究者的 h
指数。
根据维基百科上 h 指数的定义:h
代表“高引用次数” ,一名科研人员的 h
指数 是指他(她)至少发表了 h
篇论文,并且 至少 有 h
篇论文被引用次数大于等于 h
。如果 h
有多种可能的值,h
指数 是其中最大的那个。
示例 1:
输入:citations = [3,0,6,1,5]
输出:3 解释:给定数组表示研究者总共有5
篇论文,每篇论文相应的被引用了3, 0, 6, 1, 5
次。 由于研究者有3
篇论文每篇 至少 被引用了3
次,其余两篇论文每篇被引用 不多于3
次,所以她的 h 指数是3
解题思路:先排序,在判断剩余的数组长度是否比当前的值大就可以了。
TODU
时间复杂度:待完善
空间复杂度:待完善
public int hIndex(int[] citations) {
if(citations==null) return -1;
if(citations.length==0) return 0;
//可以替换成计数排序法
Arrays.sort(citations);
int n = citations.length;
for (int i = 0; i < n; i++) {
if (citations[i] >= n - i) {
return n - i;
}
}
return 0;
}
2024-06-27 除自身以外数组的乘积
给你一个整数数组 nums
,返回 数组 answer
,其中 answer[i]
等于 nums
中除 nums[i]
之外其余各元素的乘积 。
题目数据 保证 数组 nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请 不要使用除法,且在 O(n)
时间复杂度内完成此题。
示例 1:
输入: nums =[1,2,3,4]
输出:[24,12,8,6]
解题思路:要通过O(n)解决,那就要通过空间去换时间,用一个额外的数据存储累计的乘积。
时间复杂度:O(n)
空间复杂的:O(n)
public int[] productExceptSelf(int[] nums) {
if(nums.length==null) return null;
int[] aft=new int[nums.length];
int[] answer=new int[nums.length];
aft[nums.length-1]=1;
for(int i=nums.length-2;i>=0;i--){
aft[i]=nums[i+1]*aft[i+1];
}
int pre=1;
for(int i=0;i<answer.length;i++){
answer[i]=aft[i] * pre;
pre=nums[i] * pre;
}
return answer;
}
2024-06-28 加油站
在一条环路上有 n
个加油站,其中第 i
个加油站有汽油 gas[i]
升。
你有一辆油箱容量无限的的汽车,从第 i
个加油站开往第 i+1
个加油站需要消耗汽油 cost[i]
升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas
和 cost
,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1
。如果存在解,则 保证 它是 唯一 的。
示例 1:
输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2] 输出: 3 解释: 从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油 开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油 开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油 开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油 开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油 开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。 因此,3 可为起始索引。
解题思路:使用变量存储当前油箱中的汽油,假设第一个加油站点是对的,如果剩余的汽油加上当前加油站的汽油减去到下个加油站的油耗小于0(达不到),证明从这个出发是错误的,继续假设下一个站点是正确的出发点,并且将加油站所有的油、油耗加起来,如果油耗大于油总量,那么无论如何都到不了。
时间复杂度:O(n)
空间复杂度:O(1)
public int canCompleteCircuit(int[] gas, int[] cost) {
int globalgas=0;
int globalcost=0;
int n=gas.length;
for(int i=0;i<n;i++){
globalgas+=gas[i];
globalcost+=gas[i];
}
if(globalgas<globalcost) return -1;
int startIdx=0;
int maxgas=0;
for(int i=0;i<n;i++){
maxgas+=gas[i]-cost[i];
if(maxgas<0){
maxgas=0;
startIdx=i+1;
}
}
return startIdx;
}
2024-06-29 分发糖果
n
个孩子站成一排。给你一个整数数组 ratings
表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
- 每个孩子至少分配到
1
个糖果。 - 相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
示例 1:
输入:ratings = [1,0,2] 输出:5 解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。
解题思路:新增一个数组,代表每个孩子的糖果数量,然后从前到后根据原数组为新数组每个孩子多分配糖果果数量。
时间复杂度:O(n)
空间复杂度:O(1)
public int candy(int[] ratings) {
if(ratings==null) return -1;
int n=ratings.length;
if(ratings.length==0) return 0;
int[] sweets=new int[n];
//为整个数组添加初始化值
Arrays.fill(sweets,1);
for(int i=1;i<n;i++){
if(ratings[i-1]<ratings[i]){
sweets[i]=sweets[i-1]+1;
}
}
for(int i=n-1;i>0;i--){
if(ratings[i-1] > ratings[i] && sweets[i-1] <= sweets[i]){
sweets[i-1]=sweets[i]+1;
}
}
int sum=0;
for(int num : sweets){
sum+=num;
}
return sum;
}
2024-06-30 接雨水
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)
解题思路:双指针,
时间复杂度:
空间复杂度:
2024-07-01 罗马数字转整数
罗马数字包含以下七种字符: I
, V
, X
, L
,C
,D
和 M
。
字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000
例如, 罗马数字 2
写做 II
,即为两个并列的 1 。12
写做 XII
,即为 X
+ II
。 27
写做 XXVII
, 即为 XX
+ V
+ II
。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII
,而是 IV
。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX
。这个特殊的规则只适用于以下六种情况:
I
可以放在V
(5) 和X
(10) 的左边,来表示 4 和 9。X
可以放在L
(50) 和C
(100) 的左边,来表示 40 和 90。C
可以放在D
(500) 和M
(1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。
示例 1:
输入: s = "III" 输出: 3
解题思路1:用一个HashMap记录映射罗马数字和阿拉伯数字的关系。
时间复杂度1:O(n)
空间复杂的1:O(n)
public static int romanToInt(String s) {
//对将罗马数字与阿拉伯数字的映射关系存入HashMao
Map<Character,Integer> map=new HashMap<>();
map.put('I',1);
map.put('V',5);
map.put('X',10);
map.put('L',50);
map.put('C',100);
map.put('D',500);
map.put('M',1000);
int number=0;
for(int i=0;i<s.length();i++){
if(i==s.length()-1) return number+=map.get(s.charAt(i));
if(map.get(s.charAt(i)) < map.get(s.charAt(i+1))){
number+=map.get(s.charAt(i+1))-map.get(s.charAt(i));
i++;
}else{
number+=map.get(s.charAt(i));
}
}
return number;
}
解题思路2:通过switch将将罗马数字解析成阿拉伯数字
时间复杂度2:O(n)
空间复杂的2:O(1)
public static int romanToInt(String s) {
int current;
int pre;
int sum=0;
for(int i=0;i<s.length();i++){
current=parseRomeNumber(s.charAt(i));
if(i==s.length()-1) return sum+=current;
pre=parseRomeNumber(s.charAt(i+1));
if(current>=pre){
sum+=current;
}else{
sum+=pre-current;
i++;
}
}
return sum;
}
private int parseRomeNumber(char c){
switch(c){
case 'I':
return 1;
case 'V':
return 5;
case 'X':
return 10;
case 'L':
return 50;
case 'C':
return 100;
case 'D':
return 500;
case 'M':
return 1000;
default 0;
}
}
2024-07-02 整数转罗马数字
七个不同的符号代表罗马数字,其值如下:
符号 | 值 |
---|---|
I | 1 |
V | 5 |
X | 10 |
L | 50 |
C | 100 |
D | 500 |
M | 1000 |
罗马数字是通过添加从最高到最低的小数位值的转换而形成的。将小数位值转换为罗马数字有以下规则:
- 如果该值不是以 4 或 9 开头,请选择可以从输入中减去的最大值的符号,将该符号附加到结果,减去其值,然后将其余部分转换为罗马数字。
- 如果该值以 4 或 9 开头,使用 减法形式,表示从以下符号中减去一个符号,例如 4 是 5 (
V
) 减 1 (I
):IV
,9 是 10 (X
) 减 1 (I
):IX
。仅使用以下减法形式:4 (IV
),9 (IX
),40 (XL
),90 (XC
),400 (CD
) 和 900 (CM
)。 - 只有 10 的次方(
I
,X
,C
,M
)最多可以连续附加 3 次以代表 10 的倍数。你不能多次附加 5 (V
),50 (L
) 或 500 (D
)。如果需要将符号附加4次,请使用 减法形式。
给定一个整数,将其转换为罗马数字。
示例 1:
输入:num = 3749
输出: "MMMDCCXLIX"
解题思路:从高数位开始处理,一个一个去转换
时间复杂度:O(1)
空间复杂度:O(1)
public static String intToRoman(int num) {
StringBuilder sb=new StringBuilder();
int count=0;
while(num>=1000){
sb.append("M");
num-=1000;
}
if(num>=900){
sb.append("CM");
num-=900;
}else if(num>=500){
sb.append("D");
num-=500;
count=num/100;
for(int i=0;i<count;i++){
sb.append("C");
num-=100;
}
}else if(num>=400){
for(int i=0;i<=(num-400)/100;i++){
sb.append("C");
}
sb.append("D");
num-=400;
}else{
count=num/100;
for(int i=0;i<count;i++){
sb.append("C");
num-=100;
}
}
if(num>=90){
sb.append("XC");
num-=90;
}else if(num>=50){
sb.append("L");
num-=50;
count=num/10;
for(int i=0;i<count;i++){
sb.append("X");
num-=10;
}
}else if(num>=40){
for(int i=0;i<=(num-40)/10;i++){
sb.append("X");
}
sb.append("L");
num-=40;
}else{
count=num/10;
for(int i=0;i<count;i++){
sb.append("X");
num-=10;
}
}
if(num==9){
sb.append("IX");
}else if(num==4){
sb.append("IV");
}else if(num>=5){
sb.append("V");
count=num-5;
for(int i=0;i<count;i++){
sb.append("I");
}
}else {
for(int i=0;i<num;i++){
sb.append("I");
}
}
return sb.toString();
}
2024-06-30 最后一个单词的长度
给你一个字符串 s
,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。
示例 1:
输入:s = "Hello World" 输出:5 解释:最后一个单词是“World”,长度为 5。
解题思路:从后开始技术,如果遍历到空格,返回现有的长度。
时间复杂度:O(n)
空间复杂度:O(1)
public int lengthOfLastWord(String s) {
s=s.trim();
for(int i=s.length()-1;i>=0;i--){
if(s.charAt(i)==' '){
return s.length()-i-1;
}
}
return s.length();
}
2024-07-02 Z 字形变换
将一个给定字符串 s
根据给定的行数 numRows
,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING"
行数为 3
时,排列如下:
P A H N A P L S I I G Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"
。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
示例 1:
输入:s = "PAYPALISHIRING", numRows = 3 输出:"PAHNAPLSIIGYIR"
解题思路1:初始化3个StringBuilder,依次写数据,如果达到边界则往相反的方向写。
时间复杂度1:O(n)/O(3n)
空间复杂度1:O(n)
public static String convert(String s, int numRows) {
if(numRows==1) return s;
//新增StringBuilder
StringBuilder[] sbs=new StringBuilder[numRows];
for(int i=0;i<numRows;i++){
sbs[i]=new StringBuilder();
}
int value=0;
int selectSb=0;
for(int i=0;i<s.length();i++){
if(selectSb==0){
value=1;
}else if(selectSb==sbs.length-1){
value=-1;
}
sbs[selectSb].append(String.valueOf(s.charAt(i)));
selectSb+=value;
}
StringBuilder temp=new StringBuilder();
for(int i=0;i<numRows;i++){
temp.append(sbs[i]);
}
return temp.toString();
}
2024-07-03 文本左右对齐
给定一个单词数组 words
和一个长度 maxWidth
,重新排版单词,使其成为每行恰好有 maxWidth
个字符,且左右两端对齐的文本。
你应该使用 “贪心算法” 来放置给定的单词;也就是说,尽可能多地往每行中放置单词。必要时可用空格 ' '
填充,使得每行恰好有 maxWidth 个字符。
要求尽可能均匀分配单词间的空格数量。如果某一行单词间的空格不能均匀分配,则左侧放置的空格数要多于右侧的空格数。
文本的最后一行应为左对齐,且单词之间不插入额外的空格。
注意:
- 单词是指由非空格字符组成的字符序列。
- 每个单词的长度大于 0,小于等于 maxWidth。
- 输入单词数组
words
至少包含一个单词。
示例 1:
输入: words = ["This", "is", "an", "example", "of", "text", "justification."], maxWidth = 16 输出: [ "This is an", "example of text", "justification. " ]
2024-07-03 两数之和 II - 输入有序数组
给你一个下标从 1 开始的整数数组 numbers
,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target
的两个数。如果设这两个数分别是 numbers[index1]
和 numbers[index2]
,则 1 <= index1 < index2 <= numbers.length
。
以长度为 2 的整数数组 [index1, index2]
的形式返回这两个整数的下标 index1
和 index2
。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
你所设计的解决方案必须只使用常量级的额外空间。
示例 1:
输入:numbers = [2,7,11,15], target = 9 输出:[1,2] 解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
解题思路:双指针
时间复杂度:O(n)
空间复杂度:O(1)
public int[] twoSum(int[] numbers, int target) {
int left=0;
int right=numbers.length-1;
while(left<right){
int sum=numbers[left]+numbers[right];
if(sum==target) return new int[]{left+1,right+1};
if(sum>target) right--;
if(sum<target) left++;
}
return null;
}
2024-07-01 验证回文串
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
字母和数字都属于字母数字字符。
给你一个字符串 s
,如果它是 回文串 ,返回 true
;否则,返回 false
。
示例 1:
输入: s = "A man, a plan, a canal: Panama" 输出:true 解释:"amanaplanacanalpanama" 是回文串。
解题思路1:使用String中的replaceAll函数,将所有的不是字母数字转换成空串,双指针。
解题思路2:双指针,取出符合要求的字符,并比对。
时间复杂度:O(n)
空间复杂度:O(1)
public static boolean isPalindrome(String s) {
int left=0;
int right=s.length()-1;
while(left<right){
char leftVal=s.charAt(left);
char rightVal=s.charAt(right);
if(!Character.isLetterOrDigit(leftVal)){
left++;
}else if(!Character.isLetterOrDigit(rightVal)){
right--;
}else {
if(Character.toLowerCase(leftVal)!=Character.toLowerCase(rightVal)){
return false;
}
left++;
right--;
}
}
return true;
}
2024-07-03 判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"
是"abcde"
的一个子序列,而"aec"
不是)。
进阶:
如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
示例 1:
输入:s = "abc", t = "ahbgdc" 输出:true
解题思路:双指针
时间复杂度:O(n)
空间复杂度:O(1)
public boolean isSubsequence(String s, String t) {
if(s.isEmpty()) return true;
int left=0;
for(int i=0;i<t.length();i++){
if(s.charAt(left)==t.charAt(i)){
left++;
}
if(left==s.length()) return true;
}
return false;
}
2024-07-03 盛最多水的容器
给定一个长度为 n
的整数数组 height
。有 n
条垂线,第 i
条线的两个端点是 (i, 0)
和 (i, height[i])
。
找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7] 输出:49 解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
解题思路:双指针从两端开始计算最大的储水量,每次将较小的一边往里移动,计算是否超过最大容量。
时间复杂度:O(n)
空间复杂度:O(1)
public int maxArea(int[] height) {
int left=0;
int right=height.length-1;
int maxVal=0;
while(left<right){
int min= Math.min(height[left], height[right]);
if((right-left)*min > maxVal){
maxVal=(right-left)*min;
}
if(height[left]>height[right]){
right--;
}else{
left++;
}
}
return maxVal;
}
2024-07-03 三数之和
给你一个整数数组 nums
,判断是否存在三元组 [nums[i], nums[j], nums[k]]
满足 i != j
、i != k
且 j != k
,同时还满足 nums[i] + nums[j] + nums[k] == 0
。请
你返回所有和为 0
且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]] 解释: nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。 nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。 nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。 不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。 注意,输出的顺序和三元组的顺序并不重要。
解题思路:先排序
时间复杂度:
空间复杂度: