算法模板(JAVA)

算法模板

二分搜索

模板

int binarySearch(int[] nums, int val) {
    int n = nums.length;
    int left = -1, right = n;
    while (left + 1 != right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < val) left = mid;
        else right = mid;
    }
    return left;
}

使用

1、数据大小查找,以判断第一个 ≥ 5 \geq5 5的元素位置、最后一个 < 5 <5 <5的元素位置、第一个 > 5 >5 >5的元素位置、最后一个 ≤ 5 \leq5 5的元素位置为例。
第一个 ≥ 5 \geq5 5的元素位置:判断条件if (nums[mid] < val) left = mid;返回值return right;
最后一个 < 5 <5 <5的元素位置:判断条件if (nums[mid] < val) left = mid;返回值return left;
第一个 > 5 >5 >5的元素位置:判断条件if (nums[mid] >= val) left = mid;返回值return right;
最后一个 ≤ 5 \leq5 5的元素位置:判断条件if (nums[mid] <= val) left = mid;返回值return left;
2、二分答案题。

理解

将数组按边界分为blue和red,找出符合blue/red的值。

详解

二分查找板子

例题

leetcode35. 搜索插入位置leetcode704. 二分查找leetcode275. H 指数 II二分答案题

快速排序

模板

void quickSort(int[] nums, int left, int right) {
    if (left < right) {
        int temp = nums[left];
        int i = left, j = right;
        while (i < j) {
            while (i < j && nums[j] > temp) --j;
            if (i < j) nums[i++] = nums[j];
            while (i < j && nums[i] < temp) ++i;
            if (i < j) nums[j--] = nums[i];
        }
        nums[i] = temp;
        quickSort(nums, left, i - 1);
        quickSort(nums, i + 1, right);
    }
}

topK

模板

/**
 * @param l 左边界索引
 * @param r 右边界索引
 * @param k 第k大
 */
int topK(int[] nums, int l, int r, int k) {
    if (l >= r) return nums[l];
    int i = l, j = r;
    int t = nums[l];
    while (i < j) {
        while (i < j && nums[j] > t) --j;
        if (i < j) nums[i++] = nums[j];
        while (i < j && nums[i] < t) ++i;
        if (i < j) nums[j--] = nums[i];
    }
    nums[i] = t;
    if ((i - l + 1) >= k) return topK(nums, l, i, k);
    else return topK(nums, i + 1, r, k - (i - l + 1));
}

归并排序

void mergeSort(int[] nums, int l, int r) {
    if (l >= r) return;
    int mid = (r - l) / 2 + l;
    // 分别排序
    mergeSort(nums, l, mid);
    mergeSort(nums, mid + 1, r);
    // 合并排好序的数组
    int[] temp = new int[r - l + 1];
    int i = l, j = mid + 1, k = 0;
    while (i <= mid && j <= r) {
        if (nums[i] < nums[j]) temp[k++] = nums[i++];
        else temp[k++] = nums[j++];
    }
    while (i <= mid) temp[k++] = nums[i++];
    while (j <= r) temp[k++] = nums[j++];
    // 更新nums数组
    for (i = l, j = 0; i <= r; i++, j++) nums[i] = temp[j];
}

快速幂

模板

/**
 * @param a 底数
 * @param b 指数
 */
long pow(long a, long b) {
    if (a == 1 || b == 0) return 1;
    if (b < 0) {
        a = 1 / a;
        b = -b;
    }
    long res = 1;
    while (b > 0) {
        if ((b & 1) == 1) res *= a;  // 如果b为奇数,则需要额外乘一次
        a *= a;
        b >>= 1;
    }
    return res;
}

理解

