B站左神算法基础提升

左神算法提升班

10.哈希函数和哈希表

1.认识哈希表和哈希函数的实现

哈希函数特征

  1. 离散性
  2. 均匀性

2.设计RandomPool结构

package hash;

import java.util.HashMap;
public class RandomPool {
   public static class Pool<K>{
       private HashMap<K, Integer> keyIndexMap;
       private HashMap<Integer, K> indexKeyMap;
       private int size;

       public Pool() {
           this.keyIndexMap = new HashMap<K, Integer>();
           this.indexKeyMap = new HashMap<Integer, K>();
           this.size = 0;
       }

       public void insert(K key){
           if(!this.keyIndexMap.containsKey(key)){
               keyIndexMap.put(key,this.size);
               indexKeyMap.put(this.size++, key);
           }

       }

       public void delete(K key){
           if(this.keyIndexMap.containsKey(key)){
               int index = this.keyIndexMap.get(key);
               int lastIndex = --this.size;
               K lastKey = indexKeyMap.get(lastIndex);
               this.keyIndexMap.put(lastKey, index);
               this.indexKeyMap.put(index, lastKey);
               this.keyIndexMap.remove(key);
               this.indexKeyMap.remove(lastIndex);

           }
       }

       public K getRandom(){
           if(this.size == 0){
               return null;
           }
           int random = (int) Math.random() * this.size;
           return this.indexKeyMap.get(random);
       }
   }
}

3.详解布隆过滤器

n 样本量

p 预期失误率

k 哈希函数个数

m 过滤器大小

package hash;

public class BitMap {
    public static void main(String[] args) {
        int a = 0;

        int[] arr = new int[10]; // 32bit * 10   320bit

        int i = 178; //向取得第178个bit的状态

        int numIndex = 178 / 32;
        int bitIndex = 178 % 32;

        int s = ( arr[numIndex] >> (bitIndex)  & 1);

        //把第 i 为的状态改为 1
        arr[numIndex] = arr[numIndex] | (1 << bitIndex);

        //把第 i 为的状态改为 0
        arr[numIndex] = arr[numIndex] & (~ (1 << bitIndex) );

    }

}

详解一致性哈希原理

11.有序表,并查集

1.岛问题

package sortTable;

public class Islands {
    public static int countIslands(int[][] m){
        if(m == null || m[0] == null){
            return 0;
        }

        int N = m.length;
        int M = m[0].length;
        int res = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if(m[i][j] == 1){
                    //感染
                    res++;
                    infect(m, i, j, N, M);

                }
            }
        }
        return res;

    }

    private static void infect(int[][] m, int i, int j, int N, int M) {
        if(m[i][j] != 1 || i < 0 || i >= N || j < 0 || j >= M){
            return;
        }
        m[i][j] = 2;
        infect(m, i-1, j, N, M);
        infect(m, i+1, j, N, M);
        infect(m, i, j-1, N, M);
        infect(m, i, j+1, N, M);
    }

}

2.并查集

package sortTable;

import java.util.HashMap;
import java.util.List;
import java.util.Stack;

public class UnionFind {
    public static class Element<V>{
        public V value;

        public Element(V value) {
            this.value = value;
        }
    }

    public static  class UnionFindSet<V> {
        public HashMap<V, Element<V>> elementMap;
        // key 对应的父结点是 value
        public HashMap<Element<V>, Element<V>> fatherMap;
        // key  某个集合的代表元素  value表示该集合的大小
        public HashMap<Element<V>, Integer> sizeMap;

        public UnionFindSet(List<V> list){
            elementMap = new HashMap<>();
            fatherMap = new HashMap<>();
            sizeMap = new HashMap<>();
            for (V value : list) {
                Element<V> element = new Element<V>(value);
                elementMap.put(value, element);
                fatherMap.put(element, element);
                sizeMap.put(element,1);
            }

        }

        // 给定一个ele,往上一直找,把代表元素返回
        private Element<V> findHead(Element<V> element){
            Stack<Element<V>> path = new Stack<>();
            while(fatherMap.get(element) != element){
                path.push(element);
                element = fatherMap.get(element);
            }
            while (!path.isEmpty()){
                fatherMap.put(path.pop(), element);
            }
            return element;
        }

        public boolean isSameSet(V a, V b){
            if(elementMap.containsKey(a) && elementMap.containsKey(b)){
                return findHead(elementMap.get(a)) == findHead(elementMap.get(b));

            }
            return false;
        }

        public void union(V a, V b){
            if(elementMap.containsKey(a) && elementMap.containsKey(b)){
                Element<V> aF = findHead(elementMap.get(a));
                Element<V> bF = findHead(elementMap.get(b));
                if(aF != bF){
                    Element<V> big = sizeMap.get(aF) > sizeMap.get(bF) ? aF : bF;
                    Element<V> small = big == aF ? bF : aF;
                    fatherMap.put(small, big);
                    sizeMap.put(big, sizeMap.get(small) + sizeMap.get(big));
                    sizeMap.remove(small);
                }
            }

        }
    }
}

3.KMP

字符串str1和str2,str1是否包含str2,如果包含返回str2在str1中的位置 时间复杂的O(N)

  1. 暴力解法

  1. next数组解法
