剑指offer算法题汇总(java版)

1.二维数组中的查找

题目描述

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

题目思路

读取矩阵最右上角的元素,也就是第一行最后一列的元素开始遍历判断

    public boolean Find(int target, int [][] array) {
        if(array.length>0){
            int rows=array.length;
            int columns=array[0].length;
            int row=0;
            int column=columns-1;
            while(row<rows&&column>=0){
                if(array[row][column]==target)
                    return true;
                else if(array[row][column]>target)
                    column--;
                else
                    row++;
            }
            return false;
        }
        return false;
    }


2.替换空格

题目描述

请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

题目思路

主要考点是不开辟新空间,先计算替换后的长度,然后在原stringbuffer上从后往前做替换就可以节省空间了。

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


3.从尾到头打印链表

题目描述

输入一个链表,从尾到头打印链表每个节点的值。

题目思路

这题可以用递归或栈两种方式,但其实递归本质就是栈。

    public static ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        if(listNode!=null){
            printListFromTailToHead(listNode.next);
            arrayList.add(listNode.val);
        }
        return arrayList;
    }

    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list=new ArrayList<>();
        Stack<Integer> stack=new Stack<>();
        while(listNode!=null){
            stack.push(listNode.val);
            listNode=listNode.next;
        }
        while(!stack.isEmpty()){
            list.add(stack.pop());
        }
        return list;
    }


4.重建二叉树

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

题目思路

前序遍历第一个元素是根节点,然后根据这个根节点把左子树右子树分开,然后分别对左子树右子树用同样的方法,递归下去。

    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
         return build(pre,0,pre.length-1,in,0,in.length-1);
    }
    public TreeNode build(int[] pre,int pstart,int pend,int[] in,int istart,int iend){
        if(pstart>pend)	return null;
        int cur=pre[pstart];
        int find=istart;
        while(find<=iend){
            if(in[find]==cur) break;
            else find++;
        }
        int len=find-istart;
        TreeNode res=new TreeNode(cur);
        res.left=build(pre,pstart+1,pstart+len,in,istart,find-1);
        res.right=build(pre,pstart+len+1,pend,in,find+1,iend);
        return res;
    }


5.用两个栈实现队列

题目描述

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

题目思路

实现两个栈,其中一个用来存放压入的数据,另外一个用来出栈。

    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.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }

6.旋转数组的最小数字

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

题目思路

基于二分搜索,如果情况特殊,则顺序查找。比如看一组例子:{1,0,1,1,1} 和 {1,1,1,0,1} 都可以看成是递增排序数组{0,1,1,1,1}的旋转,这就是特殊情况。

    // http://blog.csdn.net/snow_7/article/details/51909764
    public int minNumberInRotateArray(int [] array) {
        if(array==null||array.length==0)
           return 0;
        int leftIndex=0;
        int rightIndex=array.length-1;
        int midIndex=leftIndex;
        while(array[leftIndex]>=array[rightIndex]){
            if(rightIndex-leftIndex<=1){
                midIndex=rightIndex;
                break;
            }
            midIndex = (leftIndex+rightIndex)/2;
            if(array[leftIndex]==array[rightIndex] && array[midIndex]==array[leftIndex]){
                return minInOrder(array,leftIndex,rightIndex); 
            }
            if(array[midIndex]>=array[leftIndex]){
                leftIndex=midIndex;
            }else if(array[midIndex]<=array[rightIndex]){
                rightIndex=midIndex;
            }
        }
           return array[midIndex];
    }
     public int minInOrder(int[] array,int leftIndex,int rightIndex){  
        int result = array[leftIndex];  
        for(int i = leftIndex +1;i<rightIndex;i++){  
            if(result> array[i]){  
                result = array[i];  
            }  
        }  
        return result;  
    } 

7.斐波那契数列

题目描述

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。

n<=39

题目思路

递归会有很多重复的计算,用迭代效率会高。

    public int Fibonacci(int n) {
        int[] result={0,1};
        if(n<2){
            return result[n];
        }
        int fibOne=0;
        int fibTwo=1;
        int fibN=0;
        for(int i=2;i<=n;i++){
            fibN=fibOne+fibTwo;
            fibOne=fibTwo;
            fibTwo=fibN;
        }
        return fibN;
    }


8.跳台阶

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

题目思路

同上一题思路一样。

    public int JumpFloor(int target) {
        if(target==1)	return 1;
        else if(target==2)	return 2;
        else return JumpFloor(target-1)+JumpFloor(target-2);
    }


9.变态跳台阶

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

题目思路

同可以得到f(n)=f(n-1)+f(n-2)+...+f(1)+1,而f(n-1)=f(n-2)+...+f(1)+1,两个公式相减,得到f(n)=2f(n-1),

    public int JumpFloorII(int target) {
        // http://blog.csdn.net/ns_code/article/details/25367797
        if(target==0) return 0;
        else if(target==1) return 1;
        else return 2*JumpFloorII(target-1);
    }


10.矩形覆盖

题目描述

我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

题目思路

其实就是个斐波那契数列。

    public int RectCover(int target) {
        if (target <= 2) {
        return target;
        }
        int one = 1;
        int two = 2;
        int result = 0;
        for (int i = 3; i <= target; i++) {
            result = one + two;
            one = two;
            two = result;
        }
        return result;
    }

11.二进制中1的个数

题目描述

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

题目思路

把n与n-1做 与 运算,就是将n最右边的1变成了0,得到的数赋值给n然后循环下去,每轮循环count+1,直到n=0时,退出循环。

    public int NumberOf1(int n) {
        int count=0;
        while(n!=0){
            count++;
            n=n&(n-1);
        }
        return count;
    }

12.数值的整数次方

题目描述

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

题目思路

1.全面考察指数的正负,底数为0等情况。

2.写出指数的二进制表达,例如13为1101。

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

4.通过&1和>>1来逐位读取1101,为1时将该位代表的乘数累乘到最终结果。(保留中间结果省去了很多操作)。

    public double Power(double base, int exponent) {
        int absExponent=exponent;
        if(exponent<0)
            absExponent=-exponent;
        if(exponent==0)
            return 1;
        if(exponent==1)
            return base;
        double result=Power(base,absExponent>>1);
        result*=result;
        if((exponent&0x1)==1)
            result*=base;
        if(exponent<0)
            return 1.0/result;
        return result;
  }

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

