Zusammenfassung der dritten Staffel von Falling in Love with Data Structure Algorithms

Etwas

- Bestimmen Sie effizient, ob eine ganze Zahl ungerade ist
boolean isOdd = (n & 1) == 1;

linearer Tisch

- Array
  • Bei den meisten Array-Sortierproblemen kann das Problem schnell und einfach mithilfe von Doppelzeigern oder Dreizeigern gelöst werden.

    • Für viele Problemlösungsmethoden, deren Zeitkomplexität O(n2) oder O(n3) ist, können Sie die Verwendung von Speicherplatz in Betracht ziehen Tauschen Sie Zeittricks aus, um die Zeitkomplexität zu reduzieren

      • Nachfolgende Array-Probleme nutzten alle diese Problemlösungstechnik.
      • Es ist nur so, dass die spezifische Methode, Raum gegen Zeit zu tauschen, bei jeder Frage unterschiedlich sein kann.
      • Aber die Idee, Raum gegen Zeit einzutauschen, ist dieselbe.
    • Doppelte Zeiger: Verwenden Sie einen Zeiger, um auf die aktuelle Position zu zeigen, und verwenden Sie einen Zeiger, um auf die auszutauschende Position zu zeigen.

    • Drei Zeiger, einer zeigt auf den aktuellen Punkt, einer zeigt auf den Anfang und einer zeigt auf das Ende.

      • Thema:

        • 75. Farbklassifizierung
          public static void sortColors(int[] nums) {
                      
                      
            if (nums == null || nums.length == 0) return;
          
            int curP = 0;
            int zeroP = 0;
            int twoP = nums.length - 1;
            while (curP <= twoP) {
                      
                      
              if (nums[curP] == 2) {
                      
                      
                nums[curP] = nums[twoP];
                nums[twoP--] = 2;
              } else if (nums[curP] == 0) {
                      
                      
                nums[curP++] = nums[zeroP];
                nums[zeroP++] = 0;
              } else {
                      
                      
                curP++;
              }
            }
          }
          
        • 88. Zwei sortierte Arrays zusammenführen

          public static void merge(int[] nums1, int m, int[] nums2, int n) {
                      
                      
            if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) return;
          
            int i1 = m - 1;
            int i2 = n - 1;
            int cur = nums1.length - 1;
            while (i2 >= 0) {
                      
                      
              if (i1 >= 0 && nums1[i1] > nums2[i2]) {
                      
                      
                nums1[cur--] = nums1[i1--];
              } else {
                      
                      
                nums1[cur--] = nums2[i2--];
              }
            }
          }
          
        • Interviewfrage 16.16. Teilweise Bestellung

          public static int[] subSort(int[] array) {
                      
                      
            if (array == null || array.length == 0) return new int[]{
                      
                      -1, -1};
          
            int i = 1;
            int m = -1;
            int max = array[0];
            while (i < array.length) {
                      
                      
              if (array[i] < max) {
                      
                      
                m = i;
              } else if (array[i] > max) {
                      
                      
                max = array[i];
              }
              i++;
            }
          
            if (m == -1) return new int[]{
                      
                      -1, -1};
          
            i = array.length - 2;
            int n = -1;
            int min = array[array.length - 1];
            while (i >= 0) {
                      
                      
              if (array[i] > min) {
                      
                      
                n = i;
              } else if (array[i] < min) {
                      
                      
                min = array[i];
              }
              i--;
            }
          
            return new int[]{
                      
                      n, m};
          }
          
        • 977. Quadrat der geordneten Anordnung

          static public int[] sortedSquares(int[] nums) {
                      
                      
            if (nums == null || nums.length == 0) return nums;
          
            int[] sortedNums = new int[nums.length];
            int begin = 0;
            int end = nums.length - 1;
            int cur = end;
            while (begin <= end) {
                      
                      
              int beginNum = nums[begin];
              if (beginNum < 0) {
                      
                      
                beginNum = -beginNum;
              }
              int endNum = nums[end];
              if (endNum < 0) {
                      
                      
                endNum = -endNum;
              }
          
              if (beginNum > endNum) {
                      
                      
                sortedNums[cur--] = beginNum * beginNum;
                begin++;
              } else {
                      
                      
                sortedNums[cur--] = endNum * endNum;
                end--;
              }
            }
            return sortedNums;
          }
          
