2021-10-19 双指针问题 + 链表问题

977. 有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
提示:
1 <= nums.length <= 10^4
-10^4 <= nums[i] <= 10 ^4
nums 已按 非递减顺序 排序

示例 1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

示例 2:
输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]
解析过程:
最简单的方法,就是直接开辟一个新的数组,然后将每个数字进行平方,并存储在新数组中,再对新数组进行排序即可。

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

结果:
执行用时:2 ms, 在所有 Java 提交中击败了42.42%的用户
内存消耗:40.2 MB, 在所有 Java 提交中击败了58.25%的用户
通过测试用例:137 / 137

189. 旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
提示:
1 <= nums.length <= 2 * 10^4
-2^31 <= nums[i] <= 2 ^31 - 1
0 <= k <= 10^5
进阶:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?

示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]

示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
解析过程:
开辟一个新的数组,遍历原数组,将原数组中下标为i的元素重新放到下标为(i+k)%nums.length的位置,之后将新数组拷贝到原数组即得所求。

class Solution {
    public void rotate(int[] nums, int k) {
        int[] nums1=new int[nums.length];
        for(int i=0;i<nums.length;i++){
            nums1[(i+k)%nums.length]=nums[i];
        }
        System.arraycopy(nums1,0,nums,0,nums.length);
    }
}

结果:
执行用时:1 ms, 在所有 Java 提交中击败了61.71%的用户
内存消耗:55.3 MB, 在所有 Java 提交中击败了44.98%的用户
通过测试用例:38 / 38

283. 移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
解析过程:
采用快慢指针的思想

class Solution {
    public void moveZeroes(int[] nums) {
        if (nums == null || nums.length== 0) {
            return;
        }
        int temp=0;
        int slow=0;
        for(int fast=0;fast<nums.length;fast++){
            if(nums[fast]!=0){
                if(fast!=slow){
                    temp=nums[fast];
                    nums[fast]=nums[slow];
                    nums[slow]=temp;
                }
                slow++;
            }
        }
    }
}

结果:
74 / 74 个通过测试用例
状态:通过
执行用时: 2 ms
内存消耗: 39.8 MB

167. 两数之和 II - 输入有序数组
给定一个已按照 非递减顺序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。
函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

示例 1:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

示例 2:
输入:numbers = [2,3,4], target = 6
输出:[1,3]

示例 3:
输入:numbers = [-1,0], target = -1
输出:[1,2]
过程解析:
采用双指针的技巧,该题解法类似于二分搜索,通过sum的大小来调节left和right的移动。与之前两数之和的区别在于,该题是有序数组!!!

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int left=0,right=numbers.length-1;
        while(left<right){
            int sum=numbers[left]+numbers[right];
            if(sum==target){
                return new int[] {left+1,right+1};
            }else if(sum<target){
                left++;
            }else if(sum>target){
                right--;
            }
        }
        return new int[] {-1,-1};
    }
}

结果:
执行用时:1 ms, 在所有 Java 提交中击败了66.14%的用户
内存消耗:38.5 MB, 在所有 Java 提交中击败了75.60%的用户
通过测试用例:19 / 19

344. 反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

示例 1:
输入:s = [“h”,“e”,“l”,“l”,“o”]
输出:[“o”,“l”,“l”,“e”,“h”]

示例 2:
输入:s = [“H”,“a”,“n”,“n”,“a”,“h”]
输出:[“h”,“a”,“n”,“n”,“a”,“H”]
过程解析:
采用双指针的思想

class Solution {
    public void reverseString(char[] s) {
        if(s==null||s.length==0){
            return;
        }
        int left=0,right=s.length-1;
        while(left<right){
            char temp=' ';
            temp=s[left];
            s[left]=s[right];
            s[right]=temp;
            left++;
            right--;
        }
    }
}

结果:
执行用时:1 ms, 在所有 Java 提交中击败了95.62%的用户
内存消耗:44.7 MB, 在所有 Java 提交中击败了89.28%的用户
通过测试用例:477 / 477

557. 反转字符串中的单词 III
给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。

示例:
输入:“Let’s take LeetCode contest”
输出:“s’teL ekat edoCteeL tsetnoc”
过程解析:

class Solution {
    public String reverseWords(String s) {
        //先将字符串转为字符串数组
        char[] arr=s.toCharArray();
        //新建一个数组,用来存放逆序的单词
        char[] word=new char[arr.length];
        int i=0,j=0;
        int k=0;// 下一次往新数组中存储单词时的起始位置(k是在数组arr中)
        while(i<arr.length){
            //判断数组中当前字符是否为空字符,若是,则将前面的一个单词反转存储在数组中
            if(arr[i]==' '){
                for(int t=i-1;t>=k;t--){
                    //将单词逆序存放
                    word[j++]=arr[t];
                }
                word[j]=' '; //在添加的逆序单词后面加一个空字符
                j++;
                k=i+1;
            }
            i++;
        }
        //由于最后一个单词后面没有空字符,所以拿出来单独存储
        for(int t=i-1;t>=k;t--){
            word[j++]=arr[t];
        }
        //将数组再转为字符串
        String str=String.valueOf(word);
        return str;
    }
}

