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;
}
}
}