- verlinkte Liste
  • Wie bei Arrays werden die meisten Probleme mit verknüpften Listen mithilfe mehrerer Zeiger gelöst.

  • Finden Sie den mittleren Knoten

    • Benutzen Sie einfach den Geschwindigkeitszeiger

      • Die schnellen und langsamen Zeiger beginnen gleichzeitig am Kopf
      • Machen Sie schnell zwei Schritte auf einmal
      • Machen Sie langsam einen Schritt nach dem anderen
      • Wenn der Schnelle den leeren Raum erreicht, ist der Langsame in der Mitte.
      • Dabei gilt das Prinzip: Der Schnellere ist doppelt so langsam wie der Langsame
      // 快慢指针找中间节点
      // 两指针从初试位置出发
      // 快指针走两步 慢指针走一步
      // 当快指针为null的时候,慢指针此时指向的就是中间节点
      ListNode middleNode(ListNode head) {
              
              
        ListNode fast = head;
        ListNode slow = head;
        while (fast.next != null && fast.next.next != null) {
              
              
          fast = fast.next.next;
          slow = slow.next;
        }
        return slow;
      }
      
  • Umgekehrt verknüpfte Liste

    • Erstellen Sie einen neuen leeren Knoten newHead

      • Beginnen Sie mit der Traversierung am ersten Knotenkopf
      • Gehen Sie zu einem Knoten und verwenden Sie zunächst einen TMP, um den nächsten aktuellen Knoten zu speichern
      • Zeigen Sie mit dem nächsten Knoten des aktuellen Knotens auf newHead
      • Zeigen Sie, dass der neue Kopf neben dem Kopf liegt
      • Schließlich zeigt head auf tmp.
      ListNode revert(ListNode head) {
              
              
        if (head == null || head.next == null) return head;
      
        ListNode cur = head;
        ListNode newHead = new ListNode(0);
        while (cur != null) {
              
              
          ListNode nextNode = cur.next;
          ListNode tmpNode = cur;
          tmpNode.next = newHead.next;
          newHead.next = tmpNode;
          cur = nextNode;
        }
        return newHead.next;
      }
      
  • Thema

    • 160. Überschnittene verknüpfte Listen

      • Ideen zur Problemlösung:Verbinden Sie die ersten Positionen der AB-verknüpften Listen
        • Das Ende der verknüpften Liste A ist mit dem Kopf der verknüpften Liste B verknüpft.

        • Das Ende der verknüpften Liste B ist mit dem Kopf der verknüpften Liste A verknüpft

        • Dadurch werden die Längen der beiden verknüpften Listen gleich

        • Scannen Sie dann einmal vom Kopfknoten von AB

        • Der erste gefundene Knoten, der gleich ist, ist die Antwort.

  public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    
    
      if (headA == null || headB == null) return null;
  
      ListNode nodeA = headA;
      ListNode nodeB = headB;
      while (nodeA != nodeB) {
    
    
        nodeA = (nodeA == null) ? headB : nodeA.next;
        nodeB = (nodeB == null) ? headA : nodeB.next;
  		}
      return nodeA;
  }
  	
  public ListNode getIntersectionNode1(ListNode headA, ListNode headB) {
    
    
      if (headA == null || headB == null) return null;
  
      ListNode nodeA = headA;
      ListNode nodeB = headB;
      boolean isHeadAConnected = false;
      boolean isHeadBConnected = false;
      while (nodeA != nodeB) {
    
    
        if (nodeA == null && !isHeadAConnected) {
    
    
          nodeA = headB;
          isHeadAConnected = true;
        } else {
    
    
          nodeA = nodeA.next;
        }
        if (nodeB == null && !isHeadBConnected) {
    
    
          nodeB = headA;
          isHeadBConnected = true;
        } else {
    
    
          nodeB = nodeB.next;
        }
      }
      return nodeA;
  }
  • 2. Addiere zwei Zahlen

    • Lösung: Es ist die normale Methode, zwei Zahlen zu addieren, die man in der Grundschule gelernt hat.
  // 这个方法也是解决大数相加的一个方法
  public static ListNode addTwoNumbers(ListNode l1, ListNode l2) {
    
    
    if (l1 == null) return l2;
    if (l2 == null) return l1;
  
    ListNode dummyHead = new ListNode(0);
    ListNode cur = dummyHead;
    int carry = 0;
    while (l1 != null || l2 != null) {
    
    
      int val1 = l1 == null ? 0 : l1.val;
      int val2 = l2 == null ? 0 : l2.val;
      int sum = val1 + val2 + carry;
      if (sum > 9) {
    
    
        carry = 1;
        sum = sum % 10;
      } else {
    
    
        carry = 0;
      }
      cur.next = new ListNode(sum);
      cur = cur.next;
      l1 = l1 == null ? l1 : l1.next;
      l2 = l2 == null ? l2 : l2.next;
    }
  
    // 如果最后两位相加结果还有进位的话,这时候需要再新建一个节点
    if (carry == 1) {
    
    
      cur.next = new ListNode(1);
    }
  
    return dummyHead.next;
  }
    ```

  - [203. 移除链表元素](https://leetcode.cn/problems/remove-linked-list-elements/)

    - https://leetcode.cn/problems/remove-linked-list-elements/

    - 这题很简单,不赘述


    ```java
  public static ListNode removeElements(ListNode head, int val) {
    
    
    if (head == null) return head;
  
    ListNode newNode = new ListNode();
    ListNode curNode = newNode;
    while (head != null) {
    
    
      if (head.val != val) {
    
    
        curNode.next = head;
        curNode = curNode.next;
      }
  
      head = head.next;
    }
    curNode.next = null;
    return newNode.next;
  }
  • 234. Palindrom-verknüpfte Liste

    • Ideen zur Problemlösung
      • Suchen Sie zuerst den mittleren Knoten

      • Dann kehren Sie die zweite Hälfte der Knoten um

      • Beginnen Sie dann am mittleren Knoten und verteilen Sie ihn auf beide Seiten.

      • Stellen Sie abschließend die zweite Hälfte der Knoten wieder her

public boolean isPalindrome(ListNode head) {
    
    
  if (head == null || head.next == null) return true;
  if (head.next.next == null) return head.val == head.next.val;

  ListNode middleNode = middleNode(head);
  ListNode revertedNode = revert(middleNode.next);
  while (revertedNode != null) {
    
    
    if (head.val != revertedNode.val) {
    
    
      return false;
    }
    head = head.next;
    revertedNode = revertedNode.next;
  }
  return true;
}

// 快慢指针找中间节点
// 两指针从初试位置出发
// 快指针走两步 慢指针走一步
// 当快指针为null的时候,慢指针此时指向的就是中间节点
ListNode middleNode(ListNode head) {
    
    
  ListNode fast = head;
  ListNode slow = head;
  while (fast.next != null && fast.next.next != null) {
    
    
    fast = fast.next.next;
    slow = slow.next;
  }
  return slow;
}

// 反转链表
ListNode revert(ListNode head) {
    
    
  if (head == null || head.next == null) return head;

  ListNode cur = head;
  ListNode newHead = new ListNode(0);
  while (cur != null) {
    
    
    ListNode nextNode = cur.next;
    ListNode tmpNode = cur;
    tmpNode.next = newHead.next;
    newHead.next = tmpNode;
    cur = nextNode;
  }
  return newHead.next;
}
  • 86. Separate verknüpfte Liste

    • Ideen zur Problemlösung
      • Verwenden Sie zwei verknüpfte Listen, um Knoten größer und kleiner als x zu speichern, und verbinden Sie die beiden verknüpften Listen, nachdem die Schleife abgeschlossen ist.
  // 思路:用两个链表存储大于和小于x的节点,循环完了之后再将两个链表相连
  public static ListNode partition(ListNode head, int x) {
    
    
    if (head == null) return null;
  
    ListNode resultNode = new ListNode(0);
    ListNode curNode = resultNode;
    ListNode rightNode = new ListNode(0);
    ListNode curRightNode = rightNode;
    while (head != null) {
    
    
      if (head.val < x) {
    
    
        curNode.next = head;
        curNode = curNode.next;
      } else {
    
    
        curRightNode.next = head;
        curRightNode = curRightNode.next;
      }
      head = head.next;
    }
    curRightNode.next = null;
    curNode.next = rightNode.next;
    return resultNode.next;
  }
- Stapel und Warteschlangen
  • Thema

    • 155. Mindeststapel
      • Lösung
        • Geben Sie eine Eigenschaft an, um den aktuellen Mindestwert zu speichern
        • Ermitteln und aktualisieren Sie einfach den Mindestwert während des Push-and-Pop-Vorgangs.