结果:
执行用时:2 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:38.4 MB, 在所有 Java 提交中击败了98.78%的用户
通过测试用例:29 / 29

876. 链表的中间结点
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
提示:给定链表的结点数介于 1 和 100 之间。

示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.

示例 2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
过程解析:
方法一:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode middleNode(ListNode head) {
        //双指针的思想(快慢指针)
        ListNode fast=head,slow=head;
        //慢指针前进一步,快指针前进两步,当fast走到链表最后一个节点时,slow所指位置即为链表的中间节点
        while(fast!=null&&fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
        }
        return slow;
    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:35.9 MB, 在所有 Java 提交中击败了30.14%的用户
通过测试用例:36 / 36
方法二:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode middleNode(ListNode head) {
        //数组思想
        ListNode[] array=new ListNode[100];
        int index=0;
        while(head!=null){
           array[index++]=head;
           head=head.next; 
        }
        return array[index/2];
    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:36 MB,在所有 Java 提交中击败了15.84%的用户
通过测试用例:36 / 36

19. 删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?

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

示例 2:
输入:head = [1], n = 1
输出:[]

示例 3:
输入:head = [1,2], n = 1
输出:[1]
过程解析:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        //快慢指针思想
        //快指针先前进n步,之后快慢指针同时前进,当快指针走到最后一个节点时,满指针所指位置即为倒数第n个节点
        //初始化一个空节点,初始赋值为0,并且list的下一个next指针指向head,指针指向为zero
        ListNode zero=new ListNode(0,head);
        ListNode fast=head,slow=zero;
        while(n>0){
            fast=fast.next;
            n--;
        }
        while(fast!=null){
            fast=fast.next;
            slow=slow.next;
        }
        slow.next=slow.next.next; //将倒数第n个节点删除
        ListNode node=zero.next;
        return node;
    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:36.3 MB, 在所有 Java 提交中击败了74.18%的用户
通过测试用例:208 / 208

141. 环形链表
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。

进阶:你能用 O(1)(即,常量)内存解决此问题吗?

示例 1:
在这里插入图片描述

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
在这里插入图片描述

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
在这里插入图片描述
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
解析过程:

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        //快慢指针思想,如果链表中没有环,则跑的快的那个指针会返回null;如果链表中含有环,则快指针超慢指针1圈,和慢指针相遇
        ListNode fast,slow;
        fast=slow=head;
        while(fast !=null && fast.next!=null){
            //快指针每次前进两步,慢指针前进一步
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow){
                return true;
            }
        }
        return false;
    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:39.7 MB, 在所有 Java 提交中击败了14.83%的用户
通过测试用例:20 / 20

21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
提示:
两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序 排列

示例 1:
在这里插入图片描述

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:
输入:l1 = [], l2 = []
输出:[]

示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
解析过程:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        //暴力解法,逐一比较取最小值
        //引入一个哑结点,值为-1
        ListNode prehead=new ListNode(-1);
        
        ListNode pre=prehead;
        while(l1!=null&&l2!=null){
            if(l1.val<l2.val){
                pre.next=l1;
                l1=l1.next;
            }else {
                pre.next=l2;
                l2=l2.next;
            }
            pre=pre.next;
        }
        //合并完后会有其中一个链表未合并完,则直接将链表的末尾指向未合并完的链表即可
        pre.next=l1==null ?l2:l1;
        return prehead.next;
    }
}

调试:

public class Main {
    public static void main(String[] args) {
        ListNode node1= new ListNode(1);
        ListNode node2= new ListNode(1);
        ListNode node3= new ListNode(2);
        ListNode node4= new ListNode(3);
        ListNode node5= new ListNode(4);
        ListNode node6= new ListNode(4);

        node1.next=node3;
        node3.next=node5;
        node2.next=node4;
        node4.next=node6;

        ListNode pr= new Leetcode21().mergeTwoLists(node1,node2);
        while(pr !=null){
            System.out.print(pr.val +" ");
            pr=pr.next;
        }
    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:38 MB, 在所有 Java 提交中击败了5.13%的用户
通过测试用例:208 / 208

203. 移除链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

示例 1:
在这里插入图片描述

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

示例 2:
输入:head = [], val = 1
输出:[]

示例 3:
输入:head = [7,7,7,7], val = 7
输出:[]
解析过程:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        //暴力解法,依次遍历,如果节点的值等于val,就删除
        //设置一个哑结点,值为-1
        ListNode prehead=new ListNode(-1);
        prehead.next=head;
        ListNode pre=prehead;
        while(pre.next !=null){
            //如果pre 的下一个节点不为空且下一个节点的节点值等于val,则需要删除下一个节点
            if( pre.next.val == val){
                pre.next=pre.next.next;
            }else {
                pre=pre.next;
            }
        }
        return prehead.next;
    }
}

