【精选】JAVA算法题(十八)

一、移除链表中指定的节点

题目:

/**
 * 删除链表中等于给定值 val 的所有节点。
 *
 * 示例:
 * 输入: 1->2->6->3->4->5->6, val = 6
 * 输出: 1->2->3->4->5
 */

题的描述很简单,做起来并不容易,有一些小坑,要注意的就是头尾的节点的处理和连续相等的情况处理,其它的就非常简单了,遍历链表,将与val相等的前一个节点和下一节点相连。

处理这样的问题肯定会用到while循环,这样更方便也更不容易出错。

因为要处理头结点就是目标节点的情况,所以你可以在开头就加上一个while循环来过滤

在循环中你可以检测下一个节点的val是否等于目标值,如果相等就while判断下一个节点的val是不是目标,直到找到不是的那一个,连接之前的节点,为此你要定义一个局部变量方便操作

在其中你还要注意加上判断以防止尾节点的问题

    public static ListNode method1(ListNode head, int val) {
        while (head!=null&&head.val==val){
            head=head.next;
        }
        if (head==null){
            return null;
        }
        ListNode operation=head;
        while (operation.next!=null){
            if (operation.next.val==val){
                if (operation.next.next==null){
                    operation.next=null;
                }else {
                    ListNode transientNode=operation.next;
                    while (transientNode!=null&&transientNode.val==val){
                        transientNode=transientNode.next;
                    }
                    if (transientNode==null){
                        operation.next=null;
                    }else {
                        operation.next=transientNode;
                        operation=transientNode;
                    }
                }
            }else {
                operation=operation.next;
            }

        }
        return head;
    }

这样的代码很长很繁琐,还有一堆判断,我们是否可以有一个更好的办法来优化算法

你要考虑头结点的问题,这个问题你可以自己给它加上一个头结点,这样就不用再为头结点的处理加一个循环了

你要考虑连续结点值都是目标值的问题,你可以采用这样的策略,如果下一个节点是目标值,那么就连接next.next,否则向前移动。这样的话即使是连续目标值也能得到判断

    public static ListNode method2(ListNode head,int val){
        ListNode header = new ListNode(-1);
        header.next = head;
        ListNode cur = header;
        while(cur.next != null){
            if(cur.next.val == val ){
                cur.next = cur.next.next;
            }else{
                cur = cur.next;
            }
        }
        return header.next;
    }

这样一看代码是不是就减少了很多了,思路也很清晰,几乎所有的while循环的问题我们都可以用递归解决

我们要关注的问题就是当前节点的val值是不是目标值,如果是就返回next,不是就返回本身

    public static ListNode method3(ListNode head,int val){
        if(head == null) return null;
        head.next = method3(head.next, val);
        return head.val == val ?  head.next : head;
    }

    public static class ListNode {
      int val;
      ListNode next;
      ListNode(int x) { val = x; }
    }

是不是思路更为清晰了呢?

二、质数的数量

题目:

/**
 * 统计所有小于非负整数 n 的质数的数量。
 *
 * 示例:
 * 输入: 10
 * 输出: 4
 * 解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
 */

入门的时候肯定学过如何判断一个数是不是质数,也学过一些优化,那就一个一个判断嘛,所以就写下了这段代码

    public static int method1(int n) {
        int count=0,j=0,k=0;
        for (int i=2;i<=n;i++){
            k=(int)sqrt(i+1);
            for (j=2;j<=k;j++){
                if (i%j==0){
                    break;
                }
            }
            if (j==k+1){
                count++;
            }
        }
        return count;
    }

然而事情并没有那么简单,它是有时间限制的,给出了一个5000000的大数,我就懵了,仔细研究,一定有什么诀窍

写下20个数,仔细观察,你会发现质数的倍数都不是质数(很傻缺的结论),但是你就可以用这个结论来届这道题,如果你检测到3是一个质数,那么之后的6,9,12,15,18.。。。。就都不是质数了,都不用判断了,这就节省了非常多的判断。

    public static int method2(int n){
        boolean[] isPrime = new boolean[n];
        for (int i = 2; i < n; i++) {
            isPrime[i] = true;
        }
        for (int i = 2; i * i < n; i++) {
            if (!isPrime[i]) continue;
            for (int j = i * i; j < n; j += i) {
                isPrime[j] = false;
            }
        }
        int count = 0;
        for (int i = 2; i < n; i++) {
            if (isPrime[i]) count++;
        }
        return count;
    }

三、同构字符串

题目:

/**
 * 给定两个字符串 s 和 t,判断它们是否是同构的。
 * 如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。
 * 所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。
 *
 * 示例 1:
 * 输入: s = "egg", t = "add"
 * 输出: true
 *
 * 示例 2:
 * 输入: s = "foo", t = "bar"
 * 输出: false
 *
 * 示例 3:
 * 输入: s = "paper", t = "title"
 * 输出: true
 */

