【精选】JAVA入门算法题(五)

把敬业变成习惯。短期来看是为了雇主,长期来看是为了自己。

1.题目:重复的字符串

/**
 * 给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
 *
 * 输入: "abab"
 * 输出: True
 * 解释: 可由子字符串 "ab" 重复两次构成。
 *
 * 输入: "aba"
 * 输出: False
 */

这道题整体的思路大体有两种,第一种从长度为1开始分割字符串,然后用分割后的字符串去拼接k次,最后比较字符串;第二种其实还是分割字符串,不过是从整体二等分开始,三等分,直到N等分,然后拼接比较。

但要注意的一点是,字符串长度不超过10000,也就是说字符串可能会有7000,8000这么长,你在这里需要加上一个过滤,如果字符串长度对分割长度取余不等于零,那么注定拼接不成目标字符串,应当直接过滤;还有就是注意拼接的方式,最好使用StringBuilder来拼接,因为这种方式比直接字符串拼接效率上有优势。

public static boolean repeatedSubstringPattern(String s) {
        int length=s.length();
        for (int i=1;i<=length/2;i++){
            if (length%i!=0) continue;
            int k=length/i;
            String string=s.substring(0,i);
            StringBuilder a=new StringBuilder();
            for (;k>0;k--){
                a.append(string);
            }
            System.out.println(a);
            if (a.toString().equals(s)){
                return true;
            }
        }
        return false;
    }

2.最小叠加次数

/**
 * 给定两个字符串 A 和 B, 寻找重复叠加字符串A的最小次数,使得字符串B成为叠加后的字符串A的子串,如果不存在则返回 -1。
 * 举个例子,A = "abcd",B = "cdabcdab"。
 * 答案为 3, 因为 A 重复叠加三遍后为 “abcdabcdabcd”,此时 B 是其子串;A 重复叠加两遍后为"abcdabcd",B 并不是其子串。
 */

如果B的长度小于A,那么有三种情况,要么A不用叠加,B就已经是A的子串,要么就加个头或者围,比如A是aaaaaaaaaab,B是ba,要么就是同时加上头和尾,就像题目中给的例子一样

如果B的长度大于A,那么A就要至少叠加两者长度之比的倍数,最多也就是这个倍数加上2

要注意一种情况就是无论A如何叠加,B都不是A的子串,也就是B中有A中没有出现的字符,那么我们可以使用indexOf判断,因为要么A长度大于B,就判断B是不是三个A加起来的子串,要么B长度大于A,判断A是不是三个B加起来的子串

public static int repeatedStringMatch(String A, String B) {
        if ((B+B+B).indexOf(A)==-1&&(A+A+A).indexOf(B)==-1){
            return -1;
        }
        int lengthA=A.length();
        int lengthB=B.length();

        int multiple=lengthB/lengthA;
        for (int i=multiple;i<multiple+3;i++){
            StringBuilder stringBuilder=new StringBuilder();
            for (int j=0;j<i;j++){
                stringBuilder.append(A);
            }
            if (stringBuilder.toString().indexOf(B)!=-1){
                return i;
            }
        }
        return -1;
    }

还有一种思路便是将两者的字符循环添加到hashset中,然后进行比较看看是否可达成条件,这样写的代码虽然代码多一些但是效率会高不少,因为字符串拼接很耗时而且indexOf的内部实现也是for循环做的

public static int repeatedStringMatch(String A, String B) {
        if (A.indexOf(B)!=-1){
            return -1;
        }
        int lengthA=A.length();
        int lengthB=B.length();
        String newB=B.replaceAll(A,"");
        HashSet<String> hashSet=new HashSet<String>();
        for (int i=0;i<lengthA;i++){
            hashSet.add(A.substring(i,i+1));
        }
        HashSet<String> hashSet2=new HashSet<String>();
        for (int i=0;i<lengthB;i++){
            hashSet2.add(B.substring(i,i+1));
        }
        for (String s:hashSet2){
            if (!hashSet.contains(s)){
                return -1;
            }
        }

        int multiple=lengthB/lengthA;
        for (int i=multiple;i<multiple+3;i++){
            StringBuilder stringBuilder=new StringBuilder();
            for (int j=0;j<i;j++){
                stringBuilder.append(A);
            }
            if (stringBuilder.toString().indexOf(B)!=-1){
                return i;
            }
        }
        return -1;
    }

3.幂次方

这是一个系列的问题,给定一个整数,判别它是否是2的幂次方

是不是2的幂次方是最好判断的,因为凡是2的幂次方的数转换成二进制都有一个特点,就是前面一个1,后面跟n个0,

2:10

4:100;

8:1000;

16:10000;

我们只需要知道里面一共有多少个1就行了

需要注意的是负数全不是2的幂次方

public static boolean isPowerOfTwo(int n) {
        if (n<=0) return false;
        int x=Integer.toBinaryString(n).indexOf("1");
        int y=Integer.toBinaryString(n).lastIndexOf("1");
        if (x==y){
            return true;
        }else {
            return false;
        }
    }