private List<Integer> list = new ArrayList<>();
	private int minVal;
	
	public MinStack() {
    
    
      this.minVal = Integer.MAX_VALUE;
  }

  public void push(int val) {
    
    
      list.add(val);
      if (val < this.minVal) {
    
    
      this.minVal = val;
    }
  }

  public void pop() {
    
    
      int val = list.remove(list.size() - 1);
      if (val == this.minVal) {
    
    
        this.minVal = Integer.MAX_VALUE;
      for (int i = 0; i < list.size(); i++) {
    
    
        int v = list.get(i);
        if (v < this.minVal) {
    
    
          this.minVal = v;
        }
      }
    }
  }

  public int top() {
    
    
      return list.get(list.size() - 1);
  }

  public int getMin() {
    
    
      return this.minVal;
  }
  • 239. Maximaler Wert des Schiebefensters – Wichtige Punkte

    • Verwenden Sie eine doppelendige Warteschlange

    • Kernprinzipien

      • Stellen Sie sicher, dass das aktuelle Kopfelement den Maximalwert hat
      • Man spricht daher auch von einer monotonen Warteschlange
    • Komplexität O(n)

public int[] maxSlidingWindow(int[] nums, int k) {
    
    
  if (nums == null || nums.length == 0 || k < 1) return null;
  if (k == 1) return nums;

  // 用于保存index
  Deque<Integer> queue = new LinkedList<>();
  queue.add(0);
  int[] result = new int[nums.length + 1 - k];
  // 记录当前队列的队头位置
  int begin = 0;
  for (int i = 1; i < nums.length; i++) {
    
    
    // i即为当前队列的队尾
    begin = i + 1 - k;
    int num = nums[i];
    // 移除比num小的index, 使队列为单调队列
    while (!queue.isEmpty() && nums[queue.getLast()] < num) {
    
    
      queue.removeLast();
    }
    queue.add(i);
    // 移除不在队列中的index
    if (queue.getFirst() < begin) {
    
    
      queue.removeFirst();
    }
    // 将最大值进行存放
    if (begin >= 0) {
    
    
      result[begin] = nums[queue.getFirst()];
    }
  }
  return result;
  // 字符串和链表很常考
}
  • 739. Tagestemperatur
    • Stack verwenden – Dieser Code muss im Hintergrund geschrieben werden
    • Vorstellungsgespräche finden häufiger statt
    • Ähnliche Fragen
      • Suchen Sie den ersten Wert auf der rechten Seite des Arrays, der größer als der aktuelle Wert ist
      • Suchen Sie den ersten Wert links im Array, der größer als der aktuelle Wert ist
    public int[] dailyTemperatures(int[] temperatures) {
          
          
      if (temperatures == null || temperatures.length == 0) return null;
    
      int[] result = new int[temperatures.length];
      // 这是一个单调递减栈
      Stack<Integer> stack = new Stack<>();
      for (int i = 0; i < temperatures.length; i++) {
          
          
        while (true) {
          
          
          if (stack.isEmpty()) {
          
          
            stack.push(i);
            break;
          }
          if (temperatures[i] <= temperatures[stack.peek()]) {
          
          
            stack.push(i);
            break;
          } else {
          
          
            // 当当前值比top的大,pop出来的时候,当前值就是右边第一个比此时pop出来的值大的值
            Integer idx = stack.pop();
            result[idx] = i - idx;
          }
        }
      }
      return result;
    }
    
  • 654. Maximaler Binärbaum
    • Gleiche Lösung wie bei der obigen Frage
    • Wir müssen nur den ersten Wert auf der linken Seite finden, der größer als er ist.
