力扣第169、171、189、198、202题
169、多数元素
代码:
class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length/2];
}
}
171、Excel表的序列号
解题思路:
- 标签:字符串遍历,进制转换
- 初始化结果ans = 0,遍历时将每个字母与A做减法,因为A表示1,所以减法后需要每个数加1,计算其代表
- 数值num = 字母 - ‘A’ + 1
- 因为有26个字母,所以相当于26进制,每26个数则向前进一位
- 所以每遍历一位则ans = ans * 26 + num
- 以ZY为例,Z的值为26,Y的值为25,则结果为26 * 26 + 25=701
- 时间复杂度:O(n)
代码:
class Solution {
public int titleToNumber(String s) {
int ans = 0;
for(int i=0;i<s.length();i++) {
int num = s.charAt(i) - 'A' + 1;
ans = ans * 26 + num;
}
return ans;
}
}
189、转转数组
解题思路一:
最简单的方法是旋转 k 次,每次将数组旋转 1 个元素。
复杂度分析
时间复杂度:O(n*k)。每个元素都被移动 1 步O(n) k次O(k) 。
空间复杂度:O(1) 。没有额外空间被使用。
代码一:
public class Solution {
public void rotate(int[] nums, int k) {
int temp, previous;
for (int i = 0; i < k; i++) {
previous = nums[nums.length - 1];
for (int j = 0; j < nums.length; j++) {
temp = nums[j];
nums[j] = previous;
previous = temp;
}
}
}
}
解题思路二:
我们可以用一个额外的数组来将每个元素放到正确的位置上,也就是原本数组里下标为 i 的我们把它放到 (i+k)%数组长度的位置。然后把新的数组拷贝到原数组中。
代码二:
class Solution {
public void rotate(int[] nums, int k) {
int[] a=new int[nums.length];
for(int i=0;i<nums.length;i++){
a[(i+k)%nums.length]=nums[i];
}
for(int i=0;i<nums.length;i++){
nums[i]=a[i];
}
}
}
198、打家劫舍
问题:
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 400
解题思路:
首先考虑最简单的情况。如果只有一间房屋,则偷窃该房屋,可以偷窃到最高总金额。如果只有两间房屋,则由于两间房屋相邻,不能同时偷窃,只能偷窃其中的一间房屋,因此选择其中金额较高的房屋进行偷窃,可以偷窃到最高总金额。
1. 如果房屋数量大于两间,应该如何计算能够偷窃到的最高总金额呢?对于第 k~(k>2)间房屋,有两个选项:
2. 偷窃第 k 间房屋,那么就不能偷窃第 k-1间房屋,偷窃总金额为前 k-2间房屋的最高总金额与第 k 间房屋的金额之和。
不偷窃第 k 间房屋,偷窃总金额为前 k-1 间房屋的最高总金额。
在两个选项中选择偷窃总金额较大的选项,该选项对应的偷窃总金额即为前 k 间房屋能偷窃到的最高总金额。
用 dp[i] 表示前 i 间房屋能偷窃到的最高总金额,那么就有如下的状态转移方程:
dp[i]=Math.max(dp[i-2]+nums[i],dp[i-1]);
代码:
class Solution {
public int rob(int[] nums) {
if(nums==null||nums.length==0){
return 0;
}
int length=nums.length;
if(length==1){
return nums[0];
}
int[] dp=new int[length];
dp[0]=nums[0];
dp[1]=Math.max(nums[0],nums[1]);
for(int i=2;i<length;i++){
dp[i]=Math.max(dp[i-2]+nums[i],dp[i-1]);
}
return dp[length-1];
}
}
202、快乐数
问题:
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。
如果 n 是快乐数就返回 True ;不是,则返回 False 。
示例:
输入:19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
解题思路一:
算法分为两部分,我们需要设计和编写代码。
1. 给一个数字 n,它的下一个数字是什么?
2. 按照一系列的数字来判断我们是否进入了一个循环。
第 1 部分我们按照题目的要求做数位分离,求平方和。
第 2 部分可以使用 HashSet 完成。每次生成链中的下一个数字时,我们都会检查它是否已经在 HashSet 中。
- 如果它不在 HashSet 中,我们应该添加它。
- 如果它在 HashSet 中,这意味着我们处于一个循环中,因此应该返回 false。
我们使用 HashSet 而不是向量、列表或数组的原因是因为我们反复检查其中是否存在某数字。检查数字是否在哈希集中需要 O(1) 的时间,而对于其他数据结构,则需要 O(n) 的时间。选择正确的数据结构是解决这些问题的关键部分。
核心思想就是利用了一个HashSet和递归,通过递归我们可以判断最后是否会出现结果1,通过HashSet我们可以知道是否是循环。
代码一:
class Solution {
private int getNext(int n) {
int totalSum = 0;
while (n > 0) {
int d = n % 10;
n = n / 10;
totalSum += d * d;
}
return totalSum;
}
public boolean isHappy(int n) {
Set<Integer> seen = new HashSet<>();
while (n != 1 && !seen.contains(n)) {
seen.add(n);
n = getNext(n);
}
return n == 1;
}
}
解题思路二:
原理就是,我们通过两个指针,一个快,一个慢,如果是一个快乐数的话,那么快的那个指针就会先到达数字1,也就结束了,如果不是一个快乐数,那么快的和慢的指针最后会在某一个数字上相遇,在代码编写上,我们可以让快的指针,每次都多执行一步,就是fastRunner = getNext(getNext(fastRunner));
代码二:
class Solution {
public int getNext(int n){
int totalSum=0;
while(n>0){
int d=n%10;
n=n/10;
totalSum+=d*d;
}
return totalSum;
}
public boolean isHappy(int n) {
int slowRunner=n;
int fastRunner=getNext(n);
while(fastRunner!=1&&slowRunner!=fastRunner){
slowRunner=getNext(slowRunner);
fastRunner=getNext(getNext(fastRunner));
}
return fastRunner==1;
}
}