剑指Offer刷题(四)及思路

和为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>> List =new ArrayList<ArrayList<Integer>>();
        
        

        int end = sum/2+1;
        for(int i =1 ; i<end;i++)
        {
            ArrayList<Integer> subList = new ArrayList<Integer>();
            int tmp= 0;
            int j =i;
           while(tmp<sum)
            {
               tmp+=j;
               subList.add(j);
               j++;
            }
            if(tmp==sum)
            List.add(subList);
        }
        return List;
    }
}

找到一个停止点,比如要求的和值的一半,从1开始依次相加,看和是否符合要求,然后再从2,3,4…开始依次相加

和为S的两个数字

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

  • 对应每个测试案例,输出两个数,小的先输出。
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        
        int i =0; int j = array.length-1;
        int x = Integer.MAX_VALUE;
        
        int mini=-1; int minj=-1;
        
        while(i<j)
        {
            //加起来大于目标S,说明大的太大了,j往前走
            if(array[i]+array[j]>sum)
            {
                j--;
            }
            //小的太小,i往后走
            else if(array[i]+array[j]<sum)
            {
                i++;
            }
            else
            {
                if(array[i]*array[j]<x)
                {
                    x= array[i]*array[j];
                    mini=i;minj=j;
                   
                }
                 i++;j--;
            }
        }
        if(mini==-1)return list;
        list.add(array[mini]);
        list.add(array[minj]);
        return list;
        
    }
}

两头向中间遍历,记录最小乘积的两个数字

左旋转字符串

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

import java.util.*;
public class Solution {
    public String LeftRotateString(String str,int n) {
        
        if(str==null)return "";
        if(str.length()==1)return str;
        if(str.length()==0)return "";
        ArrayList<Character> list = new ArrayList<Character>();
        
        //考虑循环超过一次的情况,取余
         n=n%str.length();
        
        for(int i =0;i<n;i++)
        {
            list.add(str.charAt(i));
        }
        
        String str2 = "";
        for(int i=0;i<list.size();i++)
        {
            str2+=String.valueOf(list.get(i));
        }
        return str.substring(n,str.length())+str2;
    }
}

记录循环后移出部分,即要拼接到后面的部分,然后新建字符串再拼接

翻转单词顺序列

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

public class Solution {
    public String ReverseSentence(String str) {
        if(str.equals(""))return "";
        if(str.equals(" ")) return str;
        String[] strArr =str.split(" ");
        if(strArr.length==1||strArr.length==0)return str;
        int i = 0;int j=strArr.length-1;
        while(i<j)
        {
            String tmp = strArr[i];
            strArr[i]=strArr[j];
            strArr[j]=tmp;
            i++;
            j--;
        }
        String res = "";
        for(int k=0;k<strArr.length;k++)
        {
            if(k!=strArr.length-1)
            {
                res+=strArr[k]+" ";
            }
            else
            {
                res+=strArr[k];
            }
        }
        return res;
    }
}

对称交换位置即可

扑克牌顺子

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。

import java.util.*;
public class Solution {
    public boolean isContinuous(int [] numbers) {
        if(numbers.length==0)return false;
        if(numbers.length==1)return true;
        ArrayList<Integer> list = new ArrayList<Integer>();
        //把输入的数组进行排序
        quickSort(numbers,0,numbers.length-1);
        //Arrays.sort(numbers);
        //记录癞子数量
        int king = 0;
        for(int i=0;i<numbers.length;i++)
        {
            //如果遇到大小王癞子,数量加一
            if(numbers[i]==0)king++;
            else{
                //如果出现对子,直接判定为不可能
                if(list.contains(numbers[i]))return false;
                list.add(numbers[i]);
            }
        }
        
        for(int i=1;i<list.size();i++)
        {
            //看看当前数字与前一个数字之间的差用癞子能否弥补
            //例如有2两个癞子,当前数字是4,前一个数字是2,那么刚好 2-(4-2) = 0,癞子用完
            king-=list.get(i)-list.get(i-1)-1;
        }
        if(king>=0)return true;
        return false;

    }
    
    
    public void quickSort(int[] arr,int low ,int high)
    {
        int i = low ;int j =high;int key = arr[low];
        while(i<j)
        {
            while(i<j&&arr[j]>=key)j--;
            if(arr[j]<key){int tmp =arr[j];arr[j]=arr[i];arr[i]=tmp;}
            while(i<j&&arr[i]<=key)i++;
            if(arr[i]>key){int tmp =arr[j];arr[j]=arr[i];arr[i]=tmp;}
        }
        if(low<i)quickSort(arr,low,i-1);
        if(j<high)quickSort(arr,j+1,high);
    }
}

孩子们的游戏(圆圈中最后剩下的数)

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

如果没有小朋友,请返回-1

import java.util.*;
public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n==0||m==0)return -1;
        
        int[] queue = new int[n];
        //小朋友人数
        int people = n;
        //小朋友的下标
        int index = -1;
        //当前喊出的数字
        int flag = 0;
         
        while(people>0)
        {
        	//小朋友依次报数
            index++;
            //走完一圈,从头开始
            if(index>=n)index=0;
            //判断是否被淘汰,如果是淘汰的位置,就直接跳过
            if(queue[index]==-1)
            {continue;}
            //当前喊出的数字
             flag++;
             //如果喊出的数字符合要求,就被淘汰
            if(flag == m)
            {
                queue[index]=-1;
                //当前喊出的数字归零
                flag=0;
                //减去一个人
                people--;
            }
   
        }
        return index;
        
        
    }
}