- Dynamische Programmierung - DP (Dynamische Programmierung)
  • In den meisten Fällen kann dynamische Programmierung verwendet werden, um den optimalen Wert zu finden.

  • Das Schwert zeigt auf Angebot 47. Der maximale Wert des Geschenks

    public int maxValue(int[][] grid) {
          
          
    		int r = grid.length > 1 ? 2 : 1;
    		int[][] maxRowVals = new int[r][grid[0].length];
    		// 当前row和前一个row
    		// 主要用于刷新和记录已计算的之前两行的最大值
    		int curRow = 0;
    		int preRow = 0;
    		// 动态规划求解
    		// 从最左边开始,一次向右和向下遍历
    		// 将遍历到的当前val计算出其最大值,之后存储
    		/* 求解思路:
    		 * 当前的最大值 = 当前值 + max(当前行前一行最大值, 当前列前一列最大值)
    		 * */
    		for (int row = 0; row < grid.length; row++) {
          
          
    			for (int col = 0; col < grid[0].length; col++) {
          
          
    				if (row == 0) {
          
          
    					maxRowVals[0][col] = col == 0 ? grid[0][0] : maxRowVals[0][col - 1] + grid[0][col];
    				} else {
          
          
    					curRow = row & 1;
    					preRow = 1 - curRow;
    					if (col == 0) {
          
          
    						maxRowVals[curRow][col] = grid[row][col] + maxRowVals[preRow][col];
    					} else {
          
          
    						maxRowVals[curRow][col] = Math.max(maxRowVals[preRow][col], maxRowVals[curRow][col -1]) + grid[row][col];
    					}
    				}
    			}
    		}
    		return maxRowVals[curRow][grid[0].length - 1];
    }
    
    • 121. Beste Zeit zum Kaufen und Verkaufen von Aktien

      • Optimale Lösung, ähnlich der DP-Lösung

        /*
         * 这种解法类似动态规划解决
         * 但是也可以用动态规划解决,但是没这种的效率高
         * 这种问题直接求出以每一个位置的数值结尾时的最大利益值
         * 之后保存这个最大利益值,将每次求出的最大利益值,跟当前求出的最大利益值比较
         * 即可求出最终的最大值
         * */
        public class _121_买卖股票的最佳时机 {
                  
                  
        	public int maxProfit(int[] prices) {
                  
                  
        		if (prices == null || prices.length == 0 || prices.length == 1) return 0;
        		
        		int len = prices.length;
        		int max = 0;
        		int min = prices[0];
        		for (int i = 1; i < len; i++) {
                  
                  
        			if (prices[i] < min) {
                  
                  
        				min = prices[i];
        			} else {
                  
                  
        				int tmpMax = prices[i] - min;
        				if (tmpMax > max) {
                  
                  
        					max = tmpMax;
        				}
        			}
        		}
        		return max;
            }
        }
        
      • Die dp-Lösung kann ebenfalls verwendet werden, es gibt jedoch keine bessere Lösung.

        • Idee: Berechnen Sie die Differenz zwischen den einzelnen Werten, um ein Array von Differenzen zu erhalten. Die endgültige Lösung besteht darin, die maximale Summe aufeinanderfolgender Teilsequenzen zu ermitteln.
    • 72 Distanz bearbeiten – eine wichtige klassische Frage

      • Klassische DP-Lösung

        • Dies ist zwar komplizierter, aber es handelt sich tatsächlich um eine klassische DP-Lösung
        public int minDistance(String word1, String word2) {
                  
                  
        		char[] chars1 = word1.toCharArray();
        		char[] chars2 = word2.toCharArray();
        		// 新增一个dp数组,大小为n + 1和m + 1的二维数组, 用于存储转换结果
        		// 因此,dp[i][j]即为最终结果
        		// 例如,dp[word1.length - 1][word2.length - 1]即为这个题的解
        		int len1 = word1.length() + 1;
        		int len2 = word2.length() + 1;
        		int[][] dp = new int[len1][len2];
        		// 先将0行0列的dp赋值
        		// 很明显,都是非空字符串转空串,或者空串转字非空符串
        		// 因此,结果即是:非空串长度为多长,则最短进行多少次
        		for (int i = 0; i < len2; i++) {
                  
                  
        			dp[0][i] = i;
        		}
        		for (int i = 1; i < len1; i++) {
                  
                  
        			dp[i][0] = i;
        		}
        		for (int i = 1; i < len1; i++) {
                  
                  
        			for (int j = 1; j < len2; j++) {
                  
                  
        				// 这里有三种情况
        				// 算出三种情况,求出最小值即可
        				int min1, min2 ,min3 = 0;
        				// 1.求dp[i - 1][j - 1]和dp[i][j]之间的转换方程
        				if (chars1[i - 1] == chars2[j - 1]) {
                  
                  
        					// 当i和j最后一个字符相同的时候
        					min1 = dp[i - 1][j - 1];
        				} else {
                  
                  
        					// 当i和j最后一个字符不相同的时候
        					min1 = 1 + dp[i - 1][j - 1];
        				}
        				// 2.求dp[i - 1][j]和dp[i][j]之间的转换方程
        				min2 = 1 + dp[i - 1][j];
        				// 2.求dp[i][j - 1]和dp[i][j]之间的转换方程
        				min3= 1 + dp[i][j - 1];
        				dp[i][j] = Math.min(Math.min(min1, min2), min3);
        			}
        		}
        		return dp[len1 - 1][len2 - 1];
        }
        
    • 5. Der längste Palindrom-Teilstring

      • Klassische DP-Lösung

        • Komplexität O(n^3)

          // dp解法,但不是最优解,复杂度是O(n^3)
          public String longestPalindrome(String s) {
                      
                      
            int len = s.length();
            if (len == 1) return s;
          
            char[] chars = s.toCharArray();
            boolean[][] dp = new boolean[len][len];
            int begin = 0;
            int maxLen = 0;
          
            for (int i = len - 1; i >= 0; i--) {
                      
                      
              for (int j = i; j < len ; j++) {
                      
                      
                if (j + 1 - i <= 2) {
                      
                      
                  dp[i][j] = chars[i] == chars[j];
                } else {
                      
                      
                  dp[i][j] = dp[i + 1][j - 1] && (chars[i] == chars[j]);
                }
                int tmpMax = j + 1 - i;
                if (dp[i][j] && tmpMax > maxLen) {
                      
                      
                  maxLen = tmpMax;
                  begin = i;
                }
              }
            }
            return new String(chars, begin, maxLen);
          }
          
      • Zentrale Erweiterungsmethode

        • Nehmen Sie jedes durchlaufene Zeichen als Mittelpunkt oder jede Zeichenlücke als Mittelpunkt und durchlaufen Sie gleichzeitig links und rechts, um einen Vergleich zu erhalten.
        • Komplexität O(n^2)
        private int begin = 0;
        private int maxLen = 1;
        
        // 扩展中心法,这种方法效率高于dp法
        // 以每个字符或者字符间隙为中心进行两边扩展
        // 复杂度是O(n^2)
        public String longestPalindrome(String s) {
                  
                  
          int len = s.length();
          if (len == 1) return s;
        
          begin = 0;
          maxLen = 1;
          char[] cs = s.toCharArray();
          for (int i = 1; i < len; i++) {
                  
                  
            // 以间隙为中心
            palindromeLength(cs, i - 1, i);
            // 以字符为中心
            palindromeLength(cs, i - 1, i + 1);
          }
          return new String(cs, begin + 1, maxLen);
        }
        
        // 给出l左边和r右边
        // 分别同时向左向右扫描对比,找出最长回文字串
        // 并判断求出maxLen和begin
        private void palindromeLength(char[] cs, int l, int r) {
                  
                  
          while (l >= 0 && r < cs.length) {
                  
                  
            if (cs[l] != cs[r]) {
                  
                  
              break;
            }
        
            l--;
            r++;
          }
          if (r - l - 1 > maxLen) {
                  
                  
             maxLen = r - l - 1;
             begin = l;
          }
        }
        

Binärbaum

  • Die meisten Interviewfragen zu Binärbäumen können durch Rekursion plus Traversierung gelöst werden

  • 236. Jüngster gemeinsamer Vorfahre des Binärbaums

    • Rekursion
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
          
          
      if (root == null || root == p || root == q) return root;
    
      TreeNode left = lowestCommonAncestor(root.left, p, q);
      TreeNode right = lowestCommonAncestor(root.right, p, q);
      if (left != null && right != null) return root;
    
      return left != null ? left : right;
    }
    
  • 99. Stellen Sie den binären Suchbaum wieder her

    • Nutzen Sie BST-Merkmale: Die Ergebnisse des Durchlaufs in der Reihenfolge werden in aufsteigender Reihenfolge gelöst

      • Wenn aus den Durchlaufergebnissen in der Reihenfolge nur ein Paar in umgekehrter Reihenfolge gefunden wird und die beiden falschen Knotendurchlaufergebnisse nebeneinander liegen, müssen Sie nur die Positionen der Paare in umgekehrter Reihenfolge austauschen.
      • Wenn aus den Ergebnissen der In-Order-Traversierung zwei Paare in umgekehrter Reihenfolge gefunden werden, können der erste Knoten des ersten Paares in umgekehrter Reihenfolge und der zweite Knoten des zweiten Paares in umgekehrter Reihenfolge interagieren.
      • Fügen Sie hier eine Bildbeschreibung ein
      • Fügen Sie hier eine Bildbeschreibung ein
      public class _99_恢复二叉搜索树 {
              
              
      	TreeNode prev;
      	TreeNode first;
      	TreeNode sec;
        
        // Morris遍历,可以让空间复杂度是O(1),时间复杂度O(n)
      	// 也叫 线索二叉树
      	public void recoverTree(TreeNode root) {
              
              
      		if (root == null) return;
      		
      		TreeNode node = root;
      		while (node != null) {
              
              
      			if (node.left != null) {
              
              
      				TreeNode pred = node.left;
      				while (pred.right != null && pred.right != node) {
              
              
      					pred = pred.right;
      				}
      				
      				if (pred.right == null) {
              
              
      					pred.right = node;
      					node = node.left;
      				} else {
              
              
      					System.out.print(node.val + " ");
      					if (prev != null && prev.val > node.val) {
              
              
      						// 出现逆序对
      						sec = node;
      						if (first == null) {
              
              
      							first = prev;
      						}
      					}
      					prev = node;
      					pred.right = null;
      					node = node.right;
      				}
      			} else {
              
              
      				System.out.print(node.val + " ");
      				if (prev != null && prev.val > node.val) {
              
              
      					// 出现逆序对
      					sec = node;
      					if (first == null) {
              
              
      						first = prev;
      					}
      				}
      				prev = node;
      				node = node.right;
      			}
      		}
      		
      		int val = first.val;
      		first.val = sec.val;
      		sec.val = val;
        }
      	
      	public void recoverTree1(TreeNode root) {
              
              
      		if (root == null) return;
      		
      		find(root);
      		
      		int val = first.val;
      		first.val = sec.val;
      		sec.val = val;
          }
      	
      	private void find(TreeNode node) {
              
              
      		if (node == null) return;
      		
      		find(node.left);
      		
      		if (prev != null && prev.val > node.val) {
              
              
      			// 出现逆序对
      			sec = node;
      			if (first == null) {
              
              
      				first = prev;
      			}
      		}
      		prev = node;
      		
      		find(node.right);
      	}
      }
      

