剑指Offer刷题记录(完结撒花)

前言:在刷了LeetCode之后,再来刷剑指会感觉很简单~
需要特别注意的是:剑指你需要额外导包,这一点在笔试的时候也要注意~

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

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

【2】写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
解题思路:
可以用三步走的方式计算二进制值相加:
第一步:相加各位的值,不算进位,二进制每位相加就相当于各位做异或操作
第二步:计算进位值,相当于各位做与操作,再向左移一位
第三步重复上述两步,当进位值为0,跳出循环

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

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

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

【4】一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
解题思路:
n = n时,会有n中跳的方式,1阶、2阶…n阶,得出结论:
f(n) = f(n-1)+f(n-2)+…+f(n-(n-1)) + f(n-n) => f(0) + f(1) + f(2) + f(3) + … + f(n-1)=>f(n) = f(0) + f(1) + f(2) + f(3) + … + f(n-2) + f(n-1) = f(n-1) + f(n-1)=> f(n) = 2*f(n-1)

public class Solution {
    public int JumpFloorII(int target) {
        if(target==1||target==0) return 1;
        return 2*JumpFloorII(target-1);
    }
}

【5】求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
解题思路:
1.需利用逻辑与的短路特性实现递归终止
2.当n==0时,(n>0)&&((sum+=Sum_Solution(n-1))>0)只执行前面的判断,为false,然后直接返回0
3.当n>0时,执行sum+=Sum_Solution(n-1),实现递归计算Sum_Solution(n)

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

【6】给定一个数组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]。不能使用除法。
解题思路:
B[i]的值可以看作矩阵中每行的乘积。
下三角用连乘可以很容求得,上三角,从下向上也是连乘。先算下三角中的连乘,即先算出B[i]中的一部分,然后倒过来按上三角中的分布规律,把另一部分也乘进去。

import java.util.ArrayList;
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;
    }
}

【7】HZ发话:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        int max=Integer.MIN_VALUE,d=0;
        for(int i=0;i<array.length;i++){
            d=d<0? array[i]:d+array[i];
            max=Math.max(max,d);
        }
        return max;
    }
}

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

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(stack1.empty()&&stack2.empty()){
            throw new RuntimeException("Queue is empty!");
        }
        if(stack2.empty()){
            while(!stack1.empty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
}

【9】一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

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

【10】输入一棵二叉树,判断该二叉树是否是平衡二叉树。
解题思路:从下往上遍历,如果子树是平衡二叉树,则返回子树的高度;如果发现子树不是平衡二叉树,则直接停止遍历,这样至多只对每个结点访问一次。

public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        return getDepth(root) != -1;
    }
    private int getDepth(TreeNode root) {
        if (root == null) return 0;
        int left = getDepth(root.left);
        if (left == -1) return -1;
        int right = getDepth(root.right);
        if (right == -1) return -1;
        return Math.abs(left - right) > 1 ? -1 : 1 + Math.max(left, right);
    }
}

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

public class Solution {
    public int RectCover(int target) {
        if(target==0){
            return 0;
        }
        if(target==1){
            return 1;
        }
        if(target==2){
            return 2;
        }
        return RectCover(target-1)+RectCover(target-2);
    }
}

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

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

【13】求出1 ~ 13的整数中1出现的次数,并算出100~ 1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
解题思路:设定整数点(如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)

public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
         int ones = 0;
        for (long m = 1; m <= n; m *= 10) {
            long a = n/m, b = n%m;
            if(a%10 == 0)
                ones += a / 10 * m;
            else if(a%10 == 1) 
                ones += (a/10*m) + (b+1);
            else
                ones += (a/10+1)* m;
        }
        return ones;
    }
}

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

public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
         ListNode p1=pHead1;
         ListNode p2=pHead2;
        while(p1!=p2){
            p1=p1==null? pHead2:p1.next;
            p2=p2==null? pHead1:p2.next;
        }
        return p1;
    }
}

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

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array.length==0){
            return 0;
        }
        int left=0;
        int right=array.length-1;
        while(left<right){
            int mid=(left+right)>>>1;
            if(array[mid]>array[right]){
                left=mid+1;
            }else{
                right=mid;
            }
        }
        return array[left];
    }
}