package promoteClass.kmp;
public class KMP {
    public static int getIndexOf(String s, String m) {
        if(s == null || m == null || m.length() < 1 || m.length() > s.length()){
            return -1;
        }
        char[] str1 = s.toCharArray();
        char[] str2 = m.toCharArray();
        int i = 0;
        int j = 0;
        int[] next = getNextArray(str2);

        while(i < str1.length && j < str2.length){
            if(str1[i] == str2[j]){
                i++;
                j++;
            }else if (next[j] == -1){
                i++;
            }else {
                j = next[j];
            }
        }
        return j == str2.length ? i - j : -1;

    }

    private static int[] getNextArray(char[] str) {
        if(str.length == 1){
            return new int[]{-1};
        }
        int[] next = new int[str.length];
        next[0] = -1;
        next[1] = 0;
        int i = 2;
        int cn = 0;
        while(i < str.length){
            if(str[i - 1] == str[cn]){
                next[i++] = ++cn;
            }else if(cn > 0){//cn 来到 next[cn]位置处
                cn = next[cn];
            }else {
                next[i++] = 0;
            }
        }
        return next;
    }
    public static void main(String[] args) {
        String str1 = "afatatedd";
        String str2 = "tedd";
        System.out.println(getIndexOf(str1, str2));
    }
}

12.KMP,Manacher算法

1.Manacher算法解决的问题

最长回文子串

package promoteClass.kmp;

public class Manacher {
    public static char[] manacherString(String str) {
        char[] chars = str.toCharArray();
        char[] res = new char[str.length() * 2 - 1];
        int index = 0;
        for(int i = 0; i != res.length; i++){
            res[i] = (i & 1) == 0 ? '#' : chars[index++];
        }

        return res;
    }

    public static int maxLcpsLength(String s) {
        if (s.length() == 0 || s==null){
            return 0;
        }

        char[] str = manacherString(s);// 1221 ----> #1#2#2#1#
        int[] pArr = new int[str.length];//回文半径数组
        int C = -1; //中心
        int R = -1; // 半径
        int max = Integer.MIN_VALUE;
        for(int i = 0; i != str.length; i++){
            pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1;

            while (i + pArr[i] < str.length && i - pArr[i] > -1){
                if(str[i + pArr[i]] == str[i - pArr[i]]){
                    pArr[i]++;
                }else {
                    break;
                }
            }
            if(i + pArr[i] > R){
                R = i + pArr[i];
                C = i;
            }
            max = Math.max(max, pArr[i]);
        }
        return max - 1;
    }
}

2.窗口问题

有一个整型数组arr和一个大小为w的窗口从数组的最左边滑到右边,窗口每次向右滑动一个位置,每滑动一次窗口产生一个窗口内的最大值,返回这个最大值数组

package promoteClass.kmp;

import java.util.LinkedList;

public class SlidingWindowMaxArray {

    public static class WindowMax {
        private int L;
        private int R;
        private int[] arr;
        private LinkedList<Integer> qmax;
        public WindowMax(int[] a){
            this.arr = a;
            L = -1;
            R = 0;
            qmax = new LinkedList<>();
        }

        public void addNumFromRight(){
            if(R == arr.length){
                return;
            }
            while(!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[R]){
                qmax.pollLast();
            }
            qmax.addLast(R);
            R++;
        }

        public void removeFromLeft() {
            if (L > R - 1){
                return;
            }
            L++;
            if(qmax.peekFirst() == L) {
                qmax.pollFirst();
            }
        }

        public Integer getMax() {
            if(!qmax.isEmpty()) {
                return arr[qmax.pollFirst()];
            }
            return null;
        }
    }

    public static int[] getMaxWindow(int[] arr, int w){
        if(arr == null || w < 1 || arr.length < w){
            return null;
        }

        LinkedList<Integer> qmax = new LinkedList<>();
        int[] res = new int[arr.length - w + 1];
        int index = 0;
        for (int i = 0; i < arr.length; i++) {
            while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[i]) {// 把小于arr[i]的数从队列移除
                qmax.pollLast();
            }
            qmax.addLast(i);
            if(qmax.peekFirst() == i - w){ // i - w 等于 队列 第一个数 说明队列的第一个元素已经不在窗口内 弹出
                qmax.pollFirst();
            }
            if (i >= w - 1){//窗口形成了
                res[index++] = arr[qmax.pollFirst()];
            }
        }
        return res;
    }
}

3.单调栈结构

给你一个数组arr,返回每一个数最近的大于这个数的位置,没有返回-1

package promoteClass.kmp;

import java.util.*;

//数 组 返回每一个数最近的大于这个数的位置,没有返回-1
public class GetBorderMaxIndex {
    public static void insert(Stack<ArrayList<Integer>> stack, int index){
        ArrayList<Integer> list2 = new ArrayList<>();
        list2.add(index);
        stack.push(list2);
    }

    // 合并
    public static void merge(Stack<ArrayList<Integer>> stack, int index){
        ArrayList<Integer> arrayList = stack.pop();
        arrayList.add(index);
        stack.push(arrayList);
    }

