zoukankan      html  css  js  c++  java
  • 程序员面试金典题解

    3.栈与队列

    3.4 经典汉诺塔问题,递归实现

    public class Num3_4 {
        public static void main(String[] args) {
            
        }
        /*
         * 汉诺塔问题
         * 当盘子只有一个时,则直接从柱子A移动到柱子C
         * 当盘子多余一个时,将盘子分为两部分,前n-1个和第n个,分三步进行
         * 第一步将前n-1个盘子从柱子A移动到柱子B
         * 第二步将第n个盘子从柱子A移动到柱子C
         * 第三步将前n-1个盘子从柱子B移动到柱子C
         */
        public static void TowerOfHanoi(int n, char A, char B,char C) {
            if (n < 1)
                return;
            TowerOfHanoi(n-1, A, C, B);
            System.out.println(A + "->" + C);
            TowerOfHanoi(n-1, B, A, C);
        }
    }
    View Code

    3.5 两个栈实现一个队列的两种解法

    import java.util.Stack;
    
    /*
     * 两个栈实现一个队列
     */
    public class Num3_5 {
        Stack<Integer> stack1 = new Stack<Integer>();
        Stack<Integer> stack2 = new Stack<Integer>();
    /*    
        public void push(int node) {
            int size = stack1.size();
            for (int i = 0; i < size; i++) {
                stack2.push(stack1.pop());
            }
            stack1.push(node);
            for (int i = 0; i < size; i++) {
                stack1.push(stack2.pop());
            }
        }
        
        public int pop() {
            return stack1.pop();
        }
    */
        /*
         * 更好的方法,不用每次pop()操作都要移动元素
         * stack1最顶元素为最新,stack2最顶元素为最旧元素
         * 如果stack2不为空,则直接从stack2执行pop(),反之,将stack1的元素按序压入stack2,并pop()栈底元素
         * push()则直接在stack1上操作
         */
        public void push(int node) {
            stack1.push(node);
        }
        public int pop() {
            if (!stack2.isEmpty())
            {
                return stack2.pop();
            } else {
                int size = stack1.size();
                for (int i = 0; i < size - 1; i++) {
                    stack2.push(stack1.pop());
                }
                return stack1.pop();
            }
        }
        public int peek() {
            if (!stack2.isEmpty())
            {
                return stack2.peek();
            } else {
                int size = stack1.size();
                for (int i = 0; i < size - 1; i++) {
                    stack2.push(stack1.pop());
                }
                return stack1.peek();
            }
        }
    }
    View Code

     4.树与图

    4.1 

    实现一个函数,检查二叉树是否平衡,平衡的定义如下,对于树中的任意一个结点,其两颗子树的高度差不超过1。

    给定指向树根结点的指针TreeNode* root,请返回一个bool,代表这棵树是否平衡。

    public class Num4_1 {
    /*
     * 时间复杂度为N*logN,N为树的节点数
        public boolean isBalance(TreeNode root) {
            // write code here
            if (root == null || (root.left == null && root.right == null))
                return true;
            if (Math.abs((getDepth(root.left) - getDepth(root.right))) > 1)
                return false;
            else
                return isBalance(root.left) && isBalance(root.right);        
        }
        //计算当前节点的高度
        public int getDepth(TreeNode node) {
            if (node == null)
                return 0;
            else 
                return Math.max(getDepth(node.left),getDepth(node.right)) + 1; 
        }
    */
        /*
         * 减少getDepth的调用次数,在计算高度的同时判断是否为平衡二叉树
         * 时间复杂度为O(N),还不是最优
         */
    /*    
        boolean balance = true;
        public boolean isBalance(TreeNode root) {
            // write code here
            if (root == null || (root.left == null && root.right == null))
                return true;
            getDepth(root);
            return balance;        
        }
        public int getDepth(TreeNode node) {
            if (node == null)
                return 0;
            else {
                int leftDepth = getDepth(node.left);
                int rightDepth = getDepth(node.right);
                if (Math.abs(leftDepth - rightDepth) > 1)
                    balance = false;
                return Math.max(leftDepth, rightDepth) + 1; 
            }
                
        }
    */    
        /*
         * 最优解法,当发现子树不是平衡二叉树时,立即中断getDepth递归
         * 时间复杂度是O(N),但是平均时间复杂度要比上一种解法更优
         */
        public boolean isBalance(TreeNode root) {
            // write code here
            if (getDepth(root) == -1) {
                return false;
            } else
                return true;    
        }
        
        public int getDepth(TreeNode node) {
            if (node == null)
                return 0;
            else {
                int leftDepth = getDepth(node.left);
                if (leftDepth == -1)
                    return -1;
                int rightDepth = getDepth(node.right);
                if (rightDepth == -1)
                    return -1;            
                if (Math.abs(leftDepth - rightDepth) > 1) {
                    return -1;
                } else {
                    return Math.max(leftDepth, rightDepth) + 1;
                }  
            }            
        }
    }
    View Code

     4.2

    对于一个有向图,请实现一个算法,找出两点之间是否存在一条路径。

    给定图中的两个结点的指针UndirectedGraphNode*a,UndirectedGraphNode* b(请不要在意数据类型,图是有向图),请返回一个bool,代表两点之间是否存在一条路径(a到b或b到a)。

    import java.util.HashSet;
    import java.util.LinkedList;
    import java.util.Queue;
    import java.util.Set;
    /*
    import java.util.ArrayList;
    
    public class UndirectedGraphNode {
        int label = 0;
        UndirectedGraphNode left = null;
        UndirectedGraphNode right = null;
        ArrayList<UndirectedGraphNode> neighbors = new ArrayList<UndirectedGraphNode>();
    
        public UndirectedGraphNode(int label) {
            this.label = label;
        }
    }
    
    */
    /*
     * 结果显示DFS比BFS稍微快一点
     * 注意两个方向都要遍历
     */
    public class Num4_2 {
        Set<UndirectedGraphNode> set = new HashSet<UndirectedGraphNode>();  
        public boolean checkPath(UndirectedGraphNode a, UndirectedGraphNode b) {
            // write code here
            //BFS
            //return check3(a, b) || check3(b, a);
            //DFS
            if (check(a, b))
                return true;
            else {
                set.clear();
                return check(b, a);
            }           
        }
        //DFS
        public boolean check(UndirectedGraphNode a, UndirectedGraphNode b){  
            if (a == b)
                return true;
            set.add(a);       
            for (UndirectedGraphNode u : a.neighbors) {
                if (!set.contains(u)) {           
                    if (check(u, b))
                        return true;               
                }
            }           
            return false;       
        }     
        //广度优先遍历BFS
        public boolean check3(UndirectedGraphNode a, UndirectedGraphNode b){
            Set<UndirectedGraphNode> set3 = new HashSet<UndirectedGraphNode>();
            Queue<UndirectedGraphNode> qu = new LinkedList<UndirectedGraphNode>();
            qu.add(a);
            while (!qu.isEmpty()) {
                UndirectedGraphNode u = qu.poll();         
                if (u == b)
                    return true;
                set3.add(u);           
                for (UndirectedGraphNode uu : u.neighbors) {
                    if (!set3.contains(uu)) {
                        qu.add(uu);
                    }
                }      
            }
            return false;
        }
    }
    View Code

     4.3

    对于一个元素各不相同且按升序排列的有序序列,请编写一个算法,创建一棵高度最小的二叉查找树。

    给定一个有序序列int[] vals,请返回创建的二叉查找树的高度。

    注:题意描述与书本原题是有出入的。原题是重在创建这颗二叉树,而本题只要返回高度即可,并不用创建二叉树。在代码中也给出创建二叉树的部分。

    /*
    public class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;
        public TreeNode(int val) {
            this.val = val;
        }
    }
    */
    public class Num4_3 {
        /*
         * 对于一个元素各不相同且按升序排列的有序序列,请编写一个算法,创建一棵高度最小的二叉查找树。
         * 并返回树高度
         */
        public int buildMinimalBST(int[] vals) {
            // write code here
            build(vals, 0, vals.length - 1);
            //树高度等于log2(n+1)取上整 ,用到换底公式 log2(n+1) = loge(n+1)/loge(2)
            return (int)Math.ceil(Math.log(vals.length + 1) / Math.log(2));
        }
        public TreeNode build(int[] vals, int a, int b) {
            if (a > b) {            
                return null;
            }
            TreeNode t = new TreeNode(vals[(a + b)/2]);
            t.left = build(vals, a, (a + b) / 2 - 1);
            t.right = build(vals, (a + b) / 2 + 1, b);
            return t;
        }
    }
    View Code

     4.4

    对于一棵二叉树,请设计一个算法,创建含有某一深度上所有结点的链表。

    给定二叉树的根结点指针TreeNode* root,以及链表上结点的深度,请返回一个链表ListNode,代表该深度上所有结点的值,请按树上从左往右的顺序链接,保证深度不超过树的高度,树上结点的值为非负整数且不超过100000。

    注:书中的返回是前dep层的所有节点构成的链表集合,每一层一个链表,这里返回的是第dep层的节点构成的链表

    public class Num4_4 {
        ListNode ans = null;
        ListNode last = null;
        public ListNode getTreeLevel(TreeNode root, int dep) {
            // write code here
            //前两种方法都是广度优先遍历,第一种判断每层结束的方法比较复杂,是根据上一层空节点的个数,来pandaun下一层总结点数
            //在遍历的时候再计数,如果每一层节点计数大于了该层节点数,则层数增一
    /*
            Queue<TreeNode> qt = new LinkedList<TreeNode>();
            ListNode start = null;
            ListNode last = null;
            qt.add(root);
            int count = 0;
            int depth = 1;
            int nCount = 0;
            while (!qt.isEmpty()) {  
                count++;          
                if (count > (Math.pow(2, depth - 1) - 2 * nCount)) {
                    depth++;
                    if (depth > dep)
                        return start;
                    count = 1;
                    nCount = 0;
                }                
                TreeNode temp = qt.poll();
                if (temp != null) {
                    if (depth == dep) {
                        ListNode l = new ListNode(temp.val);
                        if (start == null)
                            start = l;
                        else
                            last.next = l;
                        last = l;
                    }
                    qt.add(temp.left);
                    qt.add(temp.right);
                } else {
                    nCount++;
                }
            }
            return start;
    */
            /*
            对每一层都新建一个队列,把该层所有节点添加进去
            直到到达指定层
            */
    /*        
            Queue<TreeNode> current = new LinkedList<TreeNode>();
            ListNode ans = null;
            ListNode last = null;
            current.add(root); 
            int depth = 1;
            while (!current.isEmpty()) {
                if (depth == dep)
                    break;
                Queue<TreeNode> parents = current;
                current = new LinkedList<TreeNode>();
                for (TreeNode parent : parents) {
                    if (parent.left != null)
                        current.add(parent.left);
                    if (parent.right != null)
                        current.add(parent.right);
                } 
                depth++;
            }      
            for (TreeNode cur : current) {
                ListNode l = new ListNode(cur.val);
                if (ans == null)
                    ans = l;
                else
                    last.next = l;
                last = l;
            }
            return ans;
    */       
            get(root, dep, 1);
            return ans;
        }
        //深度优先遍历
        public void get(TreeNode root, int dep, int currentDepth) {
            if (root == null)
                return ;
            if (currentDepth == dep) {
                ListNode l = new ListNode(root.val);
                if (ans == null)
                    ans = l;
                else
                    last.next = l;
                last = l;
                return ;
            }
            get(root.left, dep, currentDepth + 1);
            get(root.right, dep, currentDepth + 1);
        }
    }
    View Code

     4.5

    请实现一个函数,检查一棵二叉树是否为二叉查找树。

    给定树的根结点指针TreeNode* root,请返回一个bool,代表该树是否为二叉查找树。

    public class Num4_5 {
    /*
        //结果显示第一种解法较好,但是其实复杂度是相当的
        int pre = Integer.MIN_VALUE;
        public boolean checkBST(TreeNode root) {
            // wurite code here
            //中序遍历
            if (root == null)
                return true;
            if (!checkBST(root.left))
                return false;
            if (root.val < pre)
                return false;
            pre = root.val;
            return checkBST(root.right);
        }
    */
        //最小最大值法(我称为夹逼法)
        public boolean checkBST(TreeNode root) {
            return check(root, Integer.MIN_VALUE, Integer.MAX_VALUE);
        }
        public boolean check(TreeNode root, int min, int max) {
            if (root == null)
                return true;
            if (root.val < min || root.val > max) {
                return false;
            }        
            if (!check(root.left, min, root.val))
                return false;
            return check(root.right, root.val, max);
        }
    }
    View Code

     4.6

    请设计一个算法,寻找二叉树中指定结点的下一个结点(即中序遍历的后继)。

    给定树的根结点指针TreeNode* root和结点的值int p,请返回值为p的结点的后继结点的值。保证结点的值大于等于零小于等于100000且没有重复值,若不存在后继返回-1。

    import java.util.ArrayList;
    import java.util.List;
    /*
    public class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;
        TreeNode parent = null;
        public TreeNode(int val) {
            this.val = val;
        }
    }
     */
    public class Num4_6 {
    /*    
     * 不使用父节点的办法,给出根节点,返回下一节点的值,没有后继结点则返回-1
     * 利用中序遍历递归找到该节点,并在递归时几下当前节点的递归次数,则下一次就是后继节点
        List<Integer> vals = new ArrayList<Integer>();
        static int count = 0;
        static int pos = -2;
        
        public static int findSucc(TreeNode root, int p) {
            // write code here
            if (root == null)
                return -1;
            int temp = findSucc(root.left, p);
            if (temp == -1) {
                count++;
                if (count == pos + 1)
                    return root.val;            
                if (root.val == p)
                    pos = count;
                return findSucc(root.right, p);  
            } 
            return temp;              
        }
    */    
        public static void main(String[] args) {
            TreeNode root = new TreeNode(1);
            TreeNode r1 = new TreeNode(2);
            TreeNode r2 = new TreeNode(3);
            TreeNode r3 = new TreeNode(4);
            TreeNode r4 = new TreeNode(5);
            TreeNode r5 = new TreeNode(6);
            TreeNode r6 = new TreeNode(7);
            root.left = r1;
            root.right = r2;
            r1.left = r3;
            r1.right = r4;
            r4.right = r5;
            r2.right = r6;
    //        System.out.println(findSucc(root, 3));
        }
        /* 可以连接到父节点的做法,给出当前节点,返回下一节点(书上给出的做法)
         * 若当前节点有右子树,则返回右子树的最左节点
         * 若当前节点没有右子树,则回溯到父节点,直到当前节点不再是右子节点,返回当前节点的父节点
         * 如果一直找不到一个父节点使当前节点为左子节点,说明已到最右节点,没有后继结点,返回null
         */
        public static TreeNode findSucc(TreeNode node) {
            if(node == null)
                return null;
            if(node.right != null) {
                TreeNode temp = node.right;
                while (temp.left != null) {
                    temp = temp.left;
                }
                return temp;
            }
            TreeNode parent = node.parent;
            while (parent != null && node == parent.right) {
                node = parent;
                parent = node.parent;
            }
            return parent;    
        }
        
    }
    View Code

    4.7

     有一棵无穷大的满二叉树,其结点按根结点一层一层地从左往右依次编号,根结点编号为1。现在有两个结点a,b。请设计一个算法,求出a和b点的最近公共祖先的编号。

    给定两个int a,b。为给定结点的编号。请返回ab的最近公共祖先的编号。注意这里结点本身也可认为是其祖先。

    测试样例:
    2,3
    返回:1
    import java.util.*;
    
    public class LCA {
        public int getLCA(int a, int b) {
            // write code here
            while (a != b) {
                if (a > b)
                    a = a / 2;
                else
                    b = b / 2;
            }
            return a;
        }
    }
    View Code

     当一般情况时,任意二叉树的任意节点,返回最近公共祖先

    //二叉树中两个节点的最近共同祖先
    public class Num4_7 {
    /*
     * 第一种情况满二叉树,并且每个节点的值是按照层序遍历1,2,3,4。。。依次排列,给出两个节点值,求共同祖先的节点值
     */
        public int findCommonAncestor(int a, int b) {
            while (a != b) {
                if (a > b)
                    a /= 2;
                if (b > a)
                    b /= 2;
            }
            return a;
        } 
        /*
         * 第二种一般情况下的二叉树,给出根节点和任意两个节点,返回祖先节点
         */
        public TreeNode find(TreeNode root, TreeNode a, TreeNode b) {
            //节点不在树中
            if (!isCoverNode(root, a) || !isCoverNode(root, b))
                return null;
            return findCommonAncestor(root, a, b);
        }
        public TreeNode findCommonAncestor(TreeNode root, TreeNode a, TreeNode b) {
            if (root == null)
                return null;
            if (root == a || root == b)
                return root;
            boolean aCoverLeft = isCoverNode(root.left, a);
            boolean bCoverleft = isCoverNode(root.left, b);
            //不在同一子树
            if (aCoverLeft != bCoverleft) {
                return root;
            } 
            //在同一子树,则继续遍历那棵子树
            return findCommonAncestor(aCoverLeft == false ? root.right : root.left, a, b);
    
        }
        //判断节点是否在以root为根节点所在的子树
        public boolean isCoverNode(TreeNode root, TreeNode node) {
            if (root == null)
                return false;
            if (root == node)
                return true;
            return isCoverNode(root.left, node) || isCoverNode(root.right, node);
        }
        public static void main(String[] args) {
            Num4_7 num4_7 = new Num4_7();
            TreeNode root = new TreeNode(1);
            TreeNode r1 = new TreeNode(2);
            TreeNode r2 = new TreeNode(3);
            TreeNode r3 = new TreeNode(4);
            TreeNode r4 = new TreeNode(5);
            TreeNode r5 = new TreeNode(6);
            TreeNode r6 = new TreeNode(7);
            TreeNode r7 = new TreeNode(7);
            root.left = r1;
            root.right = r2;
            r1.left = r3;
            r1.right = r4;
            r4.right = r5;
            r2.right = r6;
            TreeNode t = num4_7.find(root, root, r5);
            if (t != null)
                System.out.println(t.val);
            else
                System.out.println("null");
        }
    }
    View Code

    一般情况的算法的时间复杂度为O(n),因为在检查节点是否在树中的时候isCoverNode()执行了2n次,左边n次,右边n次。然后在判断节点在哪一边时,第一次是2*n/2,第二次2*n/4,以此类推,相加后求和渐进于O(4n),也就是O(n);

    4.8

    判断一棵二叉树是否为另一棵二叉树的子树

    解法一:用两个字符串分别来表示树的前序遍历和中序遍历,如果树A的中序遍历是树B的中序遍历的字串,树A的前序遍历是树B的前序遍历的字串,则树A是树B的子树,注意要用特殊字符来表示空节点。此时空间复杂度为O(n+m),时间复杂度为O(n+m)(忽略常数系数,n和m分别为两棵树的节点数),如果树的节点数较多时,这个方法就不太适用,所占用内存会过大。

    解法二:遍历搜索较大的那棵树,如果找到某个节点与另棵树相同,则从此节点开始对比余下节点是否都相同,每遇到与另一棵树根据诶点相同的节点都要搜索一次,直到判断出是否为子树。使用递归来遍历,所以空间复杂度为O(log(n)+log(m)),最坏时间复杂度为O(nm)。但是假设相同的节点只会出现k次,时间复杂度就变为O(n+km),所以这个解法总体来说比较好。

    public class Num4_8 {
        public static boolean isSubTree(TreeNode rootA, TreeNode rootB) {
            if (rootB == null)
                return true;
            if (rootA == null)
                return false;
            if (rootA.val == rootB.val) {
                if (isMatch(rootA, rootB))
                    return true;
            }
            return isSubTree(rootA.left, rootB) || isSubTree(rootA.right, rootB);    
        }
        public static boolean isMatch(TreeNode nodeA, TreeNode nodeB) {
            if (nodeA == null && nodeB == null)
                return true;
            if (nodeA == null || nodeB == null || nodeA.val != nodeB.val) {
                return false;
            } 
            return isMatch(nodeA.left, nodeB.left) && isMatch(nodeA.right, nodeB.right);
        }
    }
    View Code

     4.9

    给定一棵二叉树和一个定值,找出二叉树中节点和等于这个定值的所有路径。

    import java.util.ArrayList;
    /*
     * 起始节点和结束节点为树种的任意节点,只要构成路径即可
     * 如果没有规定节点值都为正整数,则在找到满足的路径之后还要继续往下遍历,直到叶子节点
     * 如果值都为正整数,则找到之后就可以结束当前路径的遍历
     * 如果规定路径必须从根节点到叶子节点,更加简化了题目,把findhelper的递归注释即可,见注释部分
     */
    public class Num4_9 {
        ArrayList<ArrayList<Integer>> ans = new ArrayList<ArrayList<Integer>>();
        public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
            findHelper(root, target);
            return ans;        
        }
        public void findHelper(TreeNode root, int target) {
            if (root == null)
                return ;
            //值都为正整数
    //        find(root, target, 0, new ArrayList<Integer>());
            //值为正负零三种
            find2(root, target, 0, new ArrayList<Integer>());
            findHelper(root.left, target);
            findHelper(root.right, target);
        }
        //如果规定为正整数值
        public void find(TreeNode node, int target, int sum, ArrayList<Integer> path) {
            if (node == null)
                return ;
            int curSum = sum + node.val;
            if (curSum <= target) {
                path.add(node.val);
                if (curSum == target) {
                    ans.add(path);
                } else {
                    find(node.left, target, curSum, new ArrayList<Integer>(path));
                    find(node.right, target, curSum, path);
                }                
            }
        }
        //如果节点值可能为零或负数
        public void find2(TreeNode node, int target, int sum, ArrayList<Integer> path) {
            if (node == null)
                return ;
            int curSum = sum + node.val;
            path.add(node.val);
            if (curSum == target) {
                ans.add(path);
                //注意new的使用,两边都要new
                find2(node.left, target, curSum, new ArrayList<Integer>(path));
                find2(node.right, target, curSum, new ArrayList<Integer>(path));
            } else {
                //有一边用new就行,而且这两句不能调换顺序
                find2(node.left, target, curSum, new ArrayList<Integer>(path));
                find2(node.right, target, curSum, path); 
            }           
        }
        /*
         * 规定路径必须从根节点到叶子节点,更加简化了题目
        ArrayList<ArrayList<Integer>> ans = new ArrayList<ArrayList<Integer>>();
        public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
            findHelper(root, target);
            return ans;        
        }
        public void findHelper(TreeNode root, int target) {
            if (root == null)
                return ;
            find(root, target, 0, new ArrayList<Integer>());
            
    //        findHelper(root.left, target);
    //        findHelper(root.right, target);
        }
        public void find(TreeNode node, int target, int sum, ArrayList<Integer> path) {
            if (node == null)
                return ;
            int curSum = sum + node.val;
            if (curSum <= target) {
                path.add(node.val);
                if (curSum == target && node.left == null && node.right == null) {
                    ans.add(path);
                } else {
                    find(node.left, target, curSum, new ArrayList<Integer>(path));
                    find(node.right, target, curSum, path);
                }                
            }
        }
         */
        public static void main(String[] args) {
            Num4_9 num4_9 = new Num4_9();
            TreeNode root = new TreeNode(1);
            TreeNode r1 = new TreeNode(2);
            TreeNode r2 = new TreeNode(3);
            TreeNode r3 = new TreeNode(4);
            TreeNode r4 = new TreeNode(-1);
            TreeNode r5 = new TreeNode(0);
            TreeNode r6 = new TreeNode(7);
            TreeNode r7 = new TreeNode(7);
            root.left = r1;
            root.right = r2;
            r1.left = r3;
            r1.right = r4;
            r4.right = r5;
            r2.right = r6;
            ArrayList<ArrayList<Integer>> ret = num4_9.FindPath(root, 2);
            
        }
    }
    View Code

     9.8硬币面值组合问题

    分析转自 http://www.cnblogs.com/python27/p/3303721.html

      给定一个数值sum,假设我们有m种不同类型的硬币{V1, V2, ..., Vm},如果要组合成sum,那么我们有

    sum = x1 * V1 + x2 * V2 + ... + xm * Vm 

    求所有可能的组合数,就是求满足前面等值的系数{x1, x2, ..., xm}的所有可能个数。

      [思路1] 当然我们可以采用暴力枚举,各个系数可能的取值无非是x1 = {0, 1, ..., sum / V1}, x2 = {0, 1, ..., sum/ V2}等等。这对于硬币种类数较小的题目还是可以应付的,比如华为和创新工厂的题目,但是复杂度也很高O(sum/V1 * sum/V2 * sum/V3 * ...)

      [思路2] 从上面的分析中我们也可以这么考虑,我们希望用m种硬币构成sum,根据最后一个硬币Vm的系数的取值为无非有这么几种情况,xm分别取{0, 1, 2, ..., sum/Vm},换句话说,上面分析中的等式和下面的几个等式的联合是等价的。

    sum = x1 * V1 + x2 * V2 + ... + 0 * Vm

    sum = x1 * V1 + x2 * V2 + ... + 1 * Vm

    sum = x1 * V1 + x2 * V2 + ... + 2 * Vm

    ...

    sum = x1 * V1 + x2 * V2 + ... + K * Vm  

      其中K是该xm能取的最大数值K = sum / Vm。可是这又有什么用呢?不要急,我们先进行如下变量的定义:

    dp[i][sum] = 用前i种硬币构成sum 的所有组合数。

      那么题目的问题实际上就是求dp[m][sum],即用前m种硬币(所有硬币)构成sum的所有组合数。在上面的联合等式中:当xn=0时,有多少种组合呢? 实际上就是前i-1种硬币组合sum,有dp[i-1][sum]种! xn = 1 时呢,有多少种组合? 实际上是用前i-1种硬币组合成(sum - Vm)的组合数,有dp[i-1][sum -Vm]种; xn =2呢, dp[i-1][sum - 2 * Vm]种,等等。所有的这些情况加起来就是我们的dp[i][sum]。所以:

    dp[i][sum] = dp[i-1][sum - 0*Vm] + dp[i-1][sum - 1*Vm]

    + dp[i-1][sum - 2*Vm] + ... + dp[i-1][sum - K*Vm]; 其中K = sum / Vm

      换一种更抽象的数学描述就是:

    递归公式

      通过此公式,我们可以看到问题被一步步缩小,那么初始情况是什么呢?如果sum=0,那么无论有前多少种来组合0,只有一种可能,就是各个系数都等于0;

    dp[i][0] = 1   // i = 0, 1, 2, ... , m

      如果我们用二位数组表示dp[i][sum], 我们发现第i行的值全部依赖与i-1行的值,所以我们可以逐行求解该数组。如果前0种硬币要组成sum,我们规定为dp[0][sum] = 0. 

    将二维数组转变成一维数组

        public int makeChange(int n) {
            int coins[] = {1,5,10,25};
            int dp[] = new int[n + 1];       
            dp[0] = 1;
            for (int i = 0 ;i < 4; ++i) {
                for (int j = coins[i]; j <= n; ++j) {
                    dp[j] = (dp[j] + dp[j - coins[i]]) % 1000000007;               
                }
            }
            return dp[n];
        }
    View Code

     9.9 n皇后问题

    public class Num9_9 {
        //递归算法,效率比较差
        /*
         * 从第一行开始,判断在每一列拜访皇后是否合法,若合法则摆放,并到下一行摆放,不合法则到下一列
         * 判断是否合法主要是与前面已经摆放过的皇后进行比较,看是否同一列或者同对角线
         * 判断对角线相同的原则是行数差和列数差的绝对值相等,不用判断是否在同一行,因为在摆放的时候是逐行摆放的
         */
        public int nQueens(int n) {
            // write code here
            int[] way = new int[n];
            return placeQueens(n, 0, way);
        }
        public int placeQueens(int size, int row, int[] way) {
            if (row == size)
                return 1;
            int count = 0;
            for (int i = 0; i < size ; i++) {
                if (checkValid(row, i, way)) {
                    way[row] = i;
                    count += placeQueens(size, row + 1, way);
                }
            }
            return count;
        }
        public boolean checkValid(int row, int column, int[] way) {
            for (int i = 0; i < row; i++) {
                if (column == way[i] || Math.abs(column - way[i]) == row - i)
                    return false;                       
            }        
            return true;
        }
    }
    View Code

     9.10 堆箱子

    import java.util.HashMap;
    import java.util.Map;
    
    public class Num9_10 {
        public int getHeight(int[] w, int[] l, int[] h, int n) {
            // write code here
            //这里一定要循环调用
            int maxHeight = 0;
            for (int i = 0; i < n; i++) {
                int newHeight = getDp(w, l, h, i, n);
                if (newHeight > maxHeight)
                    maxHeight = newHeight;                
            }
            return maxHeight;     
        }
        /*
         * 递归算法
         */
        public int get(int[] w, int[] l, int[] h, int bottom, int n) {
            int maxHeight = 0;
            for (int j = 0; j < n; j++) {
                if (canBeAbove(w, l, bottom, j)) {
                    int newHeight = get(w, l, h, j, n);
                    if (newHeight > maxHeight) {
                        maxHeight = newHeight;
                    }
                }
            }
            if (bottom < n) {
                maxHeight += h[bottom];
            }
            return maxHeight;
        }
        /*
         * 动态规划
         */
        //保存以编号bottom为底时的最高堆高度
        Map<Integer, Integer> bottoms = new HashMap<Integer, Integer>();
        public int getDp(int[] w, int[] l, int[] h, int bottom, int n) {
            if (bottoms.containsKey(bottom)) {            
                return bottoms.get(bottom);
            }
            int maxHeight = 0;
            for (int j = 0; j < n; j++) {
                if (canBeAbove(w, l, bottom, j)) {
                    int newHeight = getDp(w, l, h, j, n);
                    if (newHeight > maxHeight) {
                        maxHeight = newHeight;
                    }
                }
            }
            if (bottom < n) {
                maxHeight += h[bottom];
                bottoms.put(bottom, maxHeight);
            }
            return maxHeight;
        }
        public boolean canBeAbove(int[] w, int[] l, int bottom, int j) {
            if (w[j] < w[bottom] && l[j] < l[bottom])
                return true;
            return false;
        }    
    }
    View Code

     11.2

    一开始没有看清题意,在线解题和书本的题意不同

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Comparator;
    import java.util.HashMap;
    import java.util.Map;
    
    public class Num11_2 {
        public ArrayList<String> sortStrings(String[] str, int n) {
            // write code here
            Arrays.sort(str);
            ArrayList<String> ans = new ArrayList<String>();
            for (int i = 0; i < n; i++) {
                boolean insertOk = true;
    //            //变位词只保留字典序最前的一个
    //            for (int j = 0; j < ans.size(); j++) {
    //                if (str[i].length() == ans.get(j).length()) {
    //                    char[] a = ans.get(j).toCharArray();
    //                    char[] b = str[i].toCharArray();
    //                    Arrays.sort(a);
    //                    Arrays.sort(b);
    //                    String aa = String.valueOf(a);
    //                    String bb = String.valueOf(b);
    //                    if (aa.equals(bb)) {                        
    //                        insertOk = false;
    //                        break;
    //                    }                        
    //                }
    //            }
    //            if (insertOk)
    //                ans.add(str[i]);
                //变位词排在相邻位置
                for (int j = ans.size() - 1; j >= 0; j--) {
                    if (str[i].length() == ans.get(j).length()) {
                        char[] a = ans.get(j).toCharArray();
                        char[] b = str[i].toCharArray();
                        Arrays.sort(a);
                        Arrays.sort(b);
                        String aa = String.valueOf(a);
                        String bb = String.valueOf(b);
                        if (aa.equals(bb)) {  
                            ans.add(j + 1, str[i]);
                            insertOk = false;
                            break;
                        }                        
                    }                
                }
                if (insertOk)
                    ans.add(str[i]);
            }  
            return ans;
        }    
        //只考虑变位词拍在相邻位置而不考虑其他顺序
        /*
         * 方法一:重写排序函数
         */
        class MyComparator implements Comparator<String> {
            public String sortStr(String str) {
                char[] array = str.toCharArray();
                Arrays.sort(array);
                return new String(array);
            }
            @Override
            public int compare(String o1, String o2) {
                // TODO Auto-generated method stub
                return sortStr(o1).compareTo(sortStr(o2));
            }
        }
        public String[] simpleSort(String[] str) {
            Arrays.sort(str, new MyComparator());
            return str;        
        }
        /*
         * 方法二:利用散列表将变位词分组
         */
        public ArrayList<String> hashSort(String[] str) {
            Map<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();
            //将变位词分组
            for (String s : str) {
                char[] arr = s.toCharArray();
                Arrays.sort(arr);
                String ss = new String(arr);
                if (!hm.containsKey(ss))
                    hm.put(ss, new ArrayList<String>());
                ArrayList<String> al = hm.get(ss);
                al.add(s);
            }
            //将hashmap转为数组/ArrayList
            ArrayList<String> ans = new ArrayList<String>();
            int index = 0;
            for(String key : hm.keySet()) {
                ArrayList<String> al = hm.get(key);
                for (String s : al) {
                    ans.add(s);
                }
            }
            return ans;
        }
        public static void main(String[] args) {
            Num11_2 n112 = new Num11_2();
            String[] str = {"dad","add","abc","ab","cba","ba"};
            n112.simpleSort(str);
        }
    }
    View Code

     11.3

    public class Num11_3 {
        /*
         * 二分法的变形
         * 具体解释看书P257吧,懒得码了
         * 需要注意的是第三中情况也就是中间的数等于左右两头的数的时候,两边都要搜索了
         * 如果左边搜索得不到结果就要搜索右边,如果得到结果就直接返回
         * 所有元素不同的话时间复杂度为O(log(n)),很多元素重复的话,会有很多第三种情况出现,就会接近于O(n)
         */
        public int findElement(int[] A, int n, int x) {
            // write code here
            return find(A, 0, n - 1,x);
        }
        public int find(int[] arr, int left, int right, int x) {
            if (left <= right) {
                int mid = (left + right) / 2;
                if (x == arr[mid])
                    return mid;
                if (arr[left] < arr[mid]) {
                    if (x >= arr[left] && x <= arr[mid]) {
                        return find(arr, left, mid, x);
                    } 
                    return find(arr, mid + 1, right, x);
                }
                if (arr[mid + 1] < arr[right]) {
                    if (x >= arr[mid + 1] && x <= arr[right]) {
                        return find(arr, mid + 1, right, x);
                    } 
                    return find(arr, left, mid, x);                
                }
                //前面两个条件都不满足时
                int ans = find(arr, left, mid, x);
                if (ans == -1)
                    return find(arr, mid + 1, right, x); 
                return ans;
            }
            return -1;
        }
    }
    View Code

     11.6

    public class Num11_6 {
        public int[] findElement(int[][] mat, int n, int m, int x) {
            // write code here
            return find(mat, 0, 0, mat[0].length - 1, x);
        }
        /*
         * 方法一:对每一行进行二分查找
         */
        public int[] find(int[][] mat, int row, int left, int right, int x) {
            if (row < mat.length) {
                if (left <= right) {
                    int mid = (left + right) / 2;
                    if (mat[row][mid] == x) {
                        int[] ans = {row, mid};
                        return ans;
                    }
                    if (mat[row][mid] > x) {
                        if (mid == 0)
                            return null;
                        return find(mat, row, left, mid - 1, x);
                    }
                    return find(mat, row, mid + 1, right, x);
                }
                return find(mat, row + 1, 0, mat[0].length - 1, x);
            }
            return null;
        }
        /*
         * 方法二:利用以下四个原则进行查找
         * 1.列首元素如果大于X,则往该列的左一列查找
         * 2.列尾元素如果小于X,则往该列的右一列查找
         * 3.行首元素如果大于X,则往该行的上一行查找
         * 4.行尾元素如果小于X,则往该行的下一行查找
         * 我们可以从矩阵的任意位置开始查找,这里从最右上方的元素开始
         */
        public int[] find(int[][] mat, int x) {
            int row = 0;
            int col = mat[0].length - 1;
            while (row <= mat.length && col >= 0) {
                if (mat[row][col] == x) {
                    int[] ans = {row, col};
                    return ans;
                }                
                if (mat[row][col] > x) {
                    col--;
                } else
                    row++;
            }
            return null;
        }
        /*
         * 方法三:对对角线上的元素进行二分查找,把矩阵分为四个区域,进行递归查找
         * 具体解释参照书本P262
         */
        public int[] findDiv(int[][] mat, int topRow, int topCol, int botRow, int botCol, int x) {
            if (topRow >= 0 && topCol >= 0 && botRow <= mat.length && botCol <= mat[0].length && topRow <= botRow && topCol <= botCol) {
                
                //某个子矩阵只有一个元素时
                if (topRow == botRow && topCol == botCol) {
                    if (mat[topRow][topCol] == x) {
                        int[] ans = {topRow, topCol};
                        return ans;
                    }
                    return null;    
                }
                //矩阵不是正方形,所以得求出其中的最大正方形的对角线
                int min = Math.min(botRow - topRow, botCol - topCol);
                int mid = min / 2;
                if (mat[topRow + mid][topCol + mid] == x) {
                    int[] ans = {topRow + mid, topCol + mid};
                    return ans;
                } else if (mat[topRow + mid][topCol + mid] < x) {  
                    int[] ans = findDiv(mat, topRow + mid + 1, topCol + mid + 1, botRow, botCol, x ); //右下区域
                    if (ans == null) {
                        ans = findDiv(mat, topRow + mid + 1, topCol, botRow, topCol + mid, x ); // 左下区域
                        if (ans == null)
                            return findDiv(mat, topRow, topCol + mid + 1, topRow + mid, botCol, x ); //右上区域
                        return ans;
                    } else {
                        return ans;
                    }
                } else {
                    int[] ans = findDiv(mat, topRow, topCol, topRow + mid, topCol + mid, x); //左上区域
                    if (ans == null) {
                        ans = findDiv(mat, topRow + mid + 1, topCol, botRow, topCol + mid, x ); // 左下区域
                        if (ans == null)
                            return findDiv(mat, topRow, topCol + mid + 1, topRow + mid, botCol, x ); //右上区域
                        return ans;
                    } else {
                        return ans;
                    }
                }         
            }
            return null;
        }
        public static void main(String[] args) {
            Num11_6 n116 = new Num11_6();
            int[][] mat = {{0,2,3,4},{1,5,6,7}};
            n116.find(mat, 0, 0, 3, 7);
        }
    }
    View Code

    11.8

    public class Num11_8 {
        //实现一个二叉查找树来插入元素,并且在结点中存储每个结点的左子树结点数量
        RankNode root = null;
        public int[] getRankOfNumber(int[] A, int n) {
            // write code here
            int[] rank = new int[n];
            for (int i = 0; i < n; i++) {
                track(A[i]);//插入数据
                rank[i] = root.getRank(A[i]);//获得该数据的秩
            }
            return rank;
        }    
        public void track(int val) {
            if (root == null)
                root = new RankNode(val);
            else
                root.insert(val);
        }
    }
    //实现的数据结构
    class RankNode {
        RankNode left = null, right = null;
        int val = 0;
        int leftSize = 0;
        public RankNode(int val) {
            this.val = val;
        }
        //往树中插入数据    时间复杂度为O(log(n))
        public void insert(int val) {
            if (val <= this.val) {
                if (this.left != null)
                    this.left.insert(val);
                else
                    this.left = new RankNode(val);
                this.leftSize++;
            } else {
                if (this.right != null)
                    this.right.insert(val);
                else
                    this.right = new RankNode(val);
            }
        }
        //获得秩   时间复杂度为O(log(n))
        public int getRank(int val) {
            if (val == this.val)
                return this.leftSize;
            if (val > this.val) {
                if(this.right == null)
                    return -1;
                return this.leftSize + 1 + this.right.getRank(val);
            }
            if (this.left == null)
                return -1;
            return this.left.getRank(val);
        }
    }
    View Code

     17.3

    计算n!尾部零的个数

    /*
     * 计算n!尾部零的个数
     * 零的个数也就是10的个数 ,10的个数可以看成是5的倍数或2的倍数,因为是2的倍数的数比是5的倍数多
     * 所以只用求5的倍数  
     * 需要注意的是5的次方  25 = 5*5,所以相当于有两个5,125有三个5,所以次方増一,5的倍数也増一
     */
    public class Num17_3 {
        public int countZero(int n) {
            if (n < 0) {
                return -1;
            }
            int count = 0;        
            for (int i = 5; n / i > 0; i *= 5) {
                count += n / i;
            }
            return count;
        }
    }
    View Code

     18.1

    不用加号以及其他算术运算符的加法

        public int addAB(int A, int B) {
            // write code here
            /*非递归解法
            int c = 0;
            int j = 0;
            for (int i = 0; i < 32; i++) {
                if (((A & (1 << i)) | (B & (1 << i))) == 0) {
                    if (j == 1) {
                        c |= 1 << i;
                        j = 0;
                    }                    
                } else if (((A & (1 << i)) & (B & (1 << i))) == 0) {
                    if (j == 0)
                        c |= 1 << i;
                    else 
                        j = 1;
                } else {
                    if (j == 1)
                        c |= 1 << i;
                    else
                        j = 1;
                }
            }
            return c;*/
            //递归解法
            if (B == 0)
                return A;
            int sum = A ^ B;              //相加不进位
            int carry = (A & B) <<1;     //进位不想加
            return addAB(sum,carry);        
        }
    View Code

     18.2

    完美洗牌--完美打乱一个数组

    public class Num18_2 {
        /*
         * 随机打乱一个数组
         * 采用递归的方式
         * 先打乱数组的前n-1个元素,再将第n个元素与前n个元素中的元素随机交换
         */
        //产生[lower,higher]之间的随机数
        public int rand(int lower,int higher) {
            return lower + (int) (Math.random() * (higher - lower + 1));
        }
        //参数i为数组最后一个元素的索引
        public int[] shuffleArrayRecursiveLy(int[] arr, int i) {
            if (i == 0)
                return arr;
            shuffleArrayRecursiveLy(arr, i - 1);
            int k = rand(0,i);   //前i+1个元素中的随机一个
            //将第i+1个元素与其交换
            int temp = arr[k];
            arr[k] = arr[i];
            arr[i] = temp;
            return arr;
        }
        /*
         * 迭代的方式
         */
        public int[] shuffleArray(int[] arr) {
            for (int i = 1; i < arr.length; i++) {
                int k = rand(0,i);
                int temp = arr[k];
                arr[k] = arr[i];
                arr[i] = temp;
            }
            return arr;
        }
    }
    View Code

     18.4

    输出0~n中的数字含有2的个数

        public int countNumberOf2s(int n) {
            // write code here
            int count = 0;
            int length = Integer.toString(n).length();
               for (int i = 0; i < length; i ++) {
                count += countAtDigit(n,i);
            }
            return count;
        }
        public int countAtDigit(int n, int d) {
            int powerOf10 = (int)Math.pow(10,d);
            int nextPow = powerOf10 * 10;
            
            int downRound = n - n % nextPow;
            int upRound = downRound + nextPow;
            int right = n % powerOf10;
            
            int digit = (n / powerOf10) % 10; 
            if (digit == 2) {
                return downRound / 10 + right + 1;
            } else if (digit > 2) {
                return upRound / 10;
            } else 
                return downRound / 10;
       }

     18.7

    public class Num17_7 {
        /*
         * 书本中给出的使用了hashmap来缓存的前提是字符串数组已经排序了
         * 这样可以从长到短来遍历
         * 这里没有使用缓存
         */
        public int getLongest(String[] str, int n) {
            // write code here
            int ans = 0, lastLen = 0;
            for (int i = 0; i < n; i++) {
                if (lastLen < str[i].length() && divideAndFind(str, str[i], i))
                    lastLen = str[i].length();
            }
            return lastLen;
        }
        public boolean hasSub(String[] strs, String str, int index) {
            
            if (str == null)
                return true;
            for (int i = 0; i < strs.length; i++) {
                if (i != index) {
                    if (strs[i].equals(str))
                        return true;
                }
            }
            return false;
        }
        public boolean divideAndFind(String[] strs, String str, int strIndex) {
            if (str == null || str.length() == 0)
                return true;
            for (int i = 1; i <= str.length(); i++) {
                if (hasSub(strs, str.substring(0, i), strIndex) && divideAndFind(strs, str.substring(i),strIndex))
                    return true;
            }
            return false;
        }
    }
    View Code

    18.8

    利用后缀树实现的判断短字符串是否是某个字符串的子串,也附上kmp实现的代码

    package 高难度题;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    
    public class Num18_8 {
        /*
         * 利用后缀树实现
         */
        public boolean[] chkSubStr(String[] p, int n, String s) {
            // write code here
            SuffixTreeNode root = new SuffixTreeNode();
            for (int i = 0;i < s.length(); i++) {
                root.insert(s.substring(i), i);
            }
            boolean[] ans = new boolean[n];
            for (int i = 0;i < n; i++) {
                if (p[i] == null || p[i].length() == 0)
                    ans[i] = false;
                else
                    ans[i] = root.search(p[i]);            
            }
            return ans;
        }
        /*
         * 利用kmp算法实现
         */
        public boolean[] chkSubStrKmp(String[] p, int n, String s) {
            // write code here
            boolean[] ans = new boolean[n];
            for (int i = 0; i < n; i++) {
                if (isSubStr(p[i], s))
                    ans[i] = true;           
            }
            return ans;
        }
        public boolean isSubStr(String par, String ori) {
            int next[] = new int[par.length()+1];
            //求next数组每个元素值                 
            next[0] = -1;
            int k = -1, j = 0;
            while ( j < par.length()) { 
                //k == -1就表示比较到了第一位还不相等,所以next[j] = 0
                //k其实就是next[j-1](k == -1时其实也是),par.charAt(j) == par.charAt(k)满足的话,也就是说next[j] = next[j-1]+1
                //如果不满足那就要从前k个字符的前缀不相等的那一位开始
                if (k == -1 || par.charAt(j) == par.charAt(k)) {
                    next[++j] = ++k;//也就是说next[j] = next[j-1]+1
                } else {
                    k = next[k];//从前k个字符的前缀不相等的那一位开始从新比较
                }
            }
            int p = 0,q = 0;
            while (p < ori.length()) {
                if (par.charAt(q) == ori.charAt(p)) {
                    if (q == par.length()-1) {
                        q = next[q];
                        return true;//出现模式串则返回
                    } else { //相等则继续往后比较
                        p++;
                        q++;
                    }                          
                } else { //不相等则移动
                    q = next[q];
                }
                if (q == -1) { //比较了模式串的第一个字符且不相等
                    p++;
                    q++;
                }
            }      
            return false;
        }
    }
    /*
     * 后缀树的实现
     */
    class SuffixTreeNode {
        public SuffixTreeNode() {}
        HashMap<Character, SuffixTreeNode> children = new HashMap<Character, SuffixTreeNode>();
        ArrayList<Integer> indexes = new ArrayList<Integer>();
        public void insert(String str, int index) {
            if (str != null && str.length() != 0) {
                indexes.add(index);
                char value = str.charAt(0);
                SuffixTreeNode child;
                if (children.containsKey(value)) {
                    child = children.get(value);
                } else {
                    child = new SuffixTreeNode();
                    children.put(value, child);
                }
                String subStr = str.substring(1);
                child.insert(subStr, index);
            }
        }
        
        /*
         * 可以返回子串在原字符串中的索引,由于原字符串中的子串可能不止一个,所以返回的是ArrayList
        public ArrayList search(String t) {
            if (t == null || s.length() == 0)
                return indexes;
            else {
                char value = t.charAt(0);
                if (children.containsKey(value)) {
                    String subStr = str.substring(1);
                    return children.get(value).search(subStr);
                }
            }
            return null;    
        }*/
        public boolean search(String t) {
            if (t == null || t.length() == 0)
                return true;
            else {
                char value = t.charAt(0);
                if (children.containsKey(value)) {
                    String subStr = t.substring(1);
                    return children.get(value).search(subStr);
                }
            }
            return false;    
        }
    }
    View Code

     18.10

    /*
     * solution :
     * 算法基础是广度优先遍历,在此基础上加上回溯
     * 对于每一个遍历过的单词,利用hashmap backTrack将其与相差一个字母的单词映射 backTrack[v] = temp,表示v可以由temp更改得到
     * backTrack 记录的是首次出现的键值对,因为以后如果再出现v,v已经visited,所以不会沿着这次出现的查询下去,因此没必要再加入backTrack
     * 当找到了目标单词后,在backTrack中往回回溯,即可得到路径
     */
    public class Num18_10 {
        public int countChanges(String[] dic, int n, String s, String t) {
            // write code here
            if (s.equals(t))
                return 0;
            Set<String> dict = new HashSet<String>();
            for (String ss : dic) {
                dict.add(ss);
            }
            Queue<String> q = new LinkedList<String>();        
            Map<String, String> backTrack = new HashMap<String, String>();
            Set<String> visited = new HashSet<String>();
            q.add(s);
            visited.add(s);
            while(!q.isEmpty()) {
                String temp = q.poll();
                for (String v : change(temp)) {
                    if (v.equals(t)) {
                        int ret = 0;
                        //注释部分可以用于返回整个路径
                        /*
                        List<String> ans = new ArrayList<String>();
                        ans.add(v);
                        */
                        while (temp != null) {
                            ret++;
                            /*
                            ans.add(0, temp);
                            */
                            temp = backTrack.get(temp);                        
                        }
                        //返回步数
                        return ret;
                    }
                    if (dict.contains(v) && !visited.contains(v)) {
                        q.add(v);
                        visited.add(v);
                        backTrack.put(v,temp);
                    }
                }            
            }
            //没找到路径
            return -1;
        }
        public Set<String> change(String str) {
            Set<String> changes = new HashSet<String>();        
            for (int i = 0; i < str.length(); i++) {
                char[] arr = str.toCharArray();
                for (char c = 'a'; c <= 'z'; c++) {
                    if (str.charAt(i) != c) {
                        arr[i] = c;
                        changes.add(new String(arr));
                    }
                }
            }
            return changes;
        } 
    }
    View Code

    18.11

    /*
     * solution  时间复杂度 是O(n^4),最简单的做法
     * 按边长从大到小的顺序遍历矩阵中每个位置是否可以构成同值方阵,检查四条边的值是否全部相同
     * 需要注意的是方阵的中心不一定是在原始矩阵的中心
     */
    public class Num18_11 {
        public int maxSubMatrix(int[][] mat, int n) {
            // write code here    
            for (int len = n; len > 0; len--) {
                for (int i = 0; i < n; i++) {
                    for (int j = 0; j < n; j++ ) {
                        if (i + len <= n && j + len <= n) {
                            if (checkTop(mat, i, j, len) && checkRight(mat, i, j, len) && checkBottom(mat, i, j, len) && checkLeft(mat, i, j, len)) {
                                return len;
                            }
                        }
                    }
                }
            }
            return 0;
        }
        public boolean checkTop(int[][] mat, int row, int column, int len) {
            for (int i = column + 1; i < column + len; i++) {
                if (mat[row][i] != mat[row][i - 1]) {
                    return false;
                }
            }
            return true;
        }
        public boolean checkRight(int[][] mat, int row, int column, int len) {
            for (int i = row + 1; i < row + len; i++) {
                if (mat[i][column + len - 1] != mat[i - 1][column + len - 1]) {
                    return false;
                }
            }
            return true;        
        }
        public boolean checkBottom(int[][] mat, int row, int column, int len) {
            for (int i = column + 1; i < column + len; i++) {
                if (mat[row + len - 1][i] != mat[row + len - 1][i - 1]) {
                    return false;
                }
            }
            return true;        
        }
        public boolean checkLeft(int[][] mat, int row, int column, int len) {
            for (int i = row + 1; i < row + len; i++) {
                if (mat[i][column] != mat[i - 1][column]) {
                    return false;
                }
            }
            return true;          
        }    
    }
    View Code

     18.12

    /*
     * 题意:求矩阵的元素总和最大的子矩阵 ,返回 和
     * solution : 若矩阵是R行C列   时间复杂度是O(R^2*C)
     * 子矩阵是由连续的行和连续的列组成的
     * 迭代所有连续的行的组合,在每种行的组合中找使得和最大的连续的列的组合,  再得出每种行的组合的最大值即可
     * 在每种行的组合中,我们可以把问题简化为求和最大子数组问题
     * 在每种行的组合中,把每一列的和看成一维数组的一个元素,所以我们要求一维数组的和最大子数组,这个算法比较简单,已经实现
     */
    public class Num18_12 {
        public int sumOfSubMatrix(int[][] mat, int n) {
            // write code here
            if (mat == null)
                return -1;
            int maxSum = Integer.MIN_VALUE;
            int[] rowSum = new int[mat[0].length];
            for (int i = 0; i < mat.length; i++) { 
                //数组清零
                for ( int k = 0; k < mat[0].length; k++) {
                        rowSum[k] = 0;
                }
                for (int j = i; j < mat.length; j++) {
                    for ( int k = 0; k < mat[0].length; k++) {
                        rowSum[k] += mat[j][k];      
                    }
                    int curMax = maxSubArray(rowSum);
                    if (curMax > maxSum)
                        maxSum = curMax;
                } 
            }
            return maxSum;
        }
        public int maxSubArray(int[] arr) {
            if (arr == null)
                return -1;
            int max = Integer.MIN_VALUE;
            int curSum = 0;
            for (int i = 0; i < arr.length; i++) {
                curSum += arr[i];
                if (curSum > max)
                    max = curSum;
                if (curSum < 0)
                    curSum = 0;
            }
            return max;
        }
    }
    View Code
  • 相关阅读:
    delphi7在windows server 2003企业版上不能打开项目的选项(Options)窗口的解决方法
    简单的两个字“谢谢”,会让我坚持我的写作,我也要谢谢你们
    F41GUT 安装Windows server 2003系统后无法安装显卡驱动的解决办法
    远程桌面无法登录windows server 2003服务器
    F41GUT 安装Windows server 2003系统后无法安装显卡驱动的解决办法
    MS SQL Server 2000版在windows server 2003企业版系统上运行时造成数据库suspect的解决方法
    delphi7在windows server 2003企业版上不能打开项目的选项(Options)窗口的解决方法
    远程桌面无法登录windows server 2003服务器
    MS SQL Server 2000版在windows server 2003企业版系统上运行时造成数据库suspect的解决方法
    关于ajax 和josn
  • 原文地址:https://www.cnblogs.com/fisherinbox/p/5568440.html
Copyright © 2011-2022 走看看