1、对于 a 7 a^7 a7来说,其值等于 a ∗ a ∗ a ∗ a ∗ a ∗ a ∗ a a*a*a*a*a*a*a aaaaaaa,因此时间复杂度为 O ( n ) O(n) O(n)。可以使用分治的思想降低复杂度为 O ( l o g n ) O(logn) O(logn),即首先计算 a 2 = a ∗ a a^2=a*a a2=aa,那么 a 4 = a 2 ∗ a 2 a^4=a^2*a^2 a4=a2a2,而 a 7 = a 4 ∗ a 2 ∗ a a^7=a^4*a^2*a a7=a4a2a。特殊的,从上述例子可以看出,当当前指数 b b b为奇数时,下次带入计算的指数 b ′ = ⌊ b / 2 ⌋ b'=\lfloor b/2 \rfloor b=b/2,因此需要额外乘以一次底数。
2、对于需要 快速幂取模 运算时,由性质 ( x ∗ y ) % m = x % m ∗ y % m (x * y) \% m = x \% m * y \% m (xy)%m=x%my%m 可得:

while (b > 0) {
    if ((b & 1) == 1) res *= a % mod;  // 如果b为奇数,则需要额外乘一次
    a *= a % mod;
    b >>= 1;
}

详解

【数论】快速幂快速幂详解

例题

leetcode 50. Pow(x, n)leetcode 100155. 双模幂运算

树状数组

模板

class Fenwick {
    private int[] tree;
    private int n;

    Fenwick(int n) {
        this.n = n;
        tree = new int[n + 1];
    }

    private int lowbit(int x) {
        return x & -x;
    }

    // 查询前缀和
    public int query(int x) {
        int ans = 0;
        for (int i = x; i > 0; i -= lowbit(i)) ans += tree[i];
        return ans;
    }

    public void add(int x, int val) {
        for (int i = x; i <= n; i += lowbit(i)) tree[i] += val;
    }
}

使用

class Main {
    int[] nums;
    Fenwick fenwick = new Fenwick(nums.length);
    
    void init() {
        for (int i = 0; i < nums.length; i += fenwick.lowbit(i)) {
            fenwick.add(i + 1, nums[i]);
        }
    }

    void update(int x, int val) {
        fenwick.add(x + 1, val - nums[x]);
        nums[x] = val;
    }

    // [left, right]
    int sumRange(int left, int right) {
        return fenwick.query(right + 1) - fenwick.query(left);
    }
}

理解

为什么树状数组下标必须从1开始?
因为lowbit(int x)方法中,当x等于0时没有意义,返回0,因此当执行i += lowbit(i)时,i没有变化,陷入死循环。

详解

树状数组详解深刻理解树状数组

习题

leetcode307. 区域和检索 - 数组可修改leetcode1409. 查询带键的排列

跳表

模板

class SkipTable {

    private final int maxLevel = 16;
    private final Random random = new Random();

    private Node head;

    public SkipTable() {
        head = new Node(-1, -1, maxLevel);
    }

    private int randomLevel() {
        int level = 1;
        while (level < maxLevel && random.nextInt() % 2 == 0) level++;
        return level;
    }

    void insert(int key, int data) {
        Node preElem = head;
        Node[] pres = new Node[maxLevel];
        boolean flag = false; // 不重复插入
        for (int i = maxLevel - 1; i >= 0; i--) {
            for (Node next = preElem.level[i]; next != null; next = preElem.level[i]) {
                if (next.key >= key) {
                    if (next.key == key) {
                        next.data = data;  // 更新data
                        flag = true;
                    }
                    break;
                }
                preElem = next;
            }
            pres[i] = preElem;
        }
        if (flag) return;
        int level = randomLevel();
        Node node = new Node(key, data, level);
        for (int i = level - 1; i >= 0; i--) {
            node.level[i] = pres[i].level[i];
            pres[i].level[i] = node;
        }
    }

    int query(int key) {
        Node preElem = head;
        for (int i = maxLevel - 1; i >= 0; i--) {
            for (Node next = preElem.level[i]; next != null; next = preElem.level[i]) {
                if (next.key >= key) {
                    if (next.key == key) return next.data;
                    break;
                }
                preElem = next;
            }
        }
        return -1;
    }

