算法-最长递增子序列

近期准备校招,在跟牛客网左老师的算法公开课,听他的课受益匪浅,学到了很多解题思想,给我这种小白很大帮助。下面贴出自己根据课程中讲到的思想实现的代码,做个记录,方便自己以后温故。
这篇的内容为最长递增子序列的解法,以及使用其中的一个解题思想来实现一个类似leetcode354. Russian Doll Envelopes的问题。
子序列可以是数组中非连续的元素构成,但是必须在原数组中的相对位置是有一致的先后顺序。


最长子序列长度

方法一,使用一个辅助数组h[],h[i]的值表示以原数组中arr[i]结尾最长递增子序列的长度,算法复杂度为O(N*N)

private static int calMaxLength(int []arr) {
        if (arr == null || arr.length == 0) return 0;
        int len = arr.length;
        // 辅助数组,存以arr[i]结尾的最长递增子序列的长度
        int h[] = new int[len];
        int max = 0;
        for (int i = 0; i < len; i++) {
            h[i] = 1;
            int tmp = h[i];
            for (int j = i - 1; j >= 0; j--) {
                // 当遍历到j时剩下的元素个数不足tmp-1时后面的都可以不考虑了
                if (j + 1 < tmp - 1) {
                    break;
                }
                if (arr[j] < arr[i]) {
                    if (tmp < h[j] + 1) {
                        tmp = h[j] + 1;
                    }
                }
            }
            h[i] = tmp;
            max = Math.max(max, h[i]);
        }
        System.out.println(Arrays.toString(h));
        return max;
    } 

方法二,使用一个辅助数组h[](称为有效数组),长度与原数组一样,h[i]的值表示遍历原数组到当前元素时,长度为i+1的最长递增子序列的最小末尾数是多少。在求h[i]的过程中使用二分法进行加速。算法时间复杂度为O(NlogN)

private static int calMaxLength2(int[] arr) {
        if (arr == null || arr.length == 0) return 0;
        int len = arr.length;
        // 有效值数组
        int h[] = new int[len];
        h[0] = arr[0];
        int maxLen = 0;
        int max = 1;
        for (int i = 1; i < len; i++) {
            int index = binarySearch(h, 0, maxLen, arr[i]);
            if (index != -1) {
                //找到,替换
                h[index] = arr[i];
            } else {
                //没找到,添加
                maxLen++;
                h[maxLen] = arr[i];
                max = maxLen + 1;
            }
        }
        System.out.println(Arrays.toString(h));
        return max;
    }

    /**
     * 基于二分查找出arr中第一个大于key的下标
     */
    private static int binarySearch(int arr[],int fromIndex, int toIndex, int key) {
        if (toIndex < fromIndex) return -1;
        int low = fromIndex; 
        int high = toIndex;

        while (low <= high) {
            int mid = (low + high) >>> 1;
            if (arr[mid] < key) {
                low  = mid + 1; 
            } else if (arr[mid] > key) {
                high = mid - 1;
            } else {
                return mid;
            }
        }
        if (low > toIndex) {
            return -1;
        }
        return low; 
    }

基于上面方法二的算法模型解决一道更复杂的题

给定一个 N*2 的二维数组,看作是一个个二元组,例如[[a1,b1],[a2,b2],[a3,b3]],规定:一个如果想把二元组甲放在二元组乙上,甲中的 a 值必须大于乙中的 a 值,甲中的 b值必须大于乙中的 b 值。如果在二维数组中随意选择二元组,请问二元组最多可以往上摞几个?
例如:[[5,4],[6,4],[6,7],[2,3]], 最大数量可以摞 3 个,[2,3] => [5,4] => [6,7]:

import java.util.Arrays;
import java.util.Scanner;
import java.util.Stack;

/**
 * @author hyman
 *
 */
public class LongestIncreaseAlignment {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        scanner.nextLine();
        int[][] arr = new int[n][2];
        for (int i = 0; i < n; i++) {
            arr[i][0] = scanner.nextInt();
            arr[i][1] = scanner.nextInt();
            scanner.nextLine();
        }
        scanner.close();
        System.out.println(calcMaxLayer(arr));
    }

    /**
     * O(NlogN),使用二分查找加速枚举过程
     * @param arr
     * @return
     */
    private static int calMaxLength(int[] arr) {
        if (arr == null || arr.length == 0) return 0;
        int len = arr.length;
        // 有效值数组
        int h[] = new int[len];
        h[0] = arr[0];
        int maxLen = 0;
        int max = 1;
        for (int i = 1; i < len; i++) {
            int index = binarySearch(h, 0, maxLen, arr[i]);
            if (index != -1) {
                //找到,替换
                h[index] = arr[i];
            } else {
                //没找到,添加
                maxLen++;
                h[maxLen] = arr[i];
                max = maxLen + 1;
            }
        }
        System.out.println(Arrays.toString(h));
        return max;
    } 

    /**
     * 二分查找,找出有序数组arr中第一个大于n的数的下标
     * @param arr
     * @param start 起始位置下标
     * @param end 结束位置下标
     * @param n
     * @return
     */
    private static int binarySearch(int arr[],int start, int end, int n) {
        if (end < start) return -1;
        if (arr[end] < n) return -1;
        int mid = (start + end) >>> 1;
        if (mid == start) {
            if (arr[mid] >= n) {
                return mid;
            }
            return arr[mid + 1] < n ? -1 : mid + 1;  
        }
        if (arr[mid] < n) {
            return binarySearch(arr, mid + 1, end, n);
        }
        if (arr[mid] > n) {
            return binarySearch(arr, start, mid, n);
        }
        return mid;
    }

    /**
     * 二维数组,看做是一组二元组,求可以叠的最大层数
     * @param arr
     * @return
     */
    private static int calcMaxLayer(int arr[][]) {
        if (arr == null || arr.length == 0) return 0;
        //处理方法:先根据a从小到大排序,a相同则根据b从大到小排序
        int n = arr.length;
        int tmpA = 0, tmpB = 0;
        for (int i = 0; i < n; i++) {
            tmpA = arr[i][0];
            tmpB = arr[i][1];
            for (int j = i - 1; j >= 0; j--) {
                // 根据a升序排列
                if (arr[j][0] > tmpA) {
                    arr[j + 1][0] = arr[j][0];
                    arr[j + 1][1] = arr[j][1];
                    if (j == 0) {
                        arr[j][0] = tmpA;
                        arr[j][1] = tmpB;
                    }
                }else if (arr[j][0] == tmpA) {
                    arr[j + 1][0] = tmpA;
                    //a相等,按b降序列排列
                    if (arr[j][1] < tmpB) {
                        arr[j + 1][1] = arr[j][1];
                        // 何时结束
                        if (j == 0 || arr[j - 1][0] != tmpA) {
                            arr[j][1] = tmpB;
                        }
                    } else {
                        arr[j + 1][1] = tmpB;
                        break;
                    }
                } else {
                    arr[j + 1][0] = tmpA;
                    arr[j + 1][1] = tmpB;
                    break;
                }
            }
        }
        //打印排序后的二维数组
        for (int i = 0; i < arr.length; i++) {
            System.out.print("(" + arr[i][0] + ", " + arr[i][1] + ") ");
        }
        System.out.println("");
        //将二元组中的b存到数组arrB中
        int arrB[] = new int[n];
        for (int i = 0; i < n; i++) {
            arrB[i] = arr[i][1];
        }
        return calMaxLength(arrB);
    }

}

如果错误,望给予指正,共同学习、共同进步~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值