Leetcode 81-90刷题笔记(非困难题目)

81.搜索旋转排序矩阵2

该题和之前的也是类似,区别在于该题存在重复元素的可能性,所以需要针对这个重复元素进行一个处理,其余部分的思路和以前的那个题目是一样(二分法)

该题针对重复元素,我这里是选择跳过,直到遇到非重复元素为止。

    //搜索旋转数组的变种(包含重复元素)
    public boolean search(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;

        while(left <= right) {
            int mid = left + (right - left) / 2;
            if(nums[mid] == target) {
                return true;
            }
            if(nums[left] == nums[mid]) {
                left++;
                continue;
            }
            if(nums[right] == nums[mid]) {
                right--;
                continue;
            }

            //无序部分
            if(nums[left] > nums[mid]) {
                //在左边
                if(nums[mid] > target || nums[left] <= target) {
                    right = mid - 1;
                }else {
                    left = mid + 1;
                }
            }else { //有序部分
                //在左边
                if(nums[left] <= target && nums[mid] > target) {
                    right = mid - 1;
                }else {
                    left = mid + 1;
                }
            }
        }

        return false;
    }

82.删除排序链表中的重复元素

该题目有个特殊的点是:他是要把重复的元素全部删除,而不是删除重复的部分,所以这个地方算是一个难点部分。

我这里的思考是:

既然出现了重复就要全部删除,那么我干脆结果就自创一个链表,保存完全没重复的元素。

怎么实现呢?

我们通过保留一个元素的出现次数和重复元素,出现次数 > 1说明发生重复,该元素需要全部舍弃。

那么何时添加何时判断呢?

我这里是直到出现和自定义repeat值不一样的时候才开始进行判断repeat出现次数问题。

    //排序链表,删除重复结点(特殊点在于只要重复就全部删除)
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null) {
            return null;
        }
        //几率上一个非重复结点的位置
        ListNode prev = new ListNode(0);
        ListNode ans = prev;

        ListNode temp = head;
        //记录重复结点
        int repeat = Integer.MIN_VALUE;
        //记录重复次数
        int times = 1;

        while (temp != null) {
            //初始化
            if(repeat == Integer.MIN_VALUE) {
                repeat = temp.val;
                times = 1;
                temp = temp.next;
                continue;
            }
            //出现重复
            if(repeat == temp.val) {
                times++;
                temp = temp.next;
                continue;
            }

            //出现与repeat值不同的结点,则开始计算之前的出现次数情况
            if(times > 1) { //重复情况(重置重复点,然后跳过)
                repeat = temp.val;
                times = 1;
            }else { //非重复情况 (添加repeat结点)
                prev.next = new ListNode(repeat);
                prev = prev.next;
                //重置
                repeat = temp.val;
                times = 1;
            }
            temp = temp.next;
        }

        //出口还需要在进行一次判断,以防最后一位是非重复元素
        if(times <= 1) {
            prev.next = new ListNode(repeat);
        }
        return ans.next;
    }

83.删除排序链表的重复元素

该题目是简单版本,即删除重复部分(上面是全部重复元素),就用一个标记记录重复元素的第一个即可,发生重复则跳过,没发生重复则更换标记。

    //删除重复部分
    public ListNode deleteDuplicates(ListNode head) {
        //记录重复元素第一个节点
        ListNode prev = null;
        ListNode temp = head;

        while(temp != null) {
            //第一次访问,进行初始化
            if(prev == null) {
                prev = temp;
                temp = temp.next;
                continue;
            }

            //出现重复(第二,三。。。。次访问)
            if(prev.val == temp.val) {
                //尾结点
                if(temp.next == null) {
                    prev.next = null;
                }else { //非尾结点
                    prev.next = temp.next;
                }
                temp = temp.next;
            }else { //没有重复
                //出现非重复,更换标记节点
                prev = temp;
                temp = temp.next;
            }
        }
        return head;
    }

