ans
,初始可以设置成数组第一个元素cnt
cnt==0
,那么选择当前元素作为候选者;否则,如果元素等于候选者,cnt++
,否则cnt--
class Solution {
public int majorityElement(int[] nums) {
// 摩尔投票法
int cnt = 1, ans = nums[0];
int len = nums.length;
for(int i=1; i<len; ++i){
if(cnt == 0){
cnt = 1;
ans = nums[i];
}
else if(ans == nums[i]) ++ cnt;
else{
-- cnt;
}
}
return ans;
}
}
思路:
Arrays.sort(nums)
nums[k]
,然后在右侧用双指针找满足nums[i]+nums[j]==-nums[k]
的元素。注意每一次移动都需要去除重复class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<>();
for(int k=0; k<nums.length-2; ++k){
if(nums[k]>0) break; // nums[k]>0,右侧均大于nums[k],因此不可能有答案,直接跳出
else if(k>0 && nums[k]==nums[k-1]) continue; // nums[k]==nums[k-1],只能找到重复答案,去除
int i = k+1, j = nums.length-1; // 双指针
while(i < j){
int sum = nums[i]+nums[j]+nums[k];
if(sum == 0){
// 添加答案
List<Integer> temp = new ArrayList<>();
temp.add(nums[i]);
temp.add(nums[j]);
temp.add(nums[k]);
ans.add(temp);
while(i<j && nums[i]==nums[++i]){} // 跳过重复的元素,并且+1,好好体会
while(i<j && nums[j]==nums[--j]){}
}else if(sum < 0){
while(i<j && nums[i]==nums[++i]){}
}else{
while(i<j && nums[j]==nums[--j]){}
}
}
}
return ans;
}
}
>=target
的元素。由于是第一个,因此我们碰到nums[m]时,直接l=m+1
,采用向下取整,而右边界直接r=m
即可
class Solution {
public int search(int[] nums, int target) {
int l = 0, r = nums.length-1;
while(l < r){
int m = l+(r-l)/2;
if(nums[m] < target){
l = m+1;
}else{
r = m;
}
}
return nums[r] == target ? r : -1;
}
}
arr[i]。而在右侧,有arr[i]>arr[i+1]
i
就是第一个满足arr[i]>arr[i+1]
的索引,满足二分的条件i+1
,因此右边界r=len-2
class Solution {
public int peakIndexInMountainArray(int[] arr) {
// 找到第一个满足 arr[i]>arr[i+1]的i
int l = 0, r = arr.length-2;
while(l < r){
int m = l+((r-l)>>1);
if(arr[m]<arr[m+1]){
l = m+1;
}else{
r = m;
}
}
return r;
}
}
nums[i]
在区间[1,n]
内,所以可以用原数字做哈希表nums[i-1]
取负数,代表i
出现在数组中,因此在遍历的时候,数组可能被修改过,需要对元素取绝对值nums[i-1]>0
,说明i
不存在数组中class Solution {
public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> ans = new ArrayList<>();
for(int i : nums){
int num = Math.abs(i);
if(nums[num-1]>0) nums[num-1] = -nums[num-1]; // 取负数,表示num存在
}
for(int i=0;i<nums.length;++i){
if(nums[i] > 0){
ans.add(i+1);
}
}
return ans;
}
}
class Solution {
public void sortColors(int[] nums) {
int left = -1, right = nums.length; // 左右边界,包含
int i = 0;
while(i<right){
if(nums[i] == 0){
int temp = nums[++left];
nums[left] = 0;
nums[i] = temp;
++ i;
}else if(nums[i] == 2){
int temp = nums[--right];
nums[right] = 2;
nums[i] = temp;
// 这里不能++i,因为可能把0换到前面
}else{
++ i;
}
}
}
}
O(n)
x
,我们判断数组中是否存在x+1
,x+2
…。这个判断是否存在的过程,就可以用哈希表,从O(n)
降到O(1)
x
遍历后,对于x+1
,x+2
等不应该开启新的遍历,需要跳过。因此我们在只有判断得数组中不存在x-1
时,才进行第二步class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> set = new HashSet<>(); // 集合,存储数组出现的数字
for(int n : nums){
set.add(n);
}
int ans = 0;
for(int num : set){
if(!set.contains(num-1)){ // 只有在连续序列最前面,才会进入遍历
int temp = 1; // 记录连续序列长度
int cur = num; // 记录当前遍历的数字
while(set.contains(cur+1)){ // 只要哈希表中存在cur+1,就继续遍历
++ cur;
++ temp;
}
ans = Math.max(ans, temp); // 更新答案
}
}
return ans;
}
}
解法一:迭代
cur
跟踪当前节点,每次从list1
和list2
中选取小的节点,链接起来/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
// 建立一个头节点,可以简化代码
ListNode preHead = new ListNode(0);
ListNode cur = preHead; // 当前指针,用于更新链表
while(list1 != null && list2 != null){
if(list1.val < list2.val){
cur.next = list1;
list1 = list1.next;
}else{
cur.next = list2;
list2 = list2.next;
}
cur = cur.next;
}
cur.next = list1 == null ? list2 : list1; // 最后只有一个链表没走完,连接到后面即可
return preHead.next; // 头节点下一个节点即为所求
}
}
解法二:递归
list1
和list2
中较小的节点,与其余的所有节点合并的过程list1
或list2
为null
时,返回另一个/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
// 边界条件,一个为null
if(list1 == null) return list2;
if(list2 == null) return list1;
if(list1.val < list2.val){
list1.next = mergeTwoLists(list1.next, list2); // list1.next = 其余所有节点合并的结果
return list1;
}else{
list2.next = mergeTwoLists(list1, list2.next);
return list2;
}
}
}
head
和head.next
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null || head.next == null) return false;
// 快慢指针
ListNode slow = head;
ListNode fast = head.next;
// 快指针不走到null时,进行循环
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast) return true; // 相遇说明存在环
}
return false; // 快指针走完,说明没有环
}
}
思路二:哈希表
最简单直接的想法,遍历链表,将节点存储在哈希表中,如果检测到哈希表中已经存在该节点,说明有环
public class Solution {
public boolean hasCycle(ListNode head) {
Set<ListNode> set = new HashSet<>();
while(head != null){
if(!set.add(head)) return true;
head = head.next;
}
return false;
}
}
l1
和l2
,初始指向headA
和headB
l1
和l2
每次往后移动一步,当l1
变为null
时,l1
下一步指向headB
继续遍历;相同的,l2
变为null
时下一步指向headA
l1==l2
:当存在相交节点时,l1
和l2
都走了相同的长度,指向第一个相交节点;不存在相交节点时,走了相同长度,两者都为null
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null) return null;
ListNode l1 = headA, l2 = headB;
while(l1 != l2){
l1 = l1 == null ? headB : l1.next;
l2 = l2 == null ? headA : l2.next;
}
return l1;
}
}
false
;匹配,则出栈顶。false
;空说明均匹配,返回true
class Solution {
public boolean isValid(String s) {
int len = s.length();
if(len % 2 == 1) return false; // s长度为奇数,可以直接判错
Stack<Character> stack = new Stack<>();
for(int i=0;i<len;++i){
char ch = s.charAt(i);
// 这里,遇到左括号后,将对应正确的右括号入栈,方便后面判断
if(ch == '(') stack.push(')');
else if(ch == '[') stack.push(']');
else if(ch == '{') stack.push('}');
else{
if(stack.empty() || stack.peek() != ch) return false; // 右括号:栈空或者不匹配,则不合法,直接返回
stack.pop(); // 匹配,则出栈顶
}
}
return stack.empty(); // 最后检查栈空
}
}
x = x & (x-1)
:将x的二进制中,最后一个1
变为0
思路
i
次迭代时,队列的长度就是当前这一层的长度/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> q = new LinkedList<>(); // 创建一个队列
List<List<Integer>> ans = new ArrayList<List<Integer>>(); // 返回的列表
if(root == null) return ans; // 空树,直接返回空
q.offer(root); // 根节点入队
while(!q.isEmpty()){ // 队列非空时,进行BFS
List<Integer> temp = new ArrayList<Integer>(); // 存储当前所在层的节点
int size = q.size(); // 当前层的节点个数,就是队列目前的长度
for(int i=0; i<size; ++i){
TreeNode t = q.poll();
temp.add(t.val);
// 孩子节点入队
if(t.left != null) q.offer(t.left);
if(t.right != null) q.offer(t.right);
}
ans.add(temp);
}
return ans;
}
}
思路一:BFS
二叉树的深度也就是层数,可以用102题相似的思路,数层数
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
// BFS 同102题
if(root == null) return 0;
int ans = 0;
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while(!q.isEmpty()){
int size = q.size();
for(int i=0; i<size; ++i){
TreeNode node = q.poll();
if(node.left != null) q.offer(node.left);
if(node.right != null) q.offer(node.right);
}
++ ans;
}
return ans;
}
}
思路二:递归
null
返回0
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
return 1+Math.max(maxDepth(root.left), maxDepth(root.right));
}
}
思路一:递归
true
;一个节点非空一个为空,返回false
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
return func(root, root);
}
public boolean func(TreeNode root1, TreeNode root2){
if(root1 == null && root2 == null) return true;
if(root1 == null || root2 == null) return false;
return root1.val == root2.val && func(root1.left, root2.right) && func(root1.right, root2.left);
}
}
思路二:迭代
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
return func(root, root);
}
public boolean func(TreeNode root1, TreeNode root2){
Queue<TreeNode> q = new LinkedList<>();
// 初始,入队两次根节点
q.offer(root1);
q.offer(root2);
while(!q.isEmpty()){ // 队列非空时,判断
// 取出两个队头,poll方法在队空时会返回null
root1 = q.poll();
root2 = q.poll();
if(root1 == null && root2 == null) continue;
if((root1 == null || root2 == null) || root1.val != root2.val) return false;
// 按照相反次序入队
q.offer(root1.left);
q.offer(root2.right);
q.offer(root1.right);
q.offer(root2.left);
}
return true;
}
}
利用数组元素均为正数的性质
target
时,尝试缩小左窗口,以求得最小长度的满足要求的子数组。可以缩小左窗口的判断是:sum - nums[left] >= target
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int sum = 0;
int len = nums.length;
int left = 0;
int ans = len+1;
for(int right = 0; right<len; ++right){
sum += nums[right];
while(sum-nums[left] >= target){
sum -= nums[left];
++ left;
}
if(sum >= target)
ans = Math.min(ans, right-left+1);
}
return ans > len ? 0 : ans;
}
}
的最长子数组,子数组中有若干个数组都满足题意
[left...right]
有多少个子数组满足:我们固定的是right
,[left...right]、[left+1...right]...[right...right]
都满足,数量正好是right-left+1
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
if(k <= 1) return 0;
int ans = 0;
int left = 0;
int prod = 1;
int len = nums.length;
for(int right=0; right<len; ++right){
prod *= nums[right];
while(prod >= k){
prod /= nums[left];
++ left;
}
ans += right-left+1;
}
return ans;
}
}
class Solution {
public int lengthOfLongestSubstring(String s) {
Set<Character> seen = new HashSet<>();
int ans = 0;
int left = 0;
int len = s.length();
for(int right=0; right<len; ++right){
while(!seen.add(s.charAt(right))){
seen.remove(s.charAt(left));
++ left;
}
ans = Math.max(ans, right-left+1);
}
return ans;
}
}
lsum+rsum == x
left=-1
;后缀为整个数组,right=0
lsum+rsum > x
时,说明后缀长度太大,应不断缩短后缀长度;当lsum+rsum < x
,说明应该移动扩大前缀长度class Solution {
public int minOperations(int[] nums, int x) {
int sum = Arrays.stream(nums).sum(); // api数组求和
if(sum < x) return -1; // 特殊情况直接排除
int len = nums.length;
int right = 0;
int lsum = 0, rsum = sum;
int ans = len+1;
// 滑动窗口
for(int left=-1; left < len; ++left){
// 左窗口滑动,前缀加
if(left != -1){
lsum += nums[left];
}
// 右窗口滑动,后缀减
while(right<len && lsum+rsum>x){
rsum -= nums[right];
++ right;
}
// 记录答案
if(lsum+rsum==x){
ans = Math.min(ans, (left+1)+(len-right));
}
}
// 最后需要判断是否存在答案
return ans>len ? -1 : ans;
}
}
思路
典型的动态规划,我们用dp[i]
表示走到第i
级台阶的方法数
n==1
只有一种方法,n==2
有两种。那么初始条件dp[1]=1
,dp[2]=2
1
级或2
级台阶,那么走到n
级台阶,可以从第n-1
级台阶走1
级,或者从第n-2
级台阶走2
级。由此可得到状态方程dp[i]=dp[i-1]+dp[i-2]
dp[i]
只和dp[i-1]
和dp[i-2]
有关,因此可以用两个变量代替,就可以将空间从 O ( n ) O(n) O(n)降到 O ( 1 ) O(1) O(1)。这种滚动数组的思想在动态规划中挺常用class Solution {
public int climbStairs(int n) {
if(n < 3) return n;
// - 数组,比较好理解 dp[i]表示走到i级台阶有多少种方法
// int[] dp = new int[n+1];
// dp[1] = 1;
// dp[2] = 2;
// for(int i=3; i<=n; ++i){
// dp[i] = dp[i-1]+dp[i-2];
// }
// return dp[n];
// - 滚动数组优化空间
int a = 1, b = 2; // a代替dp[i-2],b代替dp[i-1]
int c = 0;
for(int i=3; i<=n; ++i){
c = a+b;
a = b;
b = c;
}
return c;
}
}
class Solution {
public int minCostClimbingStairs(int[] cost) {
int len = cost.length;
if(len == 2) return Math.min(cost[0], cost[1]);
// int[] dp = new int[len+1]; // dp[i]表示到达i层的最小花费
// for(int i=2;i<=len;++i){
// dp[i] = Math.min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]);
// }
// return dp[len];
// 滚动变量优化
int a = 0, b = 0, c = 0; // i-2, i-1, i
for(int i=2; i<=len; ++i){
c = Math.min(a+cost[i-2], b+cost[i-1]);
a = b;
b = c;
}
return c;
}
}
nums[i]
结尾的数组和最大值为多少?我们以dp[i]
来记录dp[i-1]<=0
,那么以dp[i] = nums[i]
,一个数加上负数,结果肯定是变小的。这也是状态转移方程。这个方程显然,可以用滚动变量节约空间dp[0]=nums[0]
class Solution {
// 这里没有滚动变量化简空间复杂度
public int maxSubArray(int[] nums) {
int[] dp = new int[nums.length]; // dp[i]表示以nums[i]结尾的连续子数组的最大值
dp[0] = nums[0];
int ans = nums[0];
for(int i=1;i<nums.length;++i){
dp[i] = dp[i-1]>0 ? dp[i-1]+nums[i] : nums[i];
ans = Math.max(ans, dp[i]);
}
return ans;
}
}
思路:动态规划
用一个m*n
的数组,dp[i][j]
表示到达地图(i,j)
位置总共有几种方法
dp=1
dp[i][j] = dp[i-1][j]+dp[i][j-1]
,分别表示从上面、下面走到当前格子class Solution {
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
Arrays.fill(dp[0], 1);
for(int i=1;i<m;++i){
for(int j=0;j<n;++j){
if(j == 0) dp[i][j] = 1;
else{
dp[i][j] = dp[i-1][j]+dp[i][j-1];
}
}
}
return dp[m-1][n-1];
}
}
思路:动态规划
和上一道题思路类似,多了一个障碍物
dp=0
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int m = obstacleGrid.length, n = obstacleGrid[0].length;
int[][] dp = new int[m][n];
dp[0][0] = obstacleGrid[0][0] == 0 ? 1 : 0; // dp[0][0]初始化
for(int i=0;i<m;++i){
for(int j=0;j<n;++j){
if(i == 0 && j == 0) continue;
if(obstacleGrid[i][j] == 1) dp[i][j] = 0; // 碰到障碍物,直接0
else
{
if(i == 0) dp[i][j] = dp[i][j-1]; // 第一行
else if(j == 0) dp[i][j] = dp[i-1][j]; // 第一列
else dp[i][j] = dp[i-1][j]+dp[i][j-1]; // 其他
}
}
}
return dp[m-1][n-1];
}
}
n
,并且从左往右添加时,右括号个数始终要小于左括号个数。据此可以省略掉isValid
和大量无效搜索class Solution {
List<String> ans = new ArrayList<>();
StringBuilder sb = new StringBuilder();
public List<String> generateParenthesis(int n) {
bt(n, 0, 0);
return ans;
}
public void bt(int n, int left, int right){
if(sb.length() == n*2){
ans.add(sb.toString());
}
// 左括号个数不够,尝试添加
if(left < n){
sb.append('(');
bt(n, left+1, right);
sb.deleteCharAt(sb.length()-1);
}
// 右括号个数小于左括号的,尝试添加
if(right < left){
sb.append(')');
bt(n, left, right+1);
sb.deleteCharAt(sb.length()-1);
}
}
}
class Solution {
final static int[] xDirection = {0,0,-1,1};
final static int[] yDirection = {-1,1,0,0};
boolean[][] visited;
boolean flag = false;
public boolean exist(char[][] board, String word) {
int m = board.length, n = board[0].length;
visited = new boolean[m][n];
for(int i=0;i<m;++i){
for(int j=0;j<n;++j){
if(word.charAt(0) == board[i][j]){
dfs(board, word, i, j, 0);
}
}
}
return flag;
}
public void dfs(char[][] board, String word, int x, int y, int index){
if(board[x][y] != word.charAt(index)){
return;
}else if(index == word.length()-1){
flag = true;
return;
}
visited[x][y] = true;
for(int i=0;i<4;++i){
int nx = x+xDirection[i];
int ny = y+yDirection[i];
if(nx>=0 && nx<board.length && ny>=0 && ny<board[0].length && visited[nx][ny] == false)
dfs(board, word, nx, ny, index+1);
}
visited[x][y] = false;
}
}
0
:a^a=0
;任意数和0
异或等于它本身:a^0=a
;异或具有交换性ans=0
,与数组中所有元素异或一次,最终ans
即为只出现一次的数字class Solution {
public int singleNumber(int[] nums) {
int ans = 0;
for(int i=0; i<nums.length; ++i){
ans ^= nums[i];
}
return ans;
}
}
思路:单调栈模板
唯一多的是,用一个Map
方便获取
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
Map<Integer, Integer> map = new HashMap<>();
// nums2
Stack<Integer> stk = new Stack<>();
for(int num : nums2) map.put(num, -1);
// 单调递减的栈
for(int num2 : nums2){
while(!stk.isEmpty() && stk.peek()<num2){
map.put(stk.pop(), num2);
}
stk.push(num2);
}
int[] ans = new int[nums1.length];
for(int i=0; i<nums1.length; ++i){
ans[i] = map.get(nums1[i]);
}
return ans;
}
}
i
右边第一个j
满足arr[j]>arr[i]
,那么我们可以维护一个单调递减的单调栈,这样当我们遍历到比栈顶更大的元素时,就可以尝试出栈class Solution {
public int[] dailyTemperatures(int[] temperatures) {
// 维护一个单调递减的栈
Stack<Integer> stk = new Stack<>();
int[] ans = new int[temperatures.length];
Arrays.fill(ans, 0);
for(int i=0; i<temperatures.length; ++i){
while(!stk.isEmpty() && temperatures[stk.peek()]<temperatures[i]){
ans[stk.peek()] = i-stk.pop();
}
stk.push(i);
}
return ans;
}
}
思路:单调栈
i-left-1
class Solution {
public int trap(int[] height) {
int len = height.length;
if(len <= 2) return 0; // 特殊情况
int ans = 0;
// 维护一个单调递减的栈,栈内存储下标
Stack<Integer> stk = new Stack<>();
stk.push(0); // 初始最左边入栈
for(int i=1; i<len; ++i){
int top = stk.peek(); // 与栈顶元素比较
if(height[i] < height[top]){ // 如果更小,那么直接入栈,待处理
stk.push(i);
}else if(height[i] == height[top]){ // 如果高度相等,相等的高度没法接雨水,那么需要出栈栈顶,更新成当前的
stk.pop();
stk.push(i);
}else{
while(!stk.isEmpty() && height[i] > height[stk.peek()]){ // while 接雨水
int mid = stk.pop();
if(!stk.isEmpty()){ // 出栈后,需要栈非空,也就是有凹槽左边
int left = stk.peek();
ans += (Math.min(height[left], height[i])-height[mid])*(i-left-1);
}
}
stk.push(i);
}
}
return ans;
}
}
Math.min() / Math.max()
Integer.MAX_VALUE / Integer.MIN_VALUE
List
:Java列表,是一个接口,不可以实例化ArrayList
和LinkedList
:实现了List
接口,是类,可以用于实例化因此创建一个列表,可以用List
Stack stk = new Stack<>()
:创建一个存储Character
类型值的栈bool empty()
:判断栈是否空peek()
:返回栈顶元素push()
:入栈pop()
:出栈Queue q = new LinkedList<>()
:创建一个队列Deque dq = new LinkedList<>()
:创建一个双端队列以Queue
为例,一些方法
add / offer
:队尾添加元素。对于长度有限制的队列,队列满时,add
会抛出uncheck
异常,offer
会返回false
remove / poll
:移除队头元素,并返回该元素。队列空时,remove
会抛出异常,poll
会返回null
element / peek
:返回队头,但是不删除。队列空时,和上述类似isEmpty
:判断队列是否为空size
:返回队列长度Set
接口,HashSet
和TreeSet
实现了该接口,可用于实例化
关于HashSet
Set set = new HashSet<>()
:创建一个集合。集合里的元素不能重复add()
:往集合中添加元素,如果元素已经存在,返回false
remove()
:从集合中删除一个元素s.startsWith(pref)
:检查s
是否以pref
开头s.charAt(index)
:返回index
位置处的字符s.toCharArray()
:将字符串转化为char[]
字符串截取
s.substring(int beginIndex)
:返回一个字符串,等于从beginIndex
截取到末尾s.substring(int beginIndex, int endIndex)
:返回一个字符串,等于s[beginIndex...endIndex-1]
,注意不包括endIndex
字符串替换
s.replace(old, new)
:返回一个字符串,用new
替换掉old
StringBuilder
可变长度
deleteCharAt(i)
:移除i
位置的字符toString()
:转化为String
append(ch)
:末尾添加ch