题目描述

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

题目思路

新建一个数组,遍历原数组,将奇数逐个放入新数组,然后再遍历一遍原数组,将偶数从最后一个奇数所在的位置的下一个开始放。

    public void reOrderArray(int [] array) {
        int newIndex=0;
        int[] tempArray=new int[array.length];
        for(int i=0;i<array.length;i++){
            if((array[i]&1)!=0)	tempArray[newIndex++]=array[i];
        }
        for(int j=0;j<array.length;j++){
            if((array[j]&1)==0)	tempArray[newIndex++]=array[j];
        }
        for(int n=0;n<array.length;n++){
            array[n]=tempArray[n];
        }
    }
java也可以这么解
    public void reOrderArray(int [] array) {
        IntStream even= Arrays.stream(array).filter(x->(x&0x1)==0);
        IntStream odd=Arrays.stream(array).filter(x->(x&0x1)==1);
        int []tmp= IntStream.concat(odd,even).toArray();
        for(int i=0;i<array.length;i++){
            array[i]=tmp[i];
        }
    }


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

题目描述

输入一个链表,输出该链表中倒数第k个结点。

题目思路

声明两个结点等于头结点,先让第一个结点走k步,然后两个结点同时走,当第一个结点走到终点时,第二个节点刚好走到倒数第k个结点。

    public ListNode FindKthToTail(ListNode head,int k) {
		if(head == null || k <= 0){
			return null;
		}
		ListNode ANode = head;
		ListNode BNode = null;
		for(int i = 0;i<k-1;i++){
			if(ANode.next != null)
				ANode = ANode.next;
			else
				return null;
		}
		BNode = head;
		while(ANode.next != null){
			ANode = ANode.next;
			BNode = BNode.next;
		}
		return BNode;
    }


15.反转链表

题目描述

输入一个链表,反转链表后,输出链表的所有元素。

题目思路

非递归:

    public ListNode ReverseList(ListNode head) {
        if (head == null||head.next == null)
            return head;
        ListNode p = head;
        ListNode q = head.next;
        head.next = null;
        while (q != null) {
            ListNode temp = q.next;
            q.next = p;
            p = q;
            q = temp;
        }
        return p;
    }

递归

    public ListNode ReverseList(ListNode head) {
        if (head == null||head.next == null)
            return head;
        ListNode node = ReverseList(head.next);
        head.next.next = head;
        head.next=null;
        return node;
    }


16.合并两个排序的链表

题目描述

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

题目思路

没特殊的技巧,就是先判断哪个是头结点,然后分别遍历对比下去,直到遍历完某个链表,然后将当前结点的next赋值为另一个链表的当前结点。

    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1==null)	return list2;
        if(list2==null)	return list1;
        ListNode mergeHead=null;
        ListNode current=null;
        while(list1!=null&&list2!=null){
            if(list1.val<=list2.val){
                if(mergeHead==null){
                    mergeHead=current=list1;
                }else{
                    current.next=list1;
                    current=current.next;
                }
                list1=list1.next;
            }else{
                if(mergeHead==null){
                    mergeHead=current=list2;
                }else{
                    current.next=list2;
                    current=current.next;
                }
                list2=list2.next;
            }
        }
        if(list1==null){
            current.next=list2;
        }else{
            current.next=list1;
        }
        return mergeHead;
    }

17.树的子结构

题目描述

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

题目思路

分三种情况:

1.根结点一样,A树的根结点和B树的根结点递归判断下去

2.如果前一个结果为false,A树的左孩子和B树的根结点判断下去

3.如果还是false,A树的右孩子和B树的根结点判断下去

用||省去了if判断

    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        if(root1==null||root2==null) return false;
        return isSubTree(root1,root2)||HasSubtree(root1.left,root2)||HasSubtree(root1.right,root2);
    }

    public boolean isSubTree(TreeNode root1,TreeNode root2){
        if(root2==null)
            return true;
        if(root1==null) //root2不等于null但root1等于null
            return false;
        if(root1.val==root2.val){
            return isSubTree(root1.left,root2.left)&&isSubTree(root1.right,root2.right);
        }
        return false;
    }

18.二叉树的镜像

题目描述

操作给定的二叉树,将其变换为源二叉树的镜像。

题目思路

递归:

    public void Mirror(TreeNode root) {
        if(root==null)    return;
        Mirror(root.left);
        Mirror(root.right);
        TreeNode temp=root.left;
        root.left=root.right;
        root.right=temp;
    }

非递归:(层序遍历)

    public void Mirror(TreeNode root) {
        if (root == null) return;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            TreeNode node=queue.poll();
            swap(node);
            if(node.right!=null)
                queue.offer(node.right);
            if(node.left!=null)
                queue.offer(node.left);
        }
    }
    public static void swap(TreeNode root)
    {
        TreeNode temp;
        temp=root.right;
        root.right=root.left;
        root.left=temp;
    }


19.顺时针打印矩阵

题目描述

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

题目思路

从左到右,从上到下,从右到左,从下到上,这四个步骤走一遍。坑点在于要考虑只有一行或一列的时候,不能像之前遍历那样,不然会死循环。所以必须在第三个、第四个步骤做判断。

    public ArrayList<Integer> printMatrix(int [][] matrix) {
        int row=matrix.length;
        int col=matrix[0].length;
        ArrayList<Integer> list=new ArrayList<>();
        //if(row==0||col==0)    return list;
        int top=0,bottom=row-1,left=0,right=col-1;
        while(top<=bottom && left<=right){
            for(int i=left;i<=right;i++)    list.add(matrix[top][i]);
            for(int i=top+1;i<=bottom;i++)    list.add(matrix[i][right]);
            if (top != bottom)//不加此条件对于只有一行的数组来说,会死循环
            for(int i=right-1;i>=left;i--)    list.add(matrix[bottom][i]);
            if (left != right)//不加此条件对于只有一列的数组来说,会死循环
            for(int i=bottom-1;i>top;i--)    list.add(matrix[i][left]);
            left++;top++;right--;bottom--;
        }
        return list;
    }


20.包含min函数的栈

题目描述

