Leetcode數據結構與算法(三)

Leetcode數據結構與算法

###[0033]從尾到頭打印鏈表

輸入一個鏈表的頭節點,從尾到頭反過來返回每個節點的值(用數組返回)。

示例 1:

輸入:head = [1,3,2]
輸出:[2,3,1]

限制:

0 <= 鏈表長度 <= 10000

方法一: 棧

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] reversePrint(ListNode head) {
        Stack<Integer> stack=new Stack();

        while(head != null){
            stack.push(head.val);
            head = head.next;
        }
        
        int[] res = new int[stack.size()];
        int pos = 0;
        while(!stack.empty())
            res[pos++] = stack.pop();
        return res;
    }
}

時間複雜度 O(N): 入棧和出棧共使用 O(N) 時間。
空間複雜度 O(N): 輔助棧 stack 和數組 res 共使用 O(N) 的額外空間。

方法二:遞歸

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    int[] res;
    int i;//全局變量
    public int[] reversePrint(ListNode head) {
        recur(head,0);//遞歸函數調用
        return res;
    }
    void recur(ListNode head,int count) {
        if(head == null)
        {//遞歸終止條件
            res = new int[count];
            i = count-1;
            return;
        }
        recur(head.next,count+1);
        res[i-count] =  head.val; //這裏的i-count是重點
    }
}

時間複雜度 O(N): 遍歷鏈表,遞歸 N 次。
空間複雜度 O(N): 系統遞歸需要使用 O(N) 的棧空間。

###[0034]反轉鏈表

定義一個函數,輸入一個鏈表的頭節點,反轉該鏈表並輸出反轉後鏈表的頭節點。

示例:

輸入: 1->2->3->4->5->NULL
輸出: 5->4->3->2->1->NULL

限制:

0 <= 節點個數 <= 5000

注意:本題與0002 題相同

方法一:迭代

假設存在鏈表 1 → 2 → 3 → Ø,我們想要把它改成 Ø ← 1 ← 2 ← 3。

在遍歷列表時,將當前節點的 next 指針改爲指向前一個元素。由於節點沒有引用其上一個節點,因此必須事先存儲其前一個元素。在更改引用之前,還需要另一個指針來存儲下一個節點。不要忘記在最後返回新的頭引用!

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
public ListNode reverseList(ListNode head) {
    //申請節點,pre和 cur,pre指向null
    ListNode pre = null;
    ListNode cur = head;
  	ListNode tmp = null;
    while (cur != null) {
        tmp = cur.next;//記錄當前節點的下一個節點
        cur.next = pre;//然後將當前節點指向pre
        //pre和cur節點都前進一位
        pre = cur;
        cur = tmp;
    }
    return pre;
}

複雜度分析

  • 時間複雜度:O(n),假設 nn 是列表的長度,時間複雜度是 O(n)。
  • 空間複雜度:O(1)。

方法二:遞歸

遞歸的兩個條件:

  1. 終止條件是當前節點或者下一個節點==null
  2. 在函數內部,改變節點的指向,也就是 head 的下一個節點指向 head 遞歸函數那句
head.next.next = head

很不好理解,其實就是 head 的下一個節點指向head。
遞歸函數中每次返回的 cur 其實只最後一個節點,在遞歸函數內部,改變的是當前節點的指向。

public ListNode reverseList(ListNode head) {
    if (head == null || head.next == null) return head;
    ListNode p = reverseList(head.next);
    head.next.next = head;
    head.next = null;
    return p;
}

複雜度分析

  • 時間複雜度:O(n),假設 n 是列表的長度,那麼時間複雜度爲O(n)。
  • 空間複雜度:O(n),由於使用遞歸,將會使用隱式棧空間。遞歸深度可能會達到 n 層。

###[0035]奇數值單元格的數目

給你一個 n 行 m 列的矩陣,最開始的時候,每個單元格中的值都是 0。

另有一個索引數組 indices,indices[i] = [ri, ci] 中的 ri 和 ci 分別表示指定的行和列(從 0 開始編號)。

你需要將每對 [ri, ci] 指定的行和列上的所有單元格的值加 1。