【16】给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],…,k[m]。请问k[0]xk[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
解题思路:
首先判断k[0]到k[m]可能有哪些数字,实际上只可能是2或者3。其次看2和3的数量,2的数量肯定小于3个,因为2 * 2 * 2<3 * 3,那么题目就简单了。直接用n除以3,根据得到的余数判断是一个2还是两个2还是没有2就行了。由于题目规定m>1,所以2只能是1 * 1,3只能是2 * 1,这两个特殊情况直接返回就行了。

//DP动态规划
public class Solution {
    public int cutRope(int n) {
       // n<=3的情况,m>1必须要分段,例如:3必须分成1、2;1、1、1 ,n=3最大分段乘积是2,
        if(n==2)
            return 1;
        if(n==3)
            return 2;
        int[] dp = new int[n+1];
        /*
        下面3行是n>=4的情况,跟n<=3不同,4可以分很多段,比如分成1、3,
        这里的3可以不需要再分了,因为3分段最大才2,不分就是3。记录最大的。
         */
        dp[1]=1;
        dp[2]=2;
        dp[3]=3;
        int res=0;//记录最大的
        for (int i = 4; i <= n; i++) {
            for (int j = 1; j <=i/2 ; j++) {
                res=Math.max(res,dp[j]*dp[i-j]);
            }
            dp[i]=res;
        }
        return dp[n];
    }
}
//贪心算法
public class Solution {
    public int cutRope(int target) {
        if(target==2) return 1;
        if(target==3) return 2;
        int x=target%3;
        int y=target/3;
        if(x==0){
            return (int)Math.pow(3,y);
        }else if(x==1){
            return 2*2*(int)Math.pow(3,y-1);
        }else{
            return 2*(int)Math.pow(3,y);
        }
    }
}

【17】给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0。

public class Solution {
    public double Power(double base, int exponent) {
        int n=exponent;
        if(exponent==0){
            return 1;
        }
        if(exponent<0){
            if(base==0){
                throw new RuntimeException("分母不能等于零!");
            }else{
                n=-exponent;
            }
        }
        double result=caculate(base,n);
        return (exponent<0)? 1/result:result;
  }
    public double caculate(double base,int exponent){
        double result;
        if(exponent==0){
            return 1;
        }else if(exponent==1){
            return base;
        }else{
            result=caculate(base,exponent/2);
        }
         result=result*result;
        if(exponent%2==1){
            result=result*base;
        }
        return result;
    }
}

【18】定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。

import java.util.Stack;
public class Solution {
    Stack<Integer> in=new Stack<>();
    Stack<Integer> out=new Stack<>();
    public void push(int node) {
        in.push(node);
        if(out.isEmpty()){
            out.push(node);
        }else{
            if(out.peek()>=node){
                out.push(node);
            }
        }
    }
    public void pop() {
        if(!in.isEmpty()){
            if(out.peek()==in.pop()){
                out.pop();
            }
        }
    }
    public int top() {
        return in.peek();
    }
    public int min() {
        return out.peek();
    }
}

【19】给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
解题思路:
设置快慢指针,都从链表头出发,快指针每次走两步,慢指针一次走一步,假如有环,一定相遇于环中某点(结论1)。接着让两个指针分别从相遇点和链表头出发,两者都改为每次走一步,最终相遇于环入口(结论2)。以下是两个结论证明:
两个结论:
1、设置快慢指针,假如有环,他们最后一定相遇。
2、两个指针分别从链表头和相遇点继续出发,每次走一步,最后一定相遇与环入口。
证明结论1:设置快慢指针fast和low,fast每次走两步,low每次走一步。假如有环,两者一定会相遇(因为low一旦进环,可看作fast在后面追赶low的过程,每次两者都接近一步,最后一定能追上)。
证明结论2:
设:链表头到环入口长度为a,环入口到相遇点长度为b,相遇点到环入口长度为c,如图所示:
在这里插入图片描述
则:相遇时快指针路程=a+(b+c)k+b ,k>=1 其中b+c为环的长度,k为绕环的圈数(k>=1,即最少一圈,不能是0圈,不然和慢指针走的一样长,矛盾)。慢指针路程=a+b。快指针走的路程是慢指针的两倍,所以:(a+b)*2=a+(b+c)k+b。化简可得:a=(k-1)(b+c)+c ,即链表头到环入口的距离=相遇点到环入口的距离+(k-1)圈环长度。其中k>=1,所以k-1>=0圈。所以两个指针分别从链表头和相遇点出发,最后一定相遇于环入口。

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

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

public class Solution {
    public String LeftRotateString(String str,int n) {
        int len=str.length();
        if(len==0||len==1) return str;
        n=n%len;
        return str.substring(n,len)+str.substring(0,n);
    }
}

【21】请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。如果当前字符流没有存在出现一次的字符,返回#字符。

public class Solution {
    int[] table=new int[256];
    StringBuffer sb=new StringBuffer();
    //Insert one char from stringstream
    public void Insert(char ch)
    {
        sb.append(ch);
        table[ch]++;
    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
        for(char c:sb.toString().toCharArray()){
            if(table[c]==1) return c;
        }
        return '#';
    }
}

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

public class Solution {
    boolean isSymmetrical(TreeNode pRoot)
    {
        if(pRoot==null) return true;
        return check(pRoot.left,pRoot.right);
    }
    boolean check(TreeNode left,TreeNode right){
        if(left==null&&right!=null||left!=null&&right==null){
            return false;
        }else if(left==null&&right==null){
            return true;
        }else if(left!=null&&right!=null&&left.val!=right.val){
            return false;
        }
        return check(left.left,right.right)&&check(left.right,right.left);
    }
}

【23】大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。

public class Solution {
    public int Fibonacci(int n) {
        if(n==0) return 0;
        if(n==1) return 1;
        if(n==2) return 1;
        return Fibonacci(n-1)+Fibonacci(n-2);
    }
}

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

import java.util.ArrayList;
import java.util.Queue;
import java.util.LinkedList;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> re=new ArrayList<>();
        if(pRoot==null) return re;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(pRoot);
        while(!queue.isEmpty()){
            int size=queue.size();
            ArrayList<Integer> t=new ArrayList<>();
            while(size>0){
                size--;
                TreeNode node=queue.poll();
                t.add(node.val);
                if(node.left!=null){
                    queue.offer(node.left);
                }
                if(node.right!=null){
                    queue.offer(node.right);
                }
            }
            re.add(new ArrayList<>(t));
        }
        return re;
    }
}

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

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        if(array.length==0) return 0;
       int left=0,right=array.length-1;
        while(left<right){
            int mid=(left+right)>>>1;
            if(array[mid]==k){
                left=mid;
                break;
            }
            if(array[mid]<k){
                left=mid+1;
            }else{
                right=mid;
            }
        }
        if(array[left]!=k) return 0;
        int count=1,l=left-1,r=left+1;
        while(l>=0&&array[l]==k){
            count++;
            l--;
        }
        while(r<array.length&&array[r]==k){
            count++;
            r++;
        }
        return count;
    }
}

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