    void delete(int key) {
        Node preElem = head;
        Node[] pres = new Node[maxLevel];
        int level = -1;
        for (int i = maxLevel - 1; i >= 0; i--) {
            for (Node next = preElem.level[i]; next != null; next = preElem.level[i]) {
                if (next.key >= key) {
                    if (next.key == key && level == -1) {
                        level = i;
                    }
                    break;
                }
                preElem = next;
                pres[i] = preElem;
            }
        }
        for (int i = level; i >= 0; i--) {
            pres[i].level[i] = pres[i].level[i].level[i];
        }
    }


    class Node {
        int key, data;
        Node[] level; //level数组存放node在每一层的next节点

        public Node(int key, int data, int level) {
            this.key = key;
            this.data = data;
            this.level = new Node[level];
        }
    }
}

字典树

模板

class Tire {
    private final Tire[] children; // 可替换为HashMap
    private boolean isEnd;

    public Tire() {
        children = new Tire[26];
        isEnd = false;
    }

    public void insert(String s) {
        char[] ca = s.toCharArray();
        Tire node = this;
        for (char c : ca) {
            if (node.children[c - 'a'] == null) {
                node.children[c - 'a'] = new Tire();
            }
            node = node.children[c - 'a'];
        }
        node.isEnd = true;
    }

    public boolean search(String s) {
        char[] ca = s.toCharArray();
        Tire node = this;
        for (char c : ca) {
            if (node.children[c - 'a'] == null) return false;
            node = node.children[c - 'a'];
        }
        return isEnd;
    }

    public String filter(String text, String rp) {
        if (text.isBlank()) return text;
        StringBuilder sb = new StringBuilder();
        Tire node = this;
        int l = 0, r = 0;
        while (l < text.length()) {
            node = node.children[text.charAt(r) - 'a'];
            if (node == null) {  // 以l开头的字符串未匹配中模板
                sb.append(text.charAt(l));
                l++;
                r = l;
                node = this; // 匹配成功或失败都要回到字典树根节点
            } else if (node.isEnd) {  // 以l开头的字符串匹配中模板
                sb.append(rp);
                r++;
                l = r;
                node = this;
            } else {
                r++;
                if (r >= text.length()) {  // 以l开头的剩余字符串未匹配中
                    sb.append(text.charAt(l));
                    l++;
                    r = l;
                    node = this;
                }
            }
        }
        return sb.toString();
    }
}

习题

leetcode208.实现Trie(前缀树)

LRU

模板

/**
 * 时间复杂度为O(1)的LRUCache实现
 */
class LRU{
    int capacity;
    HashMap<Integer, Node> map;

    Node dummy; // 哨兵节点,作为队头

    public LRU(int capacity){
        this.capacity = capacity;
        map = new HashMap<>();
        dummy = new Node(-1, -1);
        dummy.next = dummy;
        dummy.pre = dummy;
    }
    
    public int get(int key) {  // 取值操作
        Node node = map.get(key);
        if (node == null) return -1;
        // 将最新访问的节点放入队头
        remove(node);
        pushFront(node);
        return node.val;
    }
    
    public void put(int key, int value) { // 存值操作
        Node node = map.get(key);
        if (node != null) {
            // 有则更新value
            node.val = value;
            remove(node);
        } else {
            node = new Node(key, value);
            map.put(key, node);
        }
        // 将新加入的节点放入队头
        pushFront(node);
        if (map.size() > capacity) {
            // 当超出容量时,删除队尾节点
            map.remove(dummy.pre.key);
            remove(dummy.pre);
        }
    }
    
    private void pushFront(Node node) { // 放入队头
        node.next = dummy.next;
        node.pre = dummy;
        dummy.next = node;
        node.next.pre = node;
    }

    private void remove(Node node) {  //删除
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }
    
    class Node{
        int key, val;
        Node pre, next;
        public Node(int key, int val) {
            this.key = key;
            this.val = val;
        }
    }
}