請你在執行完所有 indices 指定的增量操作後,返回矩陣中 「奇數值單元格」 的數目。

示例 1:

img

輸入:n = 2, m = 3, indices = [[0,1],[1,1]]
輸出:6
解釋:最開始的矩陣是 [[0,0,0],[0,0,0]]。
第一次增量操作後得到 [[1,2,1],[0,1,0]]。
最後的矩陣是 [[1,3,1],[1,3,1]],裏面有 6 個奇數。

示例 2:

img

輸入:n = 2, m = 2, indices = [[1,1],[0,0]]
輸出:0
解釋:最後的矩陣是 [[2,2],[2,2]],裏面沒有奇數。

提示:

1 <= n <= 50
1 <= m <= 50
1 <= indices.length <= 100
0 <= indices[i][0] < n
0 <= indices[i][1] < m

方法一:模擬

class Solution {
    public int oddCells(int n, int m, int[][] indices) {
        int[][] ret = new int[n][m];
        int val = 0;
      	//遍歷indices,修改返回數組值
        for(int i = 0;i<indices.length;i++){     
            for (int c = 0; c< m;c++)
                    ret[indices[i][0]][c]++;
            for (int c = 0; c< n;c++)
                    ret[c][indices[i][1]]++;     
        }
        //統計奇數個數
        for(int i = 0;i<n;i++){
            for(int j = 0;j<m;j++){
                if(ret[i][j] % 2 == 1)
                    val++;
            }
        }
        return val;
    }
}

時間複雜度: O(L(M+N)+MN),其中 L 是 indices 數組的長度。

空間複雜度:O(MN)。

方法二:模擬 + 空間優化

class Solution {
    public int oddCells(int n, int m, int[][] indices) {
        int[] row=new int[n];
        int[] col=new int[m];
        //統計行數和列數出現的次數
        for(int i=0;i<indices.length;i++) {
        	row[indices[i][0]]++;
        	col[indices[i][1]]++;
        }
        //指定點的值爲行數和列數出現次數之和
        int ans=0;
        for(int i=0;i<n;i++)
        	for(int j=0;j<m;j++) {
        		if((row[i]+col[j])%2>0)
        			ans++;
        	}
        return ans;
    }
}

時間複雜度:O(L+MN),其中 L 是 indices 數組的長度。

空間複雜度:O(M+N)。

方法三:

class Solution {
    public int oddCells(int n, int m, int[][] indices) {
        boolean[] r = new boolean[n];
        boolean[] c = new boolean[m];
        int i;
        for (i = 0; i < indices.length; i++) {
            r[indices[i][0]] = !r[indices[i][0]];
            c[indices[i][1]] = !c[indices[i][1]];
        }
        int rr = 0, cc = 0;
        for (i = 0; i < r.length; i++) {
            if(r[i])rr++;
        }
        for (i = 0; i < c.length; i++) {
            if(c[i])cc++;
        }
        return rr * m + cc * n - rr * cc * 2;
    
    }
}

###[0036]6 和 9 組成的最大數字

給你一個僅由數字 6 和 9 組成的正整數 num

你最多隻能翻轉一位數字,將 6 變成 9,或者把 9 變成 6 。

請返回你可以得到的最大數字。

示例 1:

輸入:num = 9669
輸出:9969
解釋:
改變第一位數字可以得到 6669 。
改變第二位數字可以得到 9969 。
改變第三位數字可以得到 9699 。
改變第四位數字可以得到 9666 。
其中最大的數字是 9969 。

示例 2:

輸入:num = 9996
輸出:9999
解釋:將最後一位從 6 變到 9,其結果 9999 是最大的數。

示例 3:

輸入:num = 9999
輸出:9999
解釋:無需改變就已經是最大的數字了。

提示:

1 <= num <= 10^4
num 每一位上的數字都是 6 或者 9 。

方法一:

class Solution {
    public int maximum69Number (int num) {
        String s = num + "";
        s = s.replaceFirst("6", "9");
        return Integer.valueOf(s);
    }
}

方法二:

class Solution {
    public int maximum69Number (int num) {
        StringBuilder sb = new StringBuilder(String.valueOf(num));
        int index = sb.indexOf("6");
        if (index != -1) {
            sb.setCharAt(index, '9');
        }
        return Math.max(num, Integer.parseInt(sb.toString()));
    }
}

方法三:

class Solution {
    public int maximum69Number (int num) {
        int res = num;
        char[] chars = String.valueOf(num).toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == '6') {
                chars[i] = '9';
                res = Math.max(res, Integer.parseInt(String.valueOf(chars)));
                break;
            }
        }
        return res;
    }
}

###[0037]合併二叉樹

給定兩個二叉樹,想象當你將它們中的一個覆蓋到另一個上時,兩個二叉樹的一些節點便會重疊。

你需要將他們合併爲一個新的二叉樹。合併的規則是如果兩個節點重疊,那麼將他們的值相加作爲節點合併後的新值,否則不爲 NULL 的節點將直接作爲新二叉樹的節點。

示例 1:

輸入:

	Tree 1                     Tree 2                  
          1                         2                             
         / \                       / \                            
        3   2                     1   3                        
       /                           \   \                      
      5                             4   7                  

輸出:
合併後的樹:


	     3
	    / \
	   4   5
	  / \   \ 
	 5   4   7

注意: 合併必須從兩個樹的根節點開始。

方法一:遞歸

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {

     public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
        if (t1 == null)
            return t2;
        if (t2 == null)
            return t1;
        t1.val += t2.val;
        t1.left = mergeTrees(t1.left, t2.left);
        t1.right = mergeTrees(t1.right, t2.right);
        return t1;
    }
}

複雜度分析

時間複雜度:O(N),其中N 是兩棵樹中節點個數的較小值。

空間複雜度:O(N),在最壞情況下,會遞歸 N 層,需要 O(N) 的棧空間。

方法二:

/**
* Definition for a binary tree node.
* public class TreeNode {
*     int val;
*     TreeNode left;
*     TreeNode right;
*     TreeNode(int x) { val = x; }
* }
*/
class Solution {

    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
      if (t1 == null)
           return t2;
       Stack < TreeNode[] > stack = new Stack < > ();
       stack.push(new TreeNode[] {t1, t2});
       while (!stack.isEmpty()) {
           TreeNode[] t = stack.pop();
           if (t[0] == null || t[1] == null) {
               continue;
           }
           t[0].val += t[1].val;
           if (t[0].left == null) {
               t[0].left = t[1].left;
           } else {
               stack.push(new TreeNode[] {t[0].left, t[1].left});
           }
           if (t[0].right == null) {
               t[0].right = t[1].right;
           } else {
               stack.push(new TreeNode[] {t[0].right, t[1].right});
           }
       }
       return t1;
   }
}

複雜度分析
時間複雜度:O(N),其中 N 是兩棵樹中節點個數的較小值。

空間複雜度:O(N),在最壞情況下,棧中會存放 N 個節點。

###[0038]將每個元素替換爲右側最大元素

給你一個數組 arr ,請你將每個元素用它右邊最大的元素替換,如果是最後一個元素,用 -1 替換。

完成所有替換操作後,請你返回這個數組。

示例:

輸入:arr = [17,18,5,4,6,1]
輸出:[18,6,6,6,1,-1]

提示:

1 <= arr.length <= 10^4
1 <= arr[i] <= 10^5

方法一:暴力枚舉

class Solution {
    public int[] replaceElements(int[] arr) {
        int curMax = -1;
        int N = arr.length;
        for (int i = 0; i < N; i++) {
            int rightMax = -1;
            for (int j = i+1; j < N; j++) { 
                if(rightMax < arr[j])
                    rightMax = arr[j];  // 尋找當前位置i後面的最大值
        }
        arr[i] = rightMax; // 更新元素
        }
        arr[N-1] = -1;
        return arr;
    }
}

方法二:逆序遍歷

class Solution {
    public int[] replaceElements(int[] arr) {
        int rightMax = -1;
        for (int i = arr.length-1; i >= 0; i--) {
            int temp = arr[i];
            arr[i] = rightMax;
            if(temp > rightMax)
            rightMax = temp;
        }
        return arr;
    }
}