定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。

题目思路

思路很巧妙。声明两个stack,第一个正常存储数据,第二个stack判断当前min,然后压入这个min,这个min有可能是之前的数,有可能是新的数。出栈的时候,两个栈同时弹出。

    Stack<Integer> stack1=new Stack<>();
    Stack<Integer> stack2=new Stack<>();
    public void push(int node) {
        stack1.push(node);
        if(stack2.isEmpty()){
            stack2.push(node);
        }else if(node<=stack2.peek()){
            stack2.push(node);
        }else{
            stack2.push(stack2.peek());
        }
    }
    
    public void pop() {
        stack1.pop();
        stack2.pop();
    }
    
    public int top() {
        return stack1.peek();
    }
    
    public int min() {
        return stack2.peek();
    }

还有用一个栈就可以实现的思路。

    Stack<Integer> stack=new Stack<>();
    int minNum=Integer.MAX_VALUE;
    public void push(int node) {
        if(node<=minNum){
            stack.push(minNum);
            stack.push(node);
            minNum=node;
        }else{
            stack.push(node);
        }
    }
    
    public void pop() {
        if(stack.pop()==minNum)
            minNum=stack.pop();
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int min() {
        return minNum;
    }

21.栈的压入弹出序列

题目描述

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

题目思路

用一个辅助栈去压入元素,然后依靠逻辑遍历压入数组,最后判断该栈是否为空,空的话说明一一匹配上了。

    public boolean IsPopOrder(int [] pushA,int [] popA) {

        if(pushA.length == 0 || popA.length == 0)
            return false;
        Stack<Integer> s = new Stack<Integer>();
        //用于标识弹出序列的位置
        int popIndex = 0;
        for(int i = 0; i< pushA.length;i++){
            s.push(pushA[i]);
            //如果栈不为空,且栈顶元素等于弹出序列
            while(!s.empty() &&s.peek() == popA[popIndex]){
                //出栈
                s.pop();
                //弹出序列向后一位
                popIndex++;
            }
        }
        return s.empty();
    }


22.从上往下打印二叉树

题目描述

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

题目思路

层序遍历,队列实现。

    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if(root==null){
            return list;
        }
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode treeNode = queue.poll();
            if (treeNode.left != null) {
                queue.offer(treeNode.left);
            }
            if (treeNode.right != null) {
                queue.offer(treeNode.right);
            }
            list.add(treeNode.val);
        }
        return list;
    }

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

题目描述

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

题目思路

二叉搜索树的后序遍历,根结点为最后一个元素。数组前面的数小于根结点,数组后面的数大于根结点。从后往前找到第一个比根结点小的数,该数前面的数是否都小于根结点。然后分别递归前面数组和后面数组。都为true,结果才为true。

    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence.length==0)
            return false;
        if(sequence.length==1)
            return true;
        return judge(sequence,0,sequence.length-1);
    }
    public boolean judge(int[] sequence,int start,int end){
        if(start>=end)
            return true;
        int i=end;
        while(i>start&&sequence[i-1]>sequence[end])
            i--;
        for(int j=start;j<i-1;j++){
            if(sequence[j]>sequence[end])
                return false;
        }
        return judge(sequence,start,i-1)&&judge(sequence,i,end-1);
    }

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

题目描述

输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

题目思路

递归,且需要回退。

    private ArrayList<ArrayList<Integer>> listAll = new ArrayList<ArrayList<Integer>>();
    private ArrayList<Integer> list = new ArrayList<Integer>();
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        if(root==null)    return listAll;
        list.add(root.val);
        target-=root.val;
        if(target==0&&root.left==null&&root.right==null)
            listAll.add(new ArrayList<Integer>(list));
        FindPath(root.left,target);
        FindPath(root.right,target);
        target+=root.val;//要不要都可以,因为是值传递
        list.remove(list.size()-1);
        return listAll;
    }

25.复杂链表的复制

题目描述

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

题目思路

思路也很巧妙。分三步

第一步:在每个结点后面新建结点,新结点原来结点的next结点

第二步:新建的结点的随机指向是原来结点随机指向结点的下一个结点

第三步:根据结点的序号是奇数还是偶数进行拆分。

    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;
            pCur=node.next;
        }
        pCur=pHead;
        while(pCur!=null){
            if(pCur.random!=null)
                pCur.next.random=pCur.random.next;
            pCur=pCur.next.next;
        }
        RandomListNode head=pHead.next;
        RandomListNode cur=head;
        pCur=pHead;
        while(pCur!=null){
            pCur.next = pCur.next.next;
            if(cur.next!=null)
                cur.next = cur.next.next;
            cur = cur.next;
            pCur = pCur.next;
        }
        return head;
    }

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

题目描述

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

题目思路

本质上是个中序遍历。改编一下,用一个boolean类型判断是否是弹出的第一个结点来记录双向链表的根结点,还需记录前一个弹出的结点。

    public TreeNode Convert(TreeNode pRootOfTree) {
        Stack<TreeNode> stack=new Stack<>();
        boolean isFirst=true;
        TreeNode pre=null;
        TreeNode root=null;
        while(pRootOfTree!=null||!stack.isEmpty()){
            if(pRootOfTree!=null){
                stack.push(pRootOfTree);
                pRootOfTree=pRootOfTree.left;
            }else{
                pRootOfTree=stack.pop();
                if(isFirst){
                    root=pRootOfTree;
                    isFirst=false;
                    pre=root;
                }else{
                    pre.right=pRootOfTree;
                    pRootOfTree.left=pre;
                    pre=pRootOfTree;
                }
                pRootOfTree=pRootOfTree.right;
            }
        }
        return root;
    }

27.字符串的排列

题目描述

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

题目思路

全排列。把字符串看作是第一个字符与后面的字符这两个部分。然后依次将第一个字符与后面的字符逐个交换位置,递归后,再回退。递归的思路是把后面的部分也看做是这样的两个部分。

    public ArrayList<String> Permutation(String str) {
        ArrayList list=new ArrayList();
        if(str!=null&&str.length()>0){
            PermutationHelper(str.toCharArray(),0,list);
            Collections.sort(list);
        }
        return list;
    }
    public void PermutationHelper(char[] res,int i,ArrayList list){
        if(i==res.length-1){
            String str=String.valueOf(res);
            if(!list.contains(str))
                list.add(str);
        }else{
            for(int j=i;j<res.length;j++){
                swap(res,i,j);
                PermutationHelper(res,i+1,list);
                swap(res,i,j);
            }
        }
    }
    public void swap(char[] res,int i,int j){
        char temp=res[i];
        res[i]=res[j];
        res[j]=temp;
    }

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

