B站左程云算法视频高级班01

题目1:给定一个数组,求如果排序之后,相邻两数的最大差值,要求时间复杂度O(N),且要不能用非基于比较的排序

[               ]假设是长度为9的数组

首先遍历数组 找出min 和 max

等分成10份

举例:

 分为  桶内相邻和桶外相邻

中间必定存在空桶

每个桶只留该桶的最小值和最大值

public static int maxGap(int[] nums){
    if(nums == null || nums.length < 2){
        return 0;
    }
    int len = nums.length;
    int min = Integer.MAX_VALUE;
    int max = Integer.MIN_VALUE;
    for(int i = 0; i < len; i++){
        min = Math.min(min, nums[i]);
        max = Math.max(max, nums[i]); 
    }
    if(min == max){
        return 0;
    }
    boolean[] hasNum = new boolean[len + 1];//hasNum[i] i号桶是否进来过数字
    int[] maxs = new int[len + 1]; //maxs[i] i号桶收集的所有数字的最大值
    int[] mins = new int[len + 1]; //mins[i] i号桶收集的所有数字的最小值
    int bid = 0;
    for(int i = 0; i < len; i++){
        bid = bucket(nums[i], len, min, max);
        mins[bid] = hasNum[bid] ? Math.min(mins[bid], nums[i]) : nums[i];
        maxs[bid] = hasNum[bid] ? Math.max(maxs[bid], nums[i]) : nums[i];
        hasNum[bid] = true;
    }
    int res = 0;
    int lastMax = maxs[0]; //上一个非空桶的最大值
    int i = 1;
    for (; i <= len; i++){
        if(hasNum[i]){
            res = Math.max(res, mins[i] - lastMax);
            lastMax = maxs[i];
        }
    }
    return res;
}

public static int bucket(long num, long len, long min, long max){
    return (int)((num - min) * len / (max - min));
}

最后的结果不一定是空桶两侧的结果

设置空桶的意义在于设置一个平凡解,杀死一个类型的可能性(一个桶内部),进而达到优化

题目二:给定n个数字a1,...,an,问最多有多少不重复的非空区间,使得每个区间内数字的异或和都为0,返回子数组的数量

以每个数字结尾的情况下答案是什么?

最优划分下

1)[i]所在的部分异或和不是0  dp[i] = dp[i - 1]

2)[i]所在的部分异或和是0 

找到离i最近的k 异或和为0

【3,2,1,4,0,1,2,3】

dp【0,0,1,1,2,2,2,3】

public static int mostEOR(int[] arr) {
    int xor = 0;
    //dp[i] -> arr[0..i]在最优化分的情况下,异或和为0最多的部分是多少个
    int[] dp = new int[arr.length];
    //key : 从0出发的某个前缀异或和
    //value : 这个前缀异或和出现额最晚位置(index)
    HashMap<Integer, Integer> map = new HashMap<>();
    map.put(0, -1);
    for(int i = 0; i < arr.length; i++){
        xor ^= arr[i]; //xor -> 0..i所有数的异或和
        if (map.containsKey(xor)){//上一次这个异或和出现的位置
            //pre -> pre + 1 -> i ,最优化分,最后一个部分的开始位置
            //(pre + 1, i) 最后一个位置
            int pre = map.get(xor); //a   0..a  (a+1..i)
            dp[i] = pre == -1? 1 : (dp[pre] + 1);
        }
        //dp[i] = Max { dp[i - 1], dp[k-1] + 1}
        if(i > 0) {
            dp[i] = Math.max(dp[i-1],dp[i]);
        }
        map.put(xor, i);
    }
    return dp[dp.length - 1];
}

题目三:现有n1+n2种面值的硬币,其中前n1为普通币,可以取任意枚,后几种为纪念币,每种最多只能取一枚,每种硬币有一个面值,问有多少找方法拼出m的面值

举例:【3,2,5】任意枚   【1,2,4】1枚 m=10

1)       任意枚0    一枚 10   方法数 a

2)      任意枚1     一枚9     方法数b

。。。。

任意枚10  一枚0   方法数c

方法数累加

dp[i][j] = \sumdp[i-1][j-arr[i]*k]       k大于等于0且 j-arr[i]*k大于等于0

题目四:给定两个一维int数组A和B

其中,A是长度为m、元素从小到大排好序的有序数组。B是长度为n从小到大排好序的有序数组。希望从A和B数组中,找到最大的k个数字,要求:使用尽量少的比较次数

做法1:双指针外排      时间复杂度O(k)

做法2:先假设第K小的数字在A,对A做二分求出M,看M在B中有多少数在M前面,不满足然后继续二分  时间复杂度O(logN*logM)

最优解O(logN):

算法原型:如果两个有序数组长度一样,怎么找到上中位数

1)两个数组长度都为偶数