import java.util.LinkedList;
public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n<1||m<1) return -1;
        if(n==1) return 0;
        LinkedList<Integer> list=new LinkedList<>();
        for(int i=0;i<n;i++){
            list.add(i);
        }
        int index=0;
        while(list.size()>1){
            index=(index+m-1)%list.size();
            list.remove(index);
        }
        return list.get(0);
    }
}

【27】一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
解题思路:
首先:位运算中异或的性质:两个相同数字异或=0,一个数和0异或还是它本身。当只有一个数出现一次时,把数组中所有的数,依次异或运算,最后剩下的就是落单的数,因为成对儿出现的都抵消了。依照这个思路,对于两个数(假设为A、B)出现一次的数组。首先还是先异或,剩下的数字肯定是A、B异或的结果,这个结果的二进制中的1,表现的是A和B的不同的位。取第一个1所在的位数,假设是第3位,接着把原数组分成两组,分组标准是第3位是否为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:array){
            bitResult ^= i;
        }
        int index = findFirst1(bitResult);
        for(int i:array){
            if(isBit1(i, index)){
                num1[0] ^=i;
            }else{
                num2[0] ^=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;
    }
}

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

public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if(pNode==null){
            return null;
        }
        if(pNode.right!=null){
            pNode=pNode.right;
            while(pNode.left!=null){
                pNode=pNode.left;
            }
            return pNode;
        }
        while(pNode.next!=null){
            if(pNode.next.left==pNode){
                return pNode.next;
            }
            pNode=pNode.next;
        }
        return null;
    }
}

【29】输入一个链表,反转链表后,输出新链表的表头。

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

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