    //出栈
    public static void popFormIndex(Stack<ArrayList<Integer>> stack, int[] arr, HashMap<Integer, ArrayList<Integer>> map, int index) {
        // 出栈
        while (!stack.isEmpty() && arr[stack.peek().get(stack.peek().size() - 1)] < arr[index]){//栈不为空 且 栈顶 小于要入栈的
            //arr[i] > 栈顶   此时 出栈
            ArrayList<Integer> list = stack.pop();
            for (Integer popIndex : list) {
                int rightIndex = index; //右边的最大值位置 ,只要还要入栈,就不用考虑右边的最大值是否存在
                int leftIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);  // 左边最大位置  栈为空 时不存在
                ArrayList<Integer> arrayList = new ArrayList<>();
                arrayList.add(leftIndex);
                arrayList.add(rightIndex);
                map.put(popIndex, arrayList);
            }
        }
    }


    public static void popFormIndex(Stack<ArrayList<Integer>> stack, HashMap<Integer, ArrayList<Integer>> map){
        while (!stack.isEmpty()) {
            ArrayList<Integer> list = stack.pop();
            for (Integer popIndex : list) {
                //int rightIndex = -1;  //此时右边大的数没有
                int leftIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);
                ArrayList<Integer> arrayList = new ArrayList<>();
                arrayList.add(leftIndex);
                arrayList.add(-1);//此时右边大的数没有
                map.put(popIndex, arrayList);
            }


        }

    }
    public static HashMap<Integer, ArrayList<Integer>> getBorderMaxIndex(int[] arr){
        if(arr == null || arr.length == 1) {
            return null;
        }
        HashMap<Integer, ArrayList<Integer>> res = new HashMap<>();

        Stack<ArrayList<Integer>> stack = new Stack<>();
        ArrayList<Integer> list1 = new ArrayList<>();
        list1.add(0);
        stack.push(list1);

        int i = 1;
        while(!stack.isEmpty() && i < arr.length){
            // arr[i] < 栈顶  直接入栈
            if(arr[i] < arr[stack.peek().get(stack.peek().size() - 1)]){
                insert(stack, i);
                i++;
            }else if(arr[i] == arr[i - 1]){
                //相等,把 i 位置 和 栈中前一个合并
                merge(stack, i);
                i++;
            }else {
                //出栈
                popFormIndex(stack, arr, res, i);
                insert(stack, i);
                i++;
            }
        }

        // 处理栈中的元素
        popFormIndex(stack, res);
        return res;
    }

    public static void main(String[] args) {
        int[] arr = {3,5,5,4,2,5};
        HashMap<Integer, ArrayList<Integer>> map = getBorderMaxIndex(arr);
        for (int i = 0; i < arr.length; i++) {
            ArrayList<Integer> arrayList = new ArrayList<>();
            arrayList = map.get(i);
            for (Integer integer : arrayList) {
                System.out.println(integer);
            }
        }
    }
}

定义:数组中累积和与最小值的乘积,假设叫做指标A,给定一个数组,请返回子数组中,指标A的最大值

13.滑动窗口,单调栈结构

1.二叉树节点间的最大距离

从二叉树的节点a出发,可以向上或者向下走,但沿途的节点只能经过一次,到达节点b时路径上的节点个数叫作a到b的距离,那么二叉树任何两个节点之间都有距离求整棵树上的最大距离。 在基础班讲过

package promoteClass.kmp;

public class MaxDistanceInTree {
    public static class Node{
        public int value;
        public Node left;
        public Node right;

        public Node(int value) {
            this.value = value;
        }
    }

    public static int maxDistance(Node head) {
        return process(head).maxDistance;
    }

    public static class Info {
        public int maxDistance;
        public int height;
        public Info(int dis, int h) {
            this.maxDistance = dis;
            this.height = h;
        }

    }

    public static Info process(Node head){
        if(head == null){
            return new Info(0, 0);
        }

        Info leftInfo = process(head.left);
        Info rightInfo = process(head.right);

        int p1 = leftInfo.maxDistance;
        int p2 = rightInfo.maxDistance;
        int p3 = leftInfo.height + 1 + rightInfo.height;
        int maxDistance = Math.max(p3, Math.max(p1, p2));
        int height = Math.max(leftInfo.height, rightInfo.height) + 1;
        return new Info(maxDistance, height);

    }

}

2.排队的最大快乐值

派对的最大快乐值员工信息的定义如下:

class Employee {

​ public int happy;//这名员工可以带来的快乐值

​ List subordinates; //这名员工有哪些直接下级}

公司的每个员工都符合Employee类的描述。整个公司的人员结构可以看作是一棵标准的、没有环的多叉树。树的头节点是公司唯一的老板。除老板之外的每个员工都有唯一的直接上级。叶节点是没有任何下属的基层员工(subordinates列表为空),除基层员工外,每个员工都有一个或多个直接下级。这个公司现在要办party,你可以决定哪些员工来,哪些员工不来。但是要遵循如下规则。

1.如果某个员工来了,那么这个员工的所有直接下级都不能来

2.派对的整体快乐值是所有到场员工快乐值的累加

3.你的目标是让派对的整体快乐值尽量大

给定一棵多叉树的头节点boss,请返回派对的最大快乐值。

package promoteClass.kmp;
import jdk.nashorn.internal.ir.IfNode;
import java.util.List;

public class GetMaxHappy {
    public static class Employee{
        public int happy;
        public List<Employee> nexts;
    }
    public static class Info {
        public int laiMaxHappy;;
        public int buMaxHappy;

