LeetCode(1)

目录

时间复杂度分析:

 递归

题1:爬楼梯

 解法1:递归

 解法2:循环

题2:两数之和

 解法1:暴力枚举

解法2: 哈希表

题3:合并两个有序数组 

解法1:直接合并后排序

解法2:双指针 

解法3:逆向双指针 

题4:移动零 

 解法1:双指针两次遍历

 解法2:双指针一次遍历 

题5:找出所有数组中消失的数字 

解法1:哈希表

解法2:原地修改

 题6:合并两个有序链表

 解法1:循环加双指针

解法2:递归 

题7:删除排序链表中的重复元素

解法一:一次遍历 

解法2:递归 

 题8:环形链表

解 :快慢指针

 题9:环形链表 II 

​编辑解法:快慢指针 

 题10:相交链表

 解法一:双指针

解法二:双指针plus

题11:反转链表

解法一:迭代

题12:回文链表

解法一:双指针

解法二:反转+快慢指针 

题13:链表的中间结点 

​编辑 解法一:两次遍历 

解法二:快慢指针 

题14:给定一个链表,删除链表中倒数第n个结点

​编辑解法一:两次遍历 

解法二:快慢指针


时间复杂度分析:

只关注循环执行次数最多的一段代码;

总复杂度等于最高阶项的复杂度;

嵌套代码的复杂度等于嵌套内外代码复杂度的乘积。

常见时间复杂度:

O(1)         常数阶

O(N)        线性阶

O(N^{2})         平方阶

O(\log N)        对数阶

O(N\log N)        线性对数阶

O(N^{3})        立方阶

O(2^{N})        指数阶

O(N!)        阶乘阶

 从小到大依次是:

O(1)   < O(\log N)  < O(N) < O(N\log N)  < O(N^{2})  < O(N^{3})  < O(2^{N})  < O(N!)   < O(N^{N})

 递归

1.一个问题的解可以分为几个子问题的解

2.这个问题与分解之后的子问题,除了问题规模不同,求解思路完全一样

3.存在基线/中止条件。

当满足以上3点是,就可以考虑使用递归来解决问题。

题1:爬楼梯

 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

 当n=1时,只有一种方法;当n=2时,有一次爬一阶和一次性爬两阶两种方法;

当n=3时,有三种方法,一次爬一阶、一阶两阶、两阶一阶;

可以发现,n阶台阶的方法,就是n-1阶与n-2阶方法的和。

 解法1:递归

n=1,n=2已知,这里假设n=6。

要计算n=6,就是计算n=5加n=4;

要计算n=5,就是计算n=3加n=4;

要计算n=4,就是计算n=3加n=2;

LeetCode(1)_第1张图片

这里n=4,n=3会多次运算,会增大时间复杂度,

解决方法就是,定义一个HashMap,需要计算某个值时,先看看map里面有没有,如果有直接取,如果没有,在计算,然后存到map里面。

这样做,避免了重复运算,加快计算速度,减小时间复杂度。

class Solution {
    private HashMap hashMap = new HashMap<>();
    public int climbStairs(int n) {
        if (n == 1)
            return 1;
        if (n == 2)
            return 2;
        if (hashMap.get(n) != null)
            return hashMap.get(n);
        else{
            int result = climbStairs(n-1) + climbStairs(n - 2);
            hashMap.put(n,result);
            return result;
        }
        
    }
}

 运行结果:

LeetCode(1)_第2张图片

 解法2:循环

LeetCode(1)_第3张图片

因为n=1和n=2很容易计算,我直接自底向上解决问题。

如图所示,f(1) + f(2) =f(3);

                  f(2) + f(3) =f(4);以此类推,只到求出f(n)。

public int climbStairs(int n) {
        if(n == 1) return 1;
        if(n == 2) return 2;
        int result = 0;
        //计算后面的值,就是前一个加前前一个
        //first保存前前一个的值
        //second保存前一个的值
        int first = 1;
        int second = 2;
        for(int i = 3; i <=n ; i++){
            result = first + second;
            first = second;
            second = result;
        }
        return result;
    }

LeetCode(1)_第4张图片

