在刷leetcode的时候经常会使用到一些数据结构和经典的算法,还有一些很方便的写法,不记录下来总是容易忘记。因此在这里记录一下方便自己查询,也希望对正在刷题的人起到一些帮助。
一、Java集合类
1. HashMap
1.1 基本用法
HashMap<Key, Value> map=new HashMap<>();
map.clear(); //清空
map.remove(Key); //删除Key,返回Value
map.containsKey(Key); //是否包括Key
map.containsValue(Value); //判断包括Value
1.2 遍历
for(Map.Entry<String, Integer> entry : map.entrySet()){
String key = entry.getKey();
Integer value = entry.getValue();
}
1.3 初始化 getOrDefault
Map<String, Integer> map = HashMap<>();
for (String word : words) {
map.put(word, map.getOrDefault(word, 0) + 1);
}
1.4 拷贝 putAll
map1.putAll(map2)
1.5 putIfAbsent
putIfAbsent相当于
if(!map.containsKey(key)) {
map.put(key, value);
}
1.6 在声明的同时初始化
Map<String, Integer> map = new HashMap<>(){{
put("a", 1);
put("b", 2);
}};
1.7 将map的values转为ArrayList
List<T> list = new ArrayList<T>(map.values())
2. ArrayList和LinkedList
2.1. 按下标删除和按元素删除
list.remove(int) //按下标删除
list.remove(obj) //按元素删除
list.remove(Integer.valueOf(1)) //删除ArrayList<Integer>里值为1的元素
2.2 在LinkedList中,offer == offerLast,poll == pollFirst
2.3 列表数组(以LinkedList为例)
LinkedList[] data = new LinkedList[10];
for(int i = 0; i < 10; i++) {
data[i] = new LinkedLIst<Integer>();
}
3. 优先队列 PriorityQueue
// 3种增序的实现方式
//PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> a - b); //增序
//PriorityQueue<Integer> pq = new PriorityQueue<>(Comparator.naturalOrder()); //增序
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer a, Integer b) {
return a-b;
}
});
//PriorityQueue<Integer> pq = new PriorityQueue<>(Comparator.reverseOrder()); //降序
pq.add();
pq.peek(); //只看不删,失败返回null
pq.poll(); //删,失败返回null
pq.remove(node); //删除指定元素
4. TreeSet (既需要排序又需要不重复)
4.1. 正序遍历
for(Iterator iter = set.iterator(); iter.hasNext(); ) {
iter.next();
}
//foreach也可以
4.2. 逆序遍历
for(Iterator iter = set.descendingIterator(); iter.hasNext(); ) {
iter.next();
}
4.3 ceiling、floor、higher、lower
TreeSet<Integer> set = new TreeSet<>();
set.ceiling(x); //大于等于 x 的最小数
set.floor(x); //小于等于 x 的最大数
set.higher(x); //大于 x 的最小数
set.lower(x); //小于 x 的最大数
没有的话返回null
5. LinkedHashMap (有序哈希表)
这个哈希表是按照插入的顺序排序的,它的使用和普通的HashMap一样。
Map<String, Integer> map = new LinkedHashMap<>();
map.put("string", 1);
for(Map.Entry<String, Integer> entry : map.entrySet()){
String string = entry.getKey();
int num = entry.getValue();
}
二、数据结构
1. 栈和单调栈
Deque<> stack = new LinkedList<>();
stack.peek();
stack.pop();
stack.push();
stack.isEmpty();
单调栈的作用:O(n)时间确定数组中下一个比自己大/小的元素
Deque<Integer> stack = new LinkedList<>();
// 递减栈
while (!stack.empty() && arr[i] > arr[stack.peek()]) {
stack.pop();
}
stack.push(i);
// 严格单调递减栈
while (!stack.empty() && arr[i] >= arr[stack.peek()]) {
stack.pop();
}
stack.push(i);
2. 队列、双端队列和单调队列
Queue<> queue = new LinkedList<>();
queue.peek(); //空返回null
queue.element(); //空抛出异常
queue.offer(); //满返回false
queue.add(); //满抛出异常
queue.poll(); //空返回null
queue.remove(); //空抛出异常
queue.isEmpty();
双端队列的常用方法和队列一样,只要在以上的函数名称后面加上First和Last就可以了
Deque<Integer> deque = new LinkedList<>();
单调队列和以上介绍的单调栈十分相似,由于需要两端删除所以需要使用双端队列:
Deque<Integer> deque = new LinkedList<>();
while(!deque.isEmpty() && arr[deque.peekLast()] > arr[i]) {
deque.pollLast();
}
deque.offerLast();
3. 树
3.1 二叉搜索树
二叉搜索树的中序遍历的序列是递增排序的序列。
求当前节点的后继节点和前驱节点
public TreeNode successor(TreeNode root) {
root = root.right;
while (root.left != null) root = root.left;
return root;
}
public TreeNode predecessor(TreeNode root) {
root = root.left;
while (root.right != null) root = root.right;
return root;
}
三、经典算法
1. 二分搜索
1.1. 左闭右闭
public int BinarySearch(int[] arr, int key) {
int low = 0, high = arr.length - 1;
while (low <= high) {
int mid = low + (high - low)/2;
if (arr[mid] > key) {
high = mid - 1;
} else if (arr[mid] < key) {
low = mid + 1;
} else {
return mid;
}
}
return -1;
}
1.2. 左闭右开
public int BinarySearch(int[] arr, int key) {
int low = 0, high = arr.length; //不同之处
while (low < high) { //不同之处
int mid = low + (high - low)/2;
if (arr[mid] > key) {
high = mid - 1;
} else if (arr[mid] < key) {
low = mid; //不同之处
} else {
return mid;
}
}
return -1;
}
1.3. 排除法
while(left < right){
int mid = left + (right - left) / 2;
if (check(mid)) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
while(left < right){
int mid = left + (right - left + 1) / 2;
if (check(mid)) {
right = mid - 1;
} else {
left = mid;
}
}
return left;
第一种可以用于找左边界,第二种可以用于找右边界。
2. 快速排序
private void quickSort(int[] arr, int low, int high) {
if (low < high) {
int index = partition(arr, low, high);
quickSort(arr, low, index - 1);
quickSort(arr, index + 1, high);
}
}
//随机化
public int randomPartition(int[] arr, int low, int high) {
int i = random.nextInt(high - low + 1) + low;
int temp = arr[low];
arr[low] = arr[i];
arr[i] = temp;
return partition(arr, low, high);
}
private int partition(int[] arr, int low, int high) {
int tmp = arr[low];
while (low < high) {
while (low < high && arr[high] >= tmp) high--;
arr[low] = arr[high];
while (low < high && arr[low] <= tmp) low++;
arr[high] = arr[low];
}
arr[low] = tmp;
return low;
}
3. 快速选择(O(n)时间寻找topK、中位数)
private void getMinTopK(int[] arr, int low, int high, int k){
if(low < high){
int index = partition(arr, low, high);
if(index == k-1) return;
else if(index > k-1) getMinTopK(arr, low, index-1, k);
else getMinTopK(arr, index+1, high, k);
}
}
private int partition(int[] arr, int low, int high) {
int tmp = arr[low];
while (low < high) {
while (low < high && arr[high] >= tmp) high--;
arr[low] = arr[high];
while (low < high && arr[low] <= tmp) low++;
arr[high] = arr[low];
}
arr[low] = tmp;
return low;
}
4. 回溯
回溯保存数组的子集
// int[] nums 需要回溯的数组
// List<List<Integer>> ans = new ArrayList<>(); 用来保存所有子集
// ArrayList<Integer> temp = new ArrayList<>(); 当前的子集
private void backtrack(int start) {
ans.add(new ArrayList<>(temp));
for (int i = start; i < nums.length; i++) {
if() continue; //剪枝
temp.offerLast(nums[i]);
backtrack(i + 1);
temp.pollLast();
}
}
5. Floyd算法
求两两之间的最短距离 复杂度O(V^3)
for(int k=0;k<n;k++){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(e[i][j]>e[i][k]+e[k][j]){
e[i][j]=e[i][k]+e[k][j];
}
}
}
}
6. 快慢指针判断链表有无环
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
7. 快慢指针求链表的中间节点
中间有两个返回第二个
public ListNode middleNode(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
中间有两个返回第一个,把fast=head改为fast=head.next;
8. 辗转相除法求最大公约数
public int gcd(int m, int n){
if(n==0) return m;
return gcd(n, m%n);
}
9. 基数排序O(n)
public void sort(int[] nums) {
int n = nums.length;
long exp = 1;
int maxVal = Arrays.stream(nums).max().getAsInt();
int[] buf = new int[n];
while (maxVal >= exp) {
int[] cnt = new int[10];
//每个桶中有多少个数
for (int i = 0; i < n; i++) {
int digit = (nums[i] / (int) exp) % 10;
cnt[digit]++;
}
//每个桶对应的索引
for (int i = 1; i < 10; i++) {
cnt[i] += cnt[i - 1];
}
//按低位放到对应的位置
for (int i = n - 1; i >= 0; i--) {
int digit = (nums[i] / (int) exp) % 10;
buf[cnt[digit] - 1] = nums[i];
cnt[digit]--;
}
exp *= 10;
for(int i = 0; i < n; i++) {
nums[i] = buf[i];
}
}
}
10. 前缀树
class Trie {
private Trie[] children;
private boolean isEnd; //表示该节点是否为字符串的结尾
public Trie() {
children = new Trie[26];
isEnd = false;
}
public void insert(String word) {
Trie node = this;
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
int index = ch - 'a';
if (node.children[index] == null) {
node.children[index] = new Trie();
}
node = node.children[index];
}
node.isEnd = true;
}
/** Returns if the word is in the trie. */
public boolean search(String word) {
Trie node = searchPrefix(word);
return node != null && node.isEnd;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
public boolean startsWith(String prefix) {
return searchPrefix(prefix) != null;
}
private Trie searchPrefix(String prefix) {
Trie node = this;
for (int i = 0; i < prefix.length(); i++) {
char ch = prefix.charAt(i);
int index = ch - 'a';
if (node.children[index] == null) {
return null;
}
node = node.children[index];
}
return node;
}
}
11. 摩尔投票法(选出票数超过n/k的人)
在返回之前要判断被选中的元素是不是真的超过了n/k。
当k=2时
public int majorityElement(int[] nums) {
int count = 0;
Integer candidate = null;
for (int num : nums) {
if (count == 0) {
candidate = num;
}
count += (num == candidate) ? 1 : -1;
}
count = 0;
for (int num : nums) {
if(num == candidate) {
count++;
}
}
if(count < nums.length / 2) {
candidate = null;
}
return candidate;
}
当k=3时
public List<Integer> majorityElement(int[] nums) {
int element1 = 0;
int element2 = 0;
int vote1 = 0;
int vote2 = 0;
for (int num : nums) {
if (vote1 > 0 && num == element1) { //如果该元素为第一个元素,则计数加1
vote1++;
} else if (vote2 > 0 && num == element2) { //如果该元素为第二个元素,则计数加1
vote2++;
} else if (vote1 == 0) { // 选择第一个元素
element1 = num;
vote1++;
} else if (vote2 == 0) { // 选择第二个元素
element2 = num;
vote2++;
} else { //如果三个元素均不相同,则相互抵消1次
vote1--;
vote2--;
}
}
int cnt1 = 0;
int cnt2 = 0;
for (int num : nums) {
if (vote1 > 0 && num == element1) {
cnt1++;
}
if (vote2 > 0 && num == element2) {
cnt2++;
}
}
// 检测元素出现的次数是否满足要求
List<Integer> ans = new ArrayList<>();
if (vote1 > 0 && cnt1 > nums.length / 3) {
ans.add(element1);
}
if (vote2 > 0 && cnt2 > nums.length / 3) {
ans.add(element2);
}
return ans;
}
12. dijkstra算法
void dijkstra() {
// 起始先将所有的点标记为「未更新」和「距离为正无穷」
Arrays.fill(vis, false);
Arrays.fill(dist, INF);
// 只有起点最短距离为 0
dist[k] = 0;
// 迭代 n 次
for (int p = 1; p <= n; p++) {
// 每次找到「最短距离最小」且「未被更新」的点 t
int t = -1;
for (int i = 1; i <= n; i++) {
if (!vis[i] && (t == -1 || dist[i] < dist[t])) t = i;
}
// 标记点 t 为已更新
vis[t] = true;
// 用点 t 的「最小距离」更新其他点
for (int i = 1; i <= n; i++) {
dist[i] = Math.min(dist[i], dist[t] + w[t][i]);
}
}
}
13. 两个有序数组第k小的数
public int getKthElement(int[] nums1, int[] nums2, int k) {
/* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
* 这里的 "/" 表示整除
* nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
* nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
* 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
* 这样 pivot 本身最大也只能是第 k-1 小的元素
* 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
* 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
* 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
*/
int length1 = nums1.length, length2 = nums2.length;
int index1 = 0, index2 = 0;
while (true) {
// 边界情况
if (index1 == length1) {
return nums2[index2 + k - 1];
}
if (index2 == length2) {
return nums1[index1 + k - 1];
}
if (k == 1) {
return Math.min(nums1[index1], nums2[index2]);
}
// 正常情况
int half = k / 2;
int newIndex1 = Math.min(index1 + half, length1) - 1;
int newIndex2 = Math.min(index2 + half, length2) - 1;
int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2];
if (pivot1 <= pivot2) {
k -= (newIndex1 - index1 + 1);
index1 = newIndex1 + 1;
} else {
k -= (newIndex2 - index2 + 1);
index2 = newIndex2 + 1;
}
}
}
14. 利用数组翻转让数组的元素向右移动k的位置
时间复杂度O(n),空间复杂度O(1)
public void rotate(int[] nums, int k) {
k = k % nums.length();
reverse(nums, 0, nums.length - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.length - 1);
}
public void reverse(int[] nums, int start, int end) {
while (start < end) {
int temp = nums[start];
nums[start] = nums[end];
nums[end] = temp;
start += 1;
end -= 1;
}
}
15. 堆排序
private void heapSort(int[] nums) {
int len = nums.length-1;
buildMaxHeap(nums, len);
while(len >= 1) {
swap(nums, 0, len);
len--;
maxHeapify(nums, 0, len);
}
}
private void buildMaxHeap(int[] nums, int len) {
for(int i = len / 2; i >= 0; i--) {
maxHeapify(nums, i, len);
}
}
private void maxHeapify(int[] nums, int i, int len) {
while(2*i + 1 <= len) {
int left = i*2+1;
int right = i*2+2;
int large = i;
if(left <= len && nums[left] > nums[large]) {
large = left;
}
if(right <= len && nums[right] > nums[large]) {
large = right;
}
if(large != i) {
swap(nums, i, large);
i = large;
} else {
break;
}
}
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
16. 双栈实现字符串运算
class Solution {
// 运算符优先级
private Map<Character, Integer> map = new HashMap<>(){{
put('-', 1);
put('+', 1);
put('*', 2);
put('/', 2);
put('%', 2);
put('^', 3);
}};
public int calculate(String s) {
Deque<Integer> nums = new ArrayDeque<>(); // 数字栈
nums.push(0); // 为了防止第一个数为负数
Deque<Character> ops = new ArrayDeque<>(); // 符号栈
s = s.replaceAll(" ", ""); // 去掉空格
char[] ch = s.toCharArray();
int n = s.length();
for (int i = 0; i < n; i++) {
if (ch[i] == '(') {
ops.push(ch[i]);
} else if (ch[i] == ')') {
while (!ops.isEmpty()) {
if (ops.peek() != '(') {
calc(nums, ops);
} else {
ops.pop();
break;
}
}
} else if (Character.isDigit(ch[i])) {
int num = 0;
int j = i;
while (j < n && Character.isDigit(ch[j])) {
num = num * 10 + (ch[j++] - '0');
}
nums.push(num);
i = j - 1;
} else {
if (i > 0 && (ch[i - 1] == '(' || ch[i - 1] == '+' || ch[i - 1] == '-')) {
nums.push(0);
}
// 栈内运算符比当前运算符优先级高/同等,进行运算
while (!ops.isEmpty() && ops.peek() != '(') {
char prev = ops.peek();
if (map.get(prev) >= map.get(ch[i])) {
calc(nums, ops);
} else {
break;
}
}
ops.push(ch[i]);
}
}
while (!ops.isEmpty()) calc(nums, ops);
return nums.peek();
}
private void calc(Deque<Integer> nums, Deque<Character> ops) {
if (nums.isEmpty() || nums.size() < 2) return;
if (ops.isEmpty()) return;
int b = nums.pop(), a = nums.pop();
char op = ops.pop();
int ans = 0;
if (op == '+') ans = a + b;
else if (op == '-') ans = a - b;
else if (op == '*') ans = a * b;
else if (op == '/') ans = a / b;
else if (op == '^') ans = (int)Math.pow(a, b);
else if (op == '%') ans = a % b;
nums.push(ans);
}
}
17. 快速幂和快速乘
按二进制的1决定要不要加
public double quickMul(double x, long N) {
double ans = 1.0;
// 贡献的初始值为 x
double x_contribute = x;
// 在对 N 进行二进制拆分的同时计算答案
while (N > 0) {
if (N % 2 == 1) {
// 如果 N 二进制表示的最低位为 1,那么需要计入贡献
ans *= x_contribute;
}
// 将贡献不断地平方
x_contribute *= x_contribute;
// 舍弃 N 二进制表示的最低位,这样我们每次只要判断最低位即可
N /= 2;
}
return ans;
}
快速乘就是把上面的乘改成加
18. 并查集
class UnionFind {
private int[] parent;
public UnionFind(int n) {
this.parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
}
}
public boolean isConnected(int x, int y) {
return find(x) == find(y);
}
public int find(int x) {
if (x != parent[x]) {
parent[x] = find(parent[x]);
}
return parent[x];
}
public void union(int x, int y) {
int xRoot = find(x);
int yRoot = find(y);
if (xRoot == yRoot) {
return;
}
parent[xRoot] = yRoot;
}
}
带权重的并查集
private class UnionFind {
private int[] parent;
private double[] weight; //指向的父结点的权值
public UnionFind(int n) {
this.parent = new int[n];
this.weight = new double[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
weight[i] = 1.0d;
}
}
public void union(int x, int y, double value) {
int rootX = find(x);
int rootY = find(y);
if (rootX == rootY) {
return;
}
parent[rootX] = rootY;
weight[rootX] = weight[y] * value / weight[x];
}
public int find(int x) {
if (x != parent[x]) {
int origin = parent[x];
parent[x] = find(parent[x]);
weight[x] *= weight[origin];
}
return parent[x];
}
public double isConnected(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX == rootY) {
return weight[x] / weight[y];
} else {
return -1.0d;
}
}
}
19. 字典序下一个排列
class Solution {
public void nextPermutation(int[] nums) {
int i = nums.length - 2;
//找第一个a[i] < a[i + 1],此时[i + 1, n)一定为降序
while (i >= 0 && nums[i] >= nums[i + 1]) {
i--;
}
if (i >= 0) {
int j = nums.length - 1;
// 第一个比a[i]大的数字,此时[i + 1, n)还是降序
while (j >= 0 && nums[i] >= nums[j]) {
j--;
}
swap(nums, i, j);
}
//重新按照升序排列,使变大的程度最小
//本身就是降序,所以颠倒顺序就是升序
reverse(nums, i + 1);
}
public void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
public void reverse(int[] nums, int start) {
int left = start, right = nums.length - 1;
while (left < right) {
swap(nums, left, right);
left++;
right--;
}
}
}
四、一些方便的写法
1. 排序
1.1. 对一个String对象内部按照字母排序
char[] ch = s.toCharArray();
Arrays.sort(ch);
String string = String.valueOf(ch);
1.2. 对n个坐标(int[n][2])按x排序
Arrays.sort(arr, (v1, v2) -> v1[0] - v2[0]);
其中第二个参数是Comparetor对象,这里也记录一下Comparetor对象的用法:
//如果返回的值大于零,则交换object1和object2的位置
//如果返回的值小于零,则不交换object1和object2的位置
Comparetor.compare(object1, object2){
//升序排序
//在原始排序中,object1在object2前
//如果object1 < object2,返回值 < 0,不交换位置,依然是升序
//如果object1 > object2,返回值 > 0,交换位置,变成升序。
return object1 - object2;
//同理是降序
return object2 - object1;
}
2. 生成[MIN, MAX]之间的随机整数
Random random = new Random();
int rand = random.nextInt(MAX - MIN + 1) + MIN;
3. 遍历
3.1. foreach遍历String对象
for(char ch : string.toCharArray())
4. 转化
4.1 List和数组
双层List转化为二维数组
List<int[]> list = new ArrayList<>();
int[][] nums = list.toArray(new int[list.size()][]);
数组转化为List
List<> list = Arrays.asList();
4.2 int和String
int num = Integer.parseInt(string);
String string = String.valueOf(num);
5. swap
交换List中两个元素
List<Integer> list = new ArrayList<>();
Collections.swap(list, idx1, idx2);
6. 字符相关
6.1 isDigit判断是不是数字
char ch = '5';
Character.isDigit(ch);
6.2 toUpperCase() 变大写
Chacater.toUpperCase('a');
String s = 'abc';
s.toUpperCase();
6.3 StringBuilder的使用
StringBuilder sb = new StringBuilder();
sb.length(); //长度
sb.append(); //可以添加各种类型,不一定要char或者String
sb.insert(offset, ) //第一个是偏移量
sb.deleteCharAt(idx); //删除
7. 创建的同时初始化(List)
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
8. Iterator类
Iterator接口的定义
package java.util;
public interface Iterator<E> {
boolean hasNext();//判断是否存在下一个对象元素
E next();//获取下一个元素
void remove();//移除元素
}
五、一些技巧
1. O(1)时间获取栈和队列中的最小值
对于栈:使用一个辅助栈,用来储存栈中递减的元素,具体实现如下,这样辅助栈的栈顶保存的就是当前栈中最小的元素。
private Stack<Integer> stack;
private Stack<Integer> helperStack;
public void push(int x) {
stack.add(x);
if(helperStack.empty() || helperStack.peek() >= x)
helperStack.add(x);
}
}
public void pop() {
if(stack.pop().equals(helperStack.peek())){
helperStack.pop();
}
}
对于队列:使用一个双端单调队列作为辅助队列,具体实现如下,这样第一个元素就是当前队列中最小的元素。
private Deque<Integer> queue;
private Deque<Integer> helperDeque;
public void offer(int x) {
queue.offer(x);
while(!helperDeque.isEmpty() && helperDeque.peekLast() > x) {
helperDeque.pollLast();
}
deque.offerLast();
}
public void poll() {
if(queue.poll().equals(helperDeque.peekFirst())){
helperDeque.pollFirst();
}
}
进行一个简单的分析:
对于栈:假如要加入栈的数字依次是:1 3 5 -1 6 -3 3 7
在-3出栈之前,取最小的结果都是-3;-3出栈之后、-1出栈之前,取最小的结果都是-3;依次类推,辅助栈只需要保存:1 -1 -3 即可
对于队列:假设同一串数字依次加入队列
在 1 3 5 入队列之后,取最小的结果是 1,若 1 出队列之后,则最小值依次变为1 3 5;在 1 3 5 -1 出队列之后,取最小的结果是 - 1,直到 1 3 5 -1 都出队列之后;结合这两个例子可以注意到,需要的辅助队列就是在第一节介绍的单调队列
2. 补码
对一个Integer算补码的时候,对于负数,要加上的偏移量,再进行进制转换。
if(num < 0) num = (long)(Math.pow(2, 32) + num);
3. 连续个数和总数
遍历字符串的时候,求某个字符的总数不清零,求连续个数的时候要清零
六、一些重要的思想
1. 去重
在数组上取一些数字作为结果的时候,如果要去重,就判断当前值和前一个值是不是相等,相等的话就continue。
idx++; //先自增
while(idx < nums.length && nums[idx] == nums[idx-1]) continue;