Leetcode數據結構與算法(二)

[0017] 二進制鏈表轉整數

給你一個單鏈表的引用結點 head。鏈表中每個結點的值不是 0 就是 1。已知此鏈表是一個整數數字的二進制表示形式。

請你返回該鏈表所表示數字的 十進制值 。

示例 1:

輸入:head = [1,0,1]
輸出:5
解釋:二進制數 (101) 轉化爲十進制數 (5)

示例 2:

輸入:head = [0]
輸出:0

示例 3:

輸入:head = [1]
輸出:1

示例 4:

輸入:head = [1,0,0,1,0,0,1,1,1,0,0,0,0,0,0]
輸出:18880

示例 5:

輸入:head = [0,0]
輸出:0

提示:

鏈表不爲空。
鏈表的結點總數不超過 30。
每個結點的值不是 0 就是 1。

方法一:

 class Solution {
        public int getDecimalValue(ListNode head) {
            int sum = 0;
            while(head!=null){
                sum = sum*2 + head.val;
                head = head.next;
            }
            return sum;
        }
    }

方法二:

class Solution {
    public int getDecimalValue(ListNode head) {
        int sum = 0;
         while(head!=null){
            sum = (sum << 1) | (head.val);
            head = head.next;
        }
        return sum;
    }
}
  • 時間複雜度O(N)

  • 空間複雜度O(1)

[0018]訪問所有點的最小時間

平面上有 n 個點,點的位置用整數座標表示 points[i] = [xi, yi]。請你計算訪問所有這些點需要的最小時間(以秒爲單位)。

你可以按照下面的規則在平面上移動:

  • 每一秒沿水平或者豎直方向移動一個單位長度,或者跨過對角線(可以看作在一秒內向水平和豎直方向各移動一個單位長度)。
  • 必須按照數組中出現的順序來訪問這些點。

示例 1:

