Day4.牛客网剑指offer 67题之33-42题(java代码)

Day4.牛客网剑指offer 67题之33-42题(java代码)

今天起个大早准备复试的心理测试题,大概10点多就结束了,不过也挺累了,就直接睡了个回笼觉,然后起来又挨个通知考生下午思想道德面试的会议室位置,还好下午没有安排到工作又稍微轻松一点…周末工作量就很大了,白天都是安排了任务,反正周末是没了,昨天也提交返校申请了,也不知道什么时候能有个准信,在家呆着确实效率没有想象中的高啊。

33.丑数

题目描述
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
思路根据丑数的定义,无非就是从前面数组中元素乘2、3或5得到的数,这里可以设置三个标志索引用来存放分别是2 3 5倍数的数,然后每次找其中三个数分别x2
、x3和x5的最小数存入数组中。

public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index<1) return 0;
        int nums[]=new int[index];
        nums[0]=1;
        int index2=0,index3=0,index5=0;
        for(int i=1;i<index;i++){
            int temp=getMin(nums[index2]*2,nums[index3]*3,nums[index5]*5);
            if(temp==nums[index2]*2) index2++;
            if(temp==nums[index3]*3) index3++;
            if(temp==nums[index5]*5) index5++;
            nums[i]=temp;
        }
        return nums[index-1];
    }
    public int getMin(int a,int b,int c){
        return a<b?(a<c?a:c):(b<c?b:c);
    }
}

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

题目描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
思路
可以使用HashMap,首先遍历字符串,如果当前字符不存在,就将它加入到键值索引为1的位置,否则设定将该字符加入到已存在索引后一位,之后在遍历字符串找到第一个在索引为1位置的字符即可。

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

35.数组中的逆序对

题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
思路
这一题我拿到题目首先试了一下暴力法能不能过,结果当然是运行超时了,只通过50%数据,那么该如何改进呢,其实是可以考虑使用归并排序的思想,不断拆分数组归并,需要判断是否有数字的调换,如果有说明存在逆序对,且前面的数组部分也一定构成了逆序对,为mid+1-i组,最初我想用我之前的归并排序代码,发现很多地方还是需要改,这个就参考别人的代码思路把参数返回值什么的调整了一些。

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

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

题目描述
输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
思路
设定node1,node2分别指向两个表头,然后往后走,走空之后指向另一个表头,直至两个指针相遇,即为第一个公共结点。

public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode node1=pHead1;
        ListNode node2=pHead2;
        while(node1!=node2){
            if(node1==null)
                node1=pHead2;
            else{
                node1=node1.next;
            }
            if(node2==null)
                node2=pHead1;
            else
                node2=node2.next;
        }
        return node1;
    }
}

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

题目描述
统计一个数字在排序数组中出现的次数。
思路
最简单的直接遍历比较,这里可以多加一个循环条件,当计数不等于0且数组元素不为k则退出循环,优化一些的思路应该就是使用二分查找了,找到k值的起始和终止位置。

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
       int count=0;
        for(int i=0;i<array.length;i++){
            if(array[i]==k)
                count++;
            if(array[i]!=k&&count!=0)
                break;
        }
        return count;
    }
}

二分查找

public int GetNumberOfK(int[] array , int k) {
		int first = getFirstK(array, k);
		int last = getLastK(array, k);
		if(first == -1) return 0;
		if(last == -1) return 0;		
		return last - first + 1;
    }
	
	private int getFirstK(int[] array , int k) {
		int low = 0, high = array.length - 1;
	    while (low <= high) {
	        int mid = (high + low) / 2;
	        if(array[mid] >= k) {
	        	high = mid - 1;
	        } else {
	        	low = mid + 1;
	        }
	    }    
	    if(low > array.length - 1 || array[low] != k) 
	    	return -1;
	    return low;
	}
	
	private int getLastK(int[] array , int k) {
		int low = 0, high = array.length - 1;		
	    while (low <= high) {
	        int mid = (high + low) / 2;
	        if(array[mid] > k) {
	        	high = mid - 1;
	        } else {
	        	low = mid + 1;
	        }
	    }    
	    if(high < 0 || array[high] != k) 
	    	return -1;	    
	    return high;
	}

38.求二叉树的高度

题目描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
思路
练手题,二叉树不用递归就很亏的。
递归实现

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

39.平衡二叉树

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

在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
思路
根据上一题思路求高度,然后判断左右子树的高度差是否不大于1,然后递归判断即可。

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

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

题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
思路
在写这一题的时候恰好早上刷的力扣每日一题是这个的简单版,只求一个数字,然后顺手刷了求两个数字的,而且力扣会让你尝试不开辟额外的空间去求解,这就涉及到了异或运算了, 思路大家可以参考别人怎么做的,主要还是亦或运算的精髓,当然这里没有给要求,可以考虑直接用HashSet进行求解即可。
HastSet实现

import java.util.*;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        HashSet<Integer> set=new HashSet<>();
        for(int i=0;i<array.length;i++){
            if(set.contains(array[i]))
                set.remove(array[i]);
            else
                set.add(array[i]);
        }
        Iterator<Integer> it=set.iterator();
        num1[0]=it.next();
        num2[0]=it.next();
    }
}

亦或实现(不需开辟额外空间)

public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        int x=0;
        for(int val:array) x^=val;
        int flag=x&(x^(x-1));//x&(-x)
        int res=0;
        for(int val:array){
            if((flag&val)!=0)
            res^=val;
        }
        num1[0]=res;
        num2[0]=x^res;
    }
}

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

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

输出描述: 输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序

思路
这一题的话可以考虑设置两个标志位,一个为连续序列的起始位置,一个为连续序列的终止位置,如果这一个序列的值小于目标值就右移终止位置,等于的话就保存这个序列,并右移起始位置,大于的话也右移起始位置。

import java.util.ArrayList;
public class Solution {
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> res=new ArrayList<>();
        if(sum<=1) return res;
        int start=1,end=2;
        while(start<(sum+1)/2){
            int curSum=0;
            for(int i=start;i<=end;i++){
                curSum+=i;
            }
            if(curSum<sum)
                end++;
            else if(curSum>sum)
                start++;
            else{
                ArrayList<Integer> list=new ArrayList<>();
                for(int i=start;i<=end;i++){
                    list.add(i);
                }
                res.add(list);
                start++;
            }
        }
        return res;
    }
}

42.和为S的数字

题目描述
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
思路
首先可以知道的是如果乘积最小那肯定是两个数之间差值最大,数组有递增的,所以设置两个索引标志,分别从两边开始向里移动,找到的第一次符合要求的两个数即可,(因为之前使用HashMap求过两数之和,但这俩题又有些差距,额外开辟空间也不划算)

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

上一篇:牛客网剑指offer 67题之22-32题
下一篇:牛客网剑指offer 67题之43-54题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值