        public Info(int laiMaxHappy, int buMaxHappy) {
            this.laiMaxHappy = laiMaxHappy;
            this.buMaxHappy = buMaxHappy;
        }
    }

    public static int maxHappy(Employee boss) {
        Info headInfo = process(boss);
        return Math.max(headInfo.buMaxHappy, headInfo.laiMaxHappy);

    }

    public static Info process(Employee x){
        if(x.nexts.isEmpty()){
            return new Info(x.happy, 0);
        }

        int lai = x.happy;
        int bu = 0;
        for (Employee next : x.nexts) {
            Info nextInfo = process(next);
            lai += nextInfo.buMaxHappy;
            bu += Math.max(nextInfo.buMaxHappy, nextInfo.laiMaxHappy);
        }
        return new Info(lai, bu);
    }
}

3.Morris遍历

Morris遍历细节

假设来到当前节点cur,开始时cur来到头节点位置

1)如果cur没有左孩子,cur向右移动(cur = cur.right)

2)如果cur有左孩子,找到左子树上最右的节点mostRight:

​ a.如果mostRight的右指针指向空,让其指向cur,然后cur向左移动(cur = cur.left)

​ b.如果mostRight的右指针指向cur,让其指向null,然后cur向右移动(cur = cur.right)

  1. cur为空时遍历停止
package promoteClass.kmp;


public class MorrisTraversal {
    public static class Node{
        public int value;
        public Node left;
        public Node right;

        public Node(int value) {
            this.value = value;
        }
    }

    public static void process(Node head) {
        if(head == null){
            return;
        }

        process(head.left);

        process(head.right);

    }

    public static void morris(Node head) {
        if(head == null){
            return;
        }
        Node cur = head;
        Node mostRight = null;

        while (cur != null){
            mostRight = cur.left;
            if(mostRight != null) {//cur 有左子树
                while (mostRight.right != null && mostRight.right != cur) {
                    mostRight = mostRight.right;//找出左子树的最优结点
                }

                if(mostRight.right == null){//最右结点,指向cur
                    mostRight.right = cur;
                    cur = cur.left;
                    continue;
                }else {//第二次来  指向null
                    mostRight.right = null;
                }
            }
            cur = cur.right;
        }

    }

    //先序
    public static void morrisPre(Node head) {
        if(head == null){
            return;
        }
        Node cur = head;
        Node mostRight = null;

        while (cur != null){
            mostRight = cur.left;
            if(mostRight != null) {
                while (mostRight.right != null && mostRight.right != cur) {
                    mostRight = mostRight.right;
                }

                if(mostRight.right == null){
                    System.out.println(cur.value);
                    mostRight.right = cur;
                    cur = cur.left;
                    continue;
                }else {
                    mostRight.right = null;
                }
            }else {//没有左子树
                System.out.println(cur.value);
            }
            cur = cur.right;
        }

    }

    //中序
    public static void morrisIN(Node head) {
        if(head == null){
            return;
        }
        Node cur = head;
        Node mostRight = null;

        while (cur != null){
            mostRight = cur.left;
            if(mostRight != null) {
                while (mostRight.right != null && mostRight.right != cur) {
                    mostRight = mostRight.right;
                }
                if(mostRight.right == null){

                    mostRight.right = cur;
                    cur = cur.left;
                    continue;
                }else {

                    mostRight.right = null;
                }
            }
            System.out.println(cur.value);
            cur = cur.right;
        }

    }

    //后序

    public static void morrisPos(Node head) {
        if(head == null){
            return;
        }
        Node cur = head;
        Node mostRight = null;

        while (cur != null){
            mostRight = cur.left;
            if(mostRight != null) {
                while (mostRight.right != null && mostRight.right != cur) {
                    mostRight = mostRight.right;
                }
                if(mostRight.right == null){

                    mostRight.right = cur;
                    cur = cur.left;
                    continue;
                }else {
                    mostRight.right = null;
                    printEdge(cur.left);
                }
            }
            cur = cur.right;
        }

    }

    public static void printEdge(Node X) {
        Node tail = reverseEdge(X);
        Node cur = tail;
        while(cur != null){
            System.out.println(cur.value);
            cur = cur.right;
        }
        reverseEdge(tail);
    }

    private static Node reverseEdge(Node from) {
        Node pre = null;
        Node next = null;
        while (from != null){
            next = from.right;
            from.right = pre;
            pre = from;
            from = next;
        }
        return pre;
    }

}

4.未出现过得数

32位无符号整数的范围是0~4,294,967,295,现在有一个正好包含40亿个无符号整数的文件,所以在整个范围中必然存在没出现过的数。可以使用最多1GB的内存,怎么找到所有未出现过的数?

【进阶】

内存限制为10MB,但是只用找到一个没出现过的数即可

1.创建一个大小为2^32/8大小的int数组,用位信息来表示

2.例如创造一个大小为1024大小的数组,然后假设A=40亿/1024,统计出现次数,当数组某个位置小于A时,迭代

2.树形dp问题

14.二叉树的Mirrors遍历

1.找出重复的url

有一个包含100亿个URL的大文件,每个URL占用64B,找出其中重复的url

布隆过滤器或者哈希函数分流到小文件

某搜索公司一天用户的搜索词汇是百亿级的,设计出一种求每天热门Top100词汇

哈希函数分流+堆结构

2.找出出现2次的数

