文章目录
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;
}
}