题目描述

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

题目思路

num记录当前数字,count记录变化,下一个数字相同则加1,不相同则减1.最后还需要验证找出来的这个数是否真的出现次数超过一半。

    public int MoreThanHalfNum_Solution(int [] array) {
        if(array==null||array.length==0)
            return 0;
        int num=array[0];int count=1;
        for(int i=1;i<array.length;i++){
            if(count==0){
                num=array[i];
                count=1;
            }else if(array[i]==num){
                count++;
            }else{
                count--;
            }
        }
        count=0;
        for(int i=0;i<array.length;i++){
            if(array[i]==num)	count++;
        }
        return (count>array.length/2)?num:0;
    }

29.最小的k个数

题目描述

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

题目思路

java中优先队列用小顶堆实现的。先将元素都添加,然后直接按顺序输出前k个数就行了。

    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> res=new ArrayList<>();
        if(input==null||input.length==0||k>input.length)
            return res;
        PriorityQueue<Integer> queue=new PriorityQueue<>();
        for(int i=0;i<input.length;i++){
            queue.offer(input[i]);
        }
        for(int i=0;i<k;i++){
            res.add(queue.poll());
        }
        return res;
    }

30.连续子数组的最大和

题目描述

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?(子向量的长度至少是1)

题目思路

没有特殊技巧。

    public int FindGreatestSumOfSubArray(int[] array) {
        if(array==null||array.length==0)    return 0;
        int sum=array[0],tempsum=array[0];
        for(int i=1;i<array.length;i++){
            tempsum=(tempsum<0)?array[i]:tempsum+array[i];
            sum=(tempsum>sum)?tempsum:sum;
        }
        return sum;
    }

31.整数中1出现的次数(从1到n整数中1出现的次数)

题目描述

求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数。

题目思路

有难度。计算每一位出现1的次数,求和。每一位出现1的次数与高位的数和低位的数有关,研究规律,归纳为统一的计算方式

    public int NumberOf1Between1AndN_Solution(int n) {
    if(n<1)
        return 0;
    int count = 0;
    int base = 1;
    int round = n;
    while(round>0){
        int weight = round%10;
        round/=10;
        count += round*base;
        int former=n%base;
        if(weight==1)
            count+=former+1;
        else if(weight>1)
            count+=base;
        base*=10;
    }
    return count;
    }

32.把数组排成最小的数

题目描述

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

题目思路

实现比较器

    public String PrintMinNumber(int [] numbers) {
        int n;
        String s="";
        ArrayList<Integer> list=new ArrayList<Integer>();
        n=numbers.length;
        for(int i=0;i<n;i++){
            list.add(numbers[i]);
        }
        Collections.sort(list,new Comparator<Integer>(){
            public int compare(Integer str1,Integer str2){
                String s1=str1+""+str2;
                String s2=str2+""+str1;
                return s1.compareTo(s2);
            }
        });
        for(int j:list){
            s+=j;
        }
        return s;
    }

33.丑数

题目描述

把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

题目思路

有时候会不好理解。首先丑数本质上是由这三个数相互乘构成的。所以可以构建一个数组来计算前面的丑数,用三个int变量记录三个因子使用的次数。按照变量代表的数组下标表示的元素,乘以对应的因子取最小的数。

    public int GetUglyNumber_Solution(int index) {
        if(index<7)    return index;
        int[] res=new int[index];
        res[0]=1;
        int t2=0,t3=0,t5=0,i;
        for(i=1;i<index;i++){
            res[i]=Math.min(res[t2]*2,Math.min(res[t3]*3,res[t5]*5));
            if(res[i]==res[t2]*2) t2++;
            if(res[i]==res[t3]*3) t3++;
            if(res[i]==res[t5]*5) t5++;
        }
        return res[index-1];
    }

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

题目描述

在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置

题目思路

这题容易拿到手的时候想太多,但其实就是简单的构造一个hashmap或者256数组。

    public int FirstNotRepeatingChar(String str)
    {
        if(str == null || str.equals(""))
            return -1;
        int []counts = new int[256];
        char ch;
        int index;
        for(int i=0; i < str.length();i++)
        {
            ch = str.charAt(i);
            index = (int)ch;
            counts[index]++;
        }
        for(int i =0; i< str.length(); i++)
        {
            ch = str.charAt(i);
            index = (int)ch;
            if(counts[index] == 1)
                return i;
        }
        return 0;
    }

35.数组中的逆序对

题目描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

题目思路

归并排序的应用

    static final int P=1000000007;


    public int InversePairs(int [] array){
        if(array==null||array.length<2) return 0;
        int []tmp= Arrays.copyOf(array,array.length);
        return rec(0,tmp.length-1,tmp);
    }

    public int rec(int start,int end,int []array){
        if(start<end){
            int count=0;
            int mid=(start+end)/2;
            count+=rec(start,mid,array);
            count+=rec(mid+1,end,array); //若mid不加1会无限循环
            count+=merge(start,mid,end,array);
            return count%P;
        }
        return 0;
    }

    public int merge(int start,int mid,int end,int []array){
        int []newArray=new int[end-start+1];
        int i=start;
        int j=mid+1;
        int k=0,count=0;
        while(i<=mid&&j<=end){
            if(array[i]<=array[j])
                newArray[k++]=array[i++];
            else{
                count += mid - i + 1;
                newArray[k++]=array[j++];
                if(count>=P) {
                    count%=P;
                }
            }
        }
        while(i<=mid)
            newArray[k++]=array[i++];
        while (j<=end)
            newArray[k++]=array[j++];
        for(i=0;i<k;i++)
            array[start+i]=newArray[i];
        return count%P;
    }

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

题目描述

输入两个链表,找出它们的第一个公共结点。

题目思路