题2:两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

 解法1:暴力枚举

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] arr = new int[2];
        for(int i = 0;i

 LeetCode(1)_第5张图片

解法2: 哈希表

仔细观察暴力枚举法,就会发现,很多数重复参与比较。

如果不让那些数重复比较,就会减小时间复杂度。

方法也很简单,只要一个哈希表,当参加比较的数不符合条件时,将它存到哈希表中,这样只要一个for循环就ok,时间复杂度为O(n)。

class Solution {
    
    public int[] twoSum(int[] nums, int target) {
        Map map = new HashMap<>();
        int n = nums.length;
        int[] arr = new int[2];
        for(int i = 0;i<=n;i++){
            int result = target - nums[i];
            Integer resultIndex = map.get(result);
            if(resultIndex!=null){
                arr[0] = resultIndex;
                arr[1] = i;
                break;
            }else{
                map.put(nums[i],i);
            }
        }
        return arr;
    }
}

LeetCode(1)_第6张图片

题3:合并两个有序数组 

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

解法1:直接合并后排序

 从题目可以知道,num1的长度刚好可以放下num2数组。

所以,我们可以将num2直接放进num1的尾部,然后对整个数组进行排序。

public void merge(int[] nums1, int m, int[] nums2, int n) {
        for (int i = 0; i < n; i++) {
            nums1[m+i]=nums2[i];
        }
        Arrays.sort(nums1);
    }

效率很低,时间复杂度是O((m+n)log(m+n)) 

解法2:双指针 

 因为num1和num2已经是排序好的数组,我们可以利用这一点,从而省去排序的步骤,就可以减小时间复杂度。

利用双指针,指向两个数组的头部,比较两个元素的大小,每次将最小的元素取出,存进一个临时数组,最后将临时数组赋值给num1就ok了。

//双指针
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int[] temp = new int[m+n];
        int p = 0;
        int q = 0;
        //每次比较的最小值
        int l = 0;
        while(p < m || q < n){
            if(p == m){
               l = nums2[q];
                q++;
            }else if(q == n){
                l = nums1[p];
                p++;
            }else if(nums1[p]

LeetCode(1)_第7张图片

时间复杂度O(m+n),因为两个数组都循环了一次

空间复杂度:O(m+n)

解法3:逆向双指针 

解法2中空间复杂度 O(m+n),是因为需要一个长度为m+n的临时数组。

仔细观察题目,就会发现,num1后面有几个元素是0,也就是空,刚好可以存入num2的元素,利用这一点,优化双指针。

我们可以从两个数组的最后哟个元素开始遍历,将最大的元素存入num1中为0的位置即可。

//逆向双指针
    public void merge(int[] nums1, int m, int[] nums2, int n){
        int p = m-1;
        int q = n-1;
        int len = m+n-1;
        while(p>=0||q>=0){
            if(p < 0){
                nums1[len--] = nums2[q--];
            }else if(q < 0){
                nums1[len--] = nums1[p--];
            }else if(nums1[p] > nums2[q]){
                nums1[len--] = nums1[p--];
            }else{
                nums1[len--] = nums2[q--];
            }
        }
    }

LeetCode(1)_第8张图片

时间复杂度为O(m+n),

空间复杂度为O(1),因为是在原地修改数组,不需要额外空间。 

题4:移动零 

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

请注意 ,必须在不复制数组的情况下原地对数组进行操作

 解法1:双指针两次遍历

定义两个指针,都指向数组头部元素,开始遍历数组。

如果第i个元素为0,i++;

如果第i个元素不为0,将第i个元素赋值给第j个元素,i++,j++;

直到最后一个元素。

最后将j之后的元素都赋值成0就ok了。

 public void moveZeroes(int[] nums) {
        if(nums==null) {
			return;
		}
        //双指针
        int i = 0;
        int j = 0;
        int len = nums.length;
        for(;i

LeetCode(1)_第9张图片

时间复杂度:O(n) 

空间复杂度:O(1)

 解法2:双指针一次遍历 

 定义两个指针,开始时都指向数组的头部元素。

参考快速排序的思想:这里我们以0为中心点,把不等于0的放到左边,等于0的放到右边。

从头遍历,只要i处元素不等于0,就交换j处与i处的元素。

 public void moveZeroes(int[] nums) {
        if(nums==null) {
			return;
		}
        //双指针一次遍历
        int i = 0;
        int j = 0;
        int len = nums.length;
        for(;i

LeetCode(1)_第10张图片

时间复杂度:O(n)

空间复杂度:O(1) 

题5:找出所有数组中消失的数字 

 给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。

示例 1:

输入:nums = [4,3,2,7,8,2,3,1]
输出:[5,6]

示例 2:

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

进阶:你能在不使用额外空间且时间复杂度为 O(n) 的情况下解决这个问题吗? 你可以假定返回的数组不算在额外空间内。 

解法1:哈希表

我们可以使用哈希表,将数组的元素都存储到哈希表中,然后遍历数组,判断元素是否出现在哈希集合中。

public List findDisappearedNumbers(int[] nums) {
        Set map = new HashSet();
        List list = new ArrayList<>();
        for (int num : nums) {
            map.add(num);
        }

        for (int i = 1;i<=nums.length;i++){
            if(!map.contains(i)){
                list.add(i);
            }
        }
        return list;
    }

LeetCode(1)_第11张图片

时间复杂度:O(n) 

空间复杂度:O(n)

 不满足要求!!!

解法2:原地修改

 题目意思就是不让用哈希表,那我们可以模仿哈希表,在原地修改。

仔细看题,数组长度为n,数组中元素刚好在[1,n]这个范围内。

这样做,遍历数组,每遇到一个数x,我们就让nums[x-1]增加n或将其改变成对应的负数,

遍历一遍后,这个数组中不大于n或者不是负数的数的下标加1就是那个消失的数字。

注意:由于nums[x]处的元素可能被修改过,我们需要对其还原。

(nums[x] - 1)%n

public List findDisappearedNumbers(int[] nums) {
        int n = nums.length;
        List list = new ArrayList<>();
        for(int num:nums){
            //还原它本来的值
            int x = (num - 1) % n;
            nums[x] += n;
        }
        for(int i = 0; i < n; i++){
            if(nums[i] <= n){
                list.add(i + 1);
            }
        }
        return list;
    }

LeetCode(1)_第12张图片

时间复杂度:O(n) 

空间复杂度:O(1)

 题6:合并两个有序链表

 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

LeetCode(1)_第13张图片

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

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1 和 l2 均按 非递减顺序 排列

 解法1:循环加双指针

 给的链表递增排列。

定义一个节点p,定义两个指针分别指向两个链表的头节点,比较大小,第一次,谁小就把谁接到p节点后面,对应的指针向后挪一位,继续比较,依次往后面接,直到有一个指针指向空。

这个时候,将剩下的那个链表直接接到另一个链表后面。

/**
 * 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 list1, ListNode list2) {
        ListNode p = new ListNode(0);
        ListNode pre = p;
        if(list1 == null) return list2;
        if(list2 == null) return list1;
        while(list1 != null && list2!= null){
            if(list1.val < list2.val){
                pre.next = list1;
                list1 = list1.next;
            }else{
                pre.next = list2;
                list2 = list2.next;
            }
            
            pre = pre.next;
        }
        if(list1 == null){
            pre.next = list2;
        }
         if(list2 == null){
            pre.next = list1;
        }
        return p.next;
    }
}

LeetCode(1)_第14张图片 时间复杂度:O(m+n)

空间复杂度:O(1)

解法2:递归 

 递归就是自己调用自己,而且要有终止条件。

先判断头结点,谁小谁就指向其余结点的合并结果,这里的终止条件就是某个链表为空,也就是说链表已经合并完成。

递归这玩意给人的感觉就是只可意会不可言传

public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if(list1 == null) return list2;
        if(list2 == null) return list1;
        if(list1.val < list2.val){
            list1.next = mergeTwoLists(list1.next,list2);
            return list1;
        }else{
            list2.next = mergeTwoLists(list2.next,list1);
            return list2;
        }
    }

LeetCode(1)_第15张图片 时间复杂度:O(m+n),

空间复杂度:O(m+n) 

题7:删除排序链表中的重复元素

给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。

LeetCode(1)_第16张图片

LeetCode(1)_第17张图片

解法一:一次遍历 

 从链表头结点开始,依次向后遍历,判断当前结点的值是否等于下一个结点的值,如果相等,让当前结点直接指向下下一个结点,直到遍历完整个链表。

public ListNode deleteDuplicates(ListNode head) {
       if(head == null){
           return head;
       }

       ListNode h = head;
       while(h.next != null){
           if(h.val == h.next.val){
               h.next = h.next.next;
           }else{
               h = h.next;
           }
       }
       return head;
    }

LeetCode(1)_第18张图片 时间复杂度:O(n) 

空间复杂度:O(1)

解法2:递归 

 当处理完一个结点后,剩下的结点处理方式与第一个相同。

public ListNode deleteDuplicates(ListNode head) {
        if(head == null || head.next == null) return head;
        head.next = deleteDuplicates(head.next);
        return head.val == head.next.val ? head.next : head;
    }

LeetCode(1)_第19张图片

时间复杂度:O(n)

空间复杂度:O(n) 

 题8:环形链表

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

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

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

LeetCode(1)_第20张图片 LeetCode(1)_第21张图片  

解 :快慢指针

我们定义两个指针,一个快指针,一个慢指针。然后让指针往后遍历链表,如果两个指针相遇了,就说明有环。

 public boolean hasCycle(ListNode head) {
        if(head == null) return false;

        ListNode fast = head;
        ListNode slow = head;
        while(fast.next != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                return true;
            }
        }
        return false;
    }

LeetCode(1)_第22张图片  

时间复杂度:O(n)

空间复杂度:O(1) 

 题9:环形链表 II 

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

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

不允许修改 链表。

LeetCode(1)_第23张图片解法:快慢指针 

 上一题让我们确定环是否存在,我们定义两个快慢指针,如果相遇,则说明环存在。

现在,让我们确定入环的第一个结点,思路跟上一题差不多,只不过多加了一个判断。

首先,利用上一题的解题思路判断环是否存在,如果存在,我们让慢指针指向头结点,然后让快慢指针的速度相同,如果它两再次相遇,就找到了入环时的第一个结点。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head == null) return null;

        ListNode fast = head;
        ListNode slow = head;
        boolean flag = false;
        while(fast.next != null && fast.next.next!=null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                flag = true;
                break;
            }
        }
        //环存在
        if(flag){
            //慢指针指向头结点
            slow = head;
            while(slow != fast){
                //慢指针和快指针移动速度一样
                slow = slow.next;
                fast = fast.next;

            }
            return slow;
        }
        return null;
    }
}

LeetCode(1)_第24张图片 时间复杂度:O(n)

空间复杂度:O(1) 

 题10:相交链表

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

LeetCode(1)_第25张图片

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

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

自定义评测:

评测系统 的输入如下(你设计的程序 不适用 此输入):

  • intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
  • listA - 第一个链表
  • listB - 第二个链表
  • skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
  • skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数

评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。

LeetCode(1)_第26张图片

LeetCode(1)_第27张图片LeetCode(1)_第28张图片 

 解法一:双指针

 定义两个指针,都依次往后遍历链表,如果两个链表长度相同,那么当两个指针指向同一个结点时,就是他们的相交的起始结点。

如果长度不相同,当短的那条链表指向null时,让他重新指向另外一条链表的头结点,继续遍历,按照这个思路,两个指针总会相遇,相遇即找到相交的起始结点。

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null){ return null;}
        ListNode a = headA;
        ListNode b = headB;

        while(a != b){
            if(a == null){
                a = headB;
            }else{
                a = a.next;
            }
            if(b == null){
                b = headA;
            }else{
                b = b.next;
            }
        }
        return a;
    }

LeetCode(1)_第29张图片 时间复杂度:O(m+n) 

空间复杂度:O(1)

解法二:双指针plus

 我们首先遍历A链表,遍历B链表得到两个链表的长度,然后相减得到差值d。

接下来,我们让长的那个链表先移动d个结点,这是两个链表的剩余长度就相同,当两个指针指向同一个结点时,就找到了相交的起始链表。

 public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null){ return null;}
        ListNode a = headA;
        ListNode b = headB;
        int alen = 0;
        int blen = 0;
        int d = 0;
        while(a != null){
            a = a.next;
            alen+=1;
        }
        while(b != null){
            b = b.next;
            blen += 1;
        }
        if(alen>blen){
            a = headA;
            b = headB;
            d = alen - blen;
        }else{
            a = headB;
            b = headA;
            d = blen - alen;
        }
        for(int i = 0;i

LeetCode(1)_第30张图片 时间复杂度:O(m+n) 

空间复杂度:O(1)

题11:反转链表

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

LeetCode(1)_第31张图片LeetCode(1)_第32张图片 

进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

解法一:迭代

我们要反转链表,就是让链表第一个结点成为最后一个结点,最后一个结点成为第一个结点。

从头开始遍历链表,让当前结点指向上一个结点,因为头结点的没有前一个结点,所以我们要定义一个preNode=null存储前一个结点,还要存储后一个结点。

public ListNode reverseList(ListNode head) {
        ListNode preNode = null;
        ListNode curr = head;
        while(curr!=null){
            ListNode next = curr.next;
            curr.next = preNode;
            preNode = curr;
            curr = next;
        }
        return preNode;
    }

LeetCode(1)_第33张图片 时间复杂度:O(n)

空间复杂度:O(1)

题12:回文链表

 给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 

LeetCode(1)_第34张图片

进阶:你能否用 O(n) 时间复杂度和 O(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 boolean isPalindrome(ListNode head) {
        List list = new ArrayList<>();

        ListNode curr = head;
        while(curr != null){
            list.add(curr.val);
            curr = curr.next;
        }
        int len = list.size();
        int left = 0;
        int right = len - 1;
        while(left < right){
            if(list.get(left).equals(list.get(right))){
                left++;
                right--;
            }else{
                return false;
            }
        }
        return true;
    }
}

LeetCode(1)_第35张图片 时间复杂度:O(n)

空间复杂度:O(n)

解法二:反转+快慢指针 

题目进阶要求,空间复杂度为O(1)。

所以我们直接在链表内部判断是否回文。

我们先利用快慢指针找到链表中心点,然后将中心点之后的元素反转,在判断前部分元素和后半部分元素的反转结果是否相同就ok了。

/**
 * 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 boolean isPalindrome(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }

        if(fast != null){
            slow = slow.next;
        }
        slow = reverse(slow);
        fast = head;
        while(slow != null){
            if(slow.val != fast.val){
                return false;
            }
            slow = slow.next;
            fast = fast.next;
        }
        return true;

    }
    //反转链表
    public ListNode reverse(ListNode head){
        ListNode pre = null;
        ListNode curr = head;
        while(curr != null){
            ListNode next = curr.next;
            curr.next = pre;
            pre = curr;
            curr = next;
        }
        return pre;
    }
}

LeetCode(1)_第36张图片 时间复杂度:O(n)

空间复杂度:O(1)

题13:链表的中间结点 

给你单链表的头结点 head ,请你找出并返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

LeetCode(1)_第37张图片 解法一:两次遍历 

第一次遍历得到链表的长度,除以2 +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 middleNode(ListNode head) {
        ListNode h = head;
        int len = 0;
        while(h != null){
            h = h.next;
            len++;
        }
        h = head;
        int size = (len / 2) ;
        while(size > 0){
            h = h.next;
            size--;
        }
        return h;
    }
}

LeetCode(1)_第38张图片 时间复杂度:O(n)

空间复杂度:O(1)

解法二:快慢指针 

 定义两个指针,一个快,一个慢,当快指针指向最后一个结点或者null时,慢指针指向的就是中间结点。

/**
 * 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;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
}

LeetCode(1)_第39张图片 

时间复杂度:O(n)

空间复杂度:O(1)

题14:给定一个链表,删除链表中倒数第n个结点

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

进阶:能尝试使用一趟扫描实现吗?

LeetCode(1)_第40张图片

解法一:两次遍历 

第一次遍历得到链表的长度k,要求删除倒数第n个结点,反过来,删除第k-n+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) {
        ListNode dummy = new ListNode(0,head);
        ListNode cur = dummy;
        int k = 0;
        ListNode h = head;
        while(h != null){
            h = h.next;
            k++;
        }
        int len = k - n + 1;
        for(int i = 1;i

LeetCode(1)_第41张图片 

时间复杂度:O(n)

空间复杂度:O(1)

解法二:快慢指针

 题目进阶要求,一次遍历。

定义两个指针,快指针先移动n-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) {
        ListNode dummy = new ListNode(0,head);
        ListNode fast = head;
        ListNode slow = dummy;
        while(n>0){
            fast = fast.next;
            n--;
        }
        while(fast!=null){
            fast = fast.next;
            slow = slow.next;
        }

        slow.next = slow.next.next;
        ListNode result = dummy.next;
        return result;

    }
}

LeetCode(1)_第42张图片 

时间复杂度:O(n)

空间复杂度:O(1)

你可能感兴趣的:(算法,leetcode,数据结构,算法)