Java面试准备——leetcode

在刷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算补码的时候,对于负数,要加上2^{32}的偏移量,再进行进制转换。

if(num < 0) num = (long)(Math.pow(2, 32) + num);

3. 连续个数和总数

遍历字符串的时候,求某个字符的总数不清零,求连续个数的时候要清零

六、一些重要的思想

1. 去重

在数组上取一些数字作为结果的时候,如果要去重,就判断当前值和前一个值是不是相等,相等的话就continue。

idx++; //先自增
while(idx < nums.length && nums[idx] == nums[idx-1]) continue;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值