###[0038]漢明距離

兩個整數之間的漢明距離指的是這兩個數字對應二進制位不同的位置的數目。

給出兩個整數 x 和 y,計算它們之間的漢明距離。

注意:

0 ≤ x, y < 231.

示例:

輸入: x = 1, y = 4

輸出: 2

解釋:
1   (0 0 0 1)
4   (0 1 0 0)
       ↑   ↑

上面的箭頭指出了對應二進制位不同的位置。

方法一:異或

class Solution {
    public int hammingDistance(int x, int y) {
        return countBitNumber(x^y);
    }
    //1.兩個數字異或之後可以得到 不同二進制位的數字
    //2.計算該數字中1的個數,即是漢明距離
    //計算1的個數時,有幾種方法,1:不斷和左移的1 進行 與,判斷該位是否爲1.
    //                       2: n&(n-1)就是把n最右邊的bit 1 位去掉,看能去掉幾次,就有幾個1位。(布賴恩·克尼根算法)
    int countBitNumber(int n)
    {
        int count = 0;
        while(n != 0)			
        {
            n = (n & n-1);
            ++count;
        }
				//while (n != 0) {
        //    if (n % 2 == 1)
        //       count += 1;
        //    n = n >> 1;
        //}
        return count;
    }
}

方法二:內置

class Solution {
    public int hammingDistance(int x, int y) {
        return Integer.bitCount(x ^ y); 
    }
}

###[0039]轉換成小寫字母

實現函數 ToLowerCase(),該函數接收一個字符串參數 str,並將該字符串中的大寫字母轉換成小寫字母,之後返回新的字符串。

示例 1:

輸入: "Hello"
輸出: "hello"

示例 2:

輸入: "here"
輸出: "here"

示例 3:

輸入: "LOVELY"
輸出: "lovely"

方法一:

class Solution {
    public String toLowerCase(String str) {
        char[] arr = str.toCharArray();
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] >= 'A' && arr[i] <= 'Z') {
                arr[i] += 'a' - 'A';
            }
        }
        return String.valueOf(arr);
    }
}

方法二:

class Solution {
    public String toLowerCase(String str) {
        StringBuilder s =  new StringBuilder();
        for(char c:str.toCharArray()){  //增強for()循環
            if(c>=65 && c<=90)
                c+=32;
            s.append(c);
        }
        return s.toString();
    }
}

方法三:

class Solution {
    public String toLowerCase(String str) {
        return str.toLowerCase();     //也可以直接用函數實現
    }
}

###[0040]和爲零的N個唯一整數

給你一個整數 n,請你返回 任意 一個由 n各不相同 的整數組成的數組,並且這 n 個數相加和爲 0

示例 1:

輸入:n = 5
輸出:[-7,-1,1,3,4]
解釋:這些數組也是正確的 [-5,-1,1,2,3],[-3,-1,2,-2,4]。

示例 2:

輸入:n = 3
輸出:[-1,0,1]

示例 3:

輸入:n = 1
輸出:[0]

提示:

1 <= n <= 1000

方法一:枚舉

public int[] sumZero(int n) {
    int sum = 0;
    int[] arr = new int[n];
    int len = 0;
    for (int i = 0; i <= n - 2; i++) {
        arr[len++] = i;
        sum += i;
    }
    arr[len] = -sum;
    return arr;
}

方法二:

class Solution {
    public int[] sumZero(int n) {
        int[] arr = new int[n];
        search(1, n-1 , 1, arr);
        return arr;
    }

    private static void search(int l, int r, int val, int[] arr) {
        if (l > r)
            return;
        arr[l-1] = val;
        arr[r] = -val;
        search(l + 1, r - 1, val + 1, arr);
    }
}
class Solution {
    public int[] sumZero(int n) {
        int[] arr = new int[n];
        int l = 0, r = n-1;     //左邊界與右邊界
        int i = 1;
        while (l < r) {
            arr[l++] = i;
            arr[r--] = -i;
            i++;
        }
        return arr;
    }
}

###[0041] 翻轉二叉樹

