每日力扣刷题day01(从零开始版)

2024.5.22(5题)

1108. IP 地址无效化

在显示或处理IP地址时避免误解析,例如在某些HTML或JSON内容中防止被错误地识别为实际的IP地址。

在数据处理中将某些字符替换为特定格式以满足特定需求。

题目链接

给你一个有效的 IPv4 地址 address,返回这个 IP 地址的无效化版本。

所谓无效化 IP 地址,其实就是用 "[.]" 代替了每个 "."

示例 1:

输入:address = "1.1.1.1"
输出:"1[.]1[.]1[.]1"

示例 2:

输入:address = "255.100.50.0"
输出:"255[.]100[.]50[.]0"

提示:

  • 给出的 address 是一个有效的 IPv4 地址
题解一
class Solution {
    public String defangIPaddr(String address) {
        return address.replace(".", "[.]");
    }
}
  • replace()String 类中的一个方法,用于将字符串中的某个子字符串替换为另一个子字符串。
  • address.replace(".", "[.]"); 的作用是将字符串 address 中的所有点(.)替换为 [.]
题解二
class Solution {
    public String defangIPaddr(String address) {
        StringBuilder sb = new StringBuilder();
        int n = address.length();
        int index = 0;
        while (index < n) {
            char c = address.charAt(index);
            if (c == '.') {
                sb.append('[');
            }
            sb.append(c);
            if (c == '.') {
                sb.append(']');
            }
            index++;
        }
        return sb.toString();
    }
}
  • StringBuilder sb = new StringBuilder();:创建一个 StringBuilder 对象 sb

    StringBuilder 是一个可变的字符串序列,适合用于在循环中进行字符串拼接,因为它比使用 String 对象的拼接效率更高。

  • char c = address.charAt(index);:获取字符串 address 在索引 index 处的字符,并将其存储在变量 c 中。

    charAt 方法是 Java 中 String 类的一部分,它用于返回指定索引处的字符。

  • sb.append('['); :检查字符 c 是否是点(.)。如果是,则向 StringBuilder 对象 sb 中追加字符 '['

    append 方法是 Java 中 StringBuilder 类的一部分,它用于将指定的数据追加到 StringBuilder 对象的末尾。

    • 注意这个拼接顺序,当前字符c是点(.)的时候,还没把字符c拼上,所以先拼接[,然后拼接字符c,然后拼接]
  • return sb.toString();:当循环结束时,将 StringBuilder 对象 sb 转换为字符串并返回。

1480. 一维数组的动态和

这个题是对之后学前缀和算法至关重要,是一维前缀和的求法;

同时这里给出的题解一实际上是一个极简的动态规划算法。

题目链接

给你一个数组 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] 。

示例 2:

输入:nums = [1,1,1,1,1]
输出:[1,2,3,4,5]
解释:动态和计算过程为 [1, 1+1, 1+1+1, 1+1+1+1, 1+1+1+1+1] 。

示例 3:

输入:nums = [3,1,2,10,1]
输出:[3,4,6,16,17]

提示:

  • 1 <= nums.length <= 1000
  • -10^6 <= nums[i] <= 10^6
题解一
class Solution {
    public int[] runningSum(int[] nums) {
        int[] arr = new int[nums.length];
        arr[0] = nums[0];
        for (int i = 1; i < arr.length; i++) {
            arr[i] = nums[i] + arr[i-1];
        }
        return arr;
    }
}
  • 创建一个新的数组 arr 来存储动态和的结果,长度与 nums 相同。
  • 设置 arr[0]nums[0]。因为第一个就是本身,不需要跟谁相加。
  • 从索引 1 开始遍历数组 nums,每次将当前元素与前一个元素的动态和相加,得到当前元素的动态和。
题解二
class Solution {
    public int[] runningSum(int[] nums) {
        int n = nums.length;
        for (int i = 1; i < n; i++) {
            nums[i] += nums[i - 1];
        }
        return nums;
    }
}
  • 每次将当前元素与前一个元素的和赋值给当前元素,从而在原数组上直接进行累加。

1394. 找出数组中的幸运数

计数排序思想:使用一个数组来统计频次,这种方法类似于计数排序中对元素进行计数的过程。

哈希表统计频次;官方给的题解,不过我觉得这个题完全没必要有哈希的额外时空开销。

题目链接

在整数数组中,如果一个整数的出现频次和它的数值大小相等,我们就称这个整数为「幸运数」。

