常见算法模板

一、数学

1.1 素数

素数:只有1和本身两个因子。
2、3、5、7、11、13、17...

1) 单个判断

普通判断算法 O ( n ) O(\sqrt{n}) O(n )

    public boolean isPrime(int n) {
        if (n <= 1) return false;
        for (int i = 2; i * i <= n; i++) {
            if (n % i == 0) return false;
        }
        return true;
    }

稍快速的判断算法 O ( 1 2 n ) O({1\over2}\sqrt{n}) O(21n )

素数中只有一个2是偶数,其余都是奇数,这些奇素数不可能有偶因子

    public boolean isPrime2(int n) {
        if (n <= 1) return false;
        if (n == 2) return true;
        for (int i = 3; i * i <= n; i += 2) {
            if (n % i == 0) return false;
        }
        return true;
    }

2) 区间判断

循环套单个判断 O ( 1 2 n 3 2 ) O({1\over2}n^{3\over2}) O(21n23)

    public List<Integer> getRangePrime(int start, int end) {
        List<Integer> ans = new ArrayList<>();
        for (int i = start; i <= end; i++) 
            if (isPrime(i)) ans.add(i);
        return ans;
    }

    public boolean isPrime(int n) {
        if (n <= 1) return false;
        if (n == 2) return true;
        for (int i = 3; i * i <= n; i += 2) {
            if (n % i == 0) return false;
        }
        return true;
    }

埃式筛 O ( n l o g ( l o g ( n ) ) ) O(nlog(log(n))) O(nlog(log(n)))
l o g l o g n loglogn loglogn上升极慢,上述复杂度几乎可以认为是线性

table数组的值表示该下标对应的数字是否是素数

  1. 初始化table全部为true
  2. i遍历[2, sqrt(n)]
  3. 如果table[i]true,则j遍历[i * i, n],全部table[j]falsej遍历步长为i
    public int[] checkPrime(int n) { // 考察[2, n]区间的素数情况
        boolean[] table = new boolean[n + 1];
        Arrays.fill(table, true); // 初始化认为全是素数
        for (int i = 2; i * i <= n; i++) { // 从2开始,遍历到sqrt(n)
            if (table[i]) { // 如果当前是素数
                // 就把从 i*i 开始,i 的所有倍数都设置为 false。
                for (int j = i * i; j <= n; j += i) {
                    table[j] = false;
                }
            }
        }
        List<Integer> ans = new ArrayList<>();
        for (int i = 2; i <= n; i++) {
            if (table[i]) ans.add(i);
        }
        return ans.stream().mapToInt(i -> i).toArray();
    }

1.2 最大公约数 & 最小公倍数

1) 最大公约数

循环写法

public static int getGCD(int a, int b) {
    if (a < 0 || b < 0) {
        return -1; // 数学上不考虑负数的约数
    }
    if (b == 0) {
        return a;
    }
    while (a % b != 0) {
        int temp = a % b;
        a = b;
        b = temp;
    }
    return b;
}

递归写法

public static int getGCD(int a, int b) {
    if (a < 0 || b < 0) {
        return -1; // 数学上不考虑负数的约数
    }
    if (b == 0) {
        return a;
    }
    return a % b == 0 ? b : getGCD(b, a % b);
}

2) 最小公倍数

a和b的最小公倍数 = a ∗ b / g c d ( a , b ) a *b/gcd(a,b) ab/gcd(a,b)

二、排序

2.1 快排


    public void quickSort(int[] nums, int left, int right) {
        if (left >= right) return;
        int mid = partition(nums, left, right);
        quickSort(left, mid - 1);
        quickSort(mid + 1, right);
    }

    private int partition(int[] nums, int left, int right) {
        int r = (int)(Math.random() * (right - left + 1) + left);
        int temp = nums[left];
        nums[left] = nums[r];
        nums[r] = temp;
        temp = nums[left];
        while (left < right) {
            while (left < right && nums[right] < temp) right--;
            nums[left] = nums[right];
            while (left < right && nums[left] >= temp) left++;
            nums[right] = nums[left]; 
        }
        nums[left] = temp;
        return left;
    }

2.2 归并

    public int[] sortArray(int[] nums) {
        mergeSort(nums, 0, nums.length - 1);
        return nums;
    }

    private void mergeSort(int[] nums, int left, int right) {
        if (left >= right) return;
        int mid = left + ((right - left) >> 1);
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);
        merge(nums, left, mid, mid + 1, right);
    }

    private void merge(int[] nums, int l1, int r1, int l2, int r2) {
        int[] temp = new int[nums.length];
        int start = l1, cnt = 0;
        while (l1 <= r1 || l2 <= r2) {
            if (l1 <= r1 && l2 <= r2 && nums[l1] < nums[l2] || l2 > r2) 
                temp[cnt++] = nums[l1++];
            else temp[cnt++] = nums[l2++];
        }
        System.arraycopy(temp, 0, nums, start, cnt);
    }

