力扣第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)O(n∗k) 。每個元素都被移動 1 步(O(n)O(n)) k次(O(k)O(k)) 。
空間複雜度:O(1)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;
}
}