给你一个整数数组 arr,请你从中找出并返回一个幸运数。

  • 如果数组中存在多个幸运数,只需返回 最大 的那个。
  • 如果数组中不含幸运数,则返回 -1

示例 1:

输入:arr = [2,2,3,4]
输出:2
解释:数组中唯一的幸运数是 2 ,因为数值 2 的出现频次也是 2 。

示例 2:

输入:arr = [1,2,2,3,3,3]
输出:3
解释:1、2 以及 3 都是幸运数,只需要返回其中最大的 3 。

示例 3:

输入:arr = [2,2,2,3,3]
输出:-1
解释:数组中不存在幸运数。

示例 4:

输入:arr = [5]
输出:-1

示例 5:

输入:arr = [7,7,7,7,7,7,7]
输出:7

提示:

  • 1 <= arr.length <= 500
  • 1 <= arr[i] <= 500
题解一
class Solution {
    public int findLucky(int[] arr) {
        int[] counts = new int[501];
        int ans = -1;
        for (int i = 0; i < arr.length; i++) {
            counts[arr[i]]++;
        }
        for (int i = 1; i < counts.length; i++) {
            if (counts[i] == i) {
                ans = Math.max(i, ans);
            }
        }
        return ans;
    }
}
  • 创建一个长度为 501 的数组 counts,用于记录每个整数的出现频次。题目给了数组范围是 [1,500],这个数组的下标就是要统计的数字,下标对应的值就是这个数出现的次数。所以要开辟501的长度。
  • 如果不含辛运数,返回 -1,ans 初始化为-1。
  • 第一个循环是把arr数组中的每个元素赋值给counts数组,中括号内的就是数组的下标,前面说了,counts数组的下标就是要统计的数字
  • 第二个循环就是判断幸运数,下标和对应值相等时就是幸运数,然后通过max输出最大的幸运数。Math.max(i, ans) 的意思是取 ians 中的较大值。这个方法会返回两个数中较大的那个数。整个语句就是当遇到更大的幸运数时,就会更新ans
题解二
class Solution {
    public int findLucky(int[] arr) {
        Map<Integer, Integer> m = new HashMap<Integer, Integer>();
        for (int x : arr) {
            m.put(x, m.getOrDefault(x, 0) + 1);
        }
        int ans = -1;
        for (Map.Entry<Integer, Integer> entry : m.entrySet()) {
            int key = entry.getKey(), value = entry.getValue();
            if (key == value) {
                ans = Math.max(ans, key);
            }
        }
        return ans;
    }
}
  • 代码是官方的题解,本质跟题解一的计数法一样,只不过开了一个哈希表来存储次数

HashMap 是 Java 中的一个数据结构,它实现了 Map 接口,可以用来存储键值对。在 HashMap 中,每个键值对都是通过键和值进行关联的。以下是 HashMap 的一些重要概念:

  1. 键值对(Entry)HashMap 中的每个元素都是一个键值对,包含一个键对象和一个值对象。键对象用于查找值对象。

  2. 哈希表(Hash Table)HashMap 的内部实现是基于哈希表的。哈希表是一种数据结构,可以提供快速的插入、删除和查找操作。

  3. 哈希函数(Hash Function):哈希函数将键映射到哈希表中的一个位置。这个位置称为哈希桶(Hash Bucket)或槽(Slot)。

  4. 冲突(Collision):由于哈希函数的映射不是一一对应的,可能会出现不同的键被映射到相同的位置,这种情况称为哈希冲突。HashMap 使用链表或红黑树来解决冲突。

  5. 负载因子(Load Factor):负载因子是指哈希表中元素的数量与哈希桶数量的比率。当负载因子超过某个阈值时,HashMap 会自动调整哈希桶的数量,以保持操作的效率。

  6. 扩容(Rehashing):当 HashMap 中的元素数量达到一定阈值时,HashMap 会自动扩容,即重新创建一个更大的哈希表,并将所有元素重新插入到新表中。