DFS – Tiefensuche, Tiefensuche

  • Permutationen und KombinationenDie meisten davon können mitDFS gelöst werden

  • Backtracking, Rekursion wird automatisch zurückverfolgt

  • Beschneidung

  • Die wichtigsten Punkte von DFS

    • Verstehen Sie, welche Optionen auf der nächsten Ebene verfügbar sind

    • Verstehen Sie die Endbedingungen der letzten Schicht und was die letzte Schicht tut

    • Verstehen Sie, welche Bedingungen für ein Backtracking erreicht werden

  • _17_Telefonnummernalphabet

    • https://leetcode.cn/problems/letter-combinations-of-a-phone-number/
    • Rekursion
    • Zurückverfolgen
    • Fügen Sie hier eine Bildbeschreibung ein
    public class _17_电话号码的字母组合 {
          
          
    	private char[][] lettersArray = {
          
          
    			{
          
          'a', 'b', 'c'},
    			{
          
          'd', 'e', 'f'},
    			{
          
          'g', 'h', 'i'},
    			{
          
          'j', 'k', 'l'},
    			{
          
          'm', 'n', 'o'},
    			{
          
          'p', 'q', 'r', 's'},
    			{
          
          't', 'u', 'v'},
    			{
          
          'w', 'x', 'y', 'z'}
    			};
    	private char[] chars;
    	private List<String> list;
    	private char[] string;
    	
    	public List<String> letterCombinations(String digits) {
          
          
    		if (digits == null) return null;
    		
    		list = new ArrayList<>();
    		chars = digits.toCharArray();
    		if (chars.length == 0) return list;
    		
    		string = new char[chars.length];
    		dfs(0);
    		return list;
        }
    	
    	private void dfs(int idx) {
          
          
    		// 先dfs最后一层
    		if (idx == chars.length) {
          
          
    			list.add(new String(string));
    			return;
    		}
    		
    		// 再做非最后一层的处理
    		char[] letters = lettersArray[chars[idx] - '2'];
    		for (char c : letters) {
          
          
    			string[idx] = c;
    			dfs(idx + 1);
    		}
    	}
    }
    
  • 46. ​​​​Vollständige Anordnung

    • Diese Methode zur Problemlösung ist die einfachste und effizienteste
    • Fügen Sie hier eine Bildbeschreibung ein
    • Fügen Sie hier eine Bildbeschreibung ein
    public class _46_全排列 {
          
          
    	private List<List<Integer>> list = new ArrayList<>();
    	
    	public List<List<Integer>> permute(int[] nums) {
          
          
        if (nums == null) return null;
    		if (nums.length == 0) return list;
        
    		dfs(nums, 0);
    		return list;
        }
    	
    	private void dfs(int[] nums, int idx) {
          
          
    		if (nums.length == idx) {
          
          
    			List<Integer> result = new ArrayList<>();
    			for (int num : nums) {
          
          
    				result.add(num);
    			}
    			list.add(result);
    			return;
    		}
    		
    		for (int i = idx; i < nums.length; i++) {
          
          
    			// 交换位置
    			swap(nums, idx, i);
    			dfs(nums, idx + 1);
    			// 必须再次交换,不然的话,就会出错,因为后续的交换必须在之前数字顺序的情况下进行交换才行
    			// 因此这里是还原之前的位置
    			swap(nums, idx, i);
    		}
    	}
    	
      // 交换方法
    	private void swap(int[] nums, int i, int j) {
          
          
    		int tmp = nums[i];
    		nums[i] = nums[j];
    		nums[j] = tmp;
    	}
    }
    
  • 47. Vollständiges Arrangement II

    • Die gleiche Lösung wie die vollständige Anordnung 1, jedoch mit einem zusätzlichen Deduplizierungsvorgang
    • Fügen Sie hier eine Bildbeschreibung ein
    • Fügen Sie hier eine Bildbeschreibung ein
    public class _47_全排列II {
          
          
    	private List<List<Integer>> list = new ArrayList<>();
    	
    	public List<List<Integer>> permuteUnique(int[] nums) {
          
          
    		if (nums == null) return null;
    		if (nums.length == 0) return list;
    		
    		dfs(nums, 0);
    		return list;
        }
    	
    	private void dfs(int[] nums, int idx) {
          
          
    		if (idx == nums.length) {
          
           
    			List<Integer> result = new ArrayList<>();
    			for (int num : nums) {
          
          
    				result.add(num);
    			}
    			list.add(result);
    			return;
    		}
    		
    		for (int i = idx; i < nums.length; i++) {
          
          
    			if (isRepeat(nums, idx, i)) continue;
    			
    			swap(nums, idx, i);
    			dfs(nums, idx + 1);
    			swap(nums, idx, i);
    		}
    	}
    	
      // 判断重复数字操作
    	private boolean isRepeat(int[] nums, int idx, int i) {
          
          
    		for (int j = idx; j < i; j++) {
          
          
    			if (nums[i] == nums[j]) return true;
    		}
    		return false;
    	}
    	
    	private void swap(int[] nums, int i, int j) {
          
          
    		int tmp = nums[i];
    		nums[i] = nums[j];
    		nums[j] = tmp;
    	}
    }
    
  • 22. Klammergenerierung

    • Diese Frage ist ein typischer DFS-Fragetyp, sie ist jedoch nicht so offensichtlich wie die vorherige Beschreibung von Ebenen und for-Schleifen.
    • Fügen Sie hier eine Bildbeschreibung ein
    public class _22_括号生成 {
          
          
    	private List<String> list = new ArrayList<>();
    	private char[] result;
    	
    	public List<String> generateParenthesis(int n) {
          
          
    		if (n < 0) return list;
    		
    		result = new char[n*2];
    		dfs(0, n, n);
    		return list;
        }
    	
    	private void dfs(int idx, int leftRemain, int rightRemain) {
          
          
    		if (idx == result.length) {
          
           
    			list.add(new String(result));
    			System.out.println(result);
    			return;
    		}
    		
    		
    		// 这两个if就是之前常见的for循环
    		// 只是拆开成了两个单独的出来而已
    		if (leftRemain > 0) {
          
          
    			result[idx] = '(';
    			// 这里必须是在dfs的时候传参leftRemain - 1
    			// 而不是dfs之前leftRemain--
    			// 因为这里leftRemain--的话,就会让下面执行右括号的时候,leftRemain少了1
    			dfs(idx + 1, leftRemain - 1, rightRemain);
    		}
    		
        // 这里的idx和上面的idx是一样的
    		// 因此他们是平行关系
    		if (leftRemain != rightRemain && rightRemain > 0) {
          
          
    			result[idx] = ')';
    			// 这里必须是在dfs的时候传参rightRemain - 1
    			// 而不是dfs之前rightRemain--
    			dfs(idx + 1, leftRemain, rightRemain - 1);
    		}
    	}
    }
    