两个链表看作一个环。第一个链表走完了从第二个链表的头结点开始走,碰到后就是第一个公共结点。

    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode node1=pHead1,node2=pHead2;
        while(node1!=node2){
            node1=(node1==null?pHead2:node1.next);
            node2=(node2==null?pHead1:node2.next);
        }
        return node1;
    }

37.数字在排序数组中出现的次数

题目描述

统计一个数字在排序数组中出现的次数。

题目思路

二分查找。找到这个数字第一次出现和最后一次出现的位置。

    public int GetNumberOfK(int [] array , int k) {
        int start=GetLower(array,k);
        int end=GetUpper(array,k);
        if(start!=-1&&end!=-1)
            return end-start+1;
        else
            return 0;
    }
    public int GetLower(int [] array,int k){
        int start=0,end=array.length-1;
        while(start<=end){
            int mid=(end+start)>>1;
            if(array[mid]<k){
                start=mid+1;
            }else if(array[mid]>k){
                end=mid-1;
            }else if(mid-1>=0&&array[mid-1]==k){
                end=mid-1;
            }else{
                return mid;//如果mid=0了或者array[mid-1]!=k了就可以直接返回mid。
            }
        }
        return -1;
    }
    public int GetUpper(int [] array,int k){
        int start=0,end=array.length-1;
        while(start<=end){
            int mid=(end+start)>>1;
            if(array[mid]<k){
                start=mid+1;
            }else if(array[mid]>k){
                end=mid-1;
            }else if(mid+1<array.length&&array[mid+1]==k){
                start=mid+1;
            }else{
                return mid;
            }
        }
        return -1;
    }


38.二叉树的深度

题目描述

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

题目思路

递归:

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

非递归:

    public int TreeDepth(TreeNode root) {
        if(root==null)
            return 0;
        int left = TreeDepth(root.left);
        int right = TreeDepth(root.right);
        return Math.max(left, right) + 1;
    }
    public int TreeDepth2(TreeNode root) {
        if(root==null)  return 0;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        int count=0,depth=0,nextCount=1;
        while(!queue.isEmpty()){
            TreeNode node=queue.poll();
            count++;//记录这一层遍历了几个结点了
            if(node.left!=null)
                queue.offer(node.left);
            if(node.right!=null)
                queue.offer(node.right);
            if(count==nextCount){
                count=0;
                depth++;
                nextCount=queue.size();
            }
        }
        return depth;
    }

39.平衡二叉树

题目描述

输入一棵二叉树,判断该二叉树是否是平衡二叉树。

题目思路

声明一个boolean类型变量,递归的过程中一旦高度差大于1,直接返回false

    private boolean isBalanced=true;
    public boolean IsBalanced_Solution(TreeNode root) {
        getDepth(root);
        return isBalanced;
    }
    public int getDepth(TreeNode root){
        if(root==null)
            return 0;
        int left=getDepth(root.left);
        int right=getDepth(root.right);
        if(Math.abs(left-right)>1){
            isBalanced=false;
            return 0;
        }
        //更大的树才能代表树的深度
        return (left>right)?left+1:right+1;
    }

40.数组中只出现一次的数字

题目描述

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

题目思路

先将所有数异或得到的结果就是这两个数异或的结果。然后用  (~x+1)&x  的方法求得的值只有x从右到左第一次出现1的位数为1,其他位数都为0,然后再用该数与逐个数做&操作作为判断条件,相同的数肯定分在一起,再做^操作,相同的数又抵消了,得到结果。

    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        int tmp=0;
        for(int i:array)
            tmp^=i;
        int tmp2=(~tmp+1)&tmp; //tmp2只有第一个出现1的地方为1
        for(int i:array){
            if((i&tmp2)==0)
                num1[0]^=i;
            else 
                num2[0]^=i;
        }
    }

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

题目描述

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!

题目思路

声明两个变量start为1,end为2,当start<(sum+1)/2时进入循环,current记录当前区间的和,调整statt和end的位置得到满足条件的区间。

    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> lists=new ArrayList<ArrayList<Integer>>();
        if(sum<3)
            return lists;
        int start=1;
        int end=2;
        int middle=(sum+1)>>1;
        int curSum=3;
        while(start<middle){
            while(curSum<sum){
                end++;
                curSum+=end;
            }
            if(curSum==sum){
                ArrayList<Integer> list=new ArrayList<Integer>();
                for(int i=start;i<=end;i++){
                    list.add(i);
                }
                lists.add(list);
            }
            curSum-=start;
            start++;
        }
        return lists;
    }

42.和为S的两个数字

题目描述

输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

题目思路

数组满足递增,设两个头尾两个指针向中间滑动直到头指针位置>=尾指针位置

    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> res=new ArrayList<Integer>();
        int minMul=Integer.MAX_VALUE;
        int start=0;
        int end=array.length-1;
        while(start<end){
            int tempSum=array[start]+array[end];
            if(tempSum<sum){
                start++;
            }else if(tempSum==sum){
                int tempMul=array[start]*array[end];
                if(minMul>tempMul){
                    minMul=tempMul;
                    res.add(array[start]);
                    res.add(array[end]);
                }
                start++;
                end--;
            }else{
                end--;
            }
        }
        return res;
    }

43.左旋转字符串

题目描述

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它

题目思路

先旋转字符串前面的部分,再旋转后面的部分,然后全部旋转。

    public String LeftRotateString(String str,int n) {
        if(str==null||str.length()<0||str.length()<=n){
            return str;
        }
        char[] charArray=str.toCharArray();
        //翻转前n个  
        reverse(charArray,0,n-1);  
        //翻转后面的  
        reverse(charArray,n,charArray.length-1);  
        //整体翻转  
        reverse(charArray,0,charArray.length-1);  
        return String.valueOf(charArray);  
    }
    public void reverse(char[] array ,int start,int end){
        char temp=' ';
        while(start<end){
            temp=array[start];
            array[start++]=array[end];
            array[end--]=temp;
        }
    }
如果可以用库函数(连接两个字符串后再截取)
    public static String LeftRotateString(String str,int n) {
        if (str==null)  return null;
        int length=str.length();
        if(length==0) return "";
        n%=str.length();
        str+=str;
        return str.substring(n,n+length);
    }

44.翻转单词顺序列

题目描述

牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

题目思路