32位无符号数的范围是0-4294967295,现在有40个无符号数,可以使用最多1GB的内存,找出出现了两次的数

定义一个大小为2^32/4大小的int数组,每个数用2个bit位,00代表0次,01代表1次,10代表2次,11代表2次以上

找出40亿个数的中位数,限制内存大小

例如创造一个大小为1024大小的数组,然后假设A=40亿/1024,统计出现次数,然后我们将数组从下标0累加,当累加和为20亿时,将这个数继续划分

3.位运算

给定2个有符号32位整数a和b,返回a和b较大的 (要求 不能做比较判断)

//保证参数a不是1就是0的情况下
    // 把 1 --> 0
    //   0 ---> 1
    public static int flip(int n){
        return  n ^ 1;

    }
    // 把 n >= 0   1
    //  n < 0     0
    public static int sign(int n){
        return flip( ( n >> 31 ) & 1);
    }
    //不能解决溢出问题
    public static int getMax1(int a, int b) {
        int c = a - b;
        int scA = sign(c); // c >= 0  scA = 1  否则  scA = 0
        int scB = flip(scA); // scA = 1  ---> scB = 0
        return a * scA + b * scB;//返回较大的那一个
    }

    public static int getMax2(int a, int b) {
        int c = a - b;
        int sa = sign(a);
        int sb = sign(b);
        int sc = sign(c);
        int difSab = sa ^ sb; //a和 b的符号不一样为 1 一样为 0
        int sameSab = flip(difSab); // a 和 b 的符号一样为1  不一样为 0
        /*
        返回a的条件
        1.a 和 b 的符号相同  a - b 大于 0  即 sc = 1 返回a
        2.a 和 b 的符号不同  a > 0  即 sa = 1 返回 a
         */
        int returnA = difSab * sa + sameSab * sc; // returnA 表示返回 a 的情况
        /*
        difSab 和 sameSab 是互斥的  即 difSab = 0 那么 sameSab = 1
        returnA = 1 * sa + 0 * sc
        或者
        returnA = 0 * sa + 1 * sc
         */
        
        int returnB = flip(returnA);
        return a * returnA + b * returnB;

    }

4.判断幂

判断一个32位正数是不是2的幂,4的幂

package promoteClass.kmp;
//判断幂问题
public class Power {
    public static boolean is2Power(int n) {
        return (n & (n - 1)) == 0;
    }

    public static boolean is4Power(int n){
        return (n & (n - 1)) == 0 && (n & 0x55555555 ) != 0;
    }

}

给定两个有符号32位整数a和b,不能使用算术运算符,分别实现a和b的加、减、乘、除运算

如果给定a、 b执行加减乘除的运算结果就会导致数据的溢出,那么你实现的函数不必对此负责,除此之外请保证计算过程不发生溢出

package promoteClass.kmp;

public class AddMinusMMultiDivideByBit {
    public static int add(int a, int b){
        int sum = 0;
        while(b != 0){
            sum = a ^ b;//无进位信息相加结果
            b = (a & b) << 1; //进位信息
            a = sum;
        }
        return sum;
    }
    public static int negNum(int n){
        return add(~n, 1);
    }
    //减法
    public static int minus(int a, int b){
        // a - b = a + (-b)    -b = ~n + 1
        return add(a, negNum(b));

    }
    // 乘法
    public static int multi(int a, int b){
        int res  = 0;
        while (b != 0){
            if((b & 1) != 0){
                res = add(res, a);
            }
            a <<= 1;
            b >>>= 1; //无符号右移
        }
        return res;
    }

    public static boolean isNeg(int n) {

        return n < 0;

    }

    public static int div(int a, int b) {
        int x = isNeg(a) ? negNum(a) : a; // a为负数取反加1
        int y = isNeg(b) ? negNum(b) : b;
        int res = 0;
        for (int i = 31; i > -1; i = minus(i, 1)) {
            if ((x >> i) >= y){
                res |= (1 << i);
                x = minus(x, y << i);
            }
        }
        return isNeg(a) ^ isNeg(b) ? negNum(res) : res;//若a,b是一正一负,结果取反加1
    }

    public static int divide(int a, int b){
        if (b == 0){
            throw new RuntimeException("divide is 0");
        }
        if(a == Integer.MIN_VALUE && b == Integer.MIN_VALUE) {
            return 1;
        }else if (b == Integer.MIN_VALUE) {
            return 0;
        }else if (a == Integer.MIN_VALUE) {
            int res = div(add(a, 1), b);
            return add(res, divide(minus(a, multi(res, b)), b));
        }else {
            return div(a,b);
        }

    }

    public static void main(String[] args) {
        System.out.println(add(1, -2));
        System.out.println(minus(5, 2));
        System.out.println(multi(6, 2));
    }
}

15.大数据题目

1.机器人走路问题

给你一个N,表示共有 1 - N 个格子,S表示起始位置,E表示终止位置,K表示总共要走的步数,机器人从S开始走,每次都可以向左或者向右走一步,走K步后到达终止位置则表示一种走法,则机器人一共有多少种不同的走法? 在1位置只能向右走,在N位置只能向左走

package promoteClass.bigData;

public class RobotWalk {
    public static int walkWays1(int N, int E, int S, int K) {
        return f1(N, E, K, S);
    }