模仿这个过程即可

求1+2+3+…+n

求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)

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

利用&&运算符的最短路径原理,如果前面的条件不满足就不会去判定后面的
采用递归来进行求和

不用加减乘除做加法

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

public class Solution {
    public int Add(int num1,int num2) {
        while(num2!=0)
        {
        int tmp = num1^num2;
         num2 = (num1&num2)<<1;
         num1 = tmp;     
        }
        return num1;
        
    }
}
  • 异或操作:得到各位相加后的值而且不进位
  • 与操作向左进位:得到相加后的进位值
    进行不进位相加,然后保存相加后结果,再得到进位的结果,再把进位结果与不进位的相加结果相加,这样就是一次相加的过程,这样循环即可

数组中重复的数字

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

public class Solution {
    // Parameters:
    //    numbers:     an array of integers
    //    length:      the length of array numbers
    //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
    //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
    //    这里要特别注意~返回任意重复的一个,赋值duplication[0]
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        //所有数字都在0-n-1之间,可以用数组
        boolean[] flags= new boolean[length];
        
        for(int i=0;i<length;i++)
        {
            if(flags[numbers[i]])
            {
                duplication[0]=numbers[i];
                return true;
            }
            flags[numbers[i]]=true;
        }
        return false;
    }
}

构建乘积数组

给定一个数组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[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];)


public class Solution {
    public int[] multiply(int[] A) {
        int length = A.length;
        int[] B =new int[A.length];
        
        if(length!=0)
        {
            B[0]=1;
            for(int i =1;i<length;i++)
            {
                B[i]=B[i-1]*A[i-1];
            }
            int tmp =1;
            for(int i = length-2;i>=0;i--)
            {
                tmp*=A[i+1];
                B[i]*=tmp;
            }
        }
        return B;
        
    }
}

在这里插入图片描述
下三角用连乘可以很容求得,上三角,从下向上也是连乘。
因此我们的思路就很清晰了,先算下三角中的连乘,即我们先算出B[i]中的一部分,然后倒过来按上三角中的分布规律,把另一部分也乘进去

表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+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) {
        int length = str.length;
        if(length==0)return false;
        if(length==1){
            if(str[0]<'0'&&str[0]>'9')
                return false;
        }
        int point = 0;
        boolean hasE =false;
        for(int i = 0;i<length;i++)
        {
            if(i==0)
            {
                if(str[i]!='+'&&str[i]!='-'&&(str[i]<'1'||str[i]>'9'))
                    return false;
                continue;
            }
            else{
                if(str[i]!='.'&&(str[i]<'0'||str[i]>'9')&&str[i]!='E'&&str[i]!='e')
                {
                    if(str[i]=='-'&&(str[i-1]=='E'||str[i-1]=='e'))
                    {continue;}
                    if(str[i]=='+'&&(str[i-1]=='E'||str[i-1]=='e'))
                    {continue;}
                    return false;
                }
                if(str[i]=='E'||str[i]=='e')hasE=true;
                if(str[i]=='.')
                {
                    if(hasE)return false;
                    if(point>=1)return false;
                    point++;
                }
            }
            if(i==length-1)
            {
                if(str[i]=='e'||str[i]=='E')return false;
            }

        }
        return true;
    }
}

根据一些规则来判断输入字符串是否为数值即可

字符流中第一个不重复的字符

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
输出描述:

  • 如果当前字符流没有存在出现一次的字符,返回#字符。
public class Solution {
    //Insert one char from stringstream
    char[] charArr = new char[256];
    String s ="";
    public void Insert(char ch)
    {
        s+=String.valueOf(ch);
        charArr[ch]++;
    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
        for(int i =0;i<s.length();i++)
        {
            if(charArr[s.charAt(i)]==1)
                return s.charAt(i);
        }
        return '#';
    }
}

链表中环的入口结点

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

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

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {

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

根据以下结论去做:
1、设置快慢指针,假如有环,他们最后一定相遇。
2、两个指针分别从链表头和相遇点继续出发,每次走一步,最后一定相遇与环入口。

删除链表中重复的结点

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

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

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ListNode deleteDuplication(ListNode pHead)
    {
        ListNode res = new ListNode(0);
        res.next = pHead;
        ListNode cur = res.next;
        ListNode pre = res;
        while(cur!=null)
        {
           if(cur.next!=null&&cur.val==cur.next.val)
           {
               while(cur.next!=null&&cur.val==cur.next.val)
               {
                   cur = cur.next;
               }
               pre.next = cur.next;
               cur = cur.next;
           }
            else{
                pre = pre.next;
                cur = cur.next;
            }

        }
        return res.next;

    }
}

还是一个链表的操作,这里面用到了一个头节点,具体链表的操作可以参见:
https://blog.csdn.net/weixin_43925277/article/details/104694930

发布了22 篇原创文章 · 获赞 4 · 访问量 3659

猜你喜欢

转载自blog.csdn.net/weixin_43925277/article/details/104755140
今日推荐