Hochfrequente Fragen

283. Verschieben von Nullen
  • Idee: Von links nach rechts scannen und die Zahlen ungleich Null nach vorne verschieben.

  • Zeitkomplexität: O(n)

  • Raumkomplexität: O(1)

    public void moveZeroes(int[] nums) {
          
          
      if (nums == null || nums.length <= 1) return;
    
      int start = 0;
      int zeroCount = 0;
      // 先将所有非零数字向最左边移动
      for (int i = 0; i < nums.length; i++) {
          
          
        if (nums[i] != 0) {
          
          
          nums[start++] = nums[i];
        } else {
          
          
          // 记录下0的个数
          zeroCount++;
        }
      }
      // 再将所有的零放在数组的最后
      for (int i = nums.length - 1; i >= nums.length - zeroCount; i--) {
          
          
        nums[i] = 0;
      }
    }
    
1. Die Summe zweier Zahlen
  • Methode 1: Brute-Force-Methode, Zeitkomplexität O(n^2) – nicht empfohlen
  • Methode 2: Tauschen Sie Raum gegen Zeit aus.Verwenden Sie eine Hash-Tabelle, um zuvor gescannte Werte zu speichern, Zeitkomplexität O(n), Raumkomplexität O(n)
  • In vielen Fällen kann man mehr darüber nachdenken, wie man Raum gegen Zeit eintauschen kann.
public int[] twoSum(int[] nums, int target) {
    
    
  int idx1 = -1;
  int idx2 = -1;
  // 用HashMap记录之前扫描过的值
  Map<Integer, Integer> map = new HashMap<>();
  for (int i = 0; i < nums.length; i++) {
    
    
    int remain = target - nums[i];
    Integer hit = map.get(remain);
    if (hit != null) {
    
    
      idx1 = hit;
      idx2 = i;
      break;
    }
    map.put(nums[i], i);
  }

  int[] res = {
    
    idx1, idx2};
  return res;
}
15. Summe dreier Zahlen
  • Diese Frage ist problematischer

  • Zeitkomplexität: O(n^2)

    public List<List<Integer>> threeSum(int[] nums) {
          
          
      // 先排序
      Arrays.sort(nums);
    
      List<List<Integer>> list = new ArrayList<>();
      int l = 0;
      int r = 0;
    
      int len = nums.length - 2;
      int lastIdx = nums.length - 1;
      for (int i = 0; i < len; i++) {
          
          
        l = i + 1;
        r = lastIdx;
        if (i > 0 && nums[i] == nums[i - 1]) continue;
    
        while (l < r) {
          
          
          int sum = nums[i] + nums[l] + nums[r];
          if (sum == 0) {
          
          
            list.add(Arrays.asList(nums[i], nums[l], nums[r]));
    
            // 跳过相等的值
            while (l < r && nums[l] == nums[l + 1]) l++;
            while (l < r && nums[r] == nums[r - 1]) r--;
            // 往中间逼近
            l++;
            r--;
          } else if (sum > 0) {
          
          
            r--;
          } else {
          
          
            l++;
          }
        }
      }
    
      return list;
    }
    
50. Pow(x, n)
  • Problemlösungsmethode:Schnelle Macht (teile und herrsche)

  • Problemlösungsmethode: Verwenden SieTeile-und-herrsche-Denken

    // 递归法
    public double myPow(double x, int n) {
          
          
      if (n == 0) return 1;
    
      // 判断是否是奇数的高效方法
      boolean isOdd = (n & 1) == 1;
      double half = myPow(x, n / 2);
      half *= half;
      x = (n < 0) ? 1/x : x;
      return isOdd ? half * x : half;
    }
    
    // 快速幂
    public double myPow1(double x, int n) {
          
          
      if (n == 0) return 1;
    
      double res = 1.0;
      while (n > 0) {
          
          
        if ((n & 1) == 1) {
          
          
          res *= n;
        }
    
        // 舍弃掉最后一位
        n >>= 1;
      }
      return res;
    }
    