先将字符串全部旋转,然后再将每两个空格间的字符串依次旋转。注意最左边的字符串和最右边的字符串。

    public String ReverseSentence(String str) {
        if(str==null||str.length()==0){
            return str;
        }
        char[] array=str.toCharArray();
        reverse(array,0,str.length()-1);
        int blank=-1;
        for(int i=0;i<str.length();i++){
            if(array[i]==' '){
                int nextBlank=i;
                reverse(array,blank+1,nextBlank-1);
                blank=nextBlank;
            }
        }
        reverse(array,blank+1,str.length()-1);
        return new String(array);
    }
    public void reverse(char[] array,int start,int end){
        char temp=' ';
        while(start<end){
            temp=array[start];
            array[start++]=array[end];
            array[end--]=temp;
        }
    }
用库函数
    public String ReverseSentence(String str) {
        if(str==null) return null;
        if(str.length()==0) return "";
        if(str.equals(" ")) return " ";
        String [] ss=str.split(" ");
        String res="";
        for(String s:ss){
            res=s+" "+res;
        }
        return res.substring(0,res.length()-1);
    }

45.扑克牌顺子

题目描述

牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?

题目思路

循环的时候判断最大值和最小值的差是否大于或者等于5,且判断是否有相同的值(多种方法判断,用移位的方式记录也可以,数组也可以),这些条件都构不成顺子,直接返回false。

    public boolean isContinuous(int [] numbers) {
        if(numbers.length!=5)    return false;
        int min=14;
        int max=-1;
        int flag=0;
        for(int i=0;i<numbers.length;i++){
            int number=numbers[i];
            if(number<0||number>13)    return false;
            if(number==0)    continue;
            if(((flag>>number)&1)==1)    return false;
            flag |=1<<number;
            if(number>max)    max=number;
            if(number<min)    min=number;
            if(max-min>=5)    return false;
        }
        return true;
    }

46.孩子们的游戏(圆圈中最后剩下的数)

题目描述

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

题目思路

本质上是个约瑟夫环。用归纳法是最简单的。参考:https://blog.csdn.net/MapReduce/article/details/1549494

    public int LastRemaining_Solution(int n,int m) {
        if(n==0) return -1;
         
       int s=0;
       for(int i=2;i<=n;i++){
           s=(s+m)%i;
       }
       return s;
    }
用arraylist模拟环来做也可以
    public int LastRemaining_Solution(int n, int m) {
        if(n==0||m==0){
            return -1;
        }
        ArrayList<Integer> array=new ArrayList<Integer>();
        for(int i=0;i<n;i++){
            array.add(i);
        }
        int index=-1;
        while(array.size()>1){
            index=(index+m)%array.size();
            array.remove(index);
            index--;
        }
        return array.get(0);
    }

47.求1+2+3+...+n

题目描述

求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

题目思路

需要利用逻辑与来使递归终止

    public int Sum_Solution(int n) {
        int ans=n;
        //&&两侧的表达式结果必须为boolean型,所有&&右侧要用0判断是否与result相等,让右侧的表达式返回boolean型。 
        //不管返回的是true还是false,我们的目的仅仅是让&&右侧的表达式执行。
        boolean value=(n>0)&&((ans+=Sum_Solution(n-1))!=0);
        return ans;
    }

48.不用加减乘除做加法

题目描述

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

题目思路

用逻辑运算计算二进制相加。异或相当于各位相加,没计算进位的值。与操作并且左移1位相当于计算进位值了,循环,直到进位值为0

    public int Add(int num1,int num2) {
        while(num2!=0){
            int temp=num1^num2;
            num2=(num1&num2)<<1;
            num1=temp;
        }
        return num1;
    }

49.把字符串转换成整数

题目描述

将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0

题目思路

主要查考各种边界值判断条件

    public int StrToInt(String str) {
        if(str==null||str.length()==0)    return 0;
        int sign=1,base=0,i=0,n=str.length();
        while (i < n && str.charAt(i) == ' ') ++i;
        if(str.charAt(i) == '+' || str.charAt(i) == '-'){
            sign = (str.charAt(i++) == '+') ? 1 : -1;
        }
        while (i < n && str.charAt(i) >= '0' && str.charAt(i) <= '9'){
            int digit=str.charAt(i) - '0';
            if(base > Integer.MAX_VALUE / 10 || (base == Integer.MAX_VALUE / 10 && digit > Integer.MAX_VALUE%10)) {
                return (sign == 1) ? Integer.MAX_VALUE : Integer.MIN_VALUE;
            }
            base = 10 * base + digit;
            i++;
        }
        if(i<n)    return 0;
        return base * sign;
    }

50.数组中重复的数字

题目描述

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

题目思路

第一反应是用辅助数组,但是有更好的办法。再原数组上进行操作,不需要额外的空间,利用了数字在0到n-1范围的特性。遍历时,如果以该数字为索引的值如果小于length,就加上length。如果大于,就说明之前数字在这里标记过了,将其减去length,也就是还原了,然后判断这个数字为索引对应的值是否大于length,如果大于说明找到了重复值。

    public boolean duplicate(int numbers[],int length,int [] duplication) {
        for(int i=0;i<length;i++){
            int index=numbers[i];
            if(length<=index){
                index-=length;
            }
            if(length<=numbers[index]){
                duplication[0]=index;
                return true;
            }
            numbers[index]+=length;
        }
        return false;
    }

51.构建乘积数组

题目描述

给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。

题目思路

不能用除法。画出两个数组的矩阵,找规律,可以发现对角线上都为0,先迭代求左下部分元素,由上至下填充到数组,然后再求右上部分,由下至上迭代乘以数组中的值。

    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;
    }

52.正则表达式匹配

题目描述

请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配

题目思路

请实现一个函数用当模式中的第二个字符不是“*”时:

1.如果字符串第一个字符和模式中的第一个字符相匹配,那么字符串和模式都后移一个字符,然后匹配剩余的。

2.如果字符串第一个字符和模式中的第一个字符不匹配,直接返回false;

而当模式中的第二个字符是”*“时:

如果字符串第一个字符跟模式第一个字符不匹配,则模式后移2个字符,继续匹配。如果字符串第一个字符跟模式第一个字符匹配,可以有3种匹配方式:

1.模式后移2个字符,相当于x*被忽略

2.字符串后移1个字符,模式后移2个字符

3.字符串后移1个字符,模式不变,即继续匹配字符下一位,因为*可以匹配多位

    public boolean match(char[] str, char[] pattern)
    {
        if(str==null||pattern==null)
            return false;
        int strIndex=0;
        int patternIndex=0;
        return matchCore(str,strIndex,pattern,patternIndex);
    }
    public boolean matchCore(char[] str,int strIndex,char[] pattern,int patternIndex){
        //有效性检验:str到尾,pattern到尾,匹配成功
        if(strIndex==str.length&&patternIndex==pattern.length){
            return true;
        }
        //有效性检验:str没到尾,pattern到尾,匹配失败
        if(strIndex!=str.length&&patternIndex==pattern.length){
            return false;
        }
    //模式第2个是*,且字符串第1个跟模式第1个匹配,分3种匹配模式;如不匹配,模式后移2位
    if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') {
        if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) {
            return matchCore(str, strIndex, pattern, patternIndex + 2)//模式后移2,视为x*匹配0个字符
                    || matchCore(str, strIndex + 1, pattern, patternIndex + 2)//视为模式匹配1个字符
                    || matchCore(str, strIndex + 1, pattern, patternIndex);//*匹配1个,再匹配str中的下一个
        } else {
            return matchCore(str, strIndex, pattern, patternIndex + 2);
        }
    }
    //模式第2个不是*,且字符串第1个跟模式第1个匹配,则都后移1位,否则直接返回false
    if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) {
        return matchCore(str, strIndex + 1, pattern, patternIndex + 1);
    }
    return false;
    }


53.表示数值的字符串

题目描述

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。

题目思路

懂一点正则表达式的写法

    public boolean isNumeric(char[] str) {
        String string=String.valueOf(str);
        return string.matches("[\\+-]?[0-9]*(\\.[0-9]*)?([eE][\\+-]?[0-9]+)?");
    }

用库函数

    public boolean isNumeric(char[] str) {
        try {
            double re = Double.parseDouble(new String(str));
        } catch (NumberFormatException e) {
            return false;
        }
        return true;
    }


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

题目描述

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。

题目思路

长度为256的数组来记录字符出现的次数。arraylist记录出现次数为1的数,在插入的过程中如果超过1次,就删除。

    int[] countArr=new int[256];
    ArrayList<Character> charList=new ArrayList<Character>();
    public void Insert(char ch)
    {
        countArr[ch]++;
        if(countArr[ch]==1){
            charList.add(ch);
        }else{
            charList.remove((Character)ch);
        }
    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
        if(charList.size()==0){
            return '#';
        }else{
            return charList.get(0);
        }
    }


55.链表种环的入口地址

题目描述

一个链表中包含环,请找出该链表的环的入口结点。

题目思路

用快慢指针,相遇点肯定在环内,画图可以分析出,起点到环起点的距离为环长度的整数倍加上俩指针相遇的结点到环的距离,故让其中一个指针回到起点重新开始,和另外一个指针同步走直到相遇,相遇点一定是环入口点。

    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if(pHead==null||pHead.next==null||pHead.next.next==null)
            return null;
        ListNode fast=pHead.next.next;
        ListNode slow=pHead.next;
        while(slow!=fast){
            if(fast.next!=null&&fast.next.next!=null){
                fast=fast.next.next;
                slow=slow.next;
            }else{
                return null;
            }
        }
        fast=pHead;
        while(fast!=slow){
            fast=fast.next;
            slow=slow.next;
        }
        return slow;
    }


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

题目描述

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

题目思路

preNode记录前一个结点,初始化为null,curNode记录当前结点。根据curNode是否为null来遍历

    public ListNode deleteDuplication(ListNode pHead)
    {
        if(pHead==null)
            return null;
        ListNode preNode=null;
        ListNode curNode=pHead;
        while(curNode!=null){
            if(curNode.next!=null&&curNode.val==curNode.next.val){
                int value=curNode.val;
                while(curNode.next!=null&&curNode.next.val==value){
                    curNode=curNode.next;
                }
                if(preNode==null){
                    pHead=curNode.next;
                }else{
                    preNode.next=curNode.next;
                }
            }else{
                preNode=curNode;
            }
            curNode=curNode.next;
        }
        return pHead;
    }

57.二叉树的下一个结点

题目描述

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

题目思路

没特殊技巧,就是根据逻辑来写代码。如果有右子树就找右子树的最左结点;如果没有右子树,就找第一个当前结点是其父结点左孩子的结点

    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if(pNode==null) return null;
        if(pNode.right!=null){
            TreeLinkNode tmp=pNode.right;
            while(tmp.left!=null)
                tmp=tmp.left;
            return tmp;
        }else{
            TreeLinkNode parent=pNode.next;
            while(parent!=null&&parent.left!=pNode){
                pNode=parent;
                parent=pNode.next;
            }
            return parent;
        }
    }

58.对称的二叉树

题目描述

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

题目思路

层序遍历。递归

    boolean isSymmetrical(TreeNode pRoot)
    {
        if(pRoot==null)
            return true;
        return comRoot(pRoot.left,pRoot.right);
    }
    boolean comRoot(TreeNode left,TreeNode right){
        if(left==null)    return right==null;
        if(right==null)    return false;
        if(left.val!=right.val)    return false;
        return comRoot(left.right,right.left)&&comRoot(left.left,right.right);
    }
非递归,两个队列来实现,出列的时候判断
    boolean isSymmetrical(TreeNode pRoot)
    {
        if(pRoot==null) return false;
        Queue<TreeNode> queue1=new LinkedList<>();
        Queue<TreeNode> queue2=new LinkedList<>();
        TreeNode left,right;
        queue1.offer(pRoot.left);
        queue2.offer(pRoot.right);
        while (!queue1.isEmpty()&&!queue2.isEmpty()){
            left=queue1.poll();
            right=queue2.poll();
            if(left==null&&right==null)
                continue;
            if(left==null||right==null)
                return false;
            if(left.val!=right.val)
                return false;
            queue1.offer(left.left);
            queue1.offer(left.right);
            queue2.offer(right.right);
            queue2.offer(right.left);
        }
        return true;
    }