img`

輸入:points = [[1,1],[3,4],[-1,0]]
輸出:7
解釋:一條最佳的訪問路徑是: [1,1] -> [2,2] -> [3,3] -> [3,4] -> [2,3] -> [1,2] -> [0,1] -> [-1,0]   
從 [1,1] 到 [3,4] 需要 3 秒 
從 [3,4] 到 [-1,0] 需要 4 秒
一共需要 7 秒

方法一:切比雪夫距離

對於平面上的兩個點 x = (x0, x1) 和 y = (y0, y1),設它們橫座標距離之差爲 dx = |x0 - y0|,縱座標距離之差爲 dy = |x1 - y1|,對於以下三種情況,我們可以分別計算出從 x 移動到 y 的最少次數:

  • dx < dy:沿對角線移動 dx 次,再豎直移動 dy - dx 次,總計 dx + (dy - dx) = dy 次;

  • dx == dy:沿對角線移動 dx 次;

  • dx > dy:沿對角線移動 dy 次,再水平移動 dx - dy 次,總計 dy + (dx - dy) = dx 次。

可以發現,對於任意一種情況,從 x 移動到 y 的最少次數爲 dx 和 dy 中的較大值 max(dx, dy),這也被稱作 x 和 y 之間的 切比雪夫距離。

由於題目要求,需要按照數組中出現的順序來訪問這些點。因此我們遍歷整個數組,對於數組中的相鄰兩個點,計算出它們的切比雪夫距離,所有的距離之和即爲答案。

class Solution {
    public int minTimeToVisitAllPoints(int[][] points) {
        int x0 = points[0][0];
        int x1 = points[0][1];
        int ans = 0;
        for(int i = 1; i < points.length; ++i){
            int y0 = points[i][0];
            int y1 = points[i][1];
            ans += Math.max(Math.abs(x0 - y0), Math.abs(x1 - y1));
            x0 = y0;
            x1 = y1;
        }
        return ans;
    }
}

複雜度分析

  • 時間複雜度:O(N)
  • 空間複雜度:O(1)

[0019]刪除鏈表中的節點

請編寫一個函數,使其可以刪除某個鏈表中給定的(非末尾)節點,你將只被給定要求被刪除的節點。

現有一個鏈表 – head = [4,5,1,9],它可以表示爲:

img

示例 1:

輸入: head = [4,5,1,9], node = 5
輸出: [4,1,9]
解釋: 給定你鏈表中值爲 5 的第二個節點,那麼在調用了你的函數之後,該鏈表應變爲 4 -> 1 -> 9.

示例 2:

輸入: head = [4,5,1,9], node = 1
輸出: [4,5,9]
解釋: 給定你鏈表中值爲 1 的第三個節點,那麼在調用了你的函數之後,該鏈表應變爲 4 -> 5 -> 9.

說明:

鏈表至少包含兩個節點。
鏈表中所有節點的值都是唯一的。
給定的節點爲非末尾節點並且一定是鏈表中的一個有效節點。
不要從你的函數中返回任何結果。

方法一:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }
}

時間和空間複雜度都是:O(1)

###[0020]統計有序矩陣中的負數

給你一個 m * n 的矩陣 grid,矩陣中的元素無論是按行還是按列,都以非遞增順序排列。

請你統計並返回 grid負數 的數目。

示例 1:

輸入:grid = [[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]]
輸出:8
解釋:矩陣中共有 8 個負數。

示例 2:

輸入:grid = [[3,2],[1,0]]
輸出:0

示例 3:

輸入:grid = [[1,-1],[-1,-1]]
輸出:3

示例 4:

輸入:grid = [[-1]]
輸出:1

提示:

m == grid.length
n == grid[i].length
1 <= m, n <= 100
-100 <= grid[i][j] <= 100

方法一:暴力

class Solution {
    public int countNegatives(int[][] grid) {
        int m = grid.length; //row
        int n = grid[0].length;//col
        int r = 0;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(grid[i][j] < 0)
                    r++;
            }
        }
        return r;
    }
}
class Solution {
    public int countNegatives(int[][] grid) {
        int m = grid.length; //row
        int n = grid[0].length;//col
        int r = 0;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(grid[i][j] < 0){
                    r += n-j;
                    break;
                }          
            }
        }
        return r;
    }
}
  • 時間複雜度:O(nm)
  • 空間複雜度:O(1)

方法二:二分查找

class Solution {
    public int countNegatives(int[][] grid) {
        int count = 0, m = grid.length, n = grid[0].length;
        for (int i = 0; i < m; i++) {
            int[] row = grid[i];
            if (row[n - 1] >= 0) continue; // 整行非負,跳過
            if (row[0] < 0) { // 整行負數
                count += (m - i) * n; // 後面的行也計入
                break; // 無需再繼續遍歷
            }
            int first = _binarySearch(row); // 當前行二分查找第一個小於 0 的數的索引
            count += n - first;
        }
        return count;
    }

    // 查找第一個小於 0 的數的索引
    private int _binarySearch(int[] arr) {
        int begin = 0, end = arr.length;
        while (begin < end) {
            int mid = begin + ((end - begin) >> 1);
            if (arr[mid] >= 0) begin = mid + 1;
            else { // 負數之後,還要再判斷前一個不是負數
                if (arr[mid - 1] >= 0) return mid;
                end = mid;
            }
        }
        return begin;
    }
}
  • 時間複雜度:二分查找一行的時間複雜度爲logm,需要遍歷n行,所以總時間複雜度是O(nlogm)。
  • 空間複雜度:O(1)。

方法三:分治

https://leetcode-cn.com/problems/count-negative-numbers-in-a-sorted-matrix/solution/tong-ji-you-xu-ju-zhen-zhong-de-fu-shu-by-leetcode/

  • 時間複雜度:T*(n)=2T(n/2)+O(n)=O(*nlogn)

  • 空間複雜度:O(1)

方法四:倒序遍歷

class Solution {
    public int countNegatives(int[][] grid) {
        int m = grid[0].length;
        int pos = grid[0].length -1;
        int num=0;
        for(int i = 0;i < grid.length ;i++){
            int j = 0;
            for(j=pos;j>=0;--j){
                if(grid[i][j] >= 0){
                    if(j+1 < m){
                        pos = j+1;
                        num += m-pos;
                    }
                    break;
                } 
            }
            if(j == -1){
                num += m;
                pos = -1;
            }
        }
        return num;
    }
}

時間複雜度:考慮每次循環變量的起始位置是單調不降的,所以起始位置最多移動m 次,時間複雜度 O(m)。
空間複雜度:O(1)

[0021]返回倒數第 k 個節點

實現一種算法,找出單向鏈表中倒數第 k 個節點。返回該節點的值。

注意:本題相對原題稍作改動

示例:

輸入: 1->2->3->4->5 和 k = 2
輸出: 4

說明:

給定的 k 保證是有效的。

方法一:雙指針

class Solution {
    public int kthToLast(ListNode head, int k) {

        ListNode temp = head;

        while(k-- != 0){
            temp = temp.next;
        }

        while(temp != null){
            temp = temp.next;
            head = head.next;
        }

        return head.val;
    }
}

方法二:遞歸

class Solution {
   // 開始全局變量 K 保持不變
   int pos = 0;
   public int kthToLast(ListNode head, int k) {
       // 當節點在最末尾時觸發返回
       if (head == null) return 0;
       // 返回的值
       int val = kthToLast(head.next, k);
       pos++;
       if (pos == k) {
           return head.val;
       }
       return val;
   }
}

[0022]最小高度樹

給定一個有序整數數組,元素各不相同且按升序排列,編寫一個算法,創建一棵高度最小的二叉搜索樹。

示例:

給定有序數組: [-10,-3,0,5,9],

一個可能的答案是:[0,-3,9,-10,null,5],它可以表示下面這個高度平衡二叉搜索樹:

      0 
     / \ 
   -3   9 
   /   / 
 -10  5 

方法:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        if(nums.length == 0) return null;
        TreeNode n = new TreeNode(nums[nums.length/2]);
        n.left = sortedArrayToBST(Arrays.copyOfRange(nums,0,nums.length/2));
        n.right = sortedArrayToBST(Arrays.copyOfRange(nums,nums.length/2+1,nums.length));
        return 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 sortedArrayToBST(int[] nums) {
        return helper(nums,0,nums.length-1);
    }
    private TreeNode helper(int[] nums,int left,int right){
        if (left>right)
            return null;
        int mid=(left+right)/2;	//(left+right+1)/2;
        TreeNode node=new TreeNode(nums[mid]);
        node.left=helper(nums,left,mid-1);
        node.right=helper(nums,mid+1,right);
        return node;
    }
}

###[0023]鏈表中倒數第k個節點(同0021)

輸入一個鏈表,輸出該鏈表中倒數第k個節點。爲了符合大多數人的習慣,本題從1開始計數,即鏈表的尾節點是倒數第1個節點。例如,一個鏈表有6個節點,從頭節點開始,它們的值依次是1、2、3、4、5、6。這個鏈表的倒數第3個節點是值爲4的節點。

示例:

給定一個鏈表: 1->2->3->4->5, 和 k = 2.

返回鏈表 4->5.

方法一

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode temp = head;
        while(k-- != 0){
            temp = temp.next;
        }
        while(temp != null){
            temp = temp.next;
            head = head.next;
        }
        return head;
    }
}

[0024]二叉樹的深度

輸入一棵二叉樹的根節點,求該樹的深度。從根節點到葉節點依次經過的節點(含根、葉節點)形成樹的一條路徑,最長路徑的長度爲樹的深度。

例如:

給定二叉樹 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

返回它的最大深度 3 。

提示:

  1. 節點總數 <= 10000

方法一:遞歸

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    
    public int maxDepth(TreeNode root) {
       return root==null ? 0:Math.max(maxDepth(root.left),maxDepth(root.right))+1;
    }
}

方法二:棧

本質上是後序遍歷

非遞歸可以利用棧。

把根節點root先壓入棧,創建兩個TreeNode節點,一個用於返回實時的棧頂元素,一個用於返回先前已訪問過節點。對棧判空+循環,開始遍歷,【若當前訪問的節點爲葉子即無子樹,則彈出當前節點並返回至上一個節點並訪問其另外的子樹檢查是否訪問過,若沒有則遍歷一直到葉子爲止】,重複這個操作,即可遍歷整棵樹得到深度

class Solution {
    
    public int maxDepth(TreeNode root) {
       
        if(root == null) return 0;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        TreeNode top; //一個用於返回實時的棧頂元素
        TreeNode lastVisit = root;//一個用於返回先前已訪問過節點
        int cnt = 0;
        while(!stack.isEmpty()){
            cnt = Math.max(cnt,stack.size());
            top = stack.peek();
            if(top.left != null && lastVisit != top.left && lastVisit != top.right){
                stack.push(top.left);
            }
            else if(top.right != null && lastVisit != top.right){
                stack.push(top.right);
            }
            else{
                lastVisit = stack.pop();
            }
            
        }

        return cnt;
    }
}

方法3:雙向隊列Deque

本質上是層次遍歷,也可視爲root數組的從左到右的遍歷

非遞歸還能利用雙向隊列

把根節點放入隊列後再取出接着對其左右子樹判空,有則加入分別加入隊尾,再依次對剩餘節點子樹進行判空操作,若爲葉子節點則使得當前節點指向隊尾(下一次循環則下一層),此時當前層次遍歷完畢,深度+1。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    
    public int maxDepth(TreeNode root) {
       
        if(root == null) return 0;
        
        Deque<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        TreeNode queueLast = root;
        TreeNode nowVisit;
        int depth = 0;
        
        while(!queue.isEmpty()){
            nowVisit = queue.poll();
            if(nowVisit.left != null) queue.offer(nowVisit.left);
            if(nowVisit.right != null) queue.offer(nowVisit.right);
            if(nowVisit == queueLast){
                queueLast = queue.peekLast();
                depth++;
            }
        }
        return depth;
    }
}

###[0025]二叉樹的鏡像

請完成一個函數,輸入一個二叉樹,該函數輸出它的鏡像。

例如輸入:

     4
   /   \
  2     7
 / \   / \
1   3 6   9

鏡像輸出:

     4
   /   \
  7     2
 / \   / \
9   6 3   1

示例 1:

輸入:root = [4,2,7,1,3,6,9]
輸出:[4,7,2,9,6,3,1]

限制:

0 <= 節點個數 <= 1000

方法一:遞歸

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

} 

時間複雜度:每個元素都必須訪問一次,所以是O(n)
空間複雜度:最壞的情況下,需要存放O(h)個函數調用(h是樹的高度),所以是O(h)

方法二:迭代

遞歸實現也就是深度優先遍歷的方式,那麼對應的就是廣度優先遍歷。
廣度優先遍歷需要額外的數據結構–隊列,來存放臨時遍歷到的元素。
深度優先遍歷的特點是一竿子插到底,不行了再退回來繼續;而廣度優先遍歷的特點是層層掃蕩。
所以,我們需要先將根節點放入到隊列中,然後不斷的迭代隊列中的元素。
對當前元素調換其左右子樹的位置,然後:

  • 判斷其左子樹是否爲空,不爲空就放入隊列中
  • 判斷其右子樹是否爲空,不爲空就放入隊列中

動態圖如下:

226_迭代.gif

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {        
        if(root==null) {
			return null;
		}
		//將二叉樹中的節點逐層放入隊列中,再迭代處理隊列中的元素
		LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
		queue.add(root);
		while(!queue.isEmpty()) {
			//每次都從隊列中拿一個節點,並交換這個節點的左右子樹
			TreeNode tmp = queue.poll();
			TreeNode left = tmp.left;
			tmp.left = tmp.right;
			tmp.right = left;
			//如果當前節點的左子樹不爲空,則放入隊列等待後續處理
			if(tmp.left!=null) {
				queue.add(tmp.left);
			}
			//如果當前節點的右子樹不爲空,則放入隊列等待後續處理
			if(tmp.right!=null) {
				queue.add(tmp.right);
			}
			
		}
		//返回處理完的根節點
		return root;
    }

}

###[0026]打印從1到最大的n位數

輸入數字 n,按順序打印出從 1 到最大的 n 位十進制數。比如輸入 3,則打印出 1、2、3 一直到最大的 3 位數 999。

示例 1:

輸入: n = 1
輸出: [1,2,3,4,5,6,7,8,9]

說明:

  • 用返回一個整數列表來代替打印
  • n 爲正整數

方法一:

class Solution {
    public int[] printNumbers(int n) {
        int[] r = new int[(int)Math.pow(10,n) -1];
        for(int i = 0; i < r.length;i++){
            r[i] = i+1;
        }
        return r;
    }
}

方法二:

class Solution {
    StringBuilder sb;
    int idx = 0;
    public boolean increment(int n){
        boolean carry=false;
        for(int i=0;i<sb.length();++i){
            if(carry || i==0){
                if(sb.charAt(i)=='9'){
                    sb.setCharAt(i,'0');
                    carry = true;
                }else{
                    sb.setCharAt(i,(char) (sb.charAt(i)+1));
                    carry = false;
                }
            }else{
                break; // no addition on last idx, no need to compute any more
            }
        }
        if(carry){
            sb.append("1");
        }
        return sb.length()<=n; // overflow!
    }

    public void save(int ans[]){
        ans[idx] = Integer.parseInt(sb.reverse().toString());
        sb.reverse();
    }

    public int[] printNumbers(int n) {
        int[] ans = new int[(int) Math.pow(10,n) - 1];
        sb = new StringBuilder("0");
        while(increment(n)){
            save(ans);
            idx++;
        }
        return ans;
    }
}

拓展

  • 大數打印:可以設定一個閾值,例如long的最大值,當超過了這個最大值,將閾值轉爲字符數組toCharArray(),然後繼續+1打印,這樣可以提高時間效率,因爲一部分的數仍是O(1)打印
  • 全排列:求從1~pow(10,n)-1實際上可以轉化爲0-9n個位置上的全排列

[0027]替換空格

請實現一個函數,把字符串 s 中的每個空格替換成"%20"。

示例 1:

輸入:s = "We are happy."
輸出:"We%20are%20happy."

限制:

0 <= s 的長度 <= 10000

方法一:

class Solution {
    public String replaceSpace(String s) {
        return s.replace(" ","%20");
    }
}

方法二:

class Solution {
    public String replaceSpace(String s) {
        int length = s.length();
        char[] array = new char[length * 3];
        int size = 0;
        for (int i = 0; i < length; i++) {
            char c = s.charAt(i);
            if (c == ' ') {
                array[size++] = '%';
                array[size++] = '2';
                array[size++] = '0';
            } else {
                array[size++] = c;
            }
        }
        String newStr = new String(array, 0, size);
        return newStr;
    }
}

###[0028]刪除中間節點

實現一種算法,刪除單向鏈表中間的某個節點(除了第一個和最後一個節點,不一定是中間節點),假定你只能訪問該節點。

示例:

輸入:單向鏈表a->b->c->d->e->f中的節點c
結果:不返回任何數據,但該鏈表變爲a->b->d->e->f

方法:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val; 
        node.next = node.next.next; 
    }
}

[0029]分割平衡字符串

在一個「平衡字符串」中,‘L’ 和 ‘R’ 字符的數量是相同的。

給出一個平衡字符串 s,請你將它分割成儘可能多的平衡字符串。

返回可以通過分割得到的平衡字符串的最大數量。

示例 1:

輸入:s = "RLRRLLRLRL"
輸出:4
解釋:s 可以分割爲 "RL", "RRLL", "RL", "RL", 每個子字符串中都包含相同數量的 'L' 和 'R'。

示例 2:

輸入:s = "RLLLLRRRLR"
輸出:3
解釋:s 可以分割爲 "RL", "LLLRRR", "LR", 每個子字符串中都包含相同數量的 'L' 和 'R'。

示例 3:

輸入:s = "LLLLRRRR"
輸出:1
解釋:s 只能保持原樣 "LLLLRRRR".

提示:

1 <= s.length <= 1000
s[i] = 'L' 或 'R'

方法一:數組模擬棧

可以利用棧的FILO的特性解題。核心算法是通過取容器頂部的字母來甄別是否與未來要放入容器的字母是互異的。

每次遇到不一樣的,我們把當前字母擦除掉(通過指針位置移動就行)

遇到一樣的,就堆積在容器中。

但每次擦除完,都檢查容器的 size 是否等於 0,是則表明堆積的都被消滅了,找到了一個平衡字符串,計數器加 1。

class Solution {
    public int balancedStringSplit(String s) {
        int count = 0;
        int[] container = new int[s.length()];
        int size = 0;

        for (char c : s.toCharArray()) {
            if (size == 0 || c == container[size-1]) 
                container[size++] = c;              //放入容器
            else if (c != container[size-1]){
                size--;                             //匹配到了互異字母
                if (size == 0)
                    count++;
            }
        }
        return count;
    }
}

時間複雜度:O(N),N 爲字符串 s 的長度。

空間複雜度:O(N),使用了大小爲N 的數組作爲容器。

方法二:計數法

首先以 R 爲基準,設立一個 專門記錄 R 的數量的計數器 counter_R。

  • 遍歷字符串 s,是 ‘R’ 計數器 couter_R 加 1

  • 遇到 ‘L’ 則認爲是消滅 R 的,counter_R 自減 1

  • 每次消滅 R,都遍歷完一次,檢查在這之前的 R 是否還有剩餘

  • 沒有剩餘的話,該位置之前的算是一個由相同數量 L 與 R 組成的 「平衡字符串」

public int balancedStringSplit1(String s) {
    int count = 0;
    int counter_R = 0;
    for (int i = 0; i < s.length(); i++) {
        if (s.charAt(i) == 'R')
            counter_R++;    //記錄R
        else 
            counter_R--;    //消滅R
        if (counter_R == 0)
            count++;	    //證明在i位置之前的字符都有相同數量的L和R
    }
    return count;
}
  • 時間複雜度:O(N),N爲字符串 s的長度。
  • 空間複雜度:O(1),只使用常數級別的空間。

[0030] 刪除最外層的括號

有效括號字符串爲空 ("")、"(" + A + “)” 或 A + B,其中 A 和 B 都是有效的括號字符串,+ 代表字符串的連接。例如,"","()","(())()" 和 “(()(()))” 都是有效的括號字符串。

如果有效字符串 S 非空,且不存在將其拆分爲 S = A+B 的方法,我們稱其爲原語(primitive),其中 A 和 B 都是非空有效括號字符串。

給出一個非空有效字符串 S,考慮將其進行原語化分解,使得:S = P_1 + P_2 + … + P_k,其中 P_i 是有效括號字符串原語。

對 S 進行原語化分解,刪除分解中每個原語字符串的最外層括號,返回 S 。

示例 1:

輸入:"(()())(())"
輸出:"()()()"
解釋:
輸入字符串爲 "(()())(())",原語化分解得到 "(()())" + "(())",
刪除每個部分中的最外層括號後得到 "()()" + "()" = "()()()"。

示例 2:

輸入:"(()())(())(()(()))"
輸出:"()()()()(())"
解釋:
輸入字符串爲 "(()())(())(()(()))",原語化分解得到 "(()())" + "(())" + "(()(()))",
刪除每個部分中的最外層括號後得到 "()()" + "()" + "()(())" = "()()()()(())"。

示例 3:

輸入:"()()"
輸出:""
解釋:
輸入字符串爲 "()()",原語化分解得到 "()" + "()",
刪除每個部分中的最外層括號後得到 "" + "" = ""。

提示:

S.length <= 10000
S[i] 爲 "(" 或 ")"
S 是一個有效括號字符串

方法:

class Solution {
    public String removeOuterParentheses(String S) {
        StringBuilder sb = new StringBuilder();
        int level = 0;
        for (char c : S.toCharArray()) {
            if (c == ')') --level;
            if (level >= 1) sb.append(c);
            if (c == '(') ++level;
        }
        return sb.toString();
    }
}

[0031]按既定順序創建目標數組

給你兩個整數數組 nums 和 index。你需要按照以下規則創建目標數組:

目標數組 target 最初爲空。
按從左到右的順序依次讀取 nums[i] 和 index[i],在 target 數組中的下標 index[i] 處插入值 nums[i] 。
重複上一步,直到在 nums 和 index 中都沒有要讀取的元素。
請你返回目標數組。

題目保證數字插入位置總是存在。

示例 1:

輸入:nums = [0,1,2,3,4], index = [0,1,2,2,1]
輸出:[0,4,1,3,2]
解釋:
nums       index     target
0            0        [0]
1            1        [0,1]
2            2        [0,1,2]
3            2        [0,1,3,2]
4            1        [0,4,1,3,2]

示例 2:

輸入:nums = [1,2,3,4,0], index = [0,1,2,3,0]
輸出:[0,1,2,3,4]
解釋:
nums       index     target
1            0        [1]
2            1        [1,2]
3            2        [1,2,3]
4            3        [1,2,3,4]
0            0        [0,1,2,3,4]

示例 3:

輸入:nums = [1], index = [0]
輸出:[1]

提示:

1 <= nums.length, index.length <= 100
nums.length == index.length
0 <= nums[i] <= 100
0 <= index[i] <= i

方法一:List

class Solution {
    public int[] createTargetArray(int[] nums, int[] index) {
        List<Integer> target = new ArrayList();
        for(int i=0; i<index.length; i++){
            target.add(index[i], nums[i]);
        }
        int[] ret = new int[target.size()];
        for(int i = 0; i < target.size(); i++){
            ret[i] = target.get(i);
        }
        return ret;
    }
}

方法二:數組

class Solution {
    public int[] createTargetArray(int[] nums, int[] index) {
       int[] target = new int[nums.length];
       int pos = -1;
       for(int i=0; i<index.length; i++){
           pos++;
  
        for(int j = pos; j > index[i] ; j--){
            target[j] = target[j-1];
        }
                  
        target[index[i]] = nums[i];
        
       }
       return target;
    }
}

[0032]二叉搜索樹的範圍和

給定二叉搜索樹的根結點 root,返回 L 和 R(含)之間的所有結點的值的和。

二叉搜索樹保證具有唯一的值。

示例 1:

輸入:root = [10,5,15,3,7,null,18], L = 7, R = 15
輸出:32

示例 2:

輸入:root = [10,5,15,3,7,13,18,1,null,6], L = 6, R = 10
輸出:23

提示:

樹中的結點數量最多爲 10000 個。
最終的答案保證小於 2^31。

方法一:

class Solution {
    public int rangeSumBST(TreeNode root, int L, int R) {
        if (root == null) {
            return 0;
        }
        if (root.val < L) {
            return rangeSumBST(root.right, L, R);
        }
        if (root.val > R) {
            return rangeSumBST(root.left, L, R);
        }
        return root.val + rangeSumBST(root.left, L, R) + rangeSumBST(root.right, L, R);
    }
}

方法二:

class Solution {
    public int rangeSumBST(TreeNode root, int L, int R) {
        int ans = 0;
        Stack<TreeNode> stack = new Stack();
        stack.push(root);
        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            if (node != null) {
                if (L <= node.val && node.val <= R)
                    ans += node.val;
                if (L < node.val)
                    stack.push(node.left);
                if (node.val < R)
                    stack.push(node.right);
            }
        }
        return ans;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章