HashMap 的主要优点是快速的查找、插入和删除操作(平均时间复杂度为 O(1)),缺点是它不是线程安全的,需要在多线程环境中使用时进行额外的同步处理。

  • Map<Integer, Integer> m = new HashMap<Integer, Integer>();:这行代码创建了一个 HashMap 对象 m,用于存储整数键和整数值之间的映射关系。在这个 HashMap 中,键是数组中的元素,值是该元素在数组中出现的次数。

  • for (int x : arr) { m.put(x, m.getOrDefault(x, 0) + 1); }:这个循环遍历数组 arr 中的每个元素 x,并将其作为键放入 HashMap m 中。如果该元素在 HashMap 中已经存在,则将其值加 1;否则,将其值设为 1。

    getOrDefault(key, defaultValue)HashMap 类中的一个方法,用于获取指定键的值,如果该键不存在,则返回指定的默认值。具体来说,如果 HashMap 中包含指定的键,则返回与该键关联的值;否则,返回指定的默认值。

  • for (Map.Entry<Integer, Integer> entry : m.entrySet()) { ... }:这个循环遍历 HashMap m 中的每个条目,其中每个条目都是一个键值对。在循环的每次迭代中,entry 是一个表示 HashMap 中的一个键值对的对象。

    entrySet()HashMap 类中的一个方法,用于返回包含 HashMap 中所有键值对的集合。这个方法返回的集合中的每个元素都是一个 Map.Entry 对象,表示 HashMap 中的一个键值对。

  • int key = entry.getKey(), value = entry.getValue();:这两行代码从当前条目中提取键和值。在这个上下文中,键 key 是数组中的一个元素,值 value 是该元素在数组中出现的次数。

  • if (key == value) { ans = Math.max(ans, key); }:如果当前键和值相等,则将键 keyans 中的当前最大值比较,并将较大的值赋给 ans。最终,ans 将保存数组中的幸运数,即值等于其自身的键。

  • return ans;:返回数组中的幸运数,如果没有幸运数,则返回 -1。

1470. 重新排练数组

双指针解法,最易于理解版,以便理解二分,以及一些更难的双指针算法。

优化题解

题目链接

给你一个数组 nums ,数组中有 2n 个元素,按 [x1,x2,...,xn,y1,y2,...,yn] 的格式排列。

请你将数组按 [x1,y1,x2,y2,...,xn,yn] 格式重新排列,返回重排后的数组。

示例 1:

输入:nums = [2,5,1,3,4,7], n = 3
输出:[2,3,5,4,1,7] 
解释:由于 x1=2, x2=5, x3=1, y1=3, y2=4, y3=7 ,所以答案为 [2,3,5,4,1,7]

示例 2:

输入:nums = [1,2,3,4,4,3,2,1], n = 4
输出:[1,4,2,3,3,2,4,1]

示例 3:

输入:nums = [1,1,2,2], n = 2
输出:[1,2,1,2]

提示:

  • 1 <= n <= 500
  • nums.length == 2n
  • 1 <= nums[i] <= 10^3
题解一
class Solution {
    public int[] shuffle(int[] nums, int n) {
        int left = 0;
        int mid = n;
        int index = 0;
        int[] ans = new int[2*n];
        for (int i = 0; i < n; i++) {
            ans[index++] = nums[left++];
            ans[index++] = nums[mid++];
        }
        return ans;
    }
}
  • 根据题意,数组的长度是偶数,并且要求的新数组就是前半部分和后半部分交替组成的
  • 自然而然想到双指针,一个指向数组第一个元素,一个指向数组中间的元素
  • 交替赋值给新数组就行了
  • 这里维护了一个index索引(更易理解),每次“插入”新数组元素后,index要加一,这里的加一直接写在数组
  • 里面了
题解二
class Solution {
    public int[] shuffle(int[] nums, int n) {
        int[] ans = new int[2 * n];
        for (int i = 0; i < n; i++) {
            ans[2 * i] = nums[i];
            ans[2 * i + 1] = nums[i + n];
        }
        return ans;
    }
}
  • 效果和逻辑跟题解一是一样的,更简洁,理解了题解一,这个就能直接理解了
  • left 用 i 替代,因为 i 也是从 0 开始,每次循环++ ;mid 用 i + n 代替
  • 带进去算一下,发现跟题解一效果是一样的

977. 有序数组的平方

双指针+二分思想

题目链接

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

示例 2:

输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 已按 非递减顺序 排序

进阶:

  • 请你设计时间复杂度为 O(n) 的算法解决本问题
题解一
class Solution {
    public int[] sortedSquares(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            nums[i] = nums[i] * nums[i];
        }
        Arrays.sort(nums);
        return nums;
    }
}
  • 暴力解法,先把数组中的元素全都平方,然后排序

    Arrays.sort(nums) 是 Java 中用于对数组进行排序的方法。这个方法使用的是快速排序(Quick Sort)算法或归并排序(Merge Sort)算法,具体使用哪种排序算法取决于数组的长度和类型。

    快速排序的时间复杂度取决于基准元素的选择和数组的划分情况,平均情况下的时间复杂度为 O(n log n),最坏情况下的时间复杂度为 O(n^2),最好情况下的时间复杂度为 O(n log n)。因此,Arrays.sort(nums) 的时间复杂度为 O(n log n)。

  • 很明显这个题解没有达到进阶的 O(n) 的要求