Das Schwert zeigt auf Angebot 62. Die letzte verbleibende Zahl im Kreis.
  • Es ist das Joseph-Ring-Problem

    • Fügen Sie hier eine Bildbeschreibung ein
  • Eine zirkuläre verknüpfte Liste ist jedoch nicht für die Verwendung einer zirkulären verknüpften Liste in Interviews geeignet, da das Schreiben einer zirkulären verknüpften Liste relativ kompliziert ist und die Verwendung einer zirkulären verknüpften Liste daher nicht praktikabel ist.

  • Zur Lösung dieses Problems gibt es mathematische Regeln

    • Fügen Sie hier eine Bildbeschreibung ein
    • Fügen Sie hier eine Bildbeschreibung ein
    public class _剑指Offer62_圆圈中最后剩下的数字 {
          
          
    	// 非递归
    	// 自下向上
    	// f(1, 3) = 0
    	// f(2, 3) = (f(1, 3) + 3) % 1
    	// ...
    	// f(8, 3) = (f(7, 3) + 3) % 7
    	// f(9, 3) = (f(8, 3) + 3) % 8
    	// f(10, 3) = (f(9, 3) + 3) % 9
    	public int lastRemaining(int n, int m) {
          
          
    		if (n == 1) return 0;
    		
    		int res = 0;
    		int i = 1;
    		while (i <= n) {
          
          
    			res = (res + m) % i;
    			i++;
    		}
    		return res;
      }
    	
    	// 递归方式
    	// 自顶向下
    	public int lastRemaining1(int n, int m) {
          
          
    		if (n == 1) return 0;
    		
    		return (lastRemaining1(n - 1, m) + m) % n;
      }
    }
    
54. Spiralmatrix
  • Ideen zur Problemlösung: Wenn Sie vier Zeiger nach oben, unten, links und rechts haben, fügen Sie sie einfach in einem Kreis hinzu.

  • Fügen Sie hier eine Bildbeschreibung ein

    public List<Integer> spiralOrder(int[][] matrix) {
          
          
    		if (matrix == null) return null;
    		List<Integer> list = new ArrayList<>();
    		if (matrix.length == 0) return list;
    		
    		int top = 0;
        int bottom = matrix.length - 1;
        int left = 0;
        int right = matrix[0].length - 1;
    		while (left <= right && top <= bottom) {
          
          
    			// left top -> right top
          for (int i = left; i <= right; i++) {
          
          
            list.add(matrix[top][i]);
          }
          top++;
    
          // right top -> right bottom
          for (int i = top; i <= bottom; i++) {
          
          
              list.add(matrix[i][right]);
          }
          right--;
    
          // 奇数行、偶数列的时候有问题
          if (top > bottom || left > right) break;
    
          // right bottom -> left bottom
          for (int i = right; i >= left; i--) {
          
          
            list.add(matrix[bottom][i]);
          }
          bottom--;
    
          // left bottom -> left top
          for (int i = bottom; i >= top; i--) {
          
          
            list.add(matrix[i][left]);
          }
          left++;
    		}
    		
    		return list;
    }
    
146. LRU-Cache
  • Um eine O(1)-Zeitkomplexität von Put und Get zu erreichen
  • Zuletzt verwendete/zuletzt verwendete Frage
  • Eliminieren Sie die zuletzt verwendeten Daten
  • Der LRU-Cache ist ein gängiger Mechanismus zum Entfernen des Seitencaches in Betriebssystemen.
  • Implementierungsmethode: Hash-Tabelle + doppelt verknüpfte Liste
    • Hash-Tabellen werden zum Speichern von Daten verwendet und können eine O(1)-Zeitkomplexität von Put und Get erreichen.
    • Doppelt verknüpfte Listen werden zum Aufzeichnen der jüngsten und ältesten Verwendung verwendet und können eine O(1)-Zeitkomplexität für das Aufzeichnen und Löschen von Aufzeichnungsvorgängen erreichen.
  • Fügen Sie hier eine Bildbeschreibung ein
public class LRUCache {
    
    
	private int capacity = 0;
	private Map<Integer, Node<Integer, Integer>> map = new HashMap<>();
	private Node<Integer, Integer> first;
	private Node<Integer, Integer> last;
	
	
	public LRUCache(int capacity) {
    
    
		this.capacity = capacity;
		this.first = new Node<>();
		this.last = new Node<>();
		first.next = last;
		last.prev = first;
  }
    
    public int get(int key) {
    
    
    	Node<Integer, Integer> node = map.get(key);
    	if (node == null) return -1;
    	
    	int v = node.value;
      removeNode(node);
      addAfterFirst(node);
      return v;
    }
    
    public void put(int key, int value) {
    
    
    	Node<Integer, Integer> node = map.get(key);
    	if (node != null) {
    
    
    		node.value = value;
    		removeNode(node);
    	} else {
    
    
    		if (map.size() == capacity) {
    
    
        		removeNode(map.remove(last.prev.key));
        	}
        	
        	node = new Node<>(key, value);
        	map.put(key, node);
    	}
    	
    	addAfterFirst(node);
    }
    
    private void removeNode(Node<Integer, Integer> node) {
    
    
    	node.prev.next = node.next;
    	node.next.prev = node.prev;
    }
    
    private void addAfterFirst(Node<Integer, Integer> node) {
    
    
    	node.next = first.next;
    	first.next.prev = node;
    	node.prev = first;
    	first.next = node;
    }
    
    
    private class Node<K, V> {
    
    
    	public K key;
    	public V value;
    	public Node<K, V> prev;
    	public Node<K, V> next;
    	
    	public Node(K key, V value) {
    
    
    		this.key = key;
    		this.value = value;
    	}
    	
    	public Node() {
    
    }
    }
}
7. Ganzzahlige Umkehrung
  • Problemlösungsmethode
    • Verwenden Sie die Formel: res = res * 10 + x % 10
    • Durch die Verwendung von long zum Speichern des Ergebnisses können Sie den Überlaufwert nach dem Int-Flipping speichern, sodass Sie feststellen können, ob ein Überlauf vorliegt, und einen Wert von 0 zuweisen.
public int reverse(int x) {
    
    
  // 用long存储,应对翻转后溢出的问题
  long res = 0;
  while (x != 0) {
    
    
    res = res * 10 + x % 10;
    // 判断是否溢出
    if (res > Integer.MAX_VALUE || res < Integer.MIN_VALUE) {
    
    
      res = 0;
      break;
    }
    x = x / 10;
  }
  return (int) res;
}
252. Konferenzraum
  • Fügen Sie hier eine Bildbeschreibung ein

  • Problemlösungsmethode

    • Sortieren Sie zunächst nach der Startzeit des Treffens in aufsteigender Reihenfolge
    • Durchlaufen Sie dann jedes Meeting und vergleichen Sie, ob die Endzeit des aktuellen Meetings kleiner als die Startzeit des nächsten Meetings ist.
