牛客网 剑指Offer,一些值得记住的小题(四)

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

public class Solution {
    long count =0;
    public int InversePairs(int [] array) {
        if(array==null||array.length==0){
            return 0;
        }
        int[] temp = new int[array.length];
        merge_sort(array,temp,0,array.length-1);
        return (int)(count%1000000007);
    }
    public void merge_sort(int[] array,int[] temp,int first,int last){
        if(first>=last){
            return ;
        }
        int mid = (first+last)/2;
        merge_sort(array,temp,first,mid);
        merge_sort(array,temp,mid+1,last);
        merge(array,temp,first,last);
    }
    public void merge(int[] array,int[] temp,int first,int last){
        int mid = (first+last)/2;
        int rfirst = mid+1;
        int tempindex=first;
        int len = last-first+1;
        while(first<=mid&&rfirst<=last){
            if(array[first]<=array[rfirst]){
                temp[tempindex++] = array[first++];
            }else{   
                count +=(mid-first+1);
                temp[tempindex++] = array[rfirst++];
            }
        }
        while(first<=mid){
            temp[tempindex++] = array[first++];
        }
        while(rfirst<=last){
            temp[tempindex++] = array[rfirst++];
        }
        for(int i=len;i>0;i--,last--){
            array[last]=temp[last];
        }
    }
}
解析:归并排序的改进,在归并排序的过程中,没有一次前面大于后面的数对count++。。。 归并排序的主要思路是,将一列数不断的分成两部分,排好序后再合并,当两部分比较时,有一个角标first指向前一段的第一个数,另一个角标rfirst指向后一段的第一个数,比较这两个数,小的放进temp里,如果遇见前面的数 array[first] 比后面的数 array[rfirst] 大,则说明array[first]~array[mid]的数都比 array[rfirst]大。然后将排好序的temp放回到array里继续递归继续排序。


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

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

    ListNode(int val) {
        this.val = val;
    }
}*/
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;*/
        if(pHead1==null||pHead2==null){
            return null;
        }
        int len1=0;
        int len2=0;
        ListNode p1 = pHead1;
        ListNode p2 = pHead2;
        while(p1!=null){
            len1++;
            p1=p1.next;
        }
        while(p2!=null){
            len2++;
            p2=p2.next;
        }
        int minus=(len1>len2)?len1-len2:len2-len1;
        p1 = pHead1;
        p2 = pHead2;
        if(len1>len2){
            for(int i=0;i<minus;i++){
                p1=p1.next;
            }
        }else{
            for(int i=0;i<minus;i++){
                p2=p2.next;
            }
        }
        while(p1!=null&&p2!=null){
            if(p1==p2){
                return p2;
            }else{
                p1=p1.next;
                p2=p2.next;
            }
        }
        return null;
    }
}
解析:首先需要理解的是,当两个链表有公共节点时,那么这个节点后面的节点也都相等,因为一个节点只有一个next,思路是,首先算出两个链表的长度差,然后长的那个链表多走这个差值,然后两个标记结点一起向后挪,直到遇到第一个想通的节点。


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

/**
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;
        }
        if(root.left==null&&root.right==null){
            return 1;
        }
        int llength=0;
        int rlength=0;
        if(root.left!=null){
            llength = 1+TreeDepth(root.left);
        }
        if(root.right!=null){
            rlength = 1+TreeDepth(root.right);
        }  
        return (llength>rlength)?llength:rlength;
    }
}
解析:只是一个简单的递归很简单,只是我的递归还不太熟练,所以就把这道题也放进来了


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

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        for(int i=0;i<array.length-1;i++){
            for(int j=array.length-1;j>i;j--){
                if(array[i]+array[j]==sum){
                    list.add(array[i]);
                    list.add(array[j]);
                    return list;
                }
            }
        }
        return list;
    }
}
解析:这一题看似简单,但要是想降低复杂度就需要考虑怎么能够快捷的找到乘积最小的而且和是s的数对,思路是,因为已经是排好序的,所以找到距离最远的而且和为s的数对即是要找的。


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

public class Solution {
    public String ReverseSentence(String str) {
         if(str.trim().equals("")){//去除空格
            return str;
        }
        String [] sstr = str.split(" ");
        StringBuffer s = new StringBuffer();
        for(int i=sstr.length;i>0;i--){
            s.append(sstr[i-1]);
            if(i!=1){
                s.append(" ");
            }
        }
        return s.toString();
    }
}
解析:主要就是利用split()函数,然后倒序输出


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

public class Solution {
    public int Add(int num1,int num2) {
        int n1=(num1&num2)<<1;
        int n2=num1^num2;
        while((n1&n2)!=0){
            num1=n1;
            num2=n2;
            n1=(num1&num2)<<1;
            n2=num1^num2;
        }
        return n1|n2;
    }
}
解析:(n1&n2)!=0就说明还存在进位,所以要继续循环

以下是牛客网的答案:

链接:https://www.nowcoder.com/questionTerminal/59ac416b4b944300b617d4f7f111b215
来源:牛客网

首先看十进制是如何做的: 5+7=12,三步走第一步:相加各位的值,不算进位,得到2。第二步:计算进位值,得到10. 如果这一步的进位值为0,那么第一步得到的值就是最终结果。第三步:重复上述两步,只是相加的值变成上述两步的得到的结果2和10,得到12。同样我们可以用三步走的方式计算二进制值相加: 5-101,7-111 第一步:相加各位的值,不算进位,得到010,二进制每位相加就相当于各位做异或操作,101^111。第二步:计算进位值,得到1010,相当于各位做与操作得到101,再向左移一位得到1010,(101&111)<<1。第三步重复上述两步, 各位相加 010^1010=1000,进位值为100=(010&1010)<<1。 继续重复上述两步:1000^100 = 1100,进位值为0,跳出循环,1100为最终结果。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值