小根堆

模板

class MinHeap {
    private final int[] nums;
    private int size = 0;
    private final int maxSize;

    public MinHeap(int maxSize) {
        this.maxSize = maxSize;
        nums = new int[maxSize + 1];
    }

    public void push(int val) {
        if (size >= maxSize) return;
        nums[++size] = val;
        up(size);
    }

    public void pop() {
        if (size == 0) return;
        swap(1, size);
        size--;
        down(1);
    }

    public int top() {
        return size == 0 ? -1 : nums[1];
    }

    private void down(int idx) {
        if (idx > size) return;
        int l = idx * 2, r = idx * 2 + 1;
        int minIdx = idx;
        if (l <= size && nums[l] < nums[minIdx]) minIdx = l;
        if (r <= size && nums[r] < nums[minIdx]) minIdx = r;
        if (minIdx != idx) {
            swap(minIdx, idx);
            down(minIdx);
        }
    }

    private void up(int idx) {
        while (idx / 2 > 0 && nums[idx] < nums[idx / 2]) {
            swap(idx, idx / 2);
            idx /= 2;
        }
    }

    private void swap(int idx1, int idx2) {
        int temp = nums[idx1];
        nums[idx1] = nums[idx2];
        nums[idx2] = temp;
    }
}

限流算法

令牌桶算法

class TokenBucket {

    // 令牌桶容量
    private final int capacity;
    // 令牌每秒增加数量
    private final int rate;
    // 桶中当前剩余数量
    private int tokens;
    // 上次拿令牌时间戳
    private long lastTime;

    public TokenBucket(int capacity, int rate) {
        this.capacity = capacity;
        this.rate = rate;
        this.tokens = capacity;
        lastTime = System.currentTimeMillis();
    }

    /**
     * @param acquires 要获取的令牌数量
     */
    public synchronized boolean tryAcquire(int acquires) {
        long now = System.currentTimeMillis();
        long gap = now - lastTime;
        int newTokens = (int) (gap / 1000 * rate);
        tokens = Math.min(capacity, tokens + newTokens);
        lastTime = now;
        if (acquires < tokens) {
            tokens -= acquires;
            return true;
        } else {
            return false;
        }
    }
}
/* * (有向)图的深度优先遍历算法模板 */ package dsa; public abstract class DFS extends GraphTraverse { //变量 protected static int clock = 0;//遍历过程中使用的计时钟 //构造方法 public DFS(Graph g) { super(g); } //深度优先遍历算法 protected Object traverse(Vertex v, Object info) {//从顶点v出发,做深度优先查找 if (UNDISCOVERED != v.getStatus()) return null;//跳过已访问过的顶点(针对非连通图) v.setDStamp(clock++); v.setStatus(DISCOVERED); visit(v, info);//访问当前顶点 for (Iterator it = v.outEdges(); it.hasNext();) {//检查与顶点v Edge e = (Edge)it.getNext();//通过边e = (v, u) Vertex u = (Vertex)e.getVPosInV(1).getElem();//相联的每一顶点u switch (u.getStatus()) {//根据u当前的不同状态,分别做相应处理 case UNDISCOVERED ://若u尚未被发现,则 e.setType(TREE);//e被归类为“树边” traverse(u, info);//从u出发,继续做深度优先查找 break; case DISCOVERED ://若u已经被发现,但对其访问尚未结束,则 e.setType(BACKWARD);//将e归类为“后向跨边” break; default ://VISITED,即对u的访问已经结束 if (u.getDStamp() < v.getDStamp())//若相对于v,u被发现得更早,则 e.setType(CROSS);//将e归类为“横跨边” else//否则 e.setType(FORWARD);//将e归类为“前向跨边” break; } }//至此,v的所有邻居都已访问结束,故 v.setFStamp(clock++); v.setStatus(VISITED);//将v标记为VISITED return null;//然后回溯 } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值