zoukankan      html  css  js  c++  java
  • 第二章

    第二章 面试需要的基础知识

        /**
         * 面试题3(一) 找到数组中任意一个相同的数字
         * 思路:
         * 以整数的值为散列值,通过交换将该值放到相应的位置上
         * 总结:
         * 小型正整数的值可以直接作为散列值(hashcode),存放到相应的位置,以O(n)的时间复杂度实现排序
         * @param arr
         * @return
         */
        public int findSameNumber(int[] arr) {
            //非法输入
            if (arr == null || arr.length <= 1) return -1;
            //下面假设输入中符合题目要求
            for (int i = 0; i < arr.length; i++) {
                while (arr[i] != i) {
                    if (arr[arr[i]] != arr[i]) //不相等则交换
                        exch(arr, i, arr[i]);
                    else return arr[i]; //相等,则找到了相同的数字
                }
            }
            return -1;
    }
    
        private void exch(int[] arr, int a, int b) {
            int temp = arr[a];
            arr[a] = arr[b];
            arr[b] = temp;
        }
    
        public static void main(String[] args) {
            int[] ints = {2, 3, 1, 0, 2, 5, 3};
            System.out.println(new Solution().findSameNumber(ints));
        }
        /**
         * 面试题4 在二维数组中查找是否包含数组
         * 思路:不断地将问题的范围缩小
         * 总结:找规律,找出可以用循环或者递归的方法解决
         *
         * @param arr
         * @param num
         * @return
         */
        public boolean findNumber(int[][] arr, int num) {
            //非法输入
            if (arr == null) {
                return false;
            }
            for (int i = 0; i < arr.length; i++) {
                if (arr[i] == null) return false;
            }
            //从右上角开始
            int length = arr.length;
            int row = 0;
            int col = length - 1;
            while (row < length && col >= 0) {
                if (arr[row][col] < num) row++;
                else if (arr[row][col] > num) col--;
                else return true;
            }
            return false;
        }
        /**
         * 面试题5  将空格替换为%02
         * 解法:从后到前替换,将字符一次移动到位,避免了数组的频繁移动
         * 注意:位运算优先级低于加减
         * @param str
         */
        public String replaceBlank(String str) {
            //非法输入
            int length = str.length();
            if (str == null || length == 0) return null;
            //转化成数组
            char[] chars = str.toCharArray();
            int count = 0; //字符串中空格的数量
            for (int i = 0; i < length; i++) {
                if (chars[i] == ' ') count++;
            }
            char[] tempChars = new char[length + (count << 1)];
            int p = length - 1; //两个指针
            int q = tempChars.length - 1;
            while (p >= 0) {
                if (chars[p] != ' ') {
                    tempChars[q--] = chars[p];
                } else {
                    tempChars[q--] = '0';
                    tempChars[q--] = '2';
                    tempChars[q--] = '%';
                }
                p--;
            }
            return String.valueOf(tempChars);
        }
    
        public static void main(String[] args) {
            Solution solution = new Solution();
            String str = "we are world.";
            System.out.println(solution.replaceBlank(str));
        }
        /**
         * 面试题6:反向打印链表
         * 解法一:用递归的逆序遍历的方式
         * 解法二:顺序访问链表,将需要打印的值存入栈中
         *
         * @param node
         */
        public void printReverse(Node node) {
            //采用解法一
            //非法输入
            if (node == null) return;
            printReverse(node.next);
            System.out.println(node.val);
        }
    
        public void printReverseUseStack(Node node) {
            //采用解法二
            if (node == null) return;
            Stack<Integer> s = new Stack<>(); //使用栈
            for (Node i = node; i != null; i = i.next) { //顺序遍历链表
                s.push(i.val);
            }
            while (!s.empty()) { //出栈
                System.out.println(s.pop());
            }
        }
        /**
         * 面试题7
         * 根据前序遍历和中序遍历的结果数组重建二叉树
         * 解法:根据不同遍历的特点 找到根结点,然后再递归地处理子树
         *
         * @param pre
         * @param in
         * @return
         */
        public TreeNode construct(int[] pre, int[] in) {
            //非法输入
            if (pre == null || in == null || pre.length == 0 || in.length == 0)
                return null;
            try {
                return constructCore(pre, in, 0, pre.length - 1, 0, in.length - 1);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * @param pre
         * @param in
         * @param pp1      前序数组的范围指针
         * @param pp2
         * @param ip1      中序数组的范围指针
         * @param ip2
         * @return
         */
        private TreeNode constructCore(int[] pre, int[] in, int pp1, int pp2, int ip1, int ip2) throws Exception {
            if (pp1 > pp2 || ip1 > ip2) return null;//无子树,返回null
            TreeNode node = new TreeNode(); //根节点
            node.val = pre[pp1];
            int nodeIndexInInOder = -1; //根结点在中序遍历中的索引
            for (int i = ip1; i <= ip2; i++) { //从中序遍历中寻找根结点
                if (in[i] == node.val) {
                    nodeIndexInInOder = i;
                    break;
                }
            }
            if (nodeIndexInInOder == -1) throw new Exception("Invalide Input");//在中序中没有找到
            node.left = constructCore(pre, in, pp1 + 1, pp1 + (nodeIndexInInOder - ip1), ip1, nodeIndexInInOder - 1);
            node.right = constructCore(pre, in, pp1 + 1 + (nodeIndexInInOder - ip1), pp2, nodeIndexInInOder + 1, ip2);
            return node;
        }
        /**
         * 面试题8 二叉树的下一个结点
         * 分析:有右子树的情况下,下一个节点是右子树的最左侧(最小)结点;
         * 无右子树的情况下,下一个结点是沿着父结点向上查找,第一个遇到的左链接父结点。如果查找到父节点都没有找到下一结点则无下一节点。
         */
        public BTreeNode findNextNode(BTreeNode node) {
            //非法输入处理
            if (node == null) return null;
            //有右子树的情况
            if (node.right != null)
                return min(node.right); //取最小的结点
            //没有右子树的情况
            while (node.parent != null) { //有父结点,沿着父结点向上走
                if (node.parent.left == node)
                    return node.parent;
                node = node.parent;
            }
            return null; //没有找到父节点
        }
    
        private BTreeNode min(BTreeNode node) {
            if (node.left == null) return node;
            return min(node.left);
        }
        /**
         * 面试题9:用两个栈实现队列
         * 分析:一个栈用来入,一个栈用来出
         */
        class queueStack {
            //用两个栈来实现
            //为了方便使用了Integer
            private Stack<Integer> sIn;
            private Stack<Integer> sOut;
    
            public queueStack() {
                sIn = new Stack<>();
                sOut = new Stack<>();
            }
    
            //实现尾部添加
            public void appendTail(Integer val) {
                sIn.push(val);
            }
    
            //实现头部删除
            public Integer deleteHead() {
                if (sOut.empty()) { //出栈是空的情况
                    while (!sIn.empty())
                        sOut.push(sIn.pop());
                }
                if (sOut.empty()) return null; //出栈依旧是空
                return sOut.pop();
            }
        }
        /**
         * 面试题9:用两个队列实现栈操作
         * 分析:交替地从一个队列复制到另外一个队列,复制过去时留下最后添加的元素用于弹出
         */
        class StackQueue {
            private LinkedQueue<Integer> que1;
            private LinkedQueue<Integer> que2;
            private boolean isQue1 = true;//true表示当前使用Que1
    
            //入栈
            public void push(Integer val) {
                if (isQue1)
                    que1.enqueue(val);
                else
                    que2.enqueue(val);
            }
            //出
            public Integer pop() {
                if (isQue1) {
                    if (que1.isEmpty()) return null;
                    while (que1.count != 1)
                        que2.enqueue(que1.dequeue());
                    isQue1 = false;
                    return que1.dequeue();
                } else {
                    if (que2.isEmpty()) return null;
                    while (que2.count != 1)
                        que1.enqueue(que2.dequeue());
                    isQue1 = true;
                    return que2.dequeue();
                }
            }
        }
        /**
         * 面试题10 斐波那契数
         * 分析:一般来说,循环算法占用的空间比递归的方法更加少
         * 普通青蛙
         * 分析:可以变为斐波那契数列问题
         * 变态青蛙
         * 分析:通过数学的分析,可以得到n台阶跳法的函数表达式
         */
        public int fibonacci(int n) {
            if (n < 0) return -1;//-1表示错误输入
            if (n == 0) return 0;
            if (n == 1) return 1;
            //用数组保存前两个计算结果
            int[] result = {0, 1};
            int num = 2;
            while (num != n) {
                int currentResult = result[0] + result[1];
                result[0] = result[1];
                result[1] = currentResult;
            }
            return result[0] + result[1];
        }
    /**面试题12 回溯法从二维数组中寻找字符串
     * @ClassName Solution
     * @Author wangyudi
     * @Date 2019/7/14 16:13
     * @Version 1.0
     * @Description
     */
    
    class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;
    
        public TreeNode(int val) {
            this.val = val;
        }
    
    }
    
    public class Solution {
        public boolean findPath(char[][] matrix, String str) {
            //deal with invalid input
            if (str == null || matrix == null) return false;
            //下面默认输入是一个有效的矩形二维数组
            int rows = matrix.length;
            int cols = matrix[0].length;
            boolean[] visited = new boolean[rows * cols]; //存放该数组对应元素是否已被访问信息
            char[] charStr = str.toCharArray();
            int pathIndex = 0;//记录需要寻找字符串的始端
            boolean result = false;
            for (int row = 0; row < rows; row++) {
                for (int col = 0; col < cols; col++) {
                    if (findPathCore(matrix, charStr, pathIndex, row, col, rows, cols, visited)) {
                        result = true;
                        break;
                    }
                }
            }
            return result;
        }
    
        /**
         * 查找路径的迭代函数
         * @param matrix
         * @param charStr
         * @param pathIndex
         * @param row
         * @param col
         * @param rows
         * @param cols
         * @param visited
         * @return
         */
        private boolean findPathCore(char[][] matrix, char[] charStr, int pathIndex, int row, int col, int rows, int cols, boolean[] visited) {
            //1 判断是否已经找到字符串
            if (pathIndex == charStr.length) return true;
            boolean result = false;
            //2 判断二维数组的字符是否对应字符串中的字符
            if (row >= 0 && row < rows &&
                    col >= 0 && col < cols &&
                    charStr[pathIndex] == matrix[row][col] &&
                    visited[row * cols + col] == false) {
                //对应
                //寻找下一个子字符串,并修改访问信息
                pathIndex++;
                visited[row * cols + col] = true;
                //从该元素所在位置的四个方向中寻找下一个字符串
                result = findPathCore(matrix, charStr, pathIndex, row + 1, col, rows, cols, visited) ||
                        findPathCore(matrix, charStr, pathIndex, row - 1, col, rows, cols, visited) ||
                        findPathCore(matrix, charStr, pathIndex, row, col + 1, rows, cols, visited) ||
                        findPathCore(matrix, charStr, pathIndex, row, col - 1, rows, cols, visited);
                //四个方向都没有找到相应的子字符串,则说明该位置不正确
                //修改访问信息,然后回归到上一个位置
                if (!result) {
                    visited[row * cols + col] = false;
                }
            }
            return result;
        }
    
    // Test input and output
        public static void main(String[] args) {
            Solution solution = new Solution();
            char[] char1 = {'a', 'b', 't', 'g'};
            char[] char2 = {'c', 'f', 'c', 's'};
            char[] char3 = {'j', 'd', 'e', 'h'};
            char[] char4 = {'a', 'b', 't', 'g'};
            char[][] chars = {char1, char2, char3, char4};
            String str = "bfcc";
            System.out.println(solution.findPath(chars, str));
        }
    
    }
    //面试题14 剪绳子 
    public class Solution {
       /**
         * 求如何剪绳子,绳段的乘积最大
         * 法一:动态规划
         *
         * @param length 绳的长度
         * @return
         */
        public int maxProduct1(int length) {
            //deal with invalid input
            if (length <= 0) return -1;
            if (length == 1) return 1;
            if (length == 2) return 2;
            if (length == 3) return 3;
            //存储不同长度下绳段乘积最大值
            int[] products = new int[length + 1];
            products[1] = 1;
            products[2] = 2;
            products[3] = 3;
            products[4] = 4;
            int max = 0;//存放不同长度下绳段乘积最大值
            int temp = 0;
            for (int i = 5; i <= length; i++) {
                for (int j = 1; j <= i >> 1; j++) {
                    temp = products[j] * products[i - j];
                    if (max < temp) {
                        max = temp;
                    }
                }
                products[i] = max;
            }
            return products[length];
        }
    
        /**
         * 贪婪算法
         * 思路:长度大于5时,尽可能地剪3;小于5不剪
         */
        public int maxProduct2(int length) {
            if (length <= 0) return -1;
            if (length < 5) return length;
            int countOf3 = 0;
            while (length >= 5) {
                length -= 3;
                countOf3++;
            }
            return (int) Math.pow(3, countOf3) * length;
        }
    
        public static void main(String[] args) {
            Solution solution = new Solution();
            System.out.println(solution.maxProduct1(15));
            System.out.println(solution.maxProduct2(15));
        }
    }
    /*
    面试题15 二进制中1的个数
    自身 & (自身-1) 可以使最低位的1置为0
    
     Java中int类型的负数的二进制用补码的形式存储
    
    下面算法不适合负数
    */ 
    public int numberOf1(int num) {
            int count = 0;
            while (num != 0) {
                num = num & (num - 1);
                count++;
            }
            return count;
        }
    
        public static void main(String[] args) {
            Solution solution = new Solution();
            int a = 60;
            System.out.println(solution.numberOf1(a));
        }
      /**面试题17
         * 以O(1)删除列表中的一个指定结点
         * 情况分类:不是尾节点, 是尾结点有前结点, 是尾结点无前节点
         *
         * @param first
         * @param toBeDeleteNode
         */
        public void deleteNode(Node first, Node toBeDeleteNode) throws Exception {
            //invalid input
            if (first == null || toBeDeleteNode == null) return;
            //不是尾
            if (toBeDeleteNode.next != null) {
                toBeDeleteNode.val = toBeDeleteNode.next.val;
                toBeDeleteNode.next = toBeDeleteNode.next.next;
            } else {
                Node i = null;
                for (i = first; i.next != toBeDeleteNode; i = i.next) {
                }
                //有前
                if (i != null) {
                    i.next = null;
                }
                //无前
                else first = null;
            }
        }
     /**
         * 面试题18:删除连续重复的节点
         *
         * @param first
         */
        public Node deleteSameNode(Node first) {
            //invalid input
            if (first == null) return null;
            Node preNode = null; //reserve preNode
            Node node = first;
            while (node != null) {
                if (node.next != null && node.val == node.next.val)
                    if (preNode == null) {
                        node = deleteSameNodeInList(node);
                        first = node;  //值类型参数不能直接改变
                    } else {
                        node = deleteSameNodeInList(node);
                        preNode.next = node;
                    }
                else {
                    preNode = node;
                    node = node.next;
                }
            }
            return first;
        }
    
        private Node deleteSameNodeInList(Node node) {
            Node result = node;
            while (result.next != null && result.val == result.next.val) {
                result = result.next;
            }
            return result.next;
        }
        /**
         * 面试题19 匹配字符串
         *
         * @param str
         * @param strFormat
         * @return
         */
        public boolean match(char[] str, char[] strFormat) {
            if (str == null || strFormat == null) return false;
            return matchCore(str, 0, strFormat, 0);
        }
    
        private boolean matchCore(char[] str, int strIndex, char[] strFormat, int strFormatIndex) {
            if (strIndex == str.length && strFormatIndex == strFormat.length) {
                return true;
            }
            if (strIndex >= str.length || strFormatIndex >= strFormat.length) return false;
            //"."
            if (strFormat[strFormatIndex] == '.') return matchCore(str, strIndex + 1, strFormat, strFormatIndex + 1);
                //"a"
            else if (strFormatIndex == strFormat.length - 1 || strFormat[strFormatIndex + 1] != '*') {
                if (str[strIndex] != strFormat[strFormatIndex]) return false;
                else return matchCore(str, strIndex + 1, strFormat, strFormatIndex + 1);
            }
            //"a*"
            else {
                if (str[strIndex] == strFormat[strFormatIndex] || strFormat[strFormatIndex] == '.')
                    return matchCore(str, strIndex, strFormat, strFormatIndex + 2) ||
                            matchCore(str, strIndex + 1, strFormat, strFormatIndex + 2) ||
                            matchCore(str, strIndex + 1, strFormat, strFormatIndex);
                else return matchCore(str, strIndex, strFormat, strFormatIndex + 2);
            }
        }
        /**
         * 面试题21:将奇数调整到偶数的前面
         * 思路:快速排序法partition方法
         * 可以将arr[--hi]&0x01)==0判断方法提取为抽象方法(模板方法)
         *
         * @param arr
         */
        public void reorderOddEven(int[] arr) {
            if (arr == null || arr.length == 0) return;
            int lo = -1;
            int hi = arr.length;
            while (hi > lo) {
                while ((arr[++lo] & 0x01) != 0) if (lo == arr.length - 1) break;
                while ((arr[--hi] & 0x01) == 0) if (hi == 0) break;
                if (hi > lo) {
                    int temp = arr[lo];
                    arr[lo] = arr[hi];
                    arr[hi] = temp;
                }
            }
        }
        /**
         * 面试题22:查找链表中倒数第 k 个结点
         *
         * @param head
         * @param k
         * @return
         */
        public ListNode FindKthToTail(ListNode head, int k) {
            if (head == null || k <= 0) return null; //invalid input
            ListNode lo = head;
            ListNode hi = head;
            for (int i = 0; i < k - 1; i++) {
                if (hi.next == null) return null; // doesn't have Penultimate k_Th
                hi = hi.next;
            }
            while (hi.next != null) {
                lo = lo.next;
                hi = hi.next;
            }
            return lo;
        }
      /**
         * 面试题23:找到链表中环的入口结点
         * 思路:
         * 先确定有环
         * 确定换中结点个数
         * 确定环的入口结点
         *
         * @param head
         * @return
         */
        public ListNode EntryNodeOfTheLoop(ListNode head) {
            if (head == null) return null;
            ListNode p1 = head;
            ListNode p2 = head;
            ListNode meetNode = null;
            do {
                if (p1.next == null) break;
                else p1 = p1.next;
                if (p2.next == null || p2.next.next == null) break;
                else p2 = p2.next.next;
                if (p1 == p2) meetNode = p1;
            } while (p1 != p2);
            // no loop
            if (meetNode == null) return null;
            // have loop; get the number of node in the loop
            p1 = meetNode;
            p2 = meetNode.next;
            int count = 1;
            while (p1 != p2) {
                p2 = p2.next;
                ++count;
            }
            p1 = head;
            p2 = head;
            for (int i = 0; i < count; i++) {
                p2 = p2.next;
            }
            while (p1 != p2) {
                p1 = p1.next;
                p2 = p2.next;
            }
            return p1;
        }
        /**
         * 面试题24:将链表翻转
         * 思路:从最后一个结点开始翻转
         *
         * @param head
         * @return
         */
        public ListNode ReverseList(ListNode head) {
            if (head == null) return null;
            if (head.next == null) return head;
            ListNode prevNode = null;
            ListNode curNode = head;
            ListNode storNode = curNode.next;
            while (curNode != null) {
                curNode.next = prevNode;
                prevNode = curNode;
                curNode = storNode;
                storNode = curNode == null ? null : curNode.next;
            }
            return prevNode;
        }
       /**
         * 面试题25:合并两个链表
         * 思路:类似归并排序中的归并算法
         *
         * @param list1
         * @param list2
         * @return
         */
        public ListNode Merge(ListNode list1, ListNode list2) {
            if (list1 == null && list1 == null) return null;
            if (list1 == null) return list2;
            if (list2 == null) return list1;
            ListNode result = null;
            ListNode min = null;
            ListNode cur = null;
            while (list1 != null && list2 != null) {
                if (list1.val < list2.val) {
                    min = list1;
                    list1 = list1.next;
                } else {
                    min = list2;
                    list2 = list2.next;
                }
                if (result == null) {
                    result = min;
                    cur = min;
                } else {
                    cur.next = min;
                    cur = min;
                }
            }
            if (list1 != null) cur.next = list1;
            if (list2 != null) cur.next = list2;
            return result;
        }
    
        /**
         * 面试题25:合并两个链表
         * 思路:递归的方法
         * @param list1
         * @param list2
         * @return
         */
        public ListNode merge(ListNode list1, ListNode list2) {
            if(list1==null&& list2==null) return null;
            if (list1 == null) return list2;
            if (list2 == null) return list1;
            ListNode head = null;
            if(list1.val<list2.val){            
                head = list1;            
                head.next = merge(list1.next,list2);    
           }        
            else {    
            head=list2;           
            head.next = merge(list1,list2.next);        
           }        
    
            return head;   
     }    
                                                            
  • 相关阅读:
    Springboot 基于的SAP项目环境配置
    Impala 技术点梳理
    Elasticsearch 使用技巧笔记
    ELK 6.x 部署
    eclipse安装lombok
    IIS服务器管理学习
    Springboot依赖注入笔记
    Eclipse解决乱码问题
    Springboot依赖注入 Service类中使用静态变量
    javascript 操作符类型隐性转换
  • 原文地址:https://www.cnblogs.com/youzoulalala/p/11256648.html
Copyright © 2011-2022 走看看