常见算法题

股票交易日

在股市的交易日中,假设最多可进行两次买卖(即买和卖的次数均小于等于2),规则是必须一笔成交后进行另一笔(即买-卖-买-卖的顺序进行)。给出一天中的股票变化序列,请写一个程序计算一天可以获得的最大收益。请采用实践复杂度低的方法实现。

给定价格序列prices及它的长度n,请返回最大收益。保证长度小于等于500。

如下:

[10, 22, 5, 75, 65, 80], 6

返回:

87

风口的猪

风口之下,猪都能飞。当今中国股市牛市,真可谓“错过等七年”。 给你一个回顾历史的机会,已知一支股票连续n天的价格走势,以长度为n的整数数组表示,数组中第i个元素(prices[i])代表该股票第i天的股价。

假设你一开始没有股票,但有至多两次买入1股而后卖出1股的机会,并且买入前一定要先保证手上没有股票。若两次交易机会都放弃,收益为0。 设法计算你能获得的最大收益。 输入数值范围:2<=n<=100,0<=prices[i]<=100

输入:

[3, 8, 5, 1, 7, 8] 6

返回:

12

public class Stock {
    public static void main(String[] args) {
//        int[] dataSet = {10, 22, 5, 75, 65, 80};
        int[] dataSet = {3, 8, 5, 1, 7, 8};
        int result = maxProfit(dataSet, dataSet.length);
        System.out.println(result);
    }

    public static int maxProfit(int[] dataSet, int n) {
        int sum = 0;
        for (int i = 0; i < dataSet.length; i++) {
            int tmp = getMax(dataSet, 0, i - 1) + getMax(dataSet, i, dataSet.length-1);
            if (tmp > sum) {
                sum = tmp;
            }
        }
        return sum;
    }

    private static int getMax(int[] dataSet, int left, int right) {
        if (left >= right) {
            return 0;
        }
        int min = dataSet[left];
        int max = 0;
        for (int i = left; i <= right; i++) {
            if (dataSet[i] - min > max) {
                max = dataSet[i] - min;
            }
            if (dataSet[i] < min) {
                min = dataSet[i];
            }
        }
        return max;
    }
}

最长公共字串

求两个字符串的最长公共子串,如“abcdefg”和“adefgwgeweg”的最长公共子串为“defg”(子串必须是连续的)

public class MaxSubString {
    public static void main(String[] args) {
        String str1 = "abcdefg";
        String str2 = "adefgwgeweg";
        System.out.println(getMaxSubString(str1, str2));
    }

    public static List<String> getMaxSubString(String str1, String str2) {
        if (StringUtils.isEmpty(str1) || StringUtils.isEmpty(str2)) {
            return null;
        }

        String max;
        String min;
        if (str1.length() > str2.length()) {
            max = str1;
            min = str2;
        } else {
            max = str2;
            min = str1;
        }

        List<String> subStrings = Lists.newArrayList();
        String maxSubString = StringUtils.EMPTY;
        for (int i = 0; i < min.length(); i++) {
            for (int begin = 0, end = min.length() - i; begin < end; begin++) {
                String tmp = min.substring(begin, end);
                if (max.contains(tmp) && tmp.length() >= maxSubString.length()) {
                    maxSubString = tmp;
                    subStrings.add(maxSubString);
                }
            }
        }
        return subStrings;
    }
}

回文串

给定一个字符串,问是否能通过添加一个字母将其变为回文串。

输入:

coco

返回:

true

public class PharseString {
    public static void main(String[] args) {
        String str = "coco";
        System.out.println(isPhraseString(str));
    }

    public static boolean isPhraseString(String str) {
        for (int i = 1; i < str.length() / 2; i++) {
            if (str.toCharArray()[i] != str.toCharArray()[str.length() - i]) {
                return false;
            }
        }
        return true;
    }
}

最大公约数和最小公倍数

public class MaxCommonDivisorAndMinCommonMultiple {
    public static void main(String[] args) {
        int m = 8;
        int n = 12;
        System.out.println(maxCommonDivisor(m, n) + " " + minCommonMultiple(m, n));
    }

    public static int maxCommonDivisor(int m, int n) {
        if (m < n) {
            int tmp = m;
            m = n;
            n = tmp;
        }
        while (n != 0) {
            int tmp = m % n;
            m = n;
            n = tmp;
        }
        return m;
    }

    public static int minCommonMultiple(int m, int n) {
        return m * n / maxCommonDivisor(m, n);
    }
}

 

字典序列化

我们程序中用到了一个数组 a ,数组的每个元素都是一个字典(map/dict)。

字典的 key/value 都是字符串,字符串中可包含任意字符。

示例:

    a[0][“k1”] = “v1”

    a[0][“k2”] = “v2”

    a[1][“A”] = “XXX”

    …

实际使用过程中,我们自定义了一个基于字符串的存储结构,数组元素之间用“换行”分割,