見[0025]二叉樹的鏡像

###[0042]唯一摩爾斯密碼詞

國際摩爾斯密碼定義一種標準編碼方式,將每個字母對應於一個由一系列點和短線組成的字符串, 比如: “a” 對應 “.-”, “b” 對應 “-…”, “c” 對應 “-.-.”, 等等。

爲了方便,所有26個英文字母對應摩爾斯密碼錶如下:

[".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--.."]

給定一個單詞列表,每個單詞可以寫成每個字母對應摩爾斯密碼的組合。例如,“cab” 可以寫成 “-.-…–…”,(即 “-.-.” + “-…” + ".-"字符串的結合)。我們將這樣一個連接過程稱作單詞翻譯。

返回我們可以獲得所有詞不同單詞翻譯的數量。

例如:

輸入: words = ["gin", "zen", "gig", "msg"]
輸出: 2
解釋: 
各單詞翻譯如下:
"gin" -> "--...-."
"zen" -> "--...-."
"gig" -> "--...--."
"msg" -> "--...--."

共有 2 種不同翻譯, "--...-." 和 "--...--.".

注意:

  • 單詞列表words 的長度不會超過 100。
  • 每個單詞 words[i]的長度範圍爲 [1, 12]。
  • 每個單詞 words[i]只包含小寫字母。

方法一:

class Solution {
    public int uniqueMorseRepresentations(String[] words) {
        String[] MORSE = new String[]{".-","-...","-.-.","-..",".","..-.","--.",
                         "....","..",".---","-.-",".-..","--","-.",
                         "---",".--.","--.-",".-.","...","-","..-",
                         "...-",".--","-..-","-.--","--.."};

        Set<String> seen = new HashSet();
        for (String word: words) {
            StringBuilder code = new StringBuilder();
            for (char c: word.toCharArray())
                code.append(MORSE[c - 'a']);
            seen.add(code.toString());
        }

        return seen.size();
    }
}

###[0043]翻轉圖像

給定一個二進制矩陣 A,我們想先水平翻轉圖像,然後反轉圖像並返回結果。

水平翻轉圖片就是將圖片的每一行都進行翻轉,即逆序。例如,水平翻轉 [1, 1, 0] 的結果是 [0, 1, 1]。

反轉圖片的意思是圖片中的 0 全部被 1 替換, 1 全部被 0 替換。例如,反轉 [0, 1, 1] 的結果是 [1, 0, 0]。

示例 1:

輸入: [[1,1,0],[1,0,1],[0,0,0]]
輸出: [[1,0,0],[0,1,0],[1,1,1]]
解釋: 首先翻轉每一行: [[0,1,1],[1,0,1],[0,0,0]];
     然後反轉圖片: [[1,0,0],[0,1,0],[1,1,1]]

示例 2:

輸入: [[1,1,0,0],[1,0,0,1],[0,1,1,1],[1,0,1,0]]
輸出: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]]
解釋: 首先翻轉每一行: [[0,0,1,1],[1,0,0,1],[1,1,1,0],[0,1,0,1]];
     然後反轉圖片: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]]

說明:

1 <= A.length = A[0].length <= 20
0 <= A[i][j] <= 1

方法一:

class Solution {
    public int[][] flipAndInvertImage(int[][] A) {
        int C = A[0].length;
        for (int[] row: A)
            for (int i = 0; i < (C + 1) / 2; ++i) {
                int tmp = row[i] ^ 1;
                row[i] = row[C - 1 - i] ^ 1;
                row[C - 1 - i] = tmp;
            }

        return A;
    }
}

###[0044]上升下降字符串

給你一個字符串 s ,請你根據下面的算法重新構造字符串:

從 s 中選出 最小 的字符,將它 接在 結果字符串的後面。
從 s 剩餘字符中選出 最小 的字符,且該字符比上一個添加的字符大,將它 接在 結果字符串後面。
重複步驟 2 ,直到你沒法從 s 中選擇字符。
從 s 中選出 最大 的字符,將它 接在 結果字符串的後面。
從 s 剩餘字符中選出 最大 的字符,且該字符比上一個添加的字符小,將它 接在 結果字符串後面。
重複步驟 5 ,直到你沒法從 s 中選擇字符。
重複步驟 1 到 6 ,直到 s 中所有字符都已經被選過。
在任何一步中,如果最小或者最大字符不止一個 ,你可以選擇其中任意一個,並將其添加到結果字符串。

