刷题小记
# 刷题小记
# 数组
在 Java 中,数组是一种基础的数据结构,提供了一些内部方法来进行操作。
以下是一些常用的数组内部方法:
# 获取数组长度
length: 返回数组的长度。
# 复制数组
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length): 将数组指定范围的元素复制到另一个数组。
# 数组排序
Arrays.sort(int[] a): 对整型数组进行升序排序。Arrays.sort(char[] a): 对字符数组进行升序排序。Arrays.sort(T[] a, Comparator<? super T> c): 使用自定义比较器对对象数组进行排序。
# 数组填充
Arrays.fill(int[] a, int val): 将整型数组的所有元素都设置为指定值。Arrays.fill(char[] a, char val): 将字符数组的所有元素都设置为指定值。
# 查找元素
Arrays.binarySearch(int[] a, int key): 对升序数组进行二分查找,找到返回索引,否则返回负数。
# 数组转换为字符串
Arrays.toString(type[] a): 返回数组的字符串表示。
# 数组拷贝
Arrays.copyOf(type[] original, int newLength): 复制指定数组的指定长度。Arrays.copyOfRange(type[] original, int from, int to): 复制指定数组的指定范围。
# 数组填充
Arrays.fill(type[] a, type val): 用指定的值填充整个数组。
# 使用指定的生成器函数生成数组的每个元素
Arrays.setAll():
Arrays.setAll(T[] array, IntUnaryOperator generator): 使用指定的生成器函数生成数组的每个元素。- 这个方法接受一个
IntUnaryOperator函数接口作为参数,该函数接受一个索引,返回对应索引位置上元素的值。
int[] array = new int[5];
Arrays.setAll(array, i -> i * i);
// 现在 array = [0, 1, 4, 9, 16]
2
3
# 在一个已排序的数组中搜索指定元素,并返回返回元素在数组中的索引
Arrays.binarySearch(arr, element) 是用于在一个已排序的数组 arr 中搜索指定元素 element 的方法。
- 这个方法返回元素在数组中的索引,
- 如果找不到元素则返回负数。
方法签名为:
int Arrays.binarySearch(type[] arr, type key)
其中,arr 是要搜索的数组,key 是要查找的元素。
注意事项:
- 如果数组包含多个相等的元素,无法保证返回的是哪一个元素的索引。
- 如果数组中不存在指定的元素,返回的是一个负数,表示元素应该插入数组中的位置,插入后数组仍保持有序。
示例:
int[] array = {1, 2, 3, 4, 5};
int index = Arrays.binarySearch(array, 3);
// index 的值为 2,因为元素 3 在数组中的索引为 2
2
3
4
如果数组中不存在要查找的元素,返回的值将是负数。
要获得正确的插入点,可以使用以下方式:
int[] array = {1, 2, 4, 5};
int key = 3;
int index = Arrays.binarySearch(array, key);
// index 的值为 -3,表示如果要插入元素 3,它应该插入到索引为 2 的位置
2
3
4
5
注意:在使用
binarySearch方法之前,数组必须是有序的,否则结果是不确定的。如果数组无序,可以使用Arrays.sort()对数组进行排序。
# 位运算
在计算机中,位运算是指对二进制数进行的运算。Java中提供了位运算符来进行位运算操作,包括**按位与(&)、按位或(|)、按位异或(^)、按位取反(~)、左移位(<<)和右移位(>>)**等。
下面简单介绍一下位运算符的功能和使用方法:
# 1.按位与(&)
对两个二进制数进行位运算,如果两个数对应位都是 1,则结果为 1,否则为 0。例如:
int a = 0b1010; // 二进制数1010
int b = 0b1100; // 二进制数1100
int result = a & b; // 按位与运算,结果为1000(二进制)
2
3
# 2.按位或(|)
对两个二进制数进行位运算,如果两个数对应位都是 0,则结果为 0,否则为 1。例如:
int a = 0b1010; // 二进制数1010
int b = 0b1100; // 二进制数1100
int result = a | b; // 按位或运算,结果为1110(二进制)
2
3
# 3.按位异或(^)
对两个二进制数进行位运算,如果两个数对应位相同,则结果为 0,否则为 1。例如:
- 相同为 0,不同为 1
int a = 0b1010; // 二进制数1010
int b = 0b1100; // 二进制数1100
int result = a ^ b; // 按位异或运算,结果为0110(二进制)
2
3
# Integer.bitCount(x ^ y)
bitCount 实现的功能是计算一个(byte,short,char,int统一按照int方法计算)int, long 类型的数值在二进制下 “1” 的数量。
// 求汉明距离
class Solution {
public int hammingDistance(int x, int y) {
return Integer.bitCount(x ^ y);
}
}
2
3
4
5
6
# 4.按位取反(~)
对一个二进制数进行位运算,将所有的 0 变为 1,将所有的 1 变为 0。例如:
int a = 0b1010; // 二进制数1010
int result = ~a; // 按位取反运算,结果为0101(二进制)
2
# 5.左移位(<<)-- 乘
将一个二进制数向左移动指定的位数,移动后右侧补 0。例如:
- 左移一位,数变大,相当于乘于 2
int a = 0b1010; // 二进制数1010
int result = a << 2; // 左移2位,结果为101000(二进制)
size << 1 // 左移一相当于乘于2
2
3
4
# 6.右移位(>>)-- 除
将一个二进制数向右移动指定的位数,移动后左侧补 0。例如:
- 左移一位,数变大,相当于除于 2
int a = 0b1010; // 二进制数1010
int result = a >> 2; // 右移2位,结果为0010(二进制)
size >> 1 // 右移一相当于除于2
2
3
4
- 需要注意的是,位运算符只能用于整数类型(byte、short、int、long)的数据。
- 另外,在进行位运算时,需要特别注意数据类型的符号位,因为有些位运算符(如右移位运算符)会保留符号位。
# 异或性质:
1.任何数和 0 做异或运算,结果仍然是原来的数,即 a⊕0=a。
2.任何数和其自身做异或运算,结果是 a⊕a=0。
3.异或运算满足交换律和结合律,即a⊕b⊕a=b⊕a⊕a=b⊕(a⊕a)=b⊕0=b。
2
3
4
5
# 问题
# 为什么 -5 右移一位是 -3,而 -5/2 是 -2
-3 的由来
这是由于负数的二进制表示以及右移操作的工作原理所导致的。在计算机中,负数通常以二进制的补码形式表示。
首先,让我们来看看 -5 的二进制表示:
- 正数5的二进制表示是:00000101。
- 负数的补码表示是将对应正数的二进制取反(将 0 变成 1,将 1 变成 0),然后加 1。所以 -5 的补码表示是:11111011。
现在,让我们将 -5 右移一位:
- 右移一位后,最高位(符号位)仍然为 1,表示这仍然是一个负数。
- 新的补码表示是:11111101。
接下来,我们将这个补码转换回十进制,就得到了 -3。
按位操作和除法操作是不一样的。
# String 类
String 类是 Java 中的一个基础类,提供了许多有用的方法来操作字符串。
以下是一些常用的 String 内部方法:
# 获取字符串长度
length(): 返回字符串的长度。
# 获取字符
charAt(int index): 返回指定索引处的字符。
# 子串操作
substring(int beginIndex): 返回从指定索引开始到字符串末尾的子字符串。(包含索引为 beginIndex 的元素)substring(int beginIndex, int endIndex): 返回从指定索引开始到指定索引结束的子字符串。(不包含索引为 endIndex 的元素)
# 连接字符串
concat(String str): 将指定字符串连接到此字符串的末尾。
# 查找子字符串
indexOf(String str): 返回指定子字符串在此字符串中第一次出现的索引。lastIndexOf(String str): 返回指定子字符串在此字符串中最后一次出现的索引。
# 字符串比较
equals(Object obj): 将此字符串与指定对象进行比较。equalsIgnoreCase(String anotherString): 按字典顺序比较两个字符串,不考虑大小写。
# 字符串替换
replace(char oldChar, char newChar): 返回一个新字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
# 字符串分割
split(String regex): 根据给定正则表达式的匹配拆分此字符串。split(String regex, int limit): 根据给定正则表达式的匹配拆分此字符串,最多拆分出 limit 个子字符串。
# 字符串转换
toLowerCase(): 将此字符串转换为小写。toUpperCase(): 将此字符串转换为大写。
# 去除空格
trim(): 返回一个字符串,其值为此字符串,并删除了所有前导空白和尾部空白。
# 链表 LinkedList
常用方法如下:
add(E e): 将元素添加到链表。remove(E e): 从链表中移除指定元素。get(int index): 获取链表中指定索引处的元素。size(): 返回链表的大小。
# 哈希表 HashMap
常用方法如下:
put(K key, V value): 将键值对存入哈希表。get(K key): 获取键对应的值。containsKey(K key): 判断是否包含指定键。remove(K key): 移除指定键的映射。keySet(): 返回哈希表中所有键的集合。
# 集合 HashSet
常用方法如下:
add(E e): 将元素添加到集合。remove(E e): 从集合中移除指定元素。contains(E e): 判断集合是否包含指定元素。
# 队列 queue
在 Java 中,Queue 是一个接口,它表示队列的基本行为,即一个先进先出(FIFO)的数据结构。Queue 接口定义了一组方法,用于操作队列,这些方法包括:
# 1.queue.poll()
queue.poll() 是 Java 中 Queue 接口定义的一个方法,用于获取并移除队列的头部元素,如果队列为空则返回 null。
# 2.queue.add()
add(E e):将指定元素插入队列中,如果队列已满,则抛出一个 IllegalStateException 异常。
# 3.queue.offer()
offer(E e):将指定元素插入队列中,如果队列已满,则返回 false。
# 4.queue.remove()
remove():获取并移除队列头部的元素,如果队列为空,则抛出一个 NoSuchElementException 异常。
# 5.queue.element()
element():获取但不移除队列头部的元素,如果队列为空,则抛出一个 NoSuchElementException 异常。
# 6.queue.peek()
peek():获取但不移除队列头部的元素,如果队列为空,则返回 null。
此外,Queue 接口还扩展了 Collection 接口,因此还可以使用 size()、isEmpty()、contains(Object o)、iterator()、toArray() 等方法。
在 Java 中,Queue 接口有多个实现类,包括:
ArrayDeque:基于数组实现的双端队列。LinkedList:基于链表实现的队列。PriorityQueue:基于堆实现的优先队列。
不同的实现类可能会有不同的方法和行为,但它们都实现了 Queue 接口,因此都支持上述的方法和行为。
# 代码示例
/**
* 层序遍历方式反转(也相当于广度优先)
*/
public TreeNode invertTreeByQueue(TreeNode root) {
if (root == null) {
return null;
}
Queue<TreeNode> queue = new ArrayDeque<>();
// 插入
queue.offer(root);
while (!queue.isEmpty()) {
// 弹出
TreeNode node = queue.poll();
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
if (node.left != null) {
// 插入左节点
queue.offer(node.left);
}
if (node.right != null) {
// 插入右节点
queue.offer(node.right);
}
}
return root;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 双端队列 Deque
在 Java 中,Deque 接口(双端队列)有以下一些常用的方法:
# 插入操作
- addFirst(E e) 或 offerFirst(E e): 在队头插入元素。
- addLast(E e) 或 offerLast(E e): 在队尾插入元素。
# 移除操作
- removeFirst() 或 pollFirst(): 移除并返回队头的元素。
- removeLast() 或 pollLast(): 移除并返回队尾的元素。
# 访问操作
- getFirst() 或 peekFirst(): 获取但不移除队头的元素。
- getLast() 或 peekLast(): 获取但不移除队尾的元素。
# 其他操作
- size(): 返回队列的大小。
- isEmpty(): 判断队列是否为空。
- clear(): 清空队列。
这些方法允许你在双端队列的两端执行插入、移除和访问操作,使得双端队列在解决算法问题时非常灵活。在题解中,你可能会经常看到这些方法的使用,尤其是在实现滑动窗口等问题时。
# 优先队列 PriorityQueue
常用方法如下:
add(E e)或offer(E e): 将元素插入队列。poll(): 移除并返回队列中最小(或最大)的元素。peek(): 获取但不移除队列中最小(或最大)的元素。size(): 返回队列的大小。
# 栈stack
在 Java 中,Stack 是一个 LIFO(后进先出)的数据结构,它继承自 Vector 类,同时也实现了 List 接口。
虽然 Stack 类在 Java 中仍然可用,但是在 Java SE 5 中,推荐使用 Deque 接口的实现类 LinkedList 来代替 Stack 类,因为 LinkedList 类实现了 Deque 接口,提供了与 Stack 类相同的方法,并且更加高效。
常用方法如下:
push(E item): 将元素压入栈顶。pop(): 弹出并返回栈顶元素。peek(): 获取但不移除栈顶元素。empty(): 判断栈是否为空。