59.按之字形顺序打印二叉树

题目描述

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

题目思路

用到descendingIterator函数。层序遍历,一个变量记录是奇数行还是偶数行,一个变量记录当前层的节点数。

    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res=new ArrayList<ArrayList<Integer>>();
        if(pRoot==null)    return res;
        LinkedList<TreeNode> q=new LinkedList<>();
        q.offer(pRoot);
        int row=0;
        while(!q.isEmpty()){
            int size=q.size();
            Iterator<TreeNode> iter=null;
            ArrayList<Integer> tmp=new ArrayList<>();
            if((row&1)>0){
                iter=q.descendingIterator();
            }else{
                iter=q.iterator();
            }
            while(iter.hasNext()){
                tmp.add(iter.next().val);
            }
            for(int i=0;i<size;i++){
                TreeNode node1=q.poll();
                if(node1.left!=null)
                    q.offer(node1.left);
                if(node1.right!=null)
                    q.offer(node1.right);
            }
            row++;
            res.add(tmp);
        }
        return res;
    }

60.把二叉树打印成多行

题目描述

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

题目思路

就是个层序遍历,记录当前层的结点数就行了

    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res=new ArrayList<ArrayList<Integer>>();
        if(pRoot==null)    return res;
        LinkedList<TreeNode> q=new LinkedList<>();
        q.offer(pRoot);
        int row=0;
        while(!q.isEmpty()){
            int size=q.size();
            ArrayList<Integer> tmp=new ArrayList<>();
            for(int i=0;i<size;i++){
                TreeNode node1=q.poll();
                tmp.add(node1.val);
                if(node1.left!=null)
                    q.offer(node1.left);
                if(node1.right!=null)
                    q.offer(node1.right);
            }
            row++;
            res.add(tmp);
        }
        return res;
    }

61.序列化二叉树

题目描述

请实现两个函数,分别用来序列化和反序列化二叉树

题目思路

前序遍历,递归

    public int index = -1;
    String Serialize(TreeNode root) {
        StringBuffer sb = new StringBuffer();
        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;
  }

62.二叉搜索树的第k个结点

题目描述

给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。

题目思路

中序遍历

    TreeNode KthNode(TreeNode pRoot, int k)
    {
        if(k<=0)    return null;
        int count=0;
        Stack<TreeNode> stack=new Stack<>();
        while(pRoot!=null||!stack.isEmpty()){
            if(pRoot!=null){
                stack.push(pRoot);
                pRoot=pRoot.left;
            }else{
                pRoot=stack.pop();
                count++;
                if(count==k)
                    return pRoot;
                pRoot=pRoot.right;
            }
        }
        return null;
    }

63.数据流中的中位数

题目描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

题目思路

方法很巧妙,构造一个小顶堆和大顶堆,cout为偶数时,将较大的数通过大顶堆过滤后添加到小顶堆,count为奇数时,较小的数通过小顶堆过滤后加入到大顶堆

    private int count=0;
    private PriorityQueue<Integer> minHeap=new PriorityQueue<Integer>();
    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){
            maxHeap.offer(num);
            int filteredMaxNum = maxHeap.poll();
            minHeap.offer(filteredMaxNum);
        }else{
            minHeap.offer(num);
            int filteredMinNum = minHeap.poll();
            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());
        }
    }

64.滑动窗口的最大值

题目描述

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

题目思路

双端队列的头部记录当前窗口的最大值,每进来一个新数,从尾部添加,把比该数小的数都删除。还要记得判断最大值是否过期。

    public ArrayList<Integer> maxInWindows(int [] num, int size)
    {
        ArrayList<Integer> res=new ArrayList<>();
        if(size==0)    return res;
        int begin;
        ArrayDeque<Integer> q=new ArrayDeque<>();
        for(int i=0;i<num.length;i++){
            begin=i-size+1;
            if(q.isEmpty())
                q.offer(i);
            else if(begin>q.peekFirst())
                q.pollFirst();
            while((!q.isEmpty())&&num[q.peekLast()]<=num[i])
                q.pollLast();
            q.offer(i);
            if(begin>=0)
                res.add(num[q.peekFirst()]);
        }
        return res;
    }

65.矩阵中的路径

题目描述

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

题目思路

构造一个flag数组来记录是否已经经过该结点了,然后递归。

    public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
    {
        //用来记录是否被访问到了
        int  flag[]=new int[matrix.length];
        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                if(helper(matrix,rows,cols,i,j,str,0,flag))
                    return true;
            }
        }
        return false;
    }

    public boolean helper(char[] matrix,int rows,int cols,int i,int j,char[] str,int len,int[] flag){
        int index=i*cols+j;
        if(i<0||i>=rows||j<0||j>=cols||matrix[index]!=str[len]||flag[index]==1)
            return false;
        if(len==str.length-1)
            return true;
        flag[index]=1;
        if(helper(matrix,rows,cols,i-1,j,str,len+1,flag)
          ||helper(matrix,rows,cols,i+1,j,str,len+1,flag)
          ||helper(matrix,rows,cols,i,j-1,str,len+1,flag)
          ||helper(matrix,rows,cols,i,j+1,str,len+1,flag))
            return true;
        flag[index]=0;
        return false;
    }

66.机器人的运动范围

题目描述

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

题目思路

    public int movingCount(int threshold, int rows, int cols) {
        int flag[][] = new int[rows][cols]; //记录是否已经走过
        return helper(0, 0, rows, cols, flag, threshold);
    }
 
    private int helper(int i, int j, int rows, int cols, int[][] flag, int threshold) {
        if (i < 0 || i >= rows || j < 0 || j >= cols || numSum(i) + numSum(j)  > threshold || flag[i][j] == 1) return 0;    
        flag[i][j] = 1;
        return helper(i - 1, j, rows, cols, flag, threshold)
            + helper(i + 1, j, rows, cols, flag, threshold)
            + helper(i, j - 1, rows, cols, flag, threshold)
            + helper(i, j + 1, rows, cols, flag, threshold)
            + 1;
    }
 
    private int numSum(int i) {
        int sum = 0;
        do{
            sum += i%10;
        }while((i = i/10) > 0);
        return sum;
    }











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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值