請你返回將 s 中字符重新排序後的 結果字符串 。

示例 1:

輸入:s = "aaaabbbbcccc"
輸出:"abccbaabccba"
解釋:第一輪的步驟 1,2,3 後,結果字符串爲 result = "abc"
第一輪的步驟 4,5,6 後,結果字符串爲 result = "abccba"
第一輪結束,現在 s = "aabbcc" ,我們再次回到步驟 1
第二輪的步驟 1,2,3 後,結果字符串爲 result = "abccbaabc"
第二輪的步驟 4,5,6 後,結果字符串爲 result = "abccbaabccba"

示例 2:

輸入:s = "rat"
輸出:"art"
解釋:單詞 "rat" 在上述算法重排序以後變成 "art"

示例 3:

輸入:s = "leetcode"
輸出:"cdelotee"

示例 4:

輸入:s = "ggggggg"
輸出:"ggggggg"

示例 5:

輸入:s = "spo"
輸出:"ops"

提示:

1 <= s.length <= 500
s 只包含小寫英文字母。

方法一:

class Solution {
    public String sortString(String s) {
        StringBuilder builder = new StringBuilder();
        int[] map = new int[26];
        for (char c : s.toCharArray()) map[c - 'a']++;
        boolean flag;
        do {
            flag = false;
            for (int i = 0; i < 26; i++) {
                if (map[i] > 0) {
                    builder.append((char) (i + 'a'));
                    map[i]--;
                    flag = true;
                }
            }
            for (int i = 25; i >= 0; i--) {
                if (map[i] > 0) {
                    builder.append((char) (i + 'a'));
                    map[i]--;
                    flag = true;
                }
            }
        } while (flag);
        return builder.toString();
    }
}

###[0045]機器人能否返回原點

在二維平面上,有一個機器人從原點 (0, 0) 開始。給出它的移動順序,判斷這個機器人在完成移動後是否在 (0, 0) 處結束。

移動順序由字符串表示。字符 move[i] 表示其第 i 次移動。機器人的有效動作有 R(右),L(左),U(上)和 D(下)。如果機器人在完成所有動作後返回原點,則返回 true。否則,返回 false。

注意:機器人“面朝”的方向無關緊要。 “R” 將始終使機器人向右移動一次,“L” 將始終向左移動等。此外,假設每次移動機器人的移動幅度相同。

示例 1:

輸入: "UD"
輸出: true
解釋:機器人向上移動一次,然後向下移動一次。所有動作都具有相同的幅度,因此它最終回到它開始的原點。因此,我們返回 true。

示例 2:

輸入: "LL"
輸出: false
解釋:機器人向左移動兩次。它最終位於原點的左側,距原點有兩次 “移動” 的距離。我們返回 false,因爲它在移動結束時沒有返回原點。

方法一:

class Solution {
    public boolean judgeCircle(String moves) {
        int sum = 0;
        for (char c : moves.toCharArray()){
            if(c == 'R' ) sum+=1;
            if(c == 'L' ) sum-=1;
            if(c == 'U' ) sum+=2;
            if(c == 'D' ) sum-=2;
        }
        return sum == 0;
    }
}

方法二:

class Solution {
    public boolean judgeCircle(String moves) {
        int x = 0, y = 0;
        for (char move: moves.toCharArray()) {
            if (move == 'U') y--;
            else if (move == 'D') y++;
            else if (move == 'L') x--;
            else if (move == 'R') x++;
        }
        return x == 0 && y == 0;
    }
}

###[0046]解碼字母到整數映射

給你一個字符串 s,它由數字(‘0’ - ‘9’)和 ‘#’ 組成。我們希望按下述規則將 s 映射爲一些小寫英文字符:

字符(‘a’ - ‘i’)分別用(‘1’ - ‘9’)表示。
字符(‘j’ - ‘z’)分別用(‘10#’ - ‘26#’)表示。
返回映射之後形成的新字符串。

題目數據保證映射始終唯一。

示例 1:

輸入:s = "10#11#12"
輸出:"jkab"
解釋:"j" -> "10#" , "k" -> "11#" , "a" -> "1" , "b" -> "2".

示例 2:

輸入:s = "1326#"
輸出:"acz"

示例 3:

輸入:s = "25#"
輸出:"y"

示例 4:

輸入:s = "12345678910#11#12#13#14#15#16#17#18#19#20#21#22#23#24#25#26#"
輸出:"abcdefghijklmnopqrstuvwxyz"

提示:

1 <= s.length <= 1000
s[i] 只包含數字('0'-'9')和 '#' 字符。
s 是映射始終存在的有效字符串。

方法一:

class Solution {
    public String freqAlphabets(String s) {
         // 1 - 9 只有一位數, 10 - 26 有 10# - 26# 三位數
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < s.length(); ){
            if(i + 2 < s.length() && s.charAt(i + 2) == '#'){
                sb.append((char) ('a' + Integer.parseInt(s.substring(i, i + 2)) - 1));
                i += 3;
            }else{
                sb.append((char) ('a' + Integer.parseInt(s.substring(i, i + 1)) - 1));
                i++;
            }
        }
        return sb.toString();
    }
}

###[0047]合併兩個排序的鏈表

輸入兩個遞增排序的鏈表,合併這兩個鏈表並使新鏈表中的節點仍然是遞增排序的。

示例1:

輸入:1->2->4, 1->3->4
輸出:1->1->2->3->4->4

限制:

0 <= 鏈表長度 <= 1000

方法一:迭代

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        // maintain an unchanging reference to node ahead of the return node.
        ListNode prehead = new ListNode(-1);

        ListNode prev = prehead;
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                prev.next = l1;
                l1 = l1.next;
            } else {
                prev.next = l2;
                l2 = l2.next;
            }
            prev = prev.next;
        }

        // exactly one of l1 and l2 can be non-null at this point, so connect
        // the non-null list to the end of the merged list.
        prev.next = l1 == null ? l2 : l1;

        return prehead.next;
    }
}

時間複雜度:O(n+m)

空間複雜度:O(1)

方法二:遞歸

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        }
        else if (l2 == null) {
            return l1;
        }
        else if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        }
        else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}

時間複雜度:O(n+m)

空間複雜度:O(n+m)

###[0048]化棧爲隊

實現一個MyQueue類,該類用兩個棧來實現一個隊列。

示例:

MyQueue queue = new MyQueue();

queue.push(1);
queue.push(2);
queue.peek();  // 返回 1
queue.pop();   // 返回 1
queue.empty(); // 返回 false

說明:

你只能使用標準的棧操作 -- 也就是隻有 push to top, peek/pop from top, size 和 is empty 操作是合法的。
你所使用的語言也許不支持棧。你可以使用 list 或者 deque(雙端隊列)來模擬一個棧,只要是標準的棧操作即可。
假設所有操作都是有效的 (例如,一個空的隊列不會調用 pop 或者 peek 操作)。

方法一:

class MyQueue {

        private Stack<Integer> numStack;
        private Stack<Integer> helpStack;

        /** Initialize your data structure here. */
        public MyQueue() {
            numStack = new Stack<>();
            helpStack = new Stack<>();
        }

        /** Push element x to the back of queue. */
        public void push(int x) {
            numStack.push(x);
        }

        /** Removes the element from in front of queue and returns that element. */
        public int pop() {
            peek();
            return helpStack.pop();
        }

        /** Get the front element. */
        public int peek() {
            if (helpStack.isEmpty()) {
                while (!numStack.isEmpty()) {
                    helpStack.push(numStack.pop());
                }
            }

            return helpStack.peek();
        }

        /** Returns whether the queue is empty. */
        public boolean empty() {
            return helpStack.isEmpty() && numStack.isEmpty();
        }
}

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue obj = new MyQueue();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.peek();
 * boolean param_4 = obj.empty();
 */
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章