86.分割链表

就是使用新建两个链表头部,分别记录大于小于的部分,最后在进行链表的拼接即可。

这里我们可以使用原链表结点进行拼接,不用额外申请链表结点空间,空间复杂度O(1)。

    //分隔链表
    public ListNode partition(ListNode head, int x) {
        ListNode left = new ListNode(-1);
        ListNode right = new ListNode(-1);
        ListNode left_temp = left;
        ListNode right_temp = right;

        while(head != null) {
            //记录下一位
            ListNode next = head.next;
            if(head.val < x) {
                left_temp.next = head;
                left_temp = left_temp.next;
            }else {
                right_temp.next = head;
                right_temp = right_temp.next;
            }
            head.next = null;
            head = next;
        }
        //将两个链表进行拼接
        left_temp.next = right.next;

        return left.next;
    }

88.合并两个有序数组

该题目和之前的合并数组不一样在于:这个题目是原地合并,之前是采用新创建空间再进行的合并,(这里num1的空间足够容纳两个数组所有元素)

我这道题的思路就是:

1.当数组1 < 数组2,只是将计数器增加,其他不做处理。
2.当数组1 > 数组2,让数组1给数组2腾位置,然后再把数组2元素插入,并且将计数器(下标)增加。

当数组1技术器到头后则直接插入数组2即可,如果数组2先先到头则跳出循环(因为数组1的元素本来就在数组中,且有序)

    //合并两个有序数组(原地合并,nums1空间盛满两个数组)
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int index1 = 0;
        int index2 = 0;
        int i = 0;
        
        for(; i<nums1.length; i++) {
            //说明数组2已经插入数组1结束,则完成任务
            if(index2 == n) {
                break;
            }
            //说明数组1访问结束,直接插入数组2即可
            if(index1 == m) {
                nums1[i] = nums2[index2++];
                continue;
            }

            if(nums1[i] > nums2[index2]) {
                //首先需要给他腾位置
                for(int j=nums1.length-1; j>i; j--) {
                    nums1[j] = nums1[j - 1];
                }
                nums1[i] = nums2[index2++];
            }else {
                index1++;
            }
        }
    }

90.子集2

该题和以前的子集1类似,不过该题是出现了重复元素,还是一样的,该题是不允许重复子集的出现,就类似于4(1)4(2)4(3),我们只允许{4},{4,4},{4,4,4}出现,不能再有重复的。

这里我们可以换一个思路,这个说到底就是{1},{1,2},{1,2,3}(这里指的是重复元素的牌号)这个样子,那就说明他们的访问必须是顺序的,并且只有一次访问(即只能从重复元素的第一个开始,第二个不能作为开头元素),这里就有点顺序性的概念。

所以我们干脆指定一个访问规则。

发生重复时
1.前面的重复元素没有访问的时候,那么我当前元素则无法访问。
2.前面的重复元素访问过后,我才能进行访问。


public class Solution {
    private boolean[] flag;
    //子集2,存在重复元素
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> lists = new ArrayList<>();
        if(nums.length == 0) {
            return lists;
        }
        Arrays.sort(nums);
        //添加空集
        lists.add(new ArrayList<>());
        flag = new boolean[nums.length];
        backTrace(lists,new ArrayList<>(),nums,0);

        return lists;
    }

    public void backTrace(List<List<Integer>> lists, List<Integer> list, int[] nums, int index) {
        if(index == nums.length) {
            return;
        }

        //防止重复消费
        for(int i=index; i<nums.length; i++) {
            //解决重复,使他们必须顺序访问
            if(i > index && nums[i] == nums[i - 1] && !flag[i - 1]) {
                continue;
            }
            flag[i] = true;
            list.add(nums[i]);
            lists.add(new ArrayList<>(list));
            //回溯思想
            backTrace(lists, list, nums, i + 1);
            list.remove(list.size() - 1);
            flag[i] = false;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值