import java.util.ArrayList;
import java.util.Queue;
import java.util.LinkedList;
/**
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> re=new ArrayList<>();
        if(root==null) return re;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int size=queue.size();
            while(size>0){
                size--;
                TreeNode node=queue.poll();
                re.add(node.val);
                if(node.left!=null){
                    queue.offer(node.left);
                }
                if(node.right!=null){
                    queue.offer(node.right);
                }
            }
        }
        return re;
    }
}

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

import java.util.Stack;
public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        Stack<Integer> stack=new Stack<>();
        int j=0;
        for(int i:pushA){
            stack.push(i);
            while(!stack.isEmpty()&&stack.peek()==popA[j]){
                j++;
                stack.pop();
            }
        }
        return stack.isEmpty();
    }
}

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

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

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

public class Solution {
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        if(length==0) return false;
        int[] num=new int[length];
        for(int i:numbers){
            if(num[i]==0){
                num[i]++;
            }else{
                duplication[0]=i;
                return true;
            }
        }
        return false;
    }
}

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

//非递归
import java.util.Stack;
public class Solution {
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree==null){
            return null;
        }
        Stack<TreeNode> stack=new Stack<TreeNode>();
        TreeNode head=null;
        TreeNode pre=null;
        while(pRootOfTree!=null||!stack.isEmpty()){
            while(pRootOfTree!=null){
                stack.push(pRootOfTree);
                pRootOfTree=pRootOfTree.left;
            }
            pRootOfTree=stack.pop();
            if(head==null){
                head=pRootOfTree;
                pre=head;
            }else{
                pre.right=pRootOfTree;
                pRootOfTree.left=pre;
                pre=pRootOfTree;
            }
            pRootOfTree=pRootOfTree.right;
        }
        return head;
    }
}
//递归
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);
    }
}

【35】数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
解题思路:
如果有符合条件的数字,则它出现的次数比其他所有数字出现的次数和还要多。在遍历数组时保存两个值:一是数组中一个数字,一是次数。遍历下一个数字时,若它与之前保存的数字相同,则次数加1,否则次数减1;若次数为0,则保存下一个数字,并将次数置为1。遍历结束后,所保存的数字即为所求。然后再判断它是否符合条件即可。

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        int count=1,num=array[0];
        for(int i=1;i<array.length;i++){
            if(num!=array[i]){
                count--;
            }else{
                count++;
            }
            if(count==0){
                num=array[i];
                count=1;
            }
        }
        count=0;
        for(int i:array){
            if(i==num){
                count++;
            }
        }
        return count*2>array.length? num:0;
    }
}

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
public class Solution {
    public String PrintMinNumber(int [] numbers) {
        String[] num=new String[numbers.length];
        for(int i=0;i<numbers.length;i++){
            num[i]=numbers[i]+"";
        }
        Arrays.sort(num,new Comparator<String>(){
            public int compare(String str1,String str2){
                return (str1+str2).compareTo((str2+str1));
            }
        });
        StringBuffer sb=new StringBuffer();
        for(String s:num){
            sb.append(s);
        }
        return sb.toString();
    }
}

【37】在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写)。

public class Solution {
    public int FirstNotRepeatingChar(String str) {
        int[] index=new int[58];
        for(char c:str.toCharArray()){
            index[c-65]++;
        }
        for(int i=0;i<str.toCharArray().length;i++){
            if(index[str.charAt(i)-65]==1){
                return i;
            }
        }
        return -1;
    }
}

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

import java.util.ArrayList;
public class Solution {
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
         //存放结果
        ArrayList<ArrayList<Integer> > result = new ArrayList<>();
        //两个起点,相当于动态窗口的两边,根据其窗口内的值的和来确定窗口的位置和大小
        int plow = 1,phigh = 2;
        while(phigh > plow){
            //由于是连续的,差为1的一个序列,那么求和公式是(a0+an)*n/2
            int cur = (phigh + plow) * (phigh - plow + 1) / 2;
            //相等,那么就将窗口范围的所有数添加进结果集
            if(cur == sum){
                ArrayList<Integer> list = new ArrayList<>();
                for(int i=plow;i<=phigh;i++){
                    list.add(i);
                }
                result.add(list);
                plow++;
            //如果当前窗口内的值之和小于sum,那么右边窗口右移一下
            }else if(cur < sum){
                phigh++;
            }else{
            //如果当前窗口内的值之和大于sum,那么左边窗口右移一下
                plow++;
            }
        }
        return result;
	}
}

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

public class Solution {
    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(list2.next,list1);
            return list2;
        }
    }
}

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

public class Solution {
    public boolean isNumeric(char[] str) {
        boolean sign=false,decimal=false,hase=false;
        for(int i=0;i<str.length;i++){
            if(str[i]=='e'||str[i]=='E'){
                if(i==str.length-1||hase) return false;
                hase=true;
            }else if(str[i]=='.'){
                if(decimal) return false;
                if(hase) return false;
                decimal=true;
            }else if(str[i]=='+'||str[i]=='-'){
                if(sign&&str[i-1]!='e'&&str[i-1]!='E') return false;
                if(!sign&&i>0&&str[i-1]!='e'&&str[i-1]!='E') return false;
                sign=true;
            }else if(str[i]<'0'||str[i]>'9'){
                return false;
            }
        }
        return true;
    }
}

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

public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        if(head==null) return null;
        ListNode a=head;
        while(k>0&&a!=null){
            a=a.next;
            k--;
        }
        if(k!=0) return null;
        ListNode res=head;
        while(a!=null){
            res=res.next;
            a=a.next;
        }
        return res;
    }
}

【42】输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 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.

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printMatrix(int [][] matrix) {
        int left=0;
		int right=matrix[0].length-1;
		int top=0;
		int bottom=matrix.length-1;
		ArrayList<Integer> result=new ArrayList<Integer>();
		while(left<=right&&top<=bottom) {
            for(int i=left;i<=right;i++) {
                result.add(matrix[top][i]);
            }
            top++;
            if(top>bottom) break;
            for(int j=top;j<=bottom;j++) {
                result.add(matrix[j][right]);
            }
            right--;
            if(right<left) break;
            for(int k=right;k>=left;k--) {
               result.add(matrix[bottom][k]);
            }
            bottom--;
            if(top>bottom) break;
            for(int l=bottom;l>=top;l--) {
                result.add(matrix[l][left]);
            }
            left++;
		}
		return result;
    }
}

【43】请实现一个函数用来匹配包括’.‘和’ * ’ 的正则表达式。模式中的字符’.‘表示任意一个字符,而’ * '表示它前面的字符可以出现任意次(包含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 class Solution {
    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;
        }
        //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;
    }
}

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

import java.util.ArrayList;
import java.util.Arrays;
public class Solution {
    ArrayList<String> re=new ArrayList<>();
    public ArrayList<String> Permutation(String str) {
        if(str.length()==0) return re;
       char[] strs=str.toCharArray();
        boolean[] used=new boolean[strs.length];
        Arrays.sort(strs);
        StringBuffer sb=new StringBuffer();
        dfs(used,strs,sb);
        return re;
    }
    public void dfs(boolean[] used,char[] strs,StringBuffer sb){
        if(sb.length()==strs.length){
            re.add(sb.toString());
        }
        for(int i=0;i<strs.length;i++){
            if(used[i]) continue;
            if(i>0&&!used[i-1]&&strs[i-1]==strs[i]){
                continue;
            }
            used[i]=true;
            sb.append(strs[i]);
            dfs(used,strs,sb);
            used[i]=false;
            sb.deleteCharAt(sb.length()-1);
        }
    }
}

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

public class Solution {
    public ListNode deleteDuplication(ListNode pHead)
    {
        ListNode first=new ListNode(-1);
        ListNode last=first;
        ListNode cur=pHead;
        while(cur!=null){
            if(cur.next!=null&&cur.next.val==cur.val){
                int v=cur.val;
                while(cur!=null&&cur.val==v){
                    cur=cur.next;
                }
                last.next=cur;
            }else{
                last.next=cur;
                last=cur;
                cur=cur.next;
            }
        }
        return first.next;
    }
}

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

public class Solution {
    public String ReverseSentence(String str) {
        char[] chars = str.toCharArray();
        reverse(chars,0,chars.length - 1);
        int blank = -1;
        for(int i = 0;i < chars.length;i++){
            if(chars[i] == ' '){ 
                int nextBlank = i;
                reverse(chars,blank + 1,nextBlank - 1);
                blank = nextBlank;
            }
        }
        reverse(chars,blank + 1,chars.length - 1);//最后一个单词单独进行反转
        return new String(chars);
    }
    public void reverse(char[] chars,int low,int high){
        while(low < high){
            char temp = chars[low];
            chars[low] = chars[high];
            chars[high] = temp;
            low++;
            high--;
        }
    }
}

【47】在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007。题目保证输入的数组中没有的相同的数字
解题思路:
归并排序的改进,把数据分成前后两个数组(递归分到每个数组仅有一个数据项),合并数组,合并时,出现前面的数组值array[i]大于后面数组值array[j]时;则前面数组array[i]~array[mid]都是大于array[j]的,count += mid+1 - i。由于测试用例输出结果比较大,对每次返回的count mod(1000000007)求余

public class Solution {
    public int InversePairs(int [] array) {
        if(array==null||array.length==0)
        {
            return 0;
        }
        int[] copy = new int[array.length];
        for(int i=0;i<array.length;i++)
        {
            copy[i] = array[i];
        }
        int count = InversePairsCore(array,copy,0,array.length-1);//数值过大求余
        return count;
    }
    private int InversePairsCore(int[] array,int[] copy,int low,int high)
    {
        if(low==high)
        {
            return 0;
        }
        int mid = (low+high)>>1;
        int leftCount = InversePairsCore(array,copy,low,mid)%1000000007;
        int rightCount = InversePairsCore(array,copy,mid+1,high)%1000000007;
        int count = 0;
        int i=mid;
        int j=high;
        int locCopy = high;
        while(i>=low&&j>mid)
        {
            if(array[i]>array[j])
            {
                count += j-mid;
                copy[locCopy--] = array[i--];
                if(count>=1000000007)//数值过大求余
                {
                    count%=1000000007;
                }
            }
            else
            {
                copy[locCopy--] = array[j--];
            }
        }
        for(;i>=low;i--)
        {
            copy[locCopy--]=array[i];
        }
        for(;j>mid;j--)
        {
            copy[locCopy--]=array[j];
        }
        for(int s=low;s<=high;s++)
        {
            array[s] = copy[s];
        }
        return (leftCount+rightCount+count)%1000000007;
    }
}

【48】输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

public class Solution {
    ArrayList<ArrayList<Integer>> re=new ArrayList<>();
    ArrayList<Integer> list=new ArrayList<>();
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        if(target<=0||root==null){
            return re;
        }
        list.add(root.val);
        target-=root.val;
        if(target==0&&root.left==null&&root.right==null){
            re.add(new ArrayList<>(list));
        }
        FindPath(root.left,target);
        FindPath(root.right,target);
        list.remove(list.size()-1);
        return re;
    }
}

【49】如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
解题思路:
先用java集合PriorityQueue来设置一个小顶堆和大顶堆
主要的思想是:因为要求的是中位数,那么这两个堆,大顶堆用来存较小的数,从大到小排列;
小顶堆存较大的数,从小到大的顺序排序*,显然中位数就是大顶堆的根节点与小顶堆的根节点和的平均数。
⭐保证:小顶堆中的元素都大于等于大顶堆中的元素,所以每次塞值,并不是直接塞进去,而是从另一个堆中poll出一个最大(最小)的塞值
⭐当数目为偶数的时候,将这个值插入大顶堆中,再将大顶堆中根节点(即最大值)插入到小顶堆中;
⭐当数目为奇数的时候,将这个值插入小顶堆中,再讲小顶堆中根节点(即最小值)插入到大顶堆中;
⭐取中位数的时候,如果当前个数为偶数,显然是取小顶堆和大顶堆根结点的平均值;如果当前个数为奇数,显然是取小顶堆的根节点

import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
    //小顶堆
    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;
        }
    });
     
    //记录偶数个还是奇数个
    int count = 0;
    //每次插入小顶堆的是当前大顶堆中最大的数
    //每次插入大顶堆的是当前小顶堆中最小的数
    //这样保证小顶堆中的数永远大于等于大顶堆中的数
    //中位数就可以方便地从两者的根结点中获取了
    public void Insert(Integer num) {
        //个数为偶数的话,则先插入到大顶堆,然后将大顶堆中最大的数插入小顶堆中
        if(count % 2 == 0){
            maxHeap.offer(num);
            int max = maxHeap.poll();
            minHeap.offer(max);
        }else{
            //个数为奇数的话,则先插入到小顶堆,然后将小顶堆中最小的数插入大顶堆中
            minHeap.offer(num);
            int min = minHeap.poll();
            maxHeap.offer(min);
        }
        count++;
    }
    public Double GetMedian() {
        //当前为偶数个,则取小顶堆和大顶堆的堆顶元素求平均
        if(count % 2 == 0){
            return new Double(minHeap.peek() + maxHeap.peek())/2;
        }else{
            //当前为奇数个,则直接从小顶堆中取元素即可
            return new Double(minHeap.peek());
        }
    }
}

【50】LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^ _ ^)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。
解题思路:
max 记录 最大值
min 记录 最小值
min ,max 都不记0
满足条件:1、max - min <5;2、除0外没有重复的数字(牌);3、数组长度为5

import java.util.Arrays;
public class Solution {
    public boolean isContinuous(int [] numbers) {
        if(numbers.length==0) return false;
        int[]d = new int[14]; 
        d[0] = -5; 
        int len = numbers.length; 
        int max = -1; 
        int min = 14; 
        for(int i =0;i<len;i++){
            d[numbers[i]]++; 
            if(numbers[i] == 0){
                continue; 
            }
            if(d[numbers[i]]>1){
                return false; 
            }
            if(numbers[i] >max){
                max = numbers[i]; 
            } 
            if(numbers[i] <min){
                min = numbers[i]; 
            }
        }
        if(max -min<5){
            return true; 
        }
        return false;
    }
}

【51】输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

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

【52】将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
解题思路:
溢出处理是真的恶心(⊙o⊙)…

public class Solution {
    public int StrToInt(String str) {
        if (str == null)
            return 0;
        int result = 0;
        boolean negative = false;//是否负数
        int i = 0, len = str.length();
        /**
         * limit 默认初始化为 负的 最大正整数 ,假如字符串表示的是正数
         * 那么result(在返回之前一直是负数形式)就必须和这个最大正数的负数来比较,
         * 判断是否溢出
         */
        int limit = -Integer.MAX_VALUE;
        int multmin;
        int digit;
        if (len > 0) {
            char firstChar = str.charAt(0);//首先看第一位
            if (firstChar < '0') { // Possible leading "+" or "-"
                if (firstChar == '-') {
                    negative = true;
                    limit = Integer.MIN_VALUE;//在负号的情况下,判断溢出的值就变成了 整数的 最小负数了
                } else if (firstChar != '+')//第一位不是数字和-只能是+
                    return 0;
                if (len == 1) // Cannot have lone "+" or "-"
                    return 0;
                i++;
            }
            multmin = limit / 10;
            while (i < len) {
                // Accumulating negatively avoids surprises near MAX_VALUE
                digit = str.charAt(i++)-'0';//char转int
                if (digit < 0 || digit > 9)//0到9以外的数字
                    return 0;
                //判断溢出
                if (result < multmin) {
                    return 0;
                }
                result *= 10;
                if (result < limit + digit) {
                    return 0;
                }
                result -= digit;
            }
        } else {
            return 0;
        }
        //如果是正数就返回-result(result一直是负数)
        return negative ? result : -result;
    }
}