2.3 堆排

    public int[] heapSort(int[] a) {
        int[] nums = new int[a.length + 1];
        System.arraycopy(a, 0, nums, 1, a.length);
        int tail = a.length;
        for (int i = tail >> 1; i >= 1; i--) adjust(i, nums, tail);
        for (int i = 0; i < a.length; i++) {
            a[i] = nums[1];
            nums[1] = nums[tail--];
            adjust(1, nums, tail);
        }
        return a;
    }

    private void adjust(int i, int[] nums, int tail) {
        int j = i << 1;
        while (j <= tail) {
            if (j + 1 <= tail && nums[j + 1] < nums[j]) j++;
            if (nums[i] > nums[j]) {
                int temp = nums[i];
                nums[i] = nums[j];
                nums[j] = temp;
            } 
            i = j;
            j = i << 1;
        }
    }

三、树

3.1 可更改数组的区间和

leetcode q307 区域和检索-数组可修改

1)线段树

花花酱 Segment Tree 线段树 - 刷题找工作 SP14

class NumArray {

    private int[] nums;
    private Node root;

    public NumArray(int[] nums) {
        this.nums = nums;
        root = buildTree(0, nums.length - 1);
    }

    
    public void update(int index, int val) {
        updateTreeNode(index, val, root);
    }
    
    public int sumRange(int left, int right) {
        return queryRangeTree(left, right, root);
    }

	/* Segment Tree starts from here */
	
    class Node {
        int start, end, sum;
        Node left, right;
        public Node(int start, int end, int sum) {
            this.start = start;
            this.end = end;
            this.sum = sum;
        }
    }

    private Node buildTree(int start, int end) {
        if (start == end) return new Node(start, end, nums[start]);
        int mid = start + ((end - start) >> 1);
        Node left = buildTree(start, mid);
        Node right = buildTree(mid + 1, end);
        Node node = new Node(start, end, left.sum + right.sum);
        node.left = left;
        node.right = right;
        return node;
    }

    private void updateTreeNode(int idx, int val, Node root) {
        if (root.start == root.end) {
            root.sum = val;
            return;
        }
        int mid = root.start + ((root.end - root.start) >> 1);
        if (idx <= mid) updateTreeNode(idx, val, root.left);
        else updateTreeNode(idx, val, root.right);
        root.sum = root.left.sum + root.right.sum;
    }

    private int queryRangeTree(int start, int end, Node root) {
        if (start == root.start && end == root.end) return root.sum;
        int mid = root.start + ((root.end - root.start) >> 1);
        if (end <= mid) return queryRangeTree(start, end, root.left);
        else if (start > mid) return queryRangeTree(start, end, root.right);
        else return queryRangeTree(start, mid, root.left) + queryRangeTree(mid + 1, end, root.right);
    }
}

2)树状数组

花花酱 Fenwick Tree / Binary Indexed Tree - 刷题找工作 SP3

class NumArray {

    FenWickTree tree;

    private int[] nums;
    public NumArray(int[] nums) {
        this.nums = nums;
        tree = new FenWickTree(nums);
    }

    public void update(int index, int val) {
        tree.updateTree(index + 1, val - nums[index]);
        nums[index] = val;
    }
    
    public int sumRange(int left, int right) {
        return tree.queryRangeFromHead(right + 1) - tree.queryRangeFromHead(left);
    }

    /** 树状数组开始 */
    
    class FenWickTree {
        private int[] tree;

        public FenWickTree(int[] nums) {
            tree = new int[nums.length + 1];
            for (int i = 0; i < nums.length; i++) {
                updateTree(i + 1, nums[i]); // 注意,FenwickTree的下标相比于原始数组右偏移一位
            }
        }

        public void updateTree(int index, int delta) { // 注意,更新是基于增量的
            while (index < tree.length) {
                tree[index] += delta;
                index += lowbit(index);
            }
        }

        public int queryRangeFromHead(int index) { // 注意,查询是查询的从头到index的区间和
            int ans = 0;
            while (index > 0) {
                ans += tree[index];
                index -= lowbit(index);
            }
            return ans;
        }

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

四、链表

4.1 k个一组翻转链表

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        // 因为要有连接操作,所以要有pre和虚拟节点
        ListNode dummy = new ListNode(), pre = dummy, groupStart = head;
        dummy.next = head;
        while (groupStart != null) {
            ListNode[] res = reverseK(groupStart, k);
            pre.next = res[0];
            pre = res[1];
            groupStart = res[1].next;
        }
        return dummy.next;
    }

    private ListNode[] reverseK(ListNode head, int k) {
        // 翻转链表,先找到最后的非法节点
        ListNode invalid = head;
        int cnt = 0;
        while (cnt++ < k && invalid != null) invalid = invalid.next; // invalid 向后走k个位置
        // 如果cnt没有到预计最大值 k + 1,那么说明invalid == null中途退出,这样的话剩余不到k个节点
        if (cnt < k + 1) return new ListNode[] {head, new ListNode()}; // 注意这边返回的链表尾非真实的链表尾,而是新建的,但是因为他的next是null,所以下次的start是null,退出循环了就,所以不影响
        // 经典反转链表模板 ⭐️
        ListNode pre = invalid, cur = head;
        while (cur != invalid) {
            ListNode temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return new ListNode[] {pre, head};
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值