JAVA入门算法题(七)

有志者自有千计万计,无志者只感千难万难。

1.链表合并

 /**
     * 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
     * 输入:1->2->4, 1->3->4
     * 输出:1->1->2->3->4->4
     */

给出的链表结构:

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

最笨的方法:遍历两个链表,取出其中所有元素,存入List或者数组,使用sort()函数或者使用排序算法(最慢的冒泡排序或者快一些的快速排序),按照排好序的List或数组拼接成链表,下面的代码是采用的List

public static ListNode method1(ListNode l1, ListNode l2) {
        if (l1==null&&l2!=null){
            return l2;
        }else if (l1!=null&&l2==null){
            return l1;
        }else if (l1==null&&l2==null){
            return null;
        }
        List<Integer> integerList=new ArrayList<>();
        while (l1!=null){
            integerList.add(l1.val);
            l1=l1.next;
        }
        while (l2!=null){
            integerList.add(l2.val);
            l2=l2.next;
        }
        integerList.sort(new SortByInteger());
        ListNode listNode=new ListNode(integerList.get(0));
        for (int i=1;i<integerList.size();i++){
            ListNode listNode1=new ListNode(integerList.get(i));
            listNode1.next=listNode;
            listNode=listNode1;
        }
        return listNode;
    }

    public static class SortByInteger implements Comparator {
        @Override
        public int compare(Object o1, Object o2) {
            return (int)o1 > (int)o2 ? -1 : 1;
        }
    }

显然这不是一种效率高的算法,如果题目中给的条件你没有利用全你几乎很难写出高效率的算法,题目中给了一个条件:两个有序链表,注意,是有序!那我们就可以不断从头比较哪个小就添加到另一个额外链表中去,最后剩余一节就直接拼接上,这样得到的链表也是有序的。

// 类似归并排序中的合并过程
        ListNode dummyHead = new ListNode(0);
        ListNode cur = dummyHead;
        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                cur.next = l1;
                cur = cur.next;
                l1 = l1.next;
            } else {
                cur.next = l2;
                cur = cur.next;
                l2 = l2.next;
            }
        }
        // 任一为空,直接连接另一条链表
        if (l1 == null) {
            cur.next = l2;
        } else {
            cur.next = l1;
        }
        return dummyHead.next;

2.移除重复数字

/**
     * 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
     * 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
     * 示例:
     * 给定数组 nums = [1,1,2],
     * 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
     * 你不需要考虑数组中超出新长度后面的元素。
     *
     * 给定 nums = [0,0,1,1,1,2,2,3,3,4],
     * 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
     * 你不需要考虑数组中超出新长度后面的元素。
     */

题目中提到了原地删除,意思就是说在给定的参数上操作,不返回新的参数,这种算法叫做原地算法。

那我们还注意到题目中给了排序数组,也就不需要我们担心移除了一个数字后,一段数字后还会出现该数字。

解决这类问题的最佳思路便是快慢指针了,声明两个指针,如果快指针遇到和慢指针相同的数字,那就什么都不做,继续往前跑,如果遇到不同的数字,就把该位置的数字给慢指针,慢指针也前进一步,快指针一直这样跑,直到数组末尾。最后数组也修改完了,慢指针的下标前进一格便是不同数字的个数了。

public static int removeDuplicates(int[] nums) {
        if (nums.length == 0)
            return 0;
        int number = 0;
        for (int i=0; i < nums.length ; i++) {
            if ( nums[i] != nums[number] ) {
                System.out.println(nums[i]+"     "+nums[number]);
                number++;
                nums[number] = nums[i];
            }
        }
        number+=1;
        return number;
    }

3.移除指定数字

/**
     * 给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
     * 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
     * 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
     *
     * 示例:
     * 给定 nums = [3,2,2,3], val = 3,
     * 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
     *
     * 给定 nums = [0,1,2,2,3,0,4,2], val = 2,
     * 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
     */

这道题和上一题有一些相似,移除一个指定数字并返回新的长度。

那我们可以采用和上一题相似的方法,采用快慢指针不断把不是指定数字的数字移动到慢指针所在位置,因为我把慢指针所在位置先赋值再移动慢指针的顺序,所以最后返回的长度不用加一,直接返回慢指针所在位置就可以。

public int method1(int[] nums, int val) {
        if (nums.length == 0)
            return 0;
        int number = 0;
        for (int i=0; i < nums.length ; i++) {
            if ( nums[i] != val ) {
                nums[number] = nums[i];
                number++;
            }
        }
        return number;
    }

还有一种办法便是采用头指针和尾指针,两者不断向中间移动并移动数字,不断把指定的数字移动到数组末端。

public int method2(int[] nums, int val) {
        int i = 0;
        int n = nums.length;
        while (i < n) {
            if (nums[i] == val) {
                nums[i] = nums[n - 1];
                n--;
            } else {
                i++;
            }
        }
        return n;
    }

4.插入数字

/**
     * 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
     * 你可以假设数组中无重复元素。
     *
     * 输入: [1,3,5,6], 5
     * 输出: 2
     *
     * 输入: [1,3,5,6], 2
     * 输出: 1
     *
     * 输入: [1,3,5,6], 7
     * 输出: 4
     *
     * 输入: [1,3,5,6], 0
     * 输出: 0
     */

这道题可以我们都很熟悉,只是从查找变为了查找插入,我们很自然的就想到了二分查找,但最简单的办法当然是直接for循环了,在里面添加一些判断便可以搞定这道题

public int method1(int[] nums, int target) {
        for (int i=0;i<nums.length;i++){
            if (i==nums.length-1) return nums.length;
            if (target<=nums[0]) return 0;
            if (nums[i]==target) return i;
            if (nums[i]>target&&target<nums[i+1]){
                return i+1;
            }
        }
        return 0;
    }

这样的代码一看就很啰嗦,我们可以使它简化

public int method2(int[] nums, int target) {
        for(int i = 0; i < nums.length;i++){
            if(nums[i] >= target)return i;
        }
        return nums.length;
    }

多美妙,简简单单的几行代码就解决掉了问题,但我们还是得试试二分查找,因为数组一旦很长这种算法的效率便可以把上面这种算法甩的远远的。开始的时候我们需要一些判断以便减少运算量,最后还要加上一些小修改便于查找不到的插入处理。

public int method3(int[] nums, int target) {
        if (target <= nums[0]) {
            return 0;
        }
        if (target > nums[nums.length - 1]) {
            return nums.length;
        }
        int left = 0, right = nums.length - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) {
                return mid;
            } else if (target < nums[mid]) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return nums[left] < target ? left + 1 : left;
    }

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

幽蓝丶流月

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

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

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

打赏作者

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

抵扣说明:

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

余额充值