题解二
class Solution {
    public int[] sortedSquares(int[] nums) {
        int len = nums.length;
        int[] arr = new int[len];
        int l = 0;
        int r = len - 1;
        int index = len -1;

        while (l <= r) {
            int ls = nums[l] * nums[l];
            int rs = nums[r] * nums[r];
            if (ls > rs) {
                arr[index] = ls;
                l++;
            }else{
                arr[index] = rs;
                r--;
            }
            index--;
        }
        return arr;

    }
}
  • 初始化两个指针 lr,分别指向数组 nums 的第一个元素和最后一个元素。
  • 初始化变量 index,表示新数组 arr 的索引,从数组末尾开始向前遍历。
  • 进入循环,循环条件为 l 小于等于 r。在循环内部:
    • 计算左指针 l 指向元素的平方并赋值给 ls
    • 计算右指针 r 指向元素的平方并赋值给 rs
    • 如果 ls 大于 rs,将 ls 放入 arr 的当前位置,并将 l 向右移动一位;否则,将 rs 放入 arr 的当前位置,并将 r 向左移动一位。
    • index 减一,指向下一个位置。
题解三
// 优化题解二代码
class Solution {
    public int[] sortedSquares(int[] nums) {
        int n = nums.length;
        int[] result = new int[n];
        int left = 0;
        int right = n - 1;

        for (int i = n - 1; i >= 0; i--) {
            if (Math.abs(nums[left]) > Math.abs(nums[right])) {
                result[i] = nums[left] * nums[left];
                left++;
            } else {
                result[i] = nums[right] * nums[right];
                right--;
            }
        }

        return result;
    }
}
  • 先判断两个数的绝对值的大小,然后在进行平方,整个代码更符合逻辑,更简洁

    Math.abs() 是 Java 中的一个静态方法,用于返回一个数的绝对值。绝对值是指这个数到原点的距离,即无论这个数是正数还是负数,它的绝对值都是非负数。

  • 14
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,关于力扣刷题C++常用操作,我可以给你一些常见的操作和技巧: 1. 使用 STL 容器和算法库:STL(Standard Template Library)是 C++ 标准库中的一个重要组成部分,包含了许多常用的容器和算法。在力扣刷题中,使用 STL 可以大大提高代码的效率和可读性。例如,vector 可以用来存储动态数组,sort 可以用来排序等等。 2. 使用 auto 关键字:auto 关键字可以自动推导变量类型,可以减少代码量和提高可读性。例如,auto x = 1; 可以自动推导出 x 的类型为 int。 3. 使用 lambda 表达式:lambda 表达式是 C++11 中引入的一种匿名函数,可以方便地定义一些简单的函数对象。在力扣刷题中,使用 lambda 表达式可以简化代码,例如在 sort 函数中自定义比较函数。 4. 使用位运算:位运算是一种高效的运算方式,在力扣刷题中经常会用到。例如,左移运算符 << 可以用来计算 2 的幂次方,右移运算符 >> 可以用来除以 2 等等。 5. 使用递归:递归是一种常见的算法思想,在力扣刷题中也经常会用到。例如,二叉树的遍历、链表的反转等等。 6. 使用 STL 中的 priority_queue:priority_queue 是 STL 中的一个容器,可以用来实现堆。在力扣刷题中,使用 priority_queue 可以方便地实现一些需要维护最大值或最小值的算法。 7. 使用 STL 中的 unordered_map:unordered_map 是 STL 中的一个容器,可以用来实现哈希表。在力扣刷题中,使用 unordered_map 可以方便地实现一些需要快速查找和插入的算法。 8. 使用 STL 中的 string:string 是 STL 中的一个容器,可以用来存储字符串。在力扣刷题中,使用 string 可以方便地处理字符串相关的问题。 9. 注意边界条件:在力扣刷题中,边界条件往往是解决问题的关键。需要仔细分析题目,考虑各种边界情况,避免出现错误。 10. 注意时间复杂度:在力扣刷题中,时间复杂度往往是评判代码优劣的重要指标。需要仔细分析算法的时间复杂度,并尽可能优化代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈ing小甘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值