zoukankan      html  css  js  c++  java
  • 算法:剑指offer

    牛客网 剑指offer:

    https://www.nowcoder.com/ta/coding-interviews

    1.二维数组中的查找

    左到右递增,上到下递增;

    解法:从左下遍历。大于则x--,小于则y++。

    public class Solution {
        public boolean Find(int target, int [][] array) {
            int m = array.length;
            int n = array[0].length;
            
            int x = m - 1;
            int y = 0;
            while (x >= 0 && y <= n - 1) {
                if (array[x][y] > target) x--;
                else if (array[x][y] < target) y++;
                else return true;
            }
            return false;
            
        }
    }
    View Code

    2.替换空格

    直接遍历:

    public class Solution {
        public String replaceSpace(StringBuffer str) {
            String rst = "";
            for (int i = 0; i < str.length(); i++) {
                if (str.charAt(i) == ' ') rst += "%20";
                else rst += str.charAt(i);
            }
            return rst;
        }
    }
    View Code

    要求原字符串上修改:

    从前往后替换,后面字符要不断移动,效率低;从后往前,只需要移动一次,效率高。

    两个指针一次从newLen和Len从后向前移动;

    public class Solution {
        public String replaceSpace(StringBuffer str) {
            int spaceNum = 0;
            for (int i = 0; i < str.length(); i++) {
                if (str.charAt(i) == ' ') spaceNum++;
            }
            
            int newLen = str.length() + spaceNum * 2;
            int i = str.length() - 1;
            int j = newLen - 1;
            str.setLength(newLen);
            for (; i >= 0; i--) {
                if (str.charAt(i) == ' ') {
                    str.setCharAt(j--,'0');
                    str.setCharAt(j--,'2');
                    str.setCharAt(j--,'%');
                } else {
                    str.setCharAt(j--,str.charAt(i));
                }
            }
            return str.toString();
        }
    }
    View Code

    3.从尾到头打印链表

    利用ArrayList特性,头插法;据说时间复杂度高?

    import java.util.ArrayList;
    public class Solution {
        public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
            
            ArrayList<Integer> list = new ArrayList<Integer>();
            while (listNode != null) {
                list.add(0,listNode.val);
                listNode = listNode.next;
            }
            return list;
        }
    }
    View Code

    递归

    import java.util.ArrayList;
    public class Solution {
        
        ArrayList<Integer> list = new ArrayList<Integer>();
        public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
            
            if (listNode != null) {
                this.printListFromTailToHead(listNode.next);
                list.add(listNode.val);
            }
            
            return list;
            
        }
    }
    View Code

    (注意:如果用for循环stack.size要赋值保存,不然每次pop后size会变化就出错了)

    存节点:

    import java.util.Stack;
    import java.util.ArrayList;
    public class Solution {
        
        
        public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
            
            ArrayList<Integer> list = new ArrayList<Integer>();
            Stack<ListNode> stack = new Stack<ListNode>();
            while (listNode != null) {
                stack.push(listNode);
                listNode = listNode.next;
            }
            
            while (!stack.empty()) {
                list.add(stack.pop().val);
            }
            
            return list;
            
        }
    }
    View Code

    存数值:

    import java.util.Stack;
    import java.util.ArrayList;
    public class Solution {
        
        public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
            
            ArrayList<Integer> list = new ArrayList<Integer>();
            Stack<Integer> stack = new Stack<Integer>();
            while (listNode != null) {
                stack.push(listNode.val);
                listNode = listNode.next;
            }
            
            while (!stack.empty()) {
                list.add(stack.pop());
            }
            
            return list;
            
        }
    }
    View Code

    4.重建二叉树

    根据前序和中序重建二叉树;递归;preStart, inStart, inEnd;

    5.用两个栈实现队列

    实现push和pop操作;

    栈A用来作入队列;

    栈B用来出队列,当栈B为空时,栈A全部出栈到栈B,栈B再出栈(即出队列);

    import java.util.Stack;
    
    public class Solution {
        Stack<Integer> stack1 = new Stack<Integer>();
        Stack<Integer> stack2 = new Stack<Integer>();
        
        public void push(int node) {
            stack1.push(node);
        }
        
        public int pop() {
            if (stack2.empty()){
                while (!stack1.empty()) {
                    stack2.push(stack1.pop());
                }
            }
            return stack2.pop();
        }
    }
    View Code

    6.旋转数组的最小数字

    [5,6,7,1,2,3,4] 左边递增右边递减;

    1.首先判断lo和hi,如果lo<hi则返回; 2.判断mid和lo,>=则lo=mid+1;3.hi=mid;

    public class Solution {
    
        public int findMin(int[] nums) {
    
            int lo = 0, hi = nums.length - 1;
            while (lo < hi) {
                if (nums[lo] < nums[hi]) return nums[lo];
                int mid = lo + (hi - lo) / 2;
                if (nums[mid] >= nums[lo]) lo = mid + 1;
                else hi = mid;
            }
            return nums[lo];
    
        }
    }
    View Code

    7.斐波那契数列

    迭代

    public class Solution {
        public int Fibonacci(int n) {
            if (n == 0) return 0;
            int a = 1;
            int b = 1;
            for (int i = 1; i < n; i++) {
                int tmp = a + b;
                a = b;
                b = tmp;
            }
            return a;
            
        }
    }
    View Code

    8.跳台阶

    递归

    public class Solution {
        public int JumpFloor(int n) {
            if (n == 1) return 1;
            if (n == 2) return 2;
            return JumpFloor(n - 1) + JumpFloor(n - 2);
        }
    }
    View Code

    迭代(就是斐波那契)

    public class Solution {
        public int JumpFloor(int n) {
            int a = 1;
            int b = 2;
            for (int i = 1; i < n; i++) {
                int tmp = a + b;
                a = b;
                b = tmp;
            }
            return a;
        }
    }
    View Code

    9.变态跳台阶

    f(n)=f(n-1)+f(n-2)+...+f(1)

    f(n)=2*f(n-1);

    public class Solution {
        public int JumpFloorII(int n) {
            int[] a = new int[n];
            a[0] = 1;
            int sum = 1;
            for (int i = 1; i < n; i++) {
                a[i] = sum + 1;
                sum += a[i];
            }
            return a[n - 1];
        }
    }
    View Code
    public class Solution {
        public int JumpFloorII(int n) {
            return  1 << --n;
        }
    }
    View Code

    10.矩形覆盖

    public class Solution {
        public int RectCover(int n) {
            if (n == 0 || n == 1 || n == 2) return n;
            return RectCover(n - 1) + RectCover(n - 2);
        }
    }
    View Code

    11.二进制中1的个数

    位运算;n&(n-1) 把最右的1置零;

    public class Solution {
        public int NumberOf1(int n) {
            int cnt = 0;
            while (n != 0) {
                n = n & (n - 1);
                cnt++;
            }
            return cnt;
        }
    }
    View Code

    12.数值的整数次方

    举例:10^1101 = 10^0001*10^0100*10^1000。

    通过&1和>>1来逐位读取e指数,为1时将该位代表的乘数累乘到最终结果。
    public class Solution {
    
        public double Power(double base, int n) {
            double res = 1;
            int e;
        
            if (n == 0) return 1;
            else if (n < 0) e = -n;
            else e = n;
        
            while(e!=0){
                if( (e & 1) ==1 ) res *= base;
                base *= base;// 翻倍
                e >>= 1;// 右移一位
            }
            return n >= 0 ? res : (1 / res);   
        
        }
    }
    View Code

    13.调整数组顺序使奇数位于偶数前面

    (1,2,4,3,6,5,7) -> (1,3,5,7, 2,4,6) 

    要求相对位置不变,所以不能用双指针了。申请一个新数组保存偶数;

    public class Solution {
        public void reOrderArray(int [] array) {
            int[] tmp = new int[array.length];
            int m = 0;
            int n = 0;
            for (int i = 0; i < array.length; i++) {
                if (array[i] % 2 != 0) array[m++] = array[i]; //奇数放到对应位置;
                else tmp[n++] = array[i]; //偶数放在tmp数组保存;
            }
            for (int i = 0; i < n; i++) {
                array[m++] = tmp[i];
            }
            
    
        }
    }
    View Code

    用插入排序思想的话时间复杂度是O(n^2);

    14.链表中倒数第k个结点

    双指针,滑动窗口;测试用例要考虑下k>length的情况;

    public class Solution {
        public ListNode FindKthToTail(ListNode head,int k) {
            if (head == null || k <= 0) return null;
            ListNode dummy = new ListNode(0);
            dummy.next = head;
            head = dummy;
            ListNode right = head;
            for (int i = 0; i < k; i++) {
                right = right.next;
                if (right == null) return null;
            }
            
            while (right.next != null) {
                right = right.next;
                head = head.next;
            }
            
            return head.next;
        }
    }
    View Code

    15.反转链表

    (四步:1.tmp保存下一节点; 2.head指向prev; 3.head移动; 4.prev移动)

    public class Solution {
        public ListNode ReverseList(ListNode head) {
            ListNode pre = null;
            while (head != null) {
                ListNode tmp = head.next;
                head.next = pre;
                pre = head;
                head = tmp;
            }
            return pre;
        }
    }
    View Code

    16.合并两个排序的链表

    递归:

    public ListNode Merge(ListNode list1,ListNode list2) {
           if(list1 == null){
               return list2;
           }
           if(list2 == null){
               return list1;
           }
           if(list1.val <= list2.val){
               list1.next = Merge(list1.next, list2);
               return list1;
           }else{
               list2.next = Merge(list1, list2.next);
               return list2;
           }       
       }
    View Code

    非递归:三个指针head、list1、list2;注意第一个节点的处理,dummy固定,head移动;

    public class Solution {
        public ListNode Merge(ListNode list1,ListNode list2) {
            ListNode dummy = new ListNode(0);
            ListNode head = dummy;
            while (list1 != null && list2 != null) {
                if (list1.val <= list2.val) {
                    head.next = list1;
                    list1 = list1.next;
                } else {
                    head.next = list2;
                    list2 = list2.next;
                }
                head = head.next;
            }
            
            if (list1 != null) head.next = list1;
            if (list2 != null) head.next = list2;
            
            return dummy.next;
            
        }
    }
    View Code

    17.树的子结构

    ps:这题和之前做过的题不一样。。之前要求子树到叶子完全重合才是subtree,但是本题只要是属于的关系就是true;

    例如{8,8,7,9,2,#,#,#,#,4,7},{8,9,2};在原题就是false的,但本题是true。。

    todo

    18.二叉树的镜像

    递归;栈;

    import java.util.Stack;
    public class Solution {
        
    
        public void Mirror(TreeNode root) {
            /*递归解法;
            if (root == null) return;
    
            Mirror(root.right);
            Mirror(root.left);
            
            TreeNode tmp = root.right;
            root.right = root.left;
            root.left = tmp;
            */
            
            /*非递归解法*/
            if (root == null) return;
            Stack<TreeNode> stack = new Stack<TreeNode>();
            stack.push(root);
            while (!stack.empty()) {
                TreeNode cur = stack.pop();
                if (cur.left != null)  stack.push(cur.left);
                if (cur.right != null) stack.push(cur.right);
                TreeNode tmp = cur.right;
                cur.right = cur.left;
                cur.left = tmp;
            }
        }
    }
    View Code

    19.顺时针打印矩阵

    螺旋矩阵;用cnt做每轮螺旋的“外壁”,while(cnt*2<row和col),并且循环内需要判断cnt*2+1=row/col的情况,即只剩一行/列时只执行两个循环;

    import java.util.ArrayList;
    public class Solution {
        public ArrayList<Integer> printMatrix(int [][] matrix) {
            ArrayList<Integer> rst = new ArrayList<>();
            if (matrix.length == 0 || matrix[0].length == 0) return rst;
            int row = matrix.length;
            int col = matrix[0].length;
            int cnt = 0;
            while (cnt * 2 < row && cnt * 2 < col) {
                for (int i = cnt; i < col - cnt; i++) {
                    rst.add(matrix[cnt][i]);
                }
                for (int i = cnt + 1; i < row - cnt; i++) {
                    rst.add(matrix[i][col - cnt - 1]);
                }
                
                if (row - cnt * 2 == 1 || col - cnt * 2 == 1) return rst;
                
                for (int i = col - cnt - 2; i >= cnt; i--) {
                    rst.add(matrix[row - cnt - 1][i]);
                }
                for (int i = row - cnt - 2; i > cnt; i--) {
                    rst.add(matrix[i][cnt]);
                }
                   cnt++;
            }
            return rst;
        }
    }
    View Code

    20.包含min函数的栈

    使用两个栈stack和minStack,对于push操作,需要和minStack的peek()比较大小;(注:stack和minStack大小始终相同)

    import java.util.Stack;
    
    public class Solution {
    
        Stack<Integer> stack = new Stack<>();
        Stack<Integer> minStack = new Stack<>();
        
        public void push(int node) {
            stack.push(node);
            if (!minStack.empty()) {
                if (node > minStack.peek()) {
                    minStack.push(minStack.peek());
                    return;
                }
            } 
            minStack.push(node);
        }
        
        public void pop() {
            stack.pop();
            minStack.pop();
        }
        
        public int top() {
            return stack.peek();
        }
        
        public int min() {
            return minStack.peek();
        }
    }
    View Code

    21.栈的压入、弹出序列:

    (辅助栈;每次辅助栈入栈,比较辅助栈peek和pop栈当前指针j的值,相同则pop辅助栈,j指针移动;最后判断辅助栈stack是否为空)

    (注:while循环内必须加判断stack.empty)

    【思路】借用一个辅助的栈,遍历压栈顺序,先将第一个放入栈中,这里是1,然后判断栈顶元素是不是出栈顺序的第一个元素,这里是4,很显然1≠4,所以我们继续压栈,直到相等以后开始出栈,出栈一个元素,则将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。

    举例:

    入栈1,2,3,4,5

    出栈4,5,3,2,1

    首先1入辅助栈,此时栈顶1≠4,继续入栈2;此时栈顶2≠4,继续入栈3;此时栈顶3≠4,继续入栈4;

    此时栈顶4=4,出栈4,弹出序列向后一位,此时为5,辅助栈里面是1,2,3

    此时栈顶3≠5,继续入栈5

    此时栈顶5=5,出栈5,弹出序列向后一位,此时为3,,辅助栈里面是1,2,3

    ….

    依次执行,最后辅助栈为空。如果不为空说明弹出序列不是该栈的弹出顺序。
    import java.util.ArrayList;
    import java.util.Stack;
    
    public class Solution {
        public boolean IsPopOrder(int [] pushA,int [] popA) {
            if (pushA.length != popA.length) return false;
            if (pushA == null) return true;
            Stack<Integer> stack = new Stack<>();
            int j = 0;
            for (int i = 0; i < pushA.length; i++ ) {
                stack.push(pushA[i]);
                while ( !stack.empty() && stack.peek() == popA[j]) {
                    stack.pop();
                    j++;
                }
            }
            return stack.empty();
        }
    }
    View Code

    22.层序遍历

    队列Queue;size,for循环添加左右;

    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.Queue;
    /**
    public class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;
    
        public TreeNode(int val) {
            this.val = val;
    
        }
    
    }
    */
    public class Solution {
        public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
            ArrayList<Integer> rst = new ArrayList<>();
            if (root == null) return rst;
            Queue<TreeNode> queue = new LinkedList<TreeNode>();
            queue.offer(root);
            while (!queue.isEmpty()) {
                int size = queue.size();
                for (int i = 0; i < size; i++) {
                    TreeNode cur = queue.poll();
                    rst.add(cur.val);
                    if (cur.left != null) queue.offer(cur.left);
                    if (cur.right != null) queue.offer(cur.right);
                }
            }
            return rst;
        }
    }
    View Code

    23.二叉搜索树的后序遍历序列

    递归,后序遍历序列满足去掉最后一个root,剩下两部分前一部分小于root,后一部分大于root;

    (helper(a[], start, end) 首先index遍历到第一个不大于[end]的位置,然后从start开始遍历到index判断是否大于[end],大于则false;递归)

    BST的后序序列的合法序列是,对于一个序列S,最后一个元素是x (也就是根),如果去掉最后一个元素的序列为T,那么T满足:T可以分成两段,前一段(左子树)小于x,后一段(右子树)大于x,且这两段(子树)都是合法的后序序列。完美的递归定义 : ) 。

    /**
     * T: 二叉搜索树的后序遍历序列
     *
     * 题目描述
     * 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。
     * 如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
     *
     *
     */
    public class Solution {
        public boolean VerifySquenceOfBST(int [] sequence) {
            int n = sequence.length;
            if (n == 0) return false;
            if (n == 1) return true;
            return helper(sequence, 0, n - 1);
        }
        public boolean helper(int[] a, int start, int end) {
            if (start >= end) return true;
            int index = end - 1;
            while (index >= start && a[index] > a[end]) {
                index--;
            }
            for (int i = start; i < index; i++) {
                if (a[i] > a[end]) return false;
            }
            return helper(a, start, index) && helper(a, index + 1, end - 1);
        }
    }
    View Code

    24.二叉树和为某一值的路径

    回溯法;helper(root, tar, A<A<>>rst, A<>list);

    终止条件:root==null;  叶子节点:=tar则, list-add, rst-add, remove, return; 

    递归过程:add(), helper()左右递归,remove;

    import java.util.ArrayList;
    /**
    public class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;
    
        public TreeNode(int val) {
            this.val = val;
    
        }
    
    }
    */
    public class Solution {
        public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
            ArrayList<Integer> list = new ArrayList<>();
            ArrayList<ArrayList<Integer>> rst = new ArrayList<>();
            helper(root, target, rst, list);
            return rst;
        }
        public void helper(TreeNode root, int target, ArrayList<ArrayList<Integer>> rst, ArrayList<Integer> list) {
            if (root == null) return;
            if (root.left == null && root.right == null) {
                if (root.val == target) {
                    list.add(root.val);
                    rst.add(new ArrayList<Integer>(list));
                    list.remove(list.size() - 1);
                    return;
                }
            }
            list.add(root.val);
            helper(root.left, target - root.val, rst, list);
            helper(root.right, target - root.val, rst, list);
            list.remove(list.size() - 1);
            
        }
    }
    View Code

    25.复杂链表的复制

    /*
    public class RandomListNode {
        int label;
        RandomListNode next = null;
        RandomListNode random = null;
    
        RandomListNode(int label) {
            this.label = label;
        }
    }
    */
    public class Solution {
        public RandomListNode Clone(RandomListNode pHead){
            if (pHead == null) return null; 
            //复制
            RandomListNode pCur = pHead;
            while (pCur != null) {
                RandomListNode node = new RandomListNode(pCur.label);
                node.next = pCur.next;
                pCur.next = node;    //添加复制节点node;
                pCur = node.next; //移动pCur;
            }
            
            //random节点
            pCur = pHead;
            while (pCur != null) {
                if (pCur.random != null) pCur.next.random = pCur.random.next;
                pCur = pCur.next.next;
            }
            
            //拆分
            
            RandomListNode newHead = pHead.next;
            RandomListNode newCur = newHead;
            pCur = pHead;
            while (pCur != null) {
                pCur.next = newCur.next;
                if(newCur.next != null)newCur.next = newCur.next.next;
                pCur = pCur.next;
                newCur = newCur.next;
            }
            return newHead;
            
        }
    }
    View Code

    26.二叉搜索树与双向链表

    //直接用中序遍历
    public class Solution {
        TreeNode head = null;
        TreeNode realHead = null;
        public TreeNode Convert(TreeNode pRootOfTree) {
            ConvertSub(pRootOfTree);
            return realHead;
        }
         
        private void ConvertSub(TreeNode pRootOfTree) {
            if(pRootOfTree==null) return;
            ConvertSub(pRootOfTree.left);
            if (head == null) {
                head = pRootOfTree;
                realHead = pRootOfTree;
            } else {
                head.right = pRootOfTree;
                pRootOfTree.left = head;
                head = pRootOfTree;
            }
            ConvertSub(pRootOfTree.right);
        }
    }
    View Code
    链接:https://www.nowcoder.com/questionTerminal/947f6eb80d944a84850b0538bf0ec3a5
    来源:牛客网
    
    解题思路:
    1.将左子树构造成双链表,并返回链表头节点。
    2.定位至左子树双链表最后一个节点。
    3.如果左子树链表不为空的话,将当前root追加到左子树链表。
    4.将右子树构造成双链表,并返回链表头节点。
    5.如果右子树链表不为空的话,将该链表追加到root节点之后。
    6.根据左子树链表是否为空确定返回的节点。
        public TreeNode Convert(TreeNode root) {
            if(root==null)
                return null;
            if(root.left==null&&root.right==null)
                return root;
            // 1.将左子树构造成双链表,并返回链表头节点
            TreeNode left = Convert(root.left);
            TreeNode p = left;
            // 2.定位至左子树双链表最后一个节点
            while(p!=null&&p.right!=null){
                p = p.right;
            }
            // 3.如果左子树链表不为空的话,将当前root追加到左子树链表
            if(left!=null){
                p.right = root;
                root.left = p;
            }
            // 4.将右子树构造成双链表,并返回链表头节点
            TreeNode right = Convert(root.right);
            // 5.如果右子树链表不为空的话,将该链表追加到root节点之后
            if(right!=null){
                right.left = root;
                root.right = right;
            }
            return left!=null?left:root;       
        }
    View Code

    27.字符串的排列

    按字典序返回字符串字符的所有排列;

    例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

    回溯法

    helper(char[] cs, int i, ArrayList list):

    如果i是最后一位,则找到一个解,不包含则添加;

    如果i不是最后一位,for循环i,swap+递归+复位swap;

    主函数:helper、Collections.sort(rst)排序;

    import java.util.*;
    
    public class Solution {
    
        public ArrayList Permutation(String str) {
            ArrayList res = new ArrayList();
            if (str != null && str.length() > 0) {
                helper(str.toCharArray(), 0, res);
                Collections.sort(res);
            }
            return res;
        }
    
        public void helper(char[] cs, int i, ArrayList list) {
            if(i == cs.length - 1) {
                String val = String.valueOf(cs);
                if (!list.contains(val))
                    list.add(val);
            } else {
                for(int j = i; j < cs.length; ++j) {
                    swap(cs, i, j);
                    helper(cs, i + 1, list);
                    swap(cs, i, j);
                }
            }
        }
        
        public void swap(char[] str, int i, int j) {
            if (i != j) {
                char t = str[i];
                str[i] = str[j];
                str[j] = t;
            }
        }
    }
    View Code

    28.数组中出现次数超过一半的数字

    使用count计数;为0则赋值,相同cnt+,不同cnt-;

    由于主数字不一定存在,所以需要再遍历一次验证;

    public class Solution {
        public int MoreThanHalfNum_Solution(int [] array) {
            
            int count = 1;
            int len = array.length;
            int rst = array[0];
            for (int i = 1; i < len; i++) {
                if (count == 0) {
                    rst = array[i];
                } 
                if (rst == array[i]) count++;
                else count--;
            }
            
            //验证;
            count = 0;
            for (int i = 0; i < len; i++) {
                if (array[i] == rst) count++;
            }
            
            if (count * 2 > len) return rst;
            return 0;
            
        }
    }
    View Code

    29.topK最小的k个数

    基于堆排序算法,构建最大堆。时间复杂度为O(nlogk)
    import java.util.ArrayList;
    public class Solution {
        public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
            ArrayList<Integer> list=new ArrayList<Integer>();
            //检查输入的特殊情况
            if(input==null || input.length<=0 || input.length<k){
                return list;
            }
            //构建最大堆
            for(int len=k/2-1; len>=0; len--){
                adjustMaxHeapSort(input,len,k-1);
            }
            //从第k个元素开始分别与最大堆的最大值做比较,如果比最大值小,则替换并调整堆。
            //最终堆里的就是最小的K个数。
            int tmp;
            for(int i=k; i<input.length; i++){
                if(input[i]<input[0]){
                    tmp=input[0];
                    input[0]=input[i];
                    input[i]=tmp;
                    adjustMaxHeapSort(input,0,k-1);
                }
            }
            for(int j=0; j<k; j++){
                list.add(input[j]);
            }
            return list;
        }
         
        public void adjustMaxHeapSort(int[] input, int pos, int length){
            int temp;
            int child;
            for(temp=input[pos]; 2*pos+1<=length; pos=child){
                child=2*pos+1;
                if(child<length && input[child]<input[child+1]){
                    child++;
                }
                if(input[child]>temp){
                    input[pos]=input[child];
                }else{
                    break;
                }
            }
            input[pos]=temp;
        }
    }
    View Code

     30.连续子数组的最大和

     动态规划(维护两个变量:maxEndingHere和maxSoFar;)

    解法:(从A[0]开始向右遍历,如果已经解决了A[0]-A[i-1]中的最大连续subarray,那么A[0]-A[i]的最大连续subarray要么是A[0]-A[i-1]的结果,要么是以A[i]作为结尾的一段subarray;记前者是maxSoFar,后者是maxEndingHere;显然maxEndingHere的值为(前一个数的maxEndingHere加上A[i])或者(A[i])这两个数之间的最大值。)

    public static int maxSubArray(int[] A) {
        int maxSoFar=A[0], maxEndingHere=A[0];
        for (int i=1;i<A.length;++i){
            maxEndingHere= Math.max(maxEndingHere+A[i],A[i]);
            maxSoFar=Math.max(maxSoFar, maxEndingHere);    
        }
        return maxSoFar;
    }
    View Code

    31.1到n中1出现的次数

     todo

    链接:https://www.nowcoder.com/questionTerminal/bd7f978302044eee894445e244c7eee6
    来源:牛客网
    
    class Solution {
    public:
        int NumberOf1Between1AndN_Solution(int n)
        {
        //主要思路:设定整数点(如1、10、100等等)作为位置点i(对应n的各位、十位、百位等等),分别对每个数位上有多少包含1的点进行分析
        //根据设定的整数位置,对n进行分割,分为两部分,高位n/i,低位n%i
        //当i表示百位,且百位对应的数>=2,如n=31456,i=100,则a=314,b=56,此时百位为1的次数有a/10+1=32(最高两位0~31),每一次都包含100个连续的点,即共有(a%10+1)*100个点的百位为1
        //当i表示百位,且百位对应的数为1,如n=31156,i=100,则a=311,b=56,此时百位对应的就是1,则共有a%10(最高两位0-30)次是包含100个连续点,当最高两位为31(即a=311),本次只对应局部点00~56,共b+1次,所有点加起来共有(a%10*100)+(b+1),这些点百位对应为1
        //当i表示百位,且百位对应的数为0,如n=31056,i=100,则a=310,b=56,此时百位为1的次数有a/10=31(最高两位0~30)
        //综合以上三种情况,当百位对应0或>=2时,有(a+8)/10次包含所有100个点,还有当百位为1(a%10==1),需要增加局部点b+1
        //之所以补8,是因为当百位为0,则a/10==(a+8)/10,当百位>=2,补8会产生进位位,效果等同于(a/10+1)
        int count=0;
        long long i=1;
        for(i=1;i<=n;i*=10)
        {
            //i表示当前分析的是哪一个数位
            int a = n/i,b = n%i;
            count=count+(a+8)/10*i+(a%10==1)*(b+1);
        }
        return count;
        }
    };
    View Code

    32.把数组排成最小的数

    先将整型数组转换成String数组,然后将String数组排序,最后将排好序的字符串数组拼接出来。

    实现比较器:Arrays.sort(str, new Comparator<String>(){ compare(s1,s2)})

    关键就是制定排序规则。

     * 排序规则如下:
     * 若ab > ba 则 a > b,
     * 若ab < ba 则 a < b,
     * 若ab = ba 则 a = b;
     * 解释说明:
     * 比如 "3" < "31"但是 "331" > "313",所以要将二者拼接起来进行比较

    import java.util.*;
    
    public class Solution {
    
        public String PrintMinNumber(int [] numbers) {
            if(numbers == null || numbers.length == 0) return "";
            int len = numbers.length;
            String[] str = new String[len];
            StringBuilder sb = new StringBuilder();
            for(int i = 0; i < len; i++){
                str[i] = String.valueOf(numbers[i]);
            }
            Arrays.sort(str,new Comparator<String>(){
                @Override
                public int compare(String s1, String s2) {
                    String c1 = s1 + s2;
                    String c2 = s2 + s1;
                    return c1.compareTo(c2);
                }
            });
            for(int i = 0; i < len; i++){
                sb.append(str[i]);
            }
            return sb.toString();
        }
        
    }
    View Code

     33.丑数

    求第n个丑数(只含2.3.5因子):

    (new ArrayList, uglys.add(1),定义3个指针p2/p3/p5,分别代表指向3个数组中已添加的位置;和uglys的LastNumber比较,<=则指针后移一位;取3个数组所指的min作为uglys.add;注意get(p)指向的是已经添加到uglys中的数;所以代码中用到的都需要是get(p)*2/3/5)

    (ArrayList<Integer>(); add(1); for(1-n) lastNumber,while(<=)p++; add(Math.min(Math.min)))

    1-n的丑数:1,2,3,4,5,6,8,9,10,12,15…… 可以分为如下3组:

    (1)1, 1×2, 2×2, 3×2, 4×2, 5×2, 6×2, 8×2, …
    (2)1, 1×3, 2×3, 3×3, 4×3, 5×3, 6×3, 8×3, …
    (3)1, 1×5, 2×5, 3×5, 4×5, 5×5, 6×5, 8×5, … 

    只需每次添加的数为3组中最小的值

    import java.util.ArrayList;
    
    public class Solution {
        public int GetUglyNumber_Solution(int n) {
            ArrayList<Integer> uglys = new ArrayList<Integer>();
            uglys.add(1);
            int p2 = 0, p3 = 0, p5 = 0;
            // p2, p3 & p5 share the same queue: uglys
            for (int i = 1; i < n; i++) {
                int lastNumber = uglys.get(i - 1);
                while (uglys.get(p2) * 2 <= lastNumber) p2++;
                while (uglys.get(p3) * 3 <= lastNumber) p3++;
                while (uglys.get(p5) * 5 <= lastNumber) p5++;
                uglys.add(Math.min(
                    Math.min(uglys.get(p2) * 2, uglys.get(p3) * 3),
                    uglys.get(p5) * 5
                ));
            }
            return uglys.get(n - 1);
        }
    }
    View Code

    34.第一个只出现一次的字符

    解法1:HashMap统计次数 + 再次遍历;

    import java.util.HashMap;
    
    public class Solution {
        public int FirstNotRepeatingChar(String str) {
            HashMap<Character, Integer> map = new HashMap<>();
            for (int i = 0; i < str.length(); i++) {
                if (!map.containsKey(str.charAt(i)))
                    map.put(str.charAt(i), 1);
                else
                    map.put(str.charAt(i), map.get(str.charAt(i)) + 1);
            }
            
            for (int i = 0; i < str.length(); i++) {
                if (map.get(str.charAt(i)) == 1) return i;
            }
            
            return -1;
        }
    }
    View Code

    解法2:大小为('z'-'A'+1)的数组

    注ASCII值:A-Z 为 65-90; a-z 为 97- 122;

    public class Solution {
        public int FirstNotRepeatingChar(String str) {
            
            int[] a = new int['z' - 'A' + 1];
            for (int i = 0; i < str.length(); i++){
                a[str.charAt(i) - 'A']++;
            }
            
            for (int i = 0; i < str.length(); i++) {
                if (a[str.charAt(i) - 'A'] == 1) return i;
            }
            
            return -1;
        }
    }
    View Code

    35.数组中的逆序对

    解法:(归并排序。mergeSort, merge; 分成若干小数组,求出逆序对个数,再归并得出总个数。注意[left]>[right]时,sum+=mid-left+1 因为right的小数已经放入tmp数组里不参与left后面的比较了,而left后的数都大于left)

    public class Solution {
        /**
         * @param A an array
         * @return total of reverse pairs
         */
        public long reversePairs(int[] A) {
            // Write your code here
            return mergeSort(A, 0, A.length - 1);
        }
        public int mergeSort(int[] A, int low, int high) {
            if (low >= high) return 0;
            int mid = (low + high) / 2;
            int sum = 0;
            sum += mergeSort(A, low, mid);
            sum += mergeSort(A, mid + 1, high);
            sum += merge(A, low, mid, high);
            return sum;
        }
        public int merge(int[]A, int low, int mid, int high) {
            int[] tmp = new int[high - low + 1];
            int left = low;
            int right = mid + 1;
            int k = 0;
            int sum = 0;
            
            while (left <= mid && right <= high) {
                if (A[left] <= A[right]) {
                    tmp[k++] = A[left++];
                } else {
                    tmp[k++] = A[right++];
                    sum += mid - left + 1;
                }
            }
            while (left <= mid) {
                tmp[k++] = A[left++];
            }
            while (right <= high) {
                tmp[k++] = A[right++];
            }
            for (int i = 0; i < tmp.length; i++) {
                A[i + low] = tmp[i];
            }
            return sum;
        }
    }
    View Code

    牛客的要取余:

    public class Solution {
        public int InversePairs(int [] A) {
            return mergeSort(A, 0, A.length - 1) ;
        }
        
        public int mergeSort(int[] A, int low, int high) {
            if (low >= high) return 0;
            int mid = (low + high) / 2;
            int sum = 0;
            sum += mergeSort(A, low, mid) ;
            sum += mergeSort(A, mid + 1, high);
            sum += merge(A, low, mid, high);
            return sum % 1000000007;
        }
        
        public int merge(int[]A, int low, int mid, int high) {
            int[] tmp = new int[high - low + 1];
            int left = low;
            int right = mid + 1;
            int k = 0;
            int sum = 0;
            
            while (left <= mid && right <= high) {
                if (A[left] <= A[right]) {
                    tmp[k++] = A[left++];
                } else {
                    tmp[k++] = A[right++];
                    sum = (sum + (mid - left + 1)) % 1000000007 ;
                }
            }
            while (left <= mid) {
                tmp[k++] = A[left++];
            }
            while (right <= high) {
                tmp[k++] = A[right++];
            }
            for (int i = 0; i < tmp.length; i++) {
                A[i + low] = tmp[i];
            }
            return sum;
        }
        
    }
    View Code

    36.两个链表的第一个公共结

    解法1:HashSet;

    解法2:(把两个链表相接;则环的起始位置即是结果--即ListCycleII问题。题目要求链表结构不变,最后还需要把index的null节点置null)

    public class Solution {
        /**
         * @param headA: the first list
         * @param headB: the second list
         * @return: a ListNode 
         */
        public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
            // Write your code here
            if (headA == null || headB == null) return null;
            ListNode index = headA;
            while (index.next != null)  index = index.next;
            index.next = headB;
            ListNode rst = listCycle(headA);
            index.next = null;
            return rst;
        }
        public ListNode listCycle(ListNode head) {
            ListNode slow = head, fast = head.next;
            if (fast == null || fast.next == null) return null;
            while (slow != fast) {
                slow = slow.next;
                fast = fast.next.next;
            }
            slow = head;
            fast = fast.next;
            while (slow != fast) {
                slow = slow.next;
                fast = fast.next;
            }
            return slow;
        }
    }
    View Code

    解法3:遍历两个长度,长的先移动;

    public class Solution {
        public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
             
            ListNode cur1 = pHead1;
            ListNode cur2 = pHead2;
            
            int len1 = 0;
            int len2 = 0;
            
            while (cur1 != null) {
                cur1 = cur1.next;
                len1++;
            }
            while (cur2 != null) {
                cur2 = cur2.next;
                len2++;
            }
            
            cur1 = pHead1;
            cur2 = pHead2;
            if (len2 > len1) {
                for (int i = 0; i < len2 - len1; i++) {
                    cur2 = cur2.next;
                }
            } else if (len1 > len2) {
                for (int i = 0; i < len1 - len2; i++) {
                    cur1 = cur1.next;
                }
            }
            
            while (cur1 != null) {
                if (cur1 == cur2) return cur1;
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
            
            return cur1;
            
        }
    }
    View Code

    37.数字在有序数组中出现次数

    有序数组,二分求范围;

    public class Solution {
        public int GetNumberOfK(int [] a , int tar) {
            
            if (a == null || a.length == 0) return 0;
            
            //FindFirstPos;
            int lo = 0;
            int hi = a.length - 1;
            while (lo < hi) {
                int mid = (lo + hi) / 2;
                if (a[mid] < tar) lo = mid + 1;
                else hi = mid;
            }
            if (a[lo] != tar) return 0;
            int firstPos = lo;
            
            //FindLastPos;
            hi = a.length - 1;
            while (lo < hi) {
                int mid = (lo + hi + 1) / 2;
                if (a[mid] > tar) hi = mid - 1;
                else lo = mid;
            }
            
            int cnt = hi - firstPos + 1;
            return cnt;
           
            
        }
    }
    View Code

    38.二叉树的深度

    递归:

    public class Solution {
        public int TreeDepth(TreeNode root) {
            if (root == null) return 0;
            return Math.max(TreeDepth(root.left), TreeDepth(root.right)) + 1;
            
        }
    }
    View Code

    迭代,层序遍历思想:

    public class Solution {
        public int maxDepth(TreeNode root) {
            //Iterative--层序遍历思想
            if (root == null) return 0;
            Queue<TreeNode> queue = new LinkedList<>();
            queue.offer(root);
            int cnt = 0;
            while (!queue.isEmpty()) {
                int size = queue.size();
                for (int i = 0; i < size; i++) {
                    TreeNode cur = queue.poll();
                    if (cur.left != null) queue.offer(cur.left);
                    if (cur.right != null) queue.offer(cur.right);
                }
                cnt++;
            }
            return cnt;
        }
    }
    View Code

    39.平衡二叉树

    三个条件:深度差<=1 + 左右子树为平衡二叉树;

    递归 + depth函数;

    public class Solution {
        public boolean IsBalanced_Solution(TreeNode root) {
            if (root == null) return true;
            return Math.abs(depth(root.left) - depth(root.right)) <= 1 && IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
        }
        
        public int depth(TreeNode root) {
            if (root == null) return 0;
            return Math.max(depth(root.left), depth(root.right)) + 1;
        }
    }
    View Code

    解法2:(只遍历一次 用-1记录不平衡的子树;相当于自底向上遍历。解法1中首先root求左右深度O(N), 然后每个节点都要求左右深度, 所以是O(NlogN)? 而解法2每次返回节点的高度或者-1 最后判断值是否是-1即可,O(N))
    (1.return !=-1;  2.height: lh-if,rh-if; if(>1); return max()+1;)
    public class Solution {
        public boolean isBalanced(TreeNode root) {
            return height(root) != -1;
        }
        public int height(TreeNode root) {
            if (root == null) return 0;
            int lh = height(root.left);
            if (lh == -1) return -1;
            int rh = height(root.right);
            if (rh == -1) return -1;
            if (Math.abs(lh - rh) > 1) return -1;
            return Math.max(lh, rh) + 1;
        }
    }
    View Code

    40.两个只出现一次的数字

    其他都出现了两次;

    解法1:HashSet

    import java.util.*;
    //num1,num2分别为长度为1的数组。传出参数
    //将num1[0],num2[0]设置为返回结果
    public class Solution {
        public void FindNumsAppearOnce(int [] a,int num1[] , int num2[]) {
            HashSet<Integer> set = new HashSet<>();
            for (int i = 0; i < a.length; i++) {
                if (set.contains(a[i])) set.remove(a[i]);
                else set.add(a[i]);
            }
            
            ArrayList<Integer> list = new ArrayList<>();
            for (Integer num : set) {
                list.add(num);
            }
            
            num1[0] = list.get(0);
            num2[0] = list.get(1);
            
        }
    }
    View Code

    解法2:位运算、异或

    当只有一个数出现一次时,我们把数组中所有的数,依次异或运算,最后剩下的就是落单的数,因为成对儿出现的都抵消了。

    依照这个思路,我们来看两个数(我们假设是AB)出现一次的数组。我们首先还是先异或,剩下的数字肯定是A、B异或的结果,这个结果的二进制中的1,表现的是A和B的不同的位。我们就取第一个1所在的位数,假设是第3位,接着把原数组分成两组,分组标准是第3位是否为1。如此,相同的数肯定在一个组,因为相同数字所有位都相同,而不同的数,肯定不在一组。然后把这两个组按照最开始的思路,依次异或,剩余的两个结果就是这两个只出现一次的数字。

     FindFirst1(bitRst):找第一个bit结果为1的位置index;

    isBit1(tar, index):判断index位置的bit是否为1;

    public class Solution {
        public void FindNumsAppearOnce(int[] array, int[] num1, int[] num2)    {
            int length = array.length;
            if(length == 2){
                num1[0] = array[0];
                num2[0] = array[1];
                return;
            }
            int bitResult = 0;
            for(int i = 0; i < length; ++i){
                bitResult ^= array[i];
            }
            int index = findFirst1(bitResult);
            for(int i = 0; i < length; ++i){
                if(isBit1(array[i], index)){
                    num1[0] ^= array[i];
                }else{
                    num2[0] ^= array[i];
                }
            }
        }
         
        private int findFirst1(int bitResult){
            int index = 0;
            while(((bitResult & 1) == 0) && index < 32){
                bitResult >>= 1;
                index++;
            }
            return index;
        }
        
        private boolean isBit1(int target, int index){
            return ((target >> index) & 1) == 1;
        }
    }
    View Code

    41.和为S的连续正数序列

    双指针,lo和hi,初始化为前两个;小于sum则hi++,大于sum则lo++;lo增加到(1+sum)/2时停止;

    import java.util.ArrayList;
    /*
    *初始化small=1,big=2;
    *small到big序列和小于sum,big++;大于sum,small++;
    *当small增加到(1+sum)/2是停止
    */
    public class Solution {
        public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
            ArrayList<ArrayList<Integer>> lists=new ArrayList<ArrayList<Integer>>();
            if(sum<=1){return lists;}
            int small=1;
            int big=2;
            while(small!=(1+sum)/2){          //当small==(1+sum)/2的时候停止
                int curSum=sumOfList(small,big);
                if(curSum==sum){
                    ArrayList<Integer> list=new ArrayList<Integer>();
                    for(int i=small;i<=big;i++){
                        list.add(i);
                    }
                    lists.add(list);
                    small++;big++;
                }else if(curSum<sum){
                    big++;
                }else{
                    small++;
                }
            }
            return lists;
        }
         
        public int sumOfList(int lo,int hi){        //计算当前序列的和
            return (lo + hi) * (hi - lo + 1) / 2;
        }
    }
    View Code

    42.和为S的两个数字

    数组递增;如果有多对,输出乘积最小的;

    解法1:HashSet; 因为可能不存在,所以设置了一个boolean变量标记状态;

    import java.util.*;
    
    public class Solution {
        public ArrayList<Integer> FindNumbersWithSum(int [] a,int sum) {
            ArrayList<Integer> list = new ArrayList<>();
            int mul = Integer.MAX_VALUE;
            int num1 = 0;
            int num2 = 0;
            
            boolean state = false;
            
            HashSet<Integer> set = new HashSet<>();
            for (int i = 0; i < a.length; i++) {
                if (!set.contains(sum - a[i])) set.add(a[i]);
                else if ((sum - a[i]) * a[i] < mul ) {
                    num1 = Math.min(sum - a[i], a[i]);
                    num2 = sum - num1;
                    state = true;
                }
            }
            if (state == true) {
                list.add(num1);
                list.add(num2);
            }
            
            return list;
            
        }
    }
    View Code

    解法2:双指针;

    和相同,那么相差最大的乘积最小;

    左右双指针,>sum则hi--,<sum则lo++;

    import java.util.ArrayList;
    public class Solution {
        public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
            ArrayList<Integer> list = new ArrayList<Integer>();
            if (array == null || array.length < 2) {
                return list;
            }
            int i=0,j=array.length-1;
            while(i<j){
                if(array[i]+array[j]==sum){
                    list.add(array[i]);
                    list.add(array[j]);
                    return list;
               }else if(array[i]+array[j]>sum){
                    j--;
                }else{
                    i++;
                }
                 
            }
            return list;
        }
    }
    View Code

    43.左旋转字符串

    三次reverse翻转;由于string不支持修改字符,所以要先转char数组;

    public class Solution {
        public String LeftRotateString(String str,int n) {
            char[] chars = str.toCharArray();        
            if(chars.length < n) return "";
            reverse(chars, 0, n-1);
            reverse(chars, n, chars.length-1);
            reverse(chars, 0, chars.length-1);
            return new String(chars);
        }
         
        public void reverse(char[] chars,int low,int high){
            char temp;
            while(low<high){
                temp = chars[low];
                chars[low] = chars[high];
                chars[high] = temp;
                low++;
                high--;
            }
        }
    }
    View Code

    如果允许使用subString方法:

    public class Solution {
        public String LeftRotateString(String str,int n) {
            //保证旋转的位数大于字符串的长度,否则返回空字符串
            if (str == null || str.length() == 0) return str;
            n = n % str.length();
            String s1 = str.substring(0, n);
            String s2 = str.substring(n,str.length());
            return s2 + s1;
        }
    }
    View Code

    44.翻转单词顺序列

    str.trim(); str.split(" ");

    public class Solution {
        public String ReverseSentence(String str) {
            if(str.trim().equals("")){
                return str;
            }
            String[] a = str.split(" ");
            StringBuffer o = new StringBuffer();
            int i;
            for (i = a.length; i >0;i--){
                o.append(a[i-1]);
                if(i > 1){
                    o.append(" ");
                }
            }
            return o.toString();
        }
    }
    View Code

    45.扑克牌顺子

    46..圆圈中最后剩下的数

    47.求1到n的和

    要求不能使用乘除法、for、if等关键字;

    思路:利用逻辑与的短路特性实现递归终止。(前面假则后面不执行)

    当n==0时,(n>0)&&((sum+=Sum_Solution(n-1))>0)只执行前面的判断,为false,然后直接返回0当n>0时,执行sum+=Sum_Solution(n-1),实现递归计算Sum_Solution(n)。

    class Solution {
        public int Sum_Solution(int n) {
            int sum = n;
            boolean ans = (n>0)&&((sum+=Sum_Solution(n-1))>0);
            return sum;
        }
    }
    View Code

    48. 不用加减乘除做加法

    A+B:(x=x^y, y=(x&y)<<1)

    位运算实现整数加法本质就是用二进制进行运算。其主要用了两个基本表达式:

    x^y //执行加法,不考虑进位。
    (x&y)<<1 //进位操作
    令x=x^y ;y=(x&y)<<1 进行迭代,每迭代一次进位操作右面就多一位0,最多需要“加数二进制位长度”次迭代就没有进位了,此时x^y的值就是结果。

    我们来做个3位数的加法:
    101+011=1000 //正常加法
    位运算加法:
    (1) 101 ^ 011 = 110
    (101 & 011)<<1 = 010
    (2) 110 ^ 010 = 100
    (110 & 010)<<1 = 100
    (3) 100 ^ 100 = 000
    (100 & 100)<<1 = 1000
    此时进行相加操作就没有进位了,即000 ^ 1000=1000即是最后结果。

    class Solution {
        /*
         * param a: The first integer
         * param b: The second integer
         * return: The sum of a and b
         */
        public int aplusb(int a, int b) {
            while(b != 0){
                int carry = a & b;
                a = a ^ b;
                b = carry << 1;
            }
            return a;
        }
    }
    View Code
    class Solution {
        /*
         * param a: The first integer
         * param b: The second integer
         * return: The sum of a and b
         */
        public int aplusb(int a, int b) {
            // write your code here, try to do it without arithmetic operators.
            if (b == 0) {
                return a;
            }
            int sum = a ^ b;
            int carry = (a & b) << 1;
            return aplusb(sum, carry);
        }
    };
    View Code

     49.字符串转换成整数

     参考一下lintcode版本的吧;

     解法:(1.判空/null; 2.判断正负符号;3.遍历str,把数字添加到rst,rst=rst*10+(charAt(i)-0); 4.添加符号; 5.考虑溢出,转int)

    public class Solution {
        /**
         * @param str: A string
         * @return An integer
         */
        public int atoi(String str) {
            // write your code here
            if (str == null || str.length() < 1)
            return 0;
            
            // trim white spaces
            str = str.trim();
     
            char flag = '+';
     
            // check negative or positive
            int i = 0;
            if (str.charAt(0) == '-') {
                flag = '-';
                i++;
            } else if (str.charAt(0) == '+') {
                i++;
            }
            // use double to store result
            double result = 0;
        
            // calculate value
            while (str.length() > i && str.charAt(i) >= '0' && str.charAt(i) <= '9') {
                result = result * 10 + (str.charAt(i) - '0');
                i++;
            }
     
            if (flag == '-')
                result = -result;
     
            // handle max and min
            if (result > Integer.MAX_VALUE)
                return Integer.MAX_VALUE;
     
            if (result < Integer.MIN_VALUE)
                return Integer.MIN_VALUE;
     
            return (int) result;
        }
    }
    View Code

    50.数组中第一个重复的数字

    长度为n的数组,数字在0到n-1范围内;输出第一个重复过的数字

    解法1:辅助数组或者hashset

    public class Solution {
        //返回第一个重复的,赋值duplicate[0]
        public boolean duplicate(int num[],int n,int [] duplicate) {
            int[] a = new int[n];
            for (int i = 0; i < n; i++) {
                if (a[num[i]] != 0) {
                    duplicate[0] = num[i];
                    return true;
                }
                else a[num[i]]++;
            }
            return false;
        }
    }
    View Code

    优化一下,用boolean数组,boolean只占一位

    //boolean只占一位,所以还是比较省的
        public boolean duplicate(int numbers[], int length, int[] duplication) {
            boolean[] k = new boolean[length];
            for (int i = 0; i < k.length; i++) {
                if (k[numbers[i]] == true) {
                    duplication[0] = numbers[i];
                    return true;
                }
                k[numbers[i]] = true;
            }
            return false;
        }
    View Code

    解法2:不用额外空间,不过会改变数组元素;

    题目里写了数组里数字的范围保证在0 ~ n-1 之间,所以可以利用现有数组设置标志,当一个数字被访问过后,可以设置对应位上的数 + n,之后再遇到相同的数时,会发现对应位上的数已经大于等于n了,那么直接返回这个数即可。

    public class Solution {
        //返回第一个重复的,赋值duplicate[0]
        public boolean duplicate(int num[],int n,int [] duplicate) {
            for (int i = 0; i < n; i++) {
                int index = num[i] >= n ? num[i] - n : num[i];
                if (num[index] >= n) {
                    duplicate[0] = index;
                    return true;
                } 
                num[index] += n;
            }
            return false;
        }
    }
    View Code

    51.构建乘积数组

    B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。

    B[i]的值可以看作下图的矩阵中每行的乘积。
    下三角用连乘可以很容求得,上三角,从下向上也是连乘。
    因此我们的思路就很清晰了,先算下三角中的连乘,即我们先算出B[i]中的一部分,然后倒过来按上三角中的分布规律,把另一部分也乘进去。
    public class Solution {
        public int[] multiply(int[] A) {
            int length = A.length;
            int[] B = new int[length];
            if(length != 0 ){
                B[0] = 1;
                //计算下三角连乘
                for(int i = 1; i < length; i++){
                    B[i] = B[i-1] * A[i-1];
                }
                int temp = 1;
                //计算上三角
                for(int j = length-2; j >= 0; j--){
                    temp *= A[j+1];
                    B[j] *= temp;
                }
            }
            return B;
        }
    }
    View Code

    52.正则表达式匹配

    53.表示数值的字符串

    54.字符流中第一个不重复

    数组;一个字符占8位,因此不会超过256个,可以申请一个256大小的数组实现一个简易哈希表。 

    public class Solution {
        //Insert one char from stringstream
        
        char[] ca = new char[256];
        StringBuffer sb = new StringBuffer();
        public void Insert(char ch)
        {
            sb.append(ch);
            ca[ch]++;
        }
      //return the first appearence once char in current stringstream
        public char FirstAppearingOnce()
        {
            String str = sb.toString();
            for (int i = 0; i < str.length(); i++) {
                if (ca[str.charAt(i)] == 1) return str.charAt(i);
            }
            return '#';
        }
    }
    View Code

    55.链表中环的入口结点

    先slow和fast移动,再head和slow.next比较和移动;

    (1-2-3-[4-5-6-7-8-9-10], 如果slow和fast都从1开始move, 当相遇时, slow移动了k步, fast移动了2k步(绕换n圈,则2k-k=nr), 相遇地点距离循环首结点距离为m;  只要head节点从首结点开始移动, 移动k-m步,  slow从meet点移动,移动k-m步; 这时候两者就在cycle首结点相遇。因为slow已经移动了k步,再移动k步就是又到了meet点,所以移动k-m步就是到cycle的首结点)

    ps:此解法要求slow.next=head;因为fast多移动了一步;

    /*
     public class ListNode {
        int val;
        ListNode next = null;
    
        ListNode(int val) {
            this.val = val;
        }
    }
    */
    public class Solution {
    
        public ListNode EntryNodeOfLoop(ListNode pHead){
            
            if (pHead == null || pHead.next == null) return null;
            
            
            ListNode slow = pHead;
            ListNode fast = pHead.next;
            
            while (slow != fast) {
                if (fast == null || fast.next == null) return null;
                slow = slow.next;
                fast = fast.next.next;
            }
            
            while (pHead != slow.next) {
                pHead = pHead.next;
                slow = slow.next;
            }
            return pHead;
    
            
        }
    }
    View Code

    56.删除链表中重复的结点

    删除所有重复结点;三个一组(h->h.n->h.n.n) 比较h.n和h.n.n的val,一旦发现有重复值,则保存重复值,内循环while删除掉所有的对应结点;

    public class Solution {
        public ListNode deleteDuplication(ListNode head){
    
            if (head == null || head.next == null) return head;
            
            ListNode dummy = new ListNode(0);
            dummy.next = head;
            head = dummy;
            
            while (head.next != null && head.next.next != null) {
                if (head.next.val == head.next.next.val) {
                    int val = head.next.val;
                    while (head.next != null && head.next.val == val) {
                        head.next = head.next.next;
                    }
                } else {
                    head = head.next;
                }
            }
            return dummy.next;
            
        }
    }
    View Code

    57.二叉树的下一个结点

    中序遍历的下一个结点

    public class Solution {
        TreeLinkNode GetNext(TreeLinkNode node)
        {
            if(node==null) return null;
            if(node.right!=null){    //如果有右子树,则找右子树的最左节点
                node = node.right;
                while(node.left!=null) node = node.left;
                return node;
            }
            while(node.next!=null){ //没右子树,则找第一个当前节点是父节点左孩子的节点
                if(node.next.left==node) return node.next;
                node = node.next;
            }
            return null;   //退到了根节点仍没找到,则返回null
        }
    }
    View Code

    58.对称的二叉树

    递归:

    (左节点和右节点的比较,所以需要helper函数,判断left和right的null与val;递归左左和右右,左右和右左)

    public class Solution {
        boolean isSymmetrical(TreeNode root){
            if (root == null) return true;
            return helper(root.left, root.right);
        }
        
        boolean helper(TreeNode lnode, TreeNode rnode){
            if (lnode == null || rnode == null) return lnode == rnode;
            if (lnode.val != rnode.val) return false;
            return helper(lnode.left, rnode.right) && helper(lnode.right, rnode.left);
        }
    }
    View Code

    迭代:

    (Stack, 每次pop两个对称的节点,判断两个节点是否满足条件。再push进四个节点)

    public class Solution {
        public boolean isSymmetric(TreeNode root) {
            if (root == null) return true;
            Stack<TreeNode> stack = new Stack<>();
            stack.push(root.left);
            stack.push(root.right);
            while (!stack.empty()) {
                TreeNode n1 = stack.pop(), n2 = stack.pop();
                if (n1 == null && n2 == null) continue;
                if (n1 == null || n2 == null || n1.val != n2.val) return false;
                stack.push(n1.right);
                stack.push(n2.left);
                stack.push(n1.left);
                stack.push(n2.right);
            }
            return true;
        }
    }
    View Code

    59.Z型层序遍历

    设置flag,分list.add()和list.add(0,val)两种情况;

    1.Queue;2.while:size、list; 3.for():poll、list、queue; 4.flag,rst;

    import java.util.*;
    public class Solution {
        public ArrayList<ArrayList<Integer> > Print(TreeNode root) {
            ArrayList<ArrayList<Integer>> rst = new ArrayList<>();
            if (root == null) return rst;
            Queue<TreeNode> queue = new LinkedList<>();
            boolean flag = true;
            queue.offer(root);
            while (!queue.isEmpty()) {
                int size = queue.size();
                ArrayList<Integer> list = new ArrayList<>();
                for (int i = 0; i < size; i++) {
                    TreeNode cur = queue.poll();
                    if (flag) {
                        list.add(cur.val);
                    } else {
                        list.add(0, cur.val);
                    }
                    if (cur.left != null) queue.offer(cur.left);
                    if (cur.right != null) queue.offer(cur.right);
                }
                flag = !flag;
                rst.add(list);
            }
            return rst;
        }
    
    }
    View Code

    60.层序遍历

    import java.util.*;
    public class Solution {
        ArrayList<ArrayList<Integer> > Print(TreeNode root) {
            Queue<TreeNode> queue = new LinkedList<>();
            ArrayList<ArrayList<Integer>> rst = new ArrayList<>();
            if (root == null) return rst;
            queue.offer(root);
            while (!queue.isEmpty()) {
                ArrayList<Integer> list = new ArrayList<>();
                int size = queue.size();
                for (int i = 0; i < size; i++){
                    TreeNode node = queue.poll();
                    list.add(node.val);
                    if (node.left != null) queue.offer(node.left);
                    if (node.right != null) queue.offer(node.right);
                }
                rst.add(list);
            }
            return rst;
        }
        
    }
    View Code

    61.序列化二叉树

    /*
    public class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;
     
        public TreeNode(int val) {
            this.val = val;
     
        }
     
    }
    */
    /*
        算法思想:根据前序遍历规则完成序列化与反序列化。所谓序列化指的是遍历二叉树为字符串;所谓反序列化指的是依据字符串重新构造成二叉树。
        依据前序遍历序列来序列化二叉树,因为前序遍历序列是从根结点开始的。当在遍历二叉树时碰到Null指针时,这些Null指针被序列化为一个特殊的字符“#”。
        另外,结点之间的数值用逗号隔开。
    */
    public class Solution {
        int index = -1;   //计数变量
      
        String Serialize(TreeNode root) {
            StringBuilder sb = new StringBuilder();
            if(root == null){
                sb.append("#,");
                return sb.toString();
            }
            sb.append(root.val + ",");
            sb.append(Serialize(root.left));
            sb.append(Serialize(root.right));
            return sb.toString();
      }
        TreeNode Deserialize(String str) {
            index++;
            //int len = str.length();
            //if(index >= len){
            //    return null;
           // }
            String[] strr = str.split(",");
            TreeNode node = null;
            if(!strr[index].equals("#")){
                node = new TreeNode(Integer.valueOf(strr[index]));
                node.left = Deserialize(str);
                node.right = Deserialize(str);
            }
            return node;
      }
    }
    View Code

    62.二叉搜索树的第K大结点

     中序遍历,使用计数器;

    递归:

    public class Solution {
        int cnt = 0;
        TreeNode KthNode(TreeNode root, int k){
            if (root == null) return root;
            TreeNode node = KthNode(root.left, k);
            if (node != null) return node;
            cnt++;
            if (cnt == k) return root;
            node = KthNode(root.right, k);
            if (node != null) return node;
            return null;
        }
    
    }
    View Code

    63.数据流中的中位数

    一个大根堆,一个小根堆;

    todo

    链接:https://www.nowcoder.com/questionTerminal/9be0172896bd43948f8a32fb954e1be1
    来源:牛客网
    
    private int count = 0;
    private PriorityQueue<Integer> minHeap = new PriorityQueue<>();
    private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15, new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    });
     
    public void Insert(Integer num) {
        if (count %2 == 0) {//当数据总数为偶数时,新加入的元素,应当进入小根堆
            //(注意不是直接进入小根堆,而是经大根堆筛选后取大根堆中最大元素进入小根堆)
            //1.新加入的元素先入到大根堆,由大根堆筛选出堆中最大的元素
            maxHeap.offer(num);
            int filteredMaxNum = maxHeap.poll();
            //2.筛选后的【大根堆中的最大元素】进入小根堆
            minHeap.offer(filteredMaxNum);
        } else {//当数据总数为奇数时,新加入的元素,应当进入大根堆
            //(注意不是直接进入大根堆,而是经小根堆筛选后取小根堆中最大元素进入大根堆)
            //1.新加入的元素先入到小根堆,由小根堆筛选出堆中最小的元素
            minHeap.offer(num);
            int filteredMinNum = minHeap.poll();
            //2.筛选后的【小根堆中的最小元素】进入大根堆
            maxHeap.offer(filteredMinNum);
        }
        count++;
    }
     
    public Double GetMedian() {
        if (count %2 == 0) {
            return new Double((minHeap.peek() + maxHeap.peek())) / 2;
        } else {
            return new Double(minHeap.peek());
        }
    }
    View Code

    64.滑动窗口的最大值

    双端队列;

    todo

    链接:https://www.nowcoder.com/questionTerminal/1624bc35a45c42c0bc17d17fa0cba788
    来源:牛客网
    
    /**
     * 题目:滑动窗口的最大值
     * 思路:滑动窗口应当是队列,但为了得到滑动窗口的最大值,队列序可以从两端删除元素,因此使用双端队列。
     *     原则:
     *     对新来的元素k,将其与双端队列中的元素相比较
     *     1)前面比k小的,直接移出队列(因为不再可能成为后面滑动窗口的最大值了!),
     *     2)前面比k大的X,比较两者下标,判断X是否已不在窗口之内,不在了,直接移出队列
     *     队列的第一个元素是滑动窗口中的最大值
     */
    public class P65_滑动窗口的最大值 {
         
        public ArrayList<Integer> maxInWindows(int [] num, int size)
        {
            ArrayList<Integer> ret = new ArrayList<>();
            if (num == null) {
                return ret;
            }
            if (num.length < size || size < 1) {
                return ret;
            }
             
            LinkedList<Integer> indexDeque = new LinkedList<>();
            for (int i = 0; i < size - 1; i++) {
                while (!indexDeque.isEmpty() && num[i] > num[indexDeque.getLast()]) {
                    indexDeque.removeLast();
                }
                indexDeque.addLast(i);
            }
             
            for (int i = size - 1; i < num.length; i++) {
                while (!indexDeque.isEmpty() && num[i] > num[indexDeque.getLast()]) {
                    indexDeque.removeLast();
                }
                indexDeque.addLast(i);
                if (i - indexDeque.getFirst() + 1 > size) {
                    indexDeque.removeFirst();
                }
                ret.add(num[indexDeque.getFirst()]);
            }
            return ret;
        }
    }
    View Code
  • 相关阅读:
    NOI2017 游戏
    2-SAT问题的方案输出
    hdu 2433 Travel
    bzoj千题计划230:bzoj3205: [Apio2013]机器人
    bzoj千题计划229:bzoj4424: Cf19E Fairy
    hdu 6166 Senior Pan
    poj 2404 Jogging Trails
    Oracle 删除数据后释放数据文件所占磁盘空间
    安装LINUX X86-64的10201出现链接ins_ctx.mk错误
    10G之后统计信息收集后为什么执行计划不会被立马淘汰
  • 原文地址:https://www.cnblogs.com/buwenyuwu/p/7531078.html
Copyright © 2011-2022 走看看