字典元素之间使用“分号”分割, key/value 之间用“等号”分割。

上述数据序列化之后,应该得到一个字符串:

    “k1=v1;k2=v2\nA=XXX”

请实现一个“保存”函数、一个“加载”函数。

    text = store(a); //把数组保存到一个字符串中

    a = load(text);  //把字符串中的内容读取为字典数组

请考虑所有边界情况,不要出现bug。在满足上述需求的前提下,可自行增加一些规则和约定。

// 约定:每个字典的key/value不能为 换行、分割、等号
public class Test {
    public static void main(String[] args) {
//        List<Map<String, String>> mapList = Lists.newArrayList();
//        Map<String, String> map1 = Maps.newHashMap();
//        map1.put("k1", "v1");
//        map1.put("k2", "v2");
//        mapList.add(map1);
//        Map<String, String> map2 = Maps.newHashMap();
//        map2.put("A", "XXX");
//        map2.put("B", " ");
//        map2.put(" ", "C");
//        map2.put(" ", " ");
//        mapList.add(map2);
//        Object[] strings = mapList.toArray();
//        String result = store(strings);
//        System.out.println(result);
        String str = "k1=v1;?=、";
        Object[] objects = load(str);
        System.out.println(objects);
    }

    private static String store(Object[] strings) {
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < strings.length; i++) {
            Map<String, String> map = (Map<String, String>) strings[i];
            int j = 0;
            for (String key : map.keySet()) {
                str.append(key).append("=").append(map.get(key));
                if (j < map.size() - 1) {
                    str.append(";");
                }
                j++;
            }
            if (i < strings.length - 1) {
                str.append("\\n");
            }
        }
        return str.toString();
    }

    private static Object[] load(String str) {
        List<Map<String, String>> mapList = Lists.newLinkedList();
        String[] maps = str.split("\n");
        for (int i = 0; i < maps.length; i++) {
            Map map = Maps.newLinkedHashMap();
            String[] keyValues = maps[i].split(";");
            for (int j = 0; j < keyValues.length; j++) {
                String[] keyValue = keyValues[j].split("=");
                if (keyValue.length == 2) {
                    map.put(keyValue[0], keyValue[1]);
                }
            }
            mapList.add(map);
        }
        return mapList.toArray();
    }
}

路径规划

我们有一个有向无环图,权重在节点上。

需求:从一个起点开始,找到一条节点权重之和最大的最优路径。

输入: n个节点,m个路径,起点

输出: 最优路径的权重值之和

举例:

    3个节点与权重: A=1, B=2, C=2

    3条路径: A->B, B->C, A->C

    起点: A

    输出: 5 (最优路径是 A->B->C , 权重之和是 1+2+2=5)

请考虑算法效率优化,考虑异常情况(比如输入的图有环路)要避免死循环或者崩溃。

public class Travel {
    public static void main(String[] args) {
        String[] vertex = {"a", "b", "c", "d", "e"};
        int[] weight = {0, 1, 1, 3, 6};
        double[][] matrix = {
                {0, 1, 0, 1, 0},
                {0, 0, 1, 0, 0},
                {0, 0, 0, 0, 1},
                {0, 0, 0, 0, 1},
                {0, 0, 0, 0, 0}};

        Graph<String> graph = new Graph<>(matrix, vertex, weight);
        System.out.println(graph.getMinWeight(graph.startSearch()));
    }

    public static class Graph<T> {
        // 邻接矩阵
        private double[][] matrix;
        // 顶点数组
        private String[] vertex;
        // 顶点数组对应权重值
        private int[] weight;
        // 顶点的数目
        private int vertexNum;
        // 当前结点是否还有下一个结点,判断递归是否结束的标志
        private boolean noNext = false;
        // 所有路径的结果集
        private List<List<String>> result = Lists.newArrayList();

        public Graph(double[][] matrix, String[] vertex, int[] weight) {
            if (matrix.length != matrix[0].length) {
                throw new IllegalArgumentException("该邻接矩阵不是方阵");
            }
            if (matrix.length != vertex.length) {
                throw new IllegalArgumentException("结点数量和邻接矩阵大小不一致");
            }
            if (vertex.length != weight.length) {
                throw new IllegalArgumentException("邻接矩阵大小和权重值数量不一致");
            }
            this.matrix = matrix;
            this.vertex = vertex;
            this.weight = weight;
            vertexNum = matrix.length;
        }