【53】给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。

import java.util.ArrayList;
import java.util.LinkedList;
public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size)
    {
        ArrayList<Integer> re=new ArrayList<>();
        if(num.length==0||size<=0||size>num.length){
            return re;
        }
        LinkedList<Integer> list=new LinkedList<>();
        for(int i=0;i<num.length;i++){
            while(!list.isEmpty()&&num[i]>num[list.peekLast()]){
                list.pollLast();
            }
            list.addLast(i);
            if(list.peekFirst()==i-size){
                list.pollFirst();
            }
            if(i>=size-1){
                re.add(num[list.peekFirst()]);
            }
        }
        return re;
    }
}

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

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

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

public class Solution {
    public String replaceSpace(StringBuffer str) {
        int count=0;
        for(char c:str.toString().toCharArray()){
            if(c==' '){
                count++;
            }
        }
        int i=str.length()-1;
        int newLength=str.length()+2*count;
        int newindex=newLength-1;
        str.setLength(newLength);
        for(;i>=0;i--){
            if(str.charAt(i)==' '){
                str.setCharAt(newindex--,'0');
                str.setCharAt(newindex--,'2');
                str.setCharAt(newindex--,'%');
            }else{
                str.setCharAt(newindex--,str.charAt(i));
            }
        }
        return str.toString();
    }
}

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