满足同构字符串有一个关键的条件,那就是字符与字符之间必须是一对一的关系,不能一对多也不能多对一。那我们就使用HashMap建立这样的一对一的映射关系,最后遍历字符串看看是否对应正确。

    public static boolean method1(String s, String t) {
        if (s.length()!=t.length())return false;
        HashMap<Character,Character> hashMap=new HashMap<>();
        char[] chars=s.toCharArray();
        char[] chart=t.toCharArray();
        for (int i=0;i<chars.length;i++){
            if (!hashMap.keySet().contains(chars[i])&&hashMap.values().contains(chart[i])){
                return false;
            }
            hashMap.put(chars[i],chart[i]);
        }
        for (int i=0;i<chars.length;i++){
            if (!hashMap.get(chars[i]).equals(chart[i])){
                return false;
            }
        }
        return true;
    }

这样要遍历字符串两遍,可以优化一下,使它遍历一遍。要完成的任务为一对一的字符映射,借助HashMap,可以先检测键中是否含有该字符,含有的话取出来看看映射的是否正确,没有的话看看值中是否含有对应字符,含有就说明对应关系错误。这样一遍遍历就搞定了。

    public static boolean method2(String s, String t) {
        if (s.length()!=t.length())return false;
        HashMap<Character,Character> hashMap=new HashMap<>();
        char[] chars=s.toCharArray();
        char[] chart=t.toCharArray();
        for (int i=0;i<chars.length;i++){
            if (hashMap.keySet().contains(chars[i])){
                if (!hashMap.get(chars[i]).equals(chart[i])){
                    return false;
                }
            }else {
                if (hashMap.values().contains(chart[i])){
                    return false;
                }else {
                    hashMap.put(chars[i],chart[i]);
                }
            }
        }
        return true;
    }

还能更为优化一下,我们是借助HashMap来完成字符的一对一映射的,HashMap的底层是使用数组实现的,而字符刚好可以转换为Ascll码当做数组下标来使用,核心思想还是一样,但这样性能上就更为提升了一截。

    public static boolean method4(String s,String t){
        char[] sc = s.toCharArray();
        char[] tc = t.toCharArray();
        char[] map = new char[256];
        for (int i = sc.length-1;i >= 0;i--) {
            if (map[sc[i]] != map[tc[i]+128]) {
                return false;
            }
            map[sc[i]] = map[tc[i] + 128] = sc[i];
        }
        return true;
    }

更换一种思路,两个字符串相互判断,对两个结果使用与连接。不在内部处理一对一,而是简单的检查多对一,相互判断之后的结果也是一对一。

    public static boolean method3(String s,String t){
        return isIsomorphic(s,t) && isIsomorphic(t,s);
    }

    private static boolean isIsomorphic(String t, String s) {
        char[] dict = new char[256];
        Arrays.fill(dict, (char) 0);
        for (int i = 0; i < s.length(); i++) {
            if (dict[s.charAt(i)] == (char) 0) {
                dict[s.charAt(i)] = t.charAt(i);
            } else if (dict[s.charAt(i)] != t.charAt(i)) {
                return false;
            }
        }
        return true;
    }

四、重复元素

题目:

/**
 * 给定一个整数数组,判断是否存在重复元素。
 * 如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。
 *
 * 示例 1:
 * 输入: [1,2,3,1]
 * 输出: true
 *
 * 示例 2:
 * 输入: [1,2,3,4]
 * 输出: false
 *
 * 示例 3:
 * 输入: [1,1,1,3,3,4,3,2,4,2]
 * 输出: true
 */

这道题可以说是相当简单了,可以直接使用我们熟知的数据结构Set来进行结题

    public static boolean method1(int[] nums) {
        HashSet<Integer> integerHashSet=new HashSet<>();
        for (int a:nums){
            integerHashSet.add(a);
        }
        if (integerHashSet.size()!=nums.length){
            return true;
        }else {
            return false;
        }
    }

但我们还是要用一些花样来解这道题,学习嘛,使用Arrays的排序方法对数组排序,这样相同的元素就被放到一起了,然后for循环~

    public static boolean method2(int[] nums){
        Arrays.sort(nums);
        for (int i=0;i<nums.length-1;i++){
            if (nums[i]==nums[i+1]){
                return true;
            }
        }
        return false;
    }

学过java8的同学可能有一些新思路,使用流处理,分组,计数,看看是不是和数组长度相等。使用流不关注实现只关注结果,这样一行代码就搞定了,但实际上他的效率并不高。

    public static boolean method3(int[] nums){
        return Arrays.stream(nums).distinct().count() != nums.length;
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

幽蓝丶流月

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

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

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

打赏作者

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

抵扣说明:

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

余额充值