更快的版本

    public static boolean isPowerOfTwo(int n) {
        if (n<=0) return false;
        int num=0;
        char[] chars=Integer.toBinaryString(n).toCharArray();
        for (int i=0;i<chars.length;i++){
            if (chars[i]=='1'){
                num++;
            }
        }
        if (num!=1){
            return false;
        }else {
            return true;
        }
    }
/**
 * 给定一个整数,写一个函数来判断它是否是 3 的幂次方。
 * 输入: 27
 * 输出: true
 * 输入: 0
 * 输出: false
 */

判断是不是3的幂次方就不能在二进制上找灵感了,但是3的幂次方对3取余一定等于0,且除以三的结果对3取余也是零

需要注意的一就是小于等于0的数都不是3的次幂,1是任何数的0次方;

 public static boolean isPowerOfThree(int n) {
        if (n<=0) return false;
        if (n==1) return true;
        while (n!=3){
            if (n%3!=0){
                return false;
            }
            n/=3;
        }
        return true;
    }
/**
 * 给定一个整数,写一个函数来判断它是否是 4 的幂次方。
 */

对于4的判断我们可以沿用3的方法,也可以在二进制上发现规律,因为一个数是4的幂次方那它就一定是2的幂次方,从二进制上你会发现除了1,2以外,凡是4的倍数都只有一个1和偶数个0;(但是leetcode上说2不是4的幂次方,我不明白为什么)

public static boolean isPowerOfFour(int num) {
        if (num==1) return true;
        if (num<=3) return false;
        while (num!=4){
            if (num%4!=0){
                return false;
            }
            num/=4;
        }
        return true;
    }
if (num==1) return true;
        if (num<=3) return false;
        int one=0;
        int zero=0;
        char[] chars=Integer.toBinaryString(num).toCharArray();
        for (int i=0;i<chars.length;i++){
            if (chars[i]=='1'){
                one++;
            }
            if (chars[i]=='0'){
                zero++;
            }
        }
        if (one!=1){
            return false;
        }else if (zero%2==0){
            return true;
        }
        return false;

4.回文数

/**
 * 给定一个数,判断是不是一个回文数
 */

这个大家都应该做过,只要比较前一半数是不是和后一半数相等就可以了,下面这种方法用到了java自带的string.toCharArray()

 public static boolean isPalindrome(int x) {
        char[] chars=(x+"").toCharArray();
        for (int i=0;i<chars.length/2 ;i++){
            if (chars[i]!=chars[chars.length-1-i]){
                return false;
            }
        }
        return true;
    }

 如果我们不借助这个方法该如何做呢?回文数就是后面等于前面嘛,我们把后边拿出来和前面比一比就可以了,奇数位数的话就把最后一位除10抹掉。我们还可以省去求数字长度的步骤,因为不断取后边的数字拼接,这个也在不断减小,如果拼接的数大于等于这个数的时候就可以停止了。在开始还可以略去两种情况,负数肯定不是回文数,大于0的是10的倍数的数肯定不是回文数

    public boolean isPalindrome(int x) {
        if(x < 0 || (x % 10 == 0 && x != 0)) {
            return false;
        }
        int revertedNumber = 0;
        while(x > revertedNumber) {
            revertedNumber = revertedNumber * 10 + x % 10;
            x /= 10;
        }
        return x == revertedNumber || x == revertedNumber/10;
    }

这样就可以了,我们做算法题的时候一定要把注意力放到算法思想上,这样才能学到更多

/**
 * 给定一个链表,判断是不是一个回文链表
 * 输入: 1->2
 * 输出: false
 * 输入: 1->2->2->1
 * 输出: true
 */
public class ListNode {
        int val;
        ListNode next;

        ListNode(int x) {
            val = x;
        }
    }

现在整数换成了链表,如何证明这个链表是不是一个回文链表呢?

我们可以先遍历这个链表,把其中的每个数添加到列表里,然后像判断回文数一样判断这个列表

当然,我们还可以借用数据结构的力量,要把一串信息倒过来用栈来做非常合适,我们定义两个变量保存链表的初始状态,也有人叫它们快慢指针,思想都一样,使用一个遍历链表并添加到栈中,然后遍历另一个链表并与栈中pop()出来的节点进行比对,全一样那就说明这个链表调过来和原链表相同,是个回文链表,有一个不是就不是回文链表;

        Stack<ListNode> stack=new Stack<>();
        ListNode slow=head;
        ListNode fast=head;

        if(fast==null||fast.next==null)//0个节点或是1个节点
            return true;

        stack.push(slow);
        while(fast.next!=null&&fast.next.next!=null)
        {

            fast=fast.next.next;
            slow=slow.next;
            stack.push(slow);
        }
        if(fast.next!=null)//链表长度为偶数
            slow=slow.next;

        ListNode cur=slow;
        while(cur!=null)
        {
            if(cur.val!=stack.pop().val)
                return false;
            cur=cur.next;
        }
        return true;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

幽蓝丶流月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值