    /*
    暴力递归
    一共是 1 ~ N 个位置
    最终的目标是P
    当前在cur位置
    剩余rest步

     */
    private static int f1(int N, int E, int rest, int cur) {
        if (rest == 0) {
            return cur == E ? 1 : 0;
        }
        if (cur == 1) {
            return f1(N, E, rest - 1, 2);
        }
        if (cur == N) {
            return f1(N, E, rest - 1, N - 1);
        }
        return f1(N, E, rest - 1, cur - 1) + f1(N, E, rest - 1, cur + 1);

    }

    // 优化  建立一个递归缓存
    public static int walkWays2(int N, int E, int S, int K) {
        //建立一个dp缓存,已经计算过的递归就直接取值
        int[][] dp = new int[K + 1][N + 1];
        for (int i = 0; i <= K; i++) {
            for (int j = 0; j <= N; j++) {
                dp[i][j] = -1;
            }
        }
        return f2(N, E, K, S, dp);
    }

    private static int f2(int N, int E, int rest, int cur, int[][] dp) {
        if (dp[rest][cur] != -1) {
            return dp[rest][cur];
        }
        //建立缓存
        if (rest == 0) {
            dp[rest][cur] = cur == E ? 1 : 0;
            return dp[rest][cur];
        }
        if (cur == 1) {
            dp[rest][cur] = f1(N, E, rest - 1, 2);
            return dp[rest][cur];
        }
        if (cur == N) {
            dp[rest][cur] = f1(N, E, rest - 1, N - 1);
            return dp[rest][cur];
        }
        dp[rest][cur] = f1(N, E, rest - 1, cur - 1) + f1(N, E, rest - 1, cur + 1);
        return dp[rest][cur];

    }

    // 动态规划
//    public static int dpWay(int N, int P, int M, int K) {
//        int[][] dp = new int[N][N];
//        return 0;
//    }
    
}

2.硬币问题

给你一个数组arr和一个目标金钱aim,让你每次从arr数组中选取一个数来达到我们的目标aim,不可以重复取,求最少要拿几次硬币才可以达到aim

package promoteClass.bigData;

public class CoinsMin {

    // 有多少种组合
    public static int min1(int[] arr, int aim) {
        return f(arr, 0, 0, aim);
    }

    /*
    arr[] 硬币都在其中,固定参数
    aim 最终要达成的目标  固定参数
    pre  你已经选择了多少

     */
    public static int f(int[] arr, int index, int pre, int aim) {
        if (index == arr.length) {
            return pre == aim ? 1 : 0;
        }
        return f(arr, index + 1, pre, aim) + f(arr, index + 1, pre + arr[index], aim);
    }

    // 硬币数最少的组合
    public static int coinsMin1(int[] arr, int aim) {
        return process1(arr, 0, aim);
    }

    public static int process1(int[] arr, int index, int rest) {
        if (rest < 0) {
            return -1;
        }
        if (rest == 0) {
            return 0;
        }
        //rest > 0
        if (index == arr.length) {
            return -1;
        }
        // rest > 0  并且还有硬币
        int p1 = process1(arr, index + 1, rest);
        int p2Next = 1 + process1(arr, index + 1, rest - arr[index]);
        if (p1 == -1 && p2Next == -1) {
            return -1;
        } else {
            if (p1 == -1) {
                return p2Next + 1;
            }
            if (p2Next == -1) {
                return p1;
            }
            return Math.min(p1, p2Next + 1);
        }

    }


    // 硬币数最少的组合
    public static int coinsMin2(int[] arr, int aim) {
        int[][] dp = new int[arr.length + 1][aim + 1];
        for (int i = 0; i <= arr.length; i++) {
            for (int j = 0; j <= aim; j++) {
                dp[i][j] = -2;
            }
        }

        return process2(arr, 0, aim, dp);
    }

    public static int process2(int[] arr, int index, int rest, int[][] dp) {
        if (rest < 0) {
            return 0;
        }

        if (dp[index][rest] != -2) {
            return dp[index][rest];
        }
        if (rest == 0) {
            dp[index][rest] = 0;
        } else if (index == arr.length) {
            dp[index][rest] = -1;
        } else {
            int p1 = process2(arr, index + 1, rest, dp);
            int p2Next = 1 + process2(arr, index + 1, rest - arr[index], dp);
            if (p1 == -1 && p2Next == -1) {
                dp[index][rest] = -1;
            } else {
                if (p1 == -1) {
                    dp[index][rest] = p2Next + 1;
                } else if (p2Next == -1) {
                    dp[index][rest] = p1;
                } else {
                    dp[index][rest] = Math.min(p1, p2Next + 1);
                }
            }
        }
        return dp[index][rest];
    }

    public static int coinsMin3(int[] arr, int aim) {
        int N = arr.length;
        int[][] dp = new int[N + 1][aim + 1];
        for (int row = 0; row <= N; row++) {
            dp[row][0] = 0;
        }
        for (int col = 0; col <= aim; col++) {
            dp[N][col] = -1;
        }
        for (int index = N - 1; index >= 0; index--) {
            for (int rest = 1; rest <= aim; rest++) {
                int p1 = dp[index + 1][rest];
                int p2Next = -1;
                if (rest - arr[index] >= 0) {
                    p2Next = dp[index + 1][rest - arr[index]];
                }

                if (p1 == -1 && p2Next == -1) {
                    dp[index][rest] = -1;
                } else {
                    if (p1 == -1) {
                        dp[index][rest] = -1;
                    } else if (p2Next == -1) {
                        dp[index][rest] = p1;
                    } else {
                        dp[index][rest] = Math.min(p1, p2Next + 1);
                    }
                }
            }
        }
        return dp[0][aim];

    }