        /**
         * 深度遍历的递归
         */
        private void DFS(int begin, List<String> path) {
            // 将当前结点加入记录队列
            path.add(vertex[begin]);
            // 标记回滚位置
            int rollBackNum = -1;
            // 遍历相邻的结点
            for (int i = 0; i < vertexNum; i++) {
                if ((matrix[begin][i] > 0)) {
                    // 临时加入相邻结点,试探新的路径是否已遍历过
                    path.add(vertex[i]);
                    if (containBranch(result, path)) {
                        // 路径已存在,将相邻结点再移出记录队伍
                        path.remove(vertex[i]);
                        // 记录相邻点位置,用于循环结束发现仅有当前一个相邻结点时回滚事件
                        rollBackNum = i;
                        // 寻找下一相邻结点
                        continue;
                    } else {
                        // 路径为新路径,准备进入递归,将相邻结点移出记录队伍,递归中会再加入,防止重复添加
                        path.remove(vertex[i]);
                        // 递归
                        DFS(i, path);
                    }
                }
                // 终止递归
                if (noNext) {
                    return;
                }
            }
            if (rollBackNum > -1) {
                // 循环结束仅有一个相邻结点,从这个相邻结点往下递归
                DFS(rollBackNum, path);
            } else {
                // 当前结点没有相邻结点,设置flag以结束递归
                noNext = true;
            }
        }

        /**
         * 开始深度优先遍历
         */
        public List<List<String>> startSearch() {
            for (int i = 0; i < countPathNumber(); i++) {
                // 用于存储遍历过的点
                List<String> path = new LinkedList<>();
                noNext = false;
                // 开始遍历
                DFS(0, path);
                // 保存结果
                result.add(path);
            }
            return result;
        }

        /**
         * 获取权重值最大的路径
         */
        public MaxWeight getMaxWeight(List<List<String>> lists) {
            Map<String, Integer> weightMap = Maps.newHashMap();
            for (int i = 0; i < vertex.length; i++) {
                weightMap.put(vertex[i], weight[i]);
            }
            int max = 0;
            int index = 0;
            for (int i = 0; i < lists.size(); i++) {
                int w = 0;
                for (String str : lists.get(i)) {
                    w += weightMap.get(str);
                }
                if (w > max) {
                    max = w;
                    index = i;
                }
            }
            return new MaxWeight(lists.get(index), max);
        }

        /**
         * 获取权重值最小的路径
         */
        public MaxWeight getMinWeight(List<List<String>> lists) {
            Map<String, Integer> weightMap = Maps.newHashMap();
            for (int i = 0; i < vertex.length; i++) {
                weightMap.put(vertex[i], weight[i]);
            }
            int min = 0;
            int index = 0;
            for (int i = 0; i < lists.size(); i++) {
                int w = 0;
                for (String str : lists.get(i)) {
                    w += weightMap.get(str);
                }
                if(min == 0){
                    min = w;
                    index = i;
                }
                if (w < min) {
                    min = w;
                    index = i;
                }
            }
            return new MaxWeight(lists.get(index), min);
        }

        class MaxWeight {
            private List<String> path;
            private int weight;

            public List<String> getPath() {
                return path;
            }

            public void setPath(List<String> path) {
                this.path = path;
            }

            public int getWeight() {
                return weight;
            }

            public void setWeight(int weight) {
                this.weight = weight;
            }

            public MaxWeight(List<String> path, int weight) {
                this.path = path;
                this.weight = weight;
            }

            @Override
            public String toString() {
                return "MaxWeight{" +
                        "path=" + path +
                        ", weight=" + weight +
                        '}';
            }
        }

        /**
         * 计算路径的分支数量
         */
        private int countPathNumber() {
            int[] numberArray = new int[vertexNum];
            for (int i = 0; i < vertexNum; i++) {
                for (int j = 0; j < vertexNum; j++) {
                    if (matrix[j][i] > 0) {
                        numberArray[j]++;
                    }
                }
            }
            int number = 1;
            for (int k = 0; k < vertexNum; k++) {
                if (numberArray[k] > 1) {
                    number++;
                }
            }
            return number;
        }

        /**
         * 判断当前路径是否被已有路径的结果集合所包含
         */
        private boolean containBranch(List<List<String>> nodeLists, List<String> edges) {
            for (List<String> list : nodeLists) {
                if (list.containsAll(edges)) {
                    return true;
                }
            }
            return false;
        }
    }
}

字符串全排列

public class Test {

    public static void main(String[] args) {
        String str = "abc";
        allArrange(str.toCharArray(), 0);

    }

    public static void allArrange(char[] chars, int n) {
        if (n == chars.length - 1) {
            System.out.println(String.valueOf(chars));
        }
        for (int i = n; i < chars.length; i++) {
            char tmp = chars[i];
            chars[i] = chars[n];
            chars[n] = tmp;
            allArrange(chars, n + 1);
        }
    }
}

字符串转数字

public class Test {

    public static void main(String[] args) {
        String str = "123";
        System.out.print(strToInt(str));
    }

    public static int strToInt(String str) {
        int result = 0; 
        // 空字符串返回0
        if (str.length() == 0) {
            return result;
        }
        char[] chars = str.toCharArray();
        char base = '0';
        for (int i = chars.length; i > 0; i--) {
            result += (chars[chars.length - i] - base) * Math.pow(10, i - 1);
        }
        return result;
    }
}

 

点赞