public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence.length==0) return false;
        return check(sequence,0,sequence.length-1);
    }
    public boolean check(int[] sequence,int left,int right){
        if(left>=right) return true;
        int index=left;
        for(;index<right;index++){
            if(sequence[right]<sequence[index]){
                break;
            }
        }
        for(int i=index;i<right;i++){
            if(sequence[i]<sequence[right]){
                return false;
            }
        }
        return check(sequence,left,index-1)&&check(sequence,index,right-1);
    }
}

【57】给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    int count;
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        count=k;
        return dfs(pRoot);
    }
    TreeNode dfs(TreeNode pRoot){
        if(pRoot==null) return null;
        TreeNode node=dfs(pRoot.left);
        if(node!=null) return node;
        count--;
        if(count==0) return pRoot;
        node=dfs(pRoot.right);
        if(node!=null) return node;
        return null;
    }
}

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

import java.util.ArrayList;
import java.util.LinkedList;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    ArrayList<ArrayList<Integer> > re=new ArrayList<>();
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        if(pRoot==null) return re;
        LinkedList<TreeNode> list=new LinkedList<>();
        list.add(pRoot);
        int row=1;
        while(!list.isEmpty()){
            int size=list.size();
            ArrayList<Integer> t=new ArrayList<>();
            while(size>0){
                TreeNode node=list.poll();
                size--;
                if(row%2!=1){
                    t.add(0,node.val);
                }else{
                    t.add(node.val);
                }
                if(node.left!=null){
                    list.add(node.left);
                }
                if(node.right!=null){
                    list.add(node.right);
                }
            }
            row++;
            re.add(new ArrayList<>(t));
        }
        return re;
    }
}

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