    // for test
    public static int[] generateRandomArray(int len, int max) {
        int[] arr = new int[(int) (Math.random() * len) + 1];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) (Math.random() * max) + 1;
        }
        return arr;
    }

    public static void main(String[] args) {
        int len = 10;
        int max = 10;
        int testTime = 10000;
        for (int i = 0; i < testTime; i++) {
            int[] arr = generateRandomArray(len, max);
            int aim = (int) (Math.random() * 3 * max) + max;
            if (coinsMin1(arr, aim) != coinsMin2(arr, aim) && coinsMin2(arr, aim) != coinsMin3(arr, aim)) {
                System.out.println("error");
                break;
            }
        }
    }

}

16.暴力递归(上)

1.挑卡片问题

给你一个arr数组,甲乙两人分别依次从数组两边挑选一个数

// 动态规划
    public static int dp(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        // f , s 两个二维数组的左下部分是无效的
        int[][] f = new int[arr.length][arr.length];
        int[][] s = new int[arr.length][arr.length];
        for (int i = 0; i < arr.length; i++) {
            f[i][i] = arr[i];
        }
        /*
        * 表示无效数据区域
        f:
        arr[0][0]
             *       arr[1][1]
             *           *       arr[2][2]
             *           *           *       arr[3][3]

         s:
           0
           *       0
           *       *       0
           *       *       *     0
         */
        int row = 0;
        int col = 1;
        while (col < arr.length) {
            int i = row;
            int j = col;
            while (i < arr.length && j < arr.length) {
                f[i][j] = Math.max(arr[i] + s[i + 1][j], arr[j] + s[i][j - 1]);
                s[i][j] = Math.min(f[i + 1][j], f[i][j + 1]);
                i++;
                j++;
            }
            col++;
        }
        return Math.max(f[0][arr.length - 1], s[0][arr.length - 1]);

    }

2.马跳棋盘问题

有一个坐标轴,长度为8,高度为9.马的起始位置在(0,0),给定一个终点位置(x,y),给你一个k,表示马要跳的步数,问有多少种不同的跳法可以到达终点位置

package promoteClass.bigData;

public class HorseJump {
    //棋盘默认的大小是 8 * 9
    public static int getWays(int x, int y, int k){
        return process(x, y, k);
    }

    //从(0,0)位置出发要去往(x, y)位置
    // 必须跳 step 步  返回跳法的总数
    private static int process(int x, int y, int step) {
        if(x < 0 || x > 8 || y < 0 || y > 9){
            return 0;
        }
        if(step == 0) {
            return (x == 0 && y == 0) ? 1 : 0;
        }
        return process(x + 1, y + 2, step - 1)
                + process(x + 1, y - 2, step - 1)
                + process(x - 1, y + 2, step - 1)
                + process(x - 1, y - 2, step - 1)
                + process(x + 2, y + 1, step - 1)
                + process(x + 2, y - 1, step - 1)
                + process(x - 2, y + 1, step - 1)
                + process(x - 2, y - 1, step - 1);
    }
    // 三维动态规划
    public static int dpWasy(int x, int y, int step) {
        if(x < 0 || x > 8 || y < 0 || y > 9 || step < 0){
            return 0;
        }
        int[][][] dp = new int[9][10][step + 1];
        dp[0][0][0] = 1;
        for (int h = 1; h <= step; h++) {
            for (int r = 0; r < 9; r++) {
                for (int c = 0; c < 10; c++) {
                    dp[r][c][h] += getValue(dp,r + 1, c + 2, h - 1);
                    dp[r][c][h] += getValue(dp,r + 1, c - 2, h - 1);
                    dp[r][c][h] += getValue(dp,r - 1, c + 2, h - 1);
                    dp[r][c][h] += getValue(dp,r - 1, c - 2, h - 1);
                    dp[r][c][h] += getValue(dp,r + 2, c + 1, h - 1);
                    dp[r][c][h] += getValue(dp,r + 2, c - 1, h - 1);
                    dp[r][c][h] += getValue(dp,r - 2, c + 1, h - 1);
                    dp[r][c][h] += getValue(dp,r - 2, c - 1, h - 1);
                }
            }
        }
        return dp[x][y][step];
    }

    public static int getValue(int[][][] dp, int row, int col, int step) {
        if(row < 0 || row > 8 || col < 0 || col > 9) {
            return 0;
        }
        return dp[row][col][step];

    }

    public static void main(String[] args) {
        System.out.println(getWays(7, 7, 10));
        System.out.println(dpWasy(7, 7, 10));
    }
}

3.Bob的死亡概率

给你五个数 N, M, i, j, K. 有一个N * M大小的格子,(i,j)表示bob现在的坐标,走K步,每一步可以往上下左右四个方向,走出格子bob就死亡了,求bob的存活率

package promoteClass.bigData;