A[a,b,c,d]   b[a^{,},b^{,},c^{,},d^{,}

如果b==b‘ 则是上中位数

b> b' 则c不是上中位数,b'也不是上中位数,可能的是a,b,c',d',递归求上中位数

子问题的上中位数就是整个问题的上中位数

2)两个数组长度都是奇数

A[1,2,3,4,5]   B[1',2',3',4',5']

如果3 = 3',则是第五小的数

3 > 3’,则3,4,5不可能,1',2'也不可能

则可能的有1,2和3',4',5',则手动比较2和3',如果3' >=2,则是第五小的数,否则1,2和4',5'递归

假设A数组长度为10,B数组长度为17,第K小

1)1=<K<=10,A拿出1~K,B拿出1~K',求出上中位数

2)10<K=<17  排除掉B数组的K-10-1和最后17-K个数

                        手动比较B数组的K-10数和A中的10,如果大于等于,则是,否则多淘汰一个数

                        接着递归求出上中位数

3)17<K<=27  排除掉A数组的 K-17-1的数

                        排除掉B数组的K-10-1的数

然后在剩下的数中,先判断A数组的K-17和17',如果A数组的K-17>=17',则是第K小,否则抛弃

比较B数组的K-10和A数组的10比,不是就多淘汰一个

接着剩下的数递归求上中位数

public static int findKthNum(int[] arr1, int[] arr2, int kth){
    if(arr1 == null || arr2 == null){
        throw new RuntimeException("Your arr is invalid!");
    }
    if(kth < 1 || kth > arr1.length + arr2.length) {
        throw new RuntimeException("Your arr is invalid!"); 
    }
    int[] longs = arr1.length >= arr2.length ? arr1 : arr2;
    int[] shorts = arr1.length < arr2.length ? arr1 : arr2;
    int l = longs.length;
    int s = shorts.length;
    if(kth <= s){
        return getUpMedian(shorts, 0, kth - 1, longs, 0, kth - 1);
    }
    if(kth > l){
        if(shorts[kth - l - 1] >= longs[l - 1]){
            return  shorts[kth - l - 1];
        }
        if(longs[kth - s - 1] >= shorts[s - 1]){
            return longs[kth - s - 1];
        }
        return getUpMedian(shorts, kth - l, s - 1, longs, kth - s, l - 1);
    }
    //2)
    if(longs[kth - s - 1] >= shorts[s - 1]){
        return longs[kth - s - 1];
    }
    return getUpMedian(shorts, 0, s - 1, longs, kth - s, kth - 1);
}

//两个数组一定要等长
public static int getUpMedian(int[] a1, int s1, int e1, int[] a2, int s2, int e2){
    int mid1 = 0;
    int mid2 = 0;
    int offset = 0;
    while(s1 < e1){
        mid1 = (s1 + e1) / 2;
        mid2 = (s2 + e2) / 2;
        offset = ((e1 - s1 + 1) & 1) ^ 1;
        if (a1[mid] > a2[mid2]){
            e1 = mid1;
            s2 = mid2 + offset;
        } else if(a1[mid1] < a2[mid2]){
            s1 = mid1 + offset;
            e2 = mid2;
        } else{
            return a1[mid1];
        }
    }
    return Math.min(a1[s1], a2[s2]);
}

题目五:(约瑟夫环)

 构建一个公式f,求活着的节点在上一段的编号。O(1)

Y= X % i

 建立关系

编号 = (报数 - 1)%i + 1

老=((新 - 1)+ S)% i + 1

S:被杀节点编号

S=(m-1)%i + 1

老=(新+m-1)%i+1

public static intgetLive(int i, int m){
    if(i == 1){
        return 1;
    }
    return (getLive(i - 1, m) + m - 1) % i + 1;
}

public static Node josehusKill2(Node head, int m){
    if(head == null || head.next == head || m < 1){
        return head;
    }
    Node cur = head.next;
    int tmp = 1; // tmp -> list size
    while(cur != head){
        tmp++;
        cur = cur.next;
    }
    tmp = getLive(tmp, m); //tmp -> service node position
    while(--tmp != 0){
         head = head.next;
    }
    head.next = head;
    return head;
}

老=(新+m-1)%i+1 此题的m每轮都变

//0...n-1个人围成一圈,依次循环取用arr中的数字
//杀n-1轮,返回活的人的原始编号
public static int live(int n, int[] arr){
    return no(n ,arr, 0);
}

//当剩i个人,当前取用数字是arr[index],并且下面的过程,从index出发,循环取用
//返回哪个人会活(在i个人中的编号)
public stati int no(int i, int[] arr, int index){
    if(i == 1){
        return 1;
    }
    //老 = (新 + m - 1) % i + 1
    return (no(i - 1, arr, nextIndex(arr.length, index)) + arr[index] - 1) % i + 1;
}

//r如果数组长度为size,当前下标为index,返回循环的模型下,下一个index是多少
public static int nextIndex(int size, int index){
    return index == size - 1? 0 : index + 1;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值