public boolean canAttendMeeting(int[][] meetings) {
    
    
  if (meetings == null) return true;
  if (meetings.length < 2) return true;

  // 先排序
  Arrays.sort(meetings, (m1, m2) -> {
    
    
    return m1[0] - m2[0];
  });
  // 再比较
  for (int i = 0; i < meetings.length - 1; i++) {
    
    
    if (meetings[i][1] > meetings[i + 1][0]) return false;
  }
  return true;
}
253. Konferenzraum 2
  • Fügen Sie hier eine Bildbeschreibung ein

  • Implementierung

    • Min. Haufen

      • Fügen Sie hier eine Bildbeschreibung ein

      • Die Zeitkomplexität, um vom minimalen Heap nach oben zu gelangen, beträgt O(1), und die zusätzliche Zeitkomplexität beträgt O(logn).

      • Sortieren Sie die Meetings zunächst nach der Startzeit

      • Fügen Sie die Endzeit des ersten Besprechungsraums zum Min-Heap hinzu

      • Durchqueren Sie, wenn die Startzeit des aktuellen Meetings kleiner ist als die Spitzenzeit des minimalen Heaps, dann fügen Sie die Endzeit des aktuellen Meetings zum minimalen Heap hinzu< /span>

      • Wenndie Startzeit des aktuellen Meetings größer oder gleich der Spitzenzeit des minimalen Heaps ist, entfernen Sie das oberste Element des Heaps und fügen Sie die Endzeit hinzu des aktuellen Meetings auf den minimalen Heap

      • Die Durchquerung ist beendetDann ist die Anzahl der Elemente im endgültigen Mindestheap die Anzahl der Konferenzräume, die geöffnet werden müssen

        public int minMeetingRooms(int[][] meetings) {
                  
                  
          if (meetings == null || meetings.length == 0) return 0;
          
          // 先对会议按照开始时间进行排序
          Arrays.sort(meetings, (m1, m2) -> {
                  
                   return m1[0] - m2[0]; });
          
          PriorityQueue<Integer> heap = new PriorityQueue<>();
          heap.offer(meetings[0][1]);
          for (int i = 1; i < meetings.length; i++) {
                  
                  
            int[] meeting = meetings[i];
        
            if (heap.peek() <= meeting[0]) {
                  
                  
              // 如果当前会议的开始时间大于等于堆顶的时间
              // 那么就将当前堆顶时间移除
              // 并将当前会议的结束时间添加进最小堆
              
              heap.poll();
            }
            // 否则就将当前会议的结束时间直接添加进最小堆
            heap.offer(meeting[1]);
          }
        
          return heap.size();
        }
        
    • separat sortieren

      • Fügen Sie hier eine Bildbeschreibung ein

      • Der Code wird nicht mehr geschrieben, er stimmt mit der obigen Idee überein.

11. Der Behälter, der das meiste Wasser enthält
  • Problemlösungsmethode
    • Lassen Sie die Länge des Bereichs zu Beginn am größten sein
    • Reduzieren Sie dann die Höhe der kurzen Seite weiter, sodass die Wörter viele unnötige Berechnungen ersparen können.
    • Dann wird nacheinander das Quadrat berechnet und der Maximalwert verglichen.
public int maxArea(int[] height) {
    
    
  if (height == null || height.length < 2) return 0;

  int l = 0;
  int r = height.length - 1;
  int maxSquare = 0;
  // 一开始先让面积的长度最大
  // 之后不断减小高度矮的那边, 这样字就可以省去很多没必要的计算
  // 之后依次计算出square,比较出最大值
  while (l < r) {
    
    
    int square = (r - l) * Math.min(height[l], height[r]);
    if (square > maxSquare) maxSquare = square;

    if (height[l] > height[r]) {
    
    
      r--;
    } else  {
    
    
      l--;
    }
  }

  return maxSquare;
}
42. Regenwasser auffangen
  • Problemlösungsmethode

    • Fügen Sie hier eine Bildbeschreibung ein

    • Berechnen Sie die Wassermenge, die jede Säule aufnehmen kann

    • Fügen Sie dann das in allen Spalten enthaltene Wasser hinzu und die Summe ist das endgültige Ergebnis.

    • Das Bild oben ist eine Methode, um herauszufinden, wie viel Wasser jede Säule aufnehmen kann.

    public int trap(int[] height) {
          
          
      if (height == null || height.length == 0) return 0;
    
      int sum = 0;
    
      int[] leftMaxes = new int[height.length];
      int[] rightMaxes = new int[height.length];
    
      // 求出左边最大值
      // 并存放在数组中
      // 当前index存放的值就是当前index时左边最大值
      leftMaxes[0] = 0;
      for (int i = 1; i < height.length; i++) {
          
          
        leftMaxes[i] = Math.max(leftMaxes[i - 1], height[i - 1]);
      }
    
      // 求出右边最大值
      // 并存放在数组中
      // 当前index存放的值就是当前index时右边最大值
      rightMaxes[height.length - 1] = 0;
      for (int i = height.length - 2; i >= 0; i--) {
          
          
        rightMaxes[i] = Math.max(rightMaxes[i + 1], height[i + 1]);
      }
    
      for (int i = 1; i < height.length - 1; i++) {
          
          
        // 当左边最大值和右边最大值都大于当前值的时候,才进行计算操作
        if (leftMaxes[i] > height[i] && rightMaxes[i] > height[i]) {
          
          
          // 计算出当前柱子能装的水,并累加在sum中
          int max = Math.min(leftMaxes[i], rightMaxes[i]) - height[i];
          sum += max;
        }
      }
    
      return sum;
    }
    
    • Optimierung

      • Keine Notwendigkeit für leftMaxes
      • Da die for-Schleife, die berechnet, wie viel Wasser jede Spalte aufnehmen kann, ebenfalls von rechts beginnt, können Sie die Berechnung von leftMax in dieses for einfügen.
      public int trap(int[] height) {
              
              
        if (height == null || height.length == 0) return 0;
      
        int sum = 0;
      
        int[] rightMaxes = new int[height.length];
      
        // 求出右边最大值
        // 并存放在数组中
        // 当前index存放的值就是当前index时右边最大值
        rightMaxes[height.length - 1] = 0;
        for (int i = height.length - 2; i >= 0; i--) {
              
              
          rightMaxes[i] = Math.max(rightMaxes[i + 1], height[i + 1]);
        }
      
        int leftMax = 0;
        for (int i = 1; i < height.length - 1; i++) {
              
              
          leftMax = Math.max(leftMax, height[i - 1]);
          // 当左边最大值和右边最大值都大于当前值的时候,才进行计算操作
          if (leftMax > height[i] && rightMaxes[i] > height[i]) {
              
              
            // 计算出当前柱子能装的水,并累加在sum中
            int max = Math.min(leftMax, rightMaxes[i]) - height[i];
            sum += max;
          }
        }
      
        return sum;
      }
      

Zusammenfassung der Fragen

  • Fügen Sie hier eine Bildbeschreibung ein
  • Fügen Sie hier eine Bildbeschreibung ein

Guess you like

Origin blog.csdn.net/qq_20255275/article/details/131840717