public class BobDie {
    // 暴力递归
    public static String bob1(int N ,int M, int i, int j, int K) {
        long all = (long) Math.pow(4, K);
        long live = process(N, M, i, j, K);
        long gcd = gcd(all, live);
        return String.valueOf(live / gcd) + "/" + (all / gcd);
    }

    private static long process(int N, int M, int row, int col, int rest) {
        // 越界
        if(row < 0 || row == N || col < 0 || col == M) {
            return 0;
        }
        // 步数走完没有越界
        if (rest == 0) {
            return 1;
        }
        //
        long live = process(N, M, row - 1, col, rest - 1);
        live += process(N, M, row + 1, col, rest - 1);
        live += process(N, M, row, col + 1, rest - 1);
        live += process(N, M, row, col - 1, rest - 1);
        return live;
    }
    // 求最大公约数
    private static long gcd(long m, long n) {
        return n == 0 ? m : gcd(n, m % n);
    }

    //动态规划
    public static String bob2(int N, int M, int i, int j, int K) {
        int[][][] dp = new int[N + 2][M + 2][K + 1];
        for (int row = 1; row <= N; row++) {
            for (int col = 1; col <= M; col++) {
                dp[row][col][0] = 1;
            }
        }
        for (int rest = 1; rest <= K; rest++) {
            for (int row = 1; row <= N; row++) {
                for (int col = 1; col <= M; col++) {
                    dp[row][col][rest] = dp[row - 1][col][rest - 1];
                    dp[row][col][rest] += dp[row + 1][col][rest - 1];
                    dp[row][col][rest] += dp[row][col + 1][rest - 1];
                    dp[row][col][rest] += dp[row][col - 1][rest - 1];
                }
            }
        }

        long all = (long) Math.pow(4, K);
        long live = dp[i + 1][j + 1][K];
        long gcd = gcd(all, live);
        return String.valueOf(live / gcd) + "/" + (all / gcd);
    }

}

4.换零钱问题

给你数组arr,表示有哪些面额的零钱,aim表示要换取得总量,你可以使用任意张数的零钱,问一共有多少种不同的换法

package promoteClass.bigData;

public class CoinsWays {
    // 暴力递归方法
    public static int way1(int[] arr, int aim) {
        return process(arr, 0, aim);
    }

    public static int process(int[] arr, int index, int rest) {
        if (arr.length == index){
            return rest == 0 ? 1 : 0;
        }
        int ways = 0;
        for (int zhang = 0; arr[index] * zhang <= rest; zhang++){
            ways += process(arr, index + 1, rest - arr[index] * zhang);
        }
        return ways;
    }

    // 动态规划
    public static int way2(int[] arr, int aim){
        if(arr == null || arr.length  == 0) {
            return 0;
        }
        int N = arr.length;
        int[][] dp = new int[N + 1][aim + 1];
        dp[N][0] = 1;
        for (int index = N - 1; index >= 0; index--){
            for (int rest = 0; rest <= aim; rest++){
                int ways = 0;
                for (int zhang = 0; arr[index] * zhang <= rest; zhang++){
                    ways += dp[index + 1][rest - zhang * arr[index]];
                }
                dp[index][rest] = ways;
            }
        }
        return dp[0][aim];
    }

    // 枚举行为查看邻近有没有可以代替枚举的位置
    // 斜率优化
    public static int way3(int[] arr, int aim){
        if(arr == null || arr.length  == 0) {
            return 0;
        }
        int N = arr.length;
        int[][] dp = new int[N + 1][aim + 1];
        dp[N][0] = 1;
        for (int index = N - 1; index >= 0; index--){
            for (int rest = 0; rest <= aim; rest++){
                // 每个格子都需要下方的格子
                dp[index][rest] = dp[index + 1][rest];
                if (rest - arr[index] >= 0){
                    dp[index][rest] += dp[index][rest - arr[index]];
                }

            }
        }
        return dp[0][aim];
    }
    // for test
    public static int[] generateRandomArray(int len, int max) {
        int[] arr = new int[(int) (Math.random() * len) + 1];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) (Math.random() * max) + 1;
        }
        return arr;
    }

    public static void main(String[] args) {
        int len = 10;
        int max = 10;
        int testTime = 10000;
        for (int i = 0; i < testTime; i++) {
            int[] arr = generateRandomArray(len, max);
            int aim = (int) (Math.random() * 3 * max) + max;
            if (way1(arr, aim) != way3(arr, aim)) {
                System.out.println("error");
                break;
            }
        }
    }

}

17.暴力递归(下)

1.何为具有平衡度的数

有序表的时间复杂度为O(logN)
4种实现方式

  1. 红黑树
  2. AVL
  3. Size-Balance树
  4. 跳表

介绍树的左旋,树的右旋

  1. LL型 左孩子左树不平衡 左旋
  2. RR型 右孩子右树不平衡 右旋
  3. LR型 左孩子右树不平衡
  4. RL型 右孩子左树不平衡

2.介绍SB树及其实现

平衡性:
每棵子树的大小,不小于其兄弟的子树大小
平衡性:
每棵子树的大小,不小于其兄弟的子树大小
既每棵叔叔树的大小,不小于其任何侄子树的大小具体实现与调整细节

3.红黑树

  1. 每个结点都是红或者黑
  2. 头结点和叶节点(null)为黑
  3. 任意2个红结点不相邻
  4. 任意一个子树,从头结点出发,到每个叶节点的路径上黑一样多
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值