调试:

public class Main {
    public static void main(String[] args) {
        ListNode node1= new ListNode(1);
        ListNode node2= new ListNode(2);
        ListNode node3= new ListNode(6);
        ListNode node4= new ListNode(3);
        ListNode node5= new ListNode(4);
        ListNode node6= new ListNode(5);
        ListNode node7= new ListNode(6);

        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        node4.next=node5;
        node5.next=node6;
        node6.next=node7;
        int val=6;

        ListNode pr= new Leetcode203().removeElements(node1,val);
        ListNode ps= new PrintListNode().printListNode(pr);
    }

public class PrintListNode {
    public ListNode printListNode(ListNode l1){
        while(l1 !=null){
            System.out.print(l1.val +" ");
            l1=l1.next;
        }
        return l1;
    }
}

结果:
执行用时:1 ms, 在所有 Java 提交中击败了96.26%的用户
内存消耗:39.1 MB, 在所有 Java 提交中击败了87.49%的用户
通过测试用例:66 / 66

206. 反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
提示:
链表中节点的数目范围是 [0, 5000]
-5000 <= Node.val <= 5000

示例 1:
在这里插入图片描述

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

示例 2:
在这里插入图片描述

输入:head = [1,2]
输出:[2,1]

示例 3:
输入:head = []
输出:[]
解析过程:
方法一:递归

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null||head.next==null){
            return head;
        }
        ListNode rehead=reverseList(head.next);
        head.next.next=head;
        //注意,head的下一个节点一定要指向空
        head.next=null;
        return rehead;
    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:38.1 MB, 在所有 Java 提交中击败了68.66%的用户
通过测试用例:28 / 28

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        //方法二:迭代
        if(head==null||head.next==null){
            return head;
        }
        ListNode pre=null;
        ListNode rehead=head;
        while(rehead != null){
            //设置一个节点来存储后一个节点
            ListNode node=rehead.next;
            //让当前节点的指针指向上一个节点
            rehead.next=pre;
            pre=rehead;
            rehead=node;
        }
        return pre;
    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:38.2 MB, 在所有 Java 提交中击败了48.00%的用户
通过测试用例:28 / 28

83. 删除排序链表中的重复元素
存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。
返回同样按升序排列的结果链表。
提示:
链表中节点数目在范围 [0, 300] 内
-100 <= Node.val <= 100
题目数据保证链表已经按升序排列

示例 1:
在这里插入图片描述

输入:head = [1,1,2]
输出:[1,2]
示例 2:
在这里插入图片描述

输入:head = [1,1,2,3,3]
输出:[1,2,3]
解析过程:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head==null||head.next==null){
            return head;
        }
        ListNode p=head; //当前节点
        while(p!=null&&p.next!=null){
            if(p.val==p.next.val){
                p.next=p.next.next;
            }else{
                p=p.next;
            }
        }
        return head;
    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:37.8 MB, 在所有 Java 提交中击败了77.02%的用户
通过测试用例:166 / 166

237. 删除链表中的节点
请编写一个函数,用于 删除单链表中某个特定节点 。在设计函数时需要注意,你无法访问链表的头节点 head ,只能直接访问 要被删除的节点 。
题目数据保证需要删除的节点 不是末尾节点 。

提示:
链表中节点的数目范围是 [2, 1000]
-1000 <= Node.val <= 1000
链表中每个节点的值都是唯一的
需要删除的节点 node 是 链表中的一个有效节点 ,且 不是末尾节点

示例 1:
在这里插入图片描述

输入:head = [4,5,1,9], node = 5
输出:[4,1,9]
解释:指定链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9
示例 2:
在这里插入图片描述
输入:head = [4,5,1,9], node = 1
输出:[4,5,9]
解释:指定链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9
示例 3:
输入:head = [1,2,3,4], node = 3
输出:[1,2,4]
示例 4:
输入:head = [0,1], node = 0
输出:[1]
示例 5:
输入:head = [-3,5,-99], node = -3
输出:[5,-99]
解析过程:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public void deleteNode(ListNode node) {
        //题目已保证需要删除的节点不是末尾节点
        //将要删除的节点值与下一个节点的进行交换,然后删除下一个节点
           node.val=node.next.val;
           node.next=node.next.next;
    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:37.8 MB, 在所有 Java 提交中击败了68.12%的用户
通过测试用例:41 / 41

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值