Skip to content

141. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:

img

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
java
public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head==null || head.next == null) return false;
        //快慢双指针,同时出发
        ListNode slow = head,fast=slow;
        while(fast!=null&&fast.next!=null){
            slow = slow.next;
            fast = fast.next.next;
            if(fast==slow){
                return true;
            }
        }
        return false;
    }
}

142. 环形链表 II

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

java
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head==null||head.next==null) return null;
        ListNode slow=head,fast=slow;
        boolean hasCycle = false;
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
            if(fast==slow){//如果有环,跳出循环
                hasCycle=true;break;
            }
        }
        if(hasCycle==false) return null;
        //任选一个回到头节点,一步一步走
        slow=head;
        while(fast!=slow){
            fast=fast.next;slow=slow.next;
        }
        return slow;
    }
}

双指针技巧主要分为两类:快慢指针和左右指针

快慢指针 ​

26. 删除有序数组中的重复项 ​

给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。

  • 返回 k 。

  • 示例 1:

    输入:nums = [1,1,2]
    输出:2, nums = [1,2,_]
    解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
java
class Solution {
    public int removeDuplicates(int[] nums) {
        //快慢指针同时从0出发
        int slow=0,fast=0;
        //快指针在前面探路,找我们想要的不重复元素
        while(fast<nums.length){
            if(nums[slow]!=nums[fast]){
                slow++;
                nums[slow]=nums[fast];
            }
            fast++;
        }
        return slow+1;
    }
}

27. 移除元素 &#8203;

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

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

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

示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
java
class Solution {
    public int removeElement(int[] nums, int val) {
        //快慢指针同时出发
        int slow=0,fast=0;
        //快指针在前面探路,找我们想要的不为val的元素
        while(fast<nums.length){
            if(nums[fast]!=val){
                nums[slow]=nums[fast];
                slow++;
            }
            fast++;
        }
        return slow;
    }
}

206. 反转链表 &#8203;

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

img

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

img

java
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode cur = head;
        ListNode pre = null;
        ListNode tmp;
        while(cur!=null){
            tmp = cur.next;
            cur.next = pre;//反转
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
}

面试题 02.02. 返回倒数第 k 个节点 &#8203;

实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。

注意:本题相对原题稍作改动

示例:

输入: 1->2->3->4->5 和 k = 2
输出: 4
java
class Solution {
    public int kthToLast(ListNode head, int k) {
    	ListNode slow = head,fast=head;
        while(k-->0){//fast先走k步
            fast=fast.next;
        }
        while(fast!=null){
            slow=slow.next;
            fast=fast.next;
        }
        return slow.val;
    }
}

19. 删除链表的倒数第 N 个结点 &#8203;

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

img

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
java
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        //使用虚拟头节点,方便删除操作
        ListNode dummyHead=new ListNode(-1,head);
        ListNode fast = dummyHead,slow=fast;
        n=n+1;//找到倒数第n+1个节点,然后删除它的下一个
        while(n-->0){
            fast=fast.next;
        }
        while(fast!=null){
            fast=fast.next;
            slow=slow.next;
        }
        //删除节点
        slow.next=slow.next.next;
        return dummyHead.next;
    }
}

160. 相交链表 &#8203;

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

图示两个链表在节点 c1 开始相交:

img

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

java
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode curA=headA,curB=headB;
        while (curA != curB) {
            curA=curA==null?headB:curA.next;
            curB=curB==null?headA:curB.next;
        }
        return curA;
    }
}

左右指针 &#8203;

344. 反转字符串 &#8203;

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

示例 1:

输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]
java
class Solution {
    public void reverseString(char[] s) {
        int left=0,right=s.length-1;
        while(left<right){
            char tmp = s[left];
            s[left] = s[right];
            s[right] = tmp;
            left++;right--;
        }
    }
}

704. 二分查找 &#8203;

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
java
class Solution {
    public int search(int[] nums, int target) {
        int len = nums.length;
        int left = 0,right = len-1;
        while(left<=right){
            int mid = (right-left)/2+left;
            if(nums[mid]==target) return mid;
            else if(nums[mid]<target) left=mid+1;
            else if(nums[mid]>target) right=mid-1;
        }
        return -1;
    }
}

15. 三数之和 &#8203;

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

java
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
		//排序,为了排除情况,和后面用二分法
        Arrays.sort(nums);//-4, -1, -1, 0, 1, 2
        if(nums[0]>0) return new ArrayList<>();
        //放到Set里去重
        Set<List<Integer>> res = new HashSet<>();
        for (int i = 0; i < nums.length-2; i++) {
            int target = -nums[i];
            //在数组种寻找sum为target的组合
            int left = i + 1, right = nums.length - 1;
            while (left < right) {//二分查找
                int sum = nums[left] + nums[right];
                if (sum == target) {
                    res.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    left++;
                } else if (sum < target) {//说明太小了
                    left++;
                } else right--;
            }
        }
        return new ArrayList<>(res);
    }
}

3. 无重复字符的最长子串 &#8203;

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
java
class Solution {
    public int lengthOfLongestSubstring(String s) {
        int left=0,right=0;
        int res=0;
        HashMap<Character, Integer> window = new HashMap<>();
        while(right<s.length()){
            char in = s.charAt(right++);//进窗口
            window.put(in, window.getOrDefault(in, 0) + 1);
            while(window.get(in)>1){//说明重复了
                char out=s.charAt(left++);//出窗口
                window.put(out,window.get(out)-1);
            }
            res=Math.max(res,right-left);
        }
        return res;
    }
}