public class Solution {
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        if(root1==null||root2==null) return false;
        return check(root1,root2);
    }
    public boolean check(TreeNode root1,TreeNode root2){
        if(root2==null) return true;
        if(root1==null&&root2!=null) return false;
        if(root1.val==root2.val){
            if(check(root1.left,root2.left)&&check(root1.right,root2.right)){
                return true;
            }
        }
        return check(root1.left,root2)||check(root1.right,root2);
    }
}

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

public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre.length==0||in.length==0||pre.length!=in.length) return null;
        return my(pre,in,0,pre.length-1,0,in.length-1);
    }
    public TreeNode my(int[] pre,int[] in,int preleft,int preright,int inleft,int inright){
        if(inleft>inright) return null;
        int index=inleft;
        for(;index<=inright;index++){
            if(in[index]==pre[preleft]){
                break;
            }
        }
        TreeNode root=new TreeNode(pre[preleft]);
        root.left=my(pre,in,preleft+1,preleft+index-inleft,inleft,index-1);
        root.right=my(pre,in,preleft+index-inleft+1,preright,index+1,inright);
        return root;
    }
}

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

public class Solution {
    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;
    }
}

【62】请实现两个函数,分别用来序列化和反序列化二叉树
二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

import java.lang.StringBuffer;
public class Solution {
    int index=-1;
    String Serialize(TreeNode root) {
        StringBuffer str=new StringBuffer();
        if(root==null){
            str.append("#,");
            return str.toString();
        }
        str.append(root.val+",");
        str.append(Serialize(root.left));
        str.append(Serialize(root.right));
        return str.toString();
  }
    TreeNode Deserialize(String str) {
       index++;
       String[] val=str.split(",");
        if(index>=val.length){
            return null;
        }
        TreeNode root=null;
        if(!val[index].equals("#")){
            root=new TreeNode(Integer.valueOf(val[index]));
            root.left=Deserialize(str);
            root.right=Deserialize(str);
        }
        return root;
  }
}

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

public class Solution {
    public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
    {
        int[] list=new int[matrix.length];
        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                if(check(matrix,rows,cols,str,i,j,0,list)){
                    return true;
                }
            }
        }
        return false;
    }
    public boolean check(char[] matrix,int rows,int cols,char[] str,int i,int j,int k,int[] list){
        int index=cols*i+j;
        if(i<0||i>=rows||j<0||j>=cols||list[index]==1||matrix[index]!=str[k]){
            return false;
        }
        if(k==str.length-1){
            return true;
        }
        list[index]=1;
        if(check(matrix,rows,cols,str,i-1,j,k+1,list)||check(matrix,rows,cols,str,i+1,j,k+1,list)||check(matrix,rows,cols,str,i,j+1,k+1,list)||check(matrix,rows,cols,str,i,j-1,k+1,list)){
            return true;
        }
        list[index]=0;
        return false;
    }
}

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

import java.util.PriorityQueue;
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> re=new ArrayList<>();
        if(input.length==0||k>input.length) return re;
        PriorityQueue<Integer> heap=new PriorityQueue<>(
            (o1,o2)->o2-o1
        );
        for(int i:input){
            heap.offer(i);
            if(heap.size()>k){
                heap.poll();
            }
        }
        for(int i:heap){
            re.add(0,i);
        }
        return re;
    }
}

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

public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index==0){
            return 0;
        }
        int i2=0;
        int i3=0;
        int i5=0;
        int[] result=new int[index];
        result[0]=1;
        int count=0;
        while(count<index-1){
            int temp=Math.min(result[i2]*2,Math.min(result[i3]*3,result[i5]*5));
            if(temp==result[i2]*2)  i2++;
            if(temp==result[i3]*3)  i3++;
            if(temp==result[i5]*5)  i5++;
            result[++count]=temp;
        }
        return result[index-1];
    }
}

【66】输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
解题思路:
回溯算法的第一想法是将链表想象成一张图。链表中每个节点都有 2 个指针。因为随机指针给图结构添加了随机性,所以我们可能会访问相同的节点多次,这样就形成了环。此方法中,我们只需要遍历整个图并拷贝它。拷贝的意思是每当遇到一个新的未访问过的节点,你都需要创造一个新的节点。遍历按照深度优先进行。我们需要在回溯的过程中记录已经访问过的节点,否则因为随机指针的存在我们可能会产生死循环。
从头指针开始遍历整个图->当我们遍历到某个点时,如果我们已经有了当前节点的一个拷贝,我们不需要重复进行拷贝。如果我们还没拷贝过当前节点,我们创造一个新的节点,并把该节点放到已访问字典中,即:
visited_dictionary[current_node] = cloned_node_for_current_node->
我们针对两种情况进行回溯调用:一个顺着 random 指针调用,另一个沿着 next 指针调用。

//递归
/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
*/
import java.util.HashMap;
public class Solution {
    HashMap<RandomListNode,RandomListNode> map=new HashMap<>();
    public RandomListNode Clone(RandomListNode pHead)
    {
        if(pHead==null){
            return null;
        }
        if(map.containsKey(pHead)){
            return map.get(pHead);
        }
        RandomListNode node=new RandomListNode(pHead.label);
        map.put(pHead,node);
        node.next=Clone(pHead.next);
        node.random=Clone(pHead.random);
        return node;
    }
}
发布了107 篇原创文章 · 获赞 19 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/ThreeAspects/article/details/103699823
今日推荐