剑指Offer系列之题36~题40

36.数组中的逆序对

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

输入描述:

题目保证输入的数组中没有的相同的数字

数据范围:

对于%50的数据,size<=10^4

对于%75的数据,size<=10^5

对于%100的数据,size<=2*10^5

归并排序,排序的过程中判断逆序对的数量


1.

public class Solution {
    int count=0;
    public int InversePairs(int [] array) {
        //遍历不可取,时间复杂度 O(n^2)
        if(array.length<=1)
            return 0;
        //使用归并排序
        mergeSort(array,0,array.length-1);
        return count;
    }
    public void mergeSort(int a[],int low,int high){
        if(low<high){
            int mid=(low+high)/2;
            mergeSort(a,low,mid);//分别对两边排序
            mergeSort(a,mid+1,high);
            //合并,此时传进去的部分已经是有序态
            merge(a,low,mid,high);
        }
    }

    public void merge(int a[],int low,int mid,int high){
        //辅助数组存储排序后的元素
        int b[]=new int[high-low+1];
        int i=0;int j=0;int k=0;
        for(i=low,j=mid+1,k=0;i<=mid && j<=high;k++ ){
            if(a[i]<=a[j])
                b[k]=a[i++];
            else{ //左边大于右边  此时逆序
                count=count+mid-i+1;
                if(count>=1000000007)
                    count%=1000000007;
                b[k]=a[j++];
            }
        }
        while(i<=mid)//将剩余元素复制到辅助数组
            b[k++]=a[i++];
        while(j<=high)
            b[k++]=a[j++];
        //将辅助数组的值放到原数组
        for(k=0;k<b.length;k++){
            a[k+low]=b[k];
        }
    }
}

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

输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

双指针,一个指针走到末尾节点后从另一条链表首节点接着走,当两者走到第一个交点,走的距离相等。


1.双指针:

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if(pHead1==null || pHead2==null)
            return null;
        //双指针
        ListNode p1=pHead1;
        ListNode p2=pHead2;
        int count1=0;
        int count2=0;

        while(p1!=p2){//当两指针未相遇时
            if(p1.next==null){//当p1走到末尾,切换到链表2
                p1=pHead2;
                count1++;
            }else{
                p1=p1.next;
            }

            if(p2.next==null){//当p2走到末尾,切换到链表1
                p2=pHead1;
                count2++;
            }else{
                p2=p2.next;
            }
            if(count1>1 || count2>1)//当某一指针已切换两次仍未相遇,证明没有交点
                return null;
        }
        return p1;
    }
}

2.精简版:

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if(pHead1==null || pHead2==null)
            return null;
        //双指针
        ListNode p1=pHead1;
        ListNode p2=pHead2;

        while(p1!=p2){//当两指针未相遇时
            //没有交点时会在null相遇
            p1=(p1==null? pHead2:p1.next);
            p2=(p2==null? pHead1:p2.next);
        }
        return p1;
    }
}

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

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

暴力解:直接遍历到相等的数字,然后从该数字开始遍历直到数字不等。


1.暴力解:

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        if(array.length<=0)
            return 0;
       //暴力解:先找到所在位置,然后计数,直到不等于该数字
        int count=0;
        int i=0;
        for(;i<array.length;++i){
            if(array[i]==k){
                break;
            }
        }
        for(;i<array.length;++i){
            if(array[i]!=k)
                break;
            count++;
        }
        return count;
    }
}

2.二分查找:

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        if(array.length<=0)
            return 0;
        //二分查找:找到第一个k和最后一个k的位置
        int count=0;
        int first=getFirstK(array,0,array.length-1,k);
        int last=getLastK(array,0,array.length-1,k);

        if(first!=-1 && last!=-1)
            count=last-first+1;
        return count;
    }

    public int  getFirstK(int a[],int low,int high,int k){
        if(low>high)
            return -1;
        int mid=0;
        while(low<=high){
            mid=(low+high)>>1;
            if(a[mid]>k){//第一个k在其左边
                high=mid-1;
            }else if(a[mid]<k){//<k,此时第一个k在其右边
                low=mid+1;
            }else{//=k
                if(mid>0 && a[mid-1]==k)
                    high=mid-1;
                else
                    return mid;
            }
        }
        return -1;
    }
    public int  getLastK(int a[],int low,int high,int k){
        if(low>high)
            return -1;
        int mid=0;
        while(low<=high){
            mid=(low+high)>>1;
            if(a[mid]>k){//最后一个k在其左边
                high=mid-1;
            }else if(a[mid]<k){//<k最后一个k在其右边
                low=mid+1;
            }else{//=k
                if(mid<a.length-1 && a[mid+1]==k)
                    low=mid+1;
                else
                    return mid;
            }
        }
        return -1;
    }
}

39.二叉树的深度 🔺

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

递归,判断左右子树高度,返回高的一方+1。

层次遍历:辅助队列,依次遍历每一层。


1.递归:

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

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root==null)
            return 0;
        return 1+(TreeDepth(root.left)>TreeDepth(root.right)? TreeDepth(root.left):TreeDepth(root.right));

    }
}

2.层次遍历:

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

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
import java.util.Queue;
import java.util.LinkedList;
public class Solution {
    public int TreeDepth(TreeNode root) {
        //层次遍历 非递归
        if(root==null)
            return 0;
        Queue<TreeNode> queue=new LinkedList<>();//存储树中结点
        queue.offer(root);//存储根节点
        int depth=0;//深度
        int width;//宽度,即该层结点个数
        int count;//当前层已遍历的结点个数
        TreeNode temp=null;
        while(!queue.isEmpty()){
            width=queue.size();
            count=0;//重置该层已遍历结点个数
            while(count<width){
                temp=queue.poll();//队列头结点,即该层最左结点
                if(temp.left!=null){//判断左右子树是否空,非空则入队
                    queue.offer(temp.left);
                }
                if(temp.right!=null){
                    queue.offer(temp.right);
                }
                count++;//该结点已遍历
            }//该层遍历完毕
            depth++;//深度+1;
        }
        return depth;
    }
}

40.平衡二叉树

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

求深度然后计算


1.递归:

public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        //平衡二叉树:是一棵空树或它的左右两个子树的高度差的绝对值不超过1,
        //并且左右两个子树都是一棵平衡二叉树。

        if(root==null)
            return true;
        //递归
        int left=treeDepth(root.left);
        int right=treeDepth(root.right);
        int res=left-right;
        if(res>1 || res <-1)
            return false;
        return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
    }
    public int treeDepth(TreeNode root){
        if(root==null)
            return 0;
        int left=treeDepth(root.left);
        int right=treeDepth(root.right);
        return 1+(left>right? left:right);
    }
}

2.从下往上遍历:

public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        //平衡二叉树:是一棵空树或它的左右两个子树的高度差的绝对值不超过1,
        //并且左右两个子树都是一棵平衡二叉树。

        //递归 先判断左右子树,然后判断根结点,避免重复遍历 从下往上
        return getDepth(root)!=-1;

    }

    public int  getDepth(TreeNode root){
        if(root==null)
            return 0;
        //若不平衡则返回-1
        int left=getDepth(root.left);
        if(left==-1)
            return -1;
        int right=getDepth(root.right);
        if(right==-1)
            return -1;
        //判断左右子树高度,如果不平衡则返回-1,平衡则返回左右子树中的高度最大者+1作为该节点的深度
        return Math.abs(left-right)>1 ? -1 : 1+Math.max(left,right);
    }
}

如有错误,欢迎指正

posted @ 2020-04-14 12:58  雨落成尘  阅读(213)  评论(0编辑  收藏  举报