刷题01

NC105 二分查找-I

请实现无重复数字的升序数组的二分查找

给定一个 元素升序的、无重复数字的整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标(下标从 0 开始),否则返回 -1

数据范围:0≤len(nums)≤2×1050 \le len(nums) \le 2\times10^50≤len(nums)≤2×105 , 数组中任意值满足 ∣val∣≤109|val| \le 10^9∣val∣≤109
进阶:时间复杂度 O(log⁡n)O(\log n)O(logn) ,空间复杂度 O(1)O(1)O(1)

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型一维数组 
     * @param target int整型 
     * @return int整型
     */
    public int search (int[] nums, int target) {
        // write code here
        int left=0;
        int right=nums.length;
        int mid;
        while(left<right)
        {
            mid=left+((right-left)>>1);
            if(nums[mid]>target)
            {
                right=mid;
            }
            else if(nums[mid]<target)
            {
                
                left=mid+1;
            }
            else{
                return mid;
            }
        }
        return -1;
    }
}

NC105 二分查找-II

请实现有重复数字的升序数组的二分查找
给定一个 元素有序的(升序)长度为n的整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的第一个出现的target,如果目标值存在返回下标,否则返回 -1

数据范围:0≤n≤100000 0 \le n \le 100000\ 0≤n≤100000
进阶:时间复杂度O(logn) O(logn)\ O(logn) ,空间复杂度O(n) O(n)\ O(n)

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 如果目标值存在返回下标,否则返回 -1
     * @param nums int整型一维数组 
     * @param target int整型 
     * @return int整型
     */
    public int search (int[] nums, int target) {
        // write code here
        int left=0;
        int right=nums.length;
        int mid=-1;
        while(left<right)
        {
            mid=(left+right)>>1;
            if(nums[mid]>target)
            {
                right=mid;
             
            }
            else if(nums[mid]<target)
            {
                 left=mid+1;
            }
            else
            {
                while(mid>0&&nums[mid-1]==nums[mid])
                {
                   mid--;
                }
                
                return mid;
            }
        }
        
        return -1;
        
    }
}

移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

class Solution {
    public int removeElement(int[] nums, int val) {
       int slow;
       int fast=0;
       for(slow=0;fast<nums.length;fast++){
           if(nums[fast]!=val){
               nums[slow]=nums[fast];
               slow++;
           }
       }
       return slow;
    }
}

有序数据的平方

class Solution {
    public int[] sortedSquares(int[] nums) {
       for(int i=0;i<nums.length;i++)
       {
           nums[i]=nums[i]*nums[i];
       }
       Arrays.sort(nums);
       return nums;
    }

}

长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
       int left=0;
       int right=0;
       int sum=0;
       int result=Integer.MAX_VALUE;
       for(right=0;right<nums.length;right++)
       {
           sum=sum+nums[right];
            while(sum>=target)
            {
                result=Math.min(result,right-left+1);
                sum=sum-nums[left++];
            }
       }
       return result==Integer.MAX_VALUE?0:result;
    }
}

螺旋矩阵II

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] result=new int[n][n];
        int sum=1;
        int j=0;
        while(sum<=n*n)
        {
            for(int i=j;i<n-j;i++)
            {
                result[j][i]=sum++;
            }
            for(int i=j+1;i<n-j;i++)
            {
                result[i][n-j-1]=sum++;
            }
            for(int i=n-j-2;i>=j;i--)
            {
                result[n-j-1][i]=sum++;
            }
            for(int i=n-j-2;i>j;i--)
            {
                result[i][j]=sum++;
            }
            j++;
        }
        return result;
    }
}

移除链表元素

双指针

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode DummyHead=new ListNode(0);
        DummyHead.next=head;
        ListNode pre=DummyHead;
        while(pre.next!=null)
        {
            if(pre.next.val==val)
            {
                pre.next=pre.next.next;
            }
            else
            {
                pre=pre.next;
            }
        }
        return DummyHead.next;
    }
}

反转链表

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode cur=head;
        ListNode temp=null;
        ListNode pre=null;
        while(cur!=null)
        {
            temp=cur.next;
            cur.next=pre;
            pre=cur;
            cur=temp;
        }
        return pre;
    }
}


##两两交换链表中节点

```java
class Solution {
    public ListNode swapPairs(ListNode head) {
      if(head==null||head.next==null) return head;
      ListNode next=head.next;
      ListNode temp=swapPairs(next.next);
      next.next=head;
      head.next=temp;
      return next;
    }
}

删除链表中倒数第n个节点

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head==null) return head;
        ListNode dummyHead=new ListNode(0);
        dummyHead.next=head;
        ListNode slow=dummyHead;
        ListNode fast=dummyHead;
        for(int i=0;i<n;i++)
        {
            fast=fast.next;
        }
        while(fast.next!=null)
        {
            slow=slow.next;
            fast=fast.next;
        }
        slow.next=slow.next.next;
        return dummyHead.next;
    }
}

链表相交

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
     ListNode h1=headA;
     ListNode h2=headB;
     while(h1!=h2)
     {
         h1=h1==null?headB:h1.next;
         h2=h2==null?headA:h2.next;
     }
     return h1;
    }
    //假设相交(相交点8->8):headA(1->2->3->8->8): 3+2;headB(7->6->8->8): 2+2。则 h1走3+2+2+2 = 9步(每一步访问:1->2->3->8->8->7->6->8->8); h2走2+2+2+3 = 9步(每一步访问:7->6->8->8->1->2->3->8->8)。 看最后两步。 不相交最终满足 h1 == h2 == null
}

环形链表Ⅱ

判断链表是否有环

可以使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。

为什么fast 走两个节点,slow走一个节点,有环的话,一定会在环内相遇呢,而不是永远的错开呢

首先第一点:fast指针一定先进入环中,如果fast指针和slow指针相遇的话,一定是在环中相遇,这是毋庸置疑的。

那么来看一下,为什么fast指针和slow指针一定会相遇呢?

可以画一个环,然后让 fast指针在任意一个节点开始追赶slow指针。

会发现最终都是这种情况, 如下图:

fast和slow各自再走一步, fast和slow就相遇了

这是因为fast是走两步,slow是走一步,其实相对于slow来说,fast是一个节点一个节点的靠近slow的,所以fast一定可以和slow重合。

动画如下:

如果有环,如何找到这个环的入口

此时已经可以判断链表是否有环了,那么接下来要找这个环的入口了。

假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。 如图所示:

那么相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。

因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:

(x + y) * 2 = x + y + n (y + z)

两边消掉一个(x+y): x + y = n (y + z)

因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。

所以要求x ,将x单独放在左面:x = n (y + z) - y ,

再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z 注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。

这个公式说明什么呢?

先拿n为1的情况来举例,意味着fast指针在环形里转了一圈之后,就遇到了 slow指针了。

当 n为1的时候,公式就化解为 x = z,

这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。

也就是在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。

让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。

那么 n如果大于1是什么情况呢,就是fast指针在环形转n圈之后才遇到 slow指针。

其实这种情况和n为1的时候 效果是一样的,一样可以通过这个方法找到 环形的入口节点,只不过,index1 指针在环里 多转了(n-1)圈,然后再遇到index2,相遇点依然是环形的入口节点。

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null)
        {
            slow=slow.next;
            fast=fast.next.next;
            if(slow==fast)
            {
                ListNode x=head;
                ListNode y=fast;
                while(x!=y)
                {
                    x=x.next;
                    y=y.next;
                }
                return x;
            }
        }
        return null;
    }
}
posted @ 2022-02-01 19:13  一刹流云散  阅读(74)  评论(0编辑  收藏  举报