算法打卡第五天 牛客 BM8 链表中倒数最后k个结点和BM9 删除链表的倒数第n个节点

        今天是秋招预备队准备算法第五天,今天打算写两道题

      问题1:链表中倒数最后k个结点

      描述:   

        输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。如果该链表长度小于k,请返回一个长度为 0 的链表。

        

        数据范围:0≤n≤10^5,0≤ai​≤10^9,0≤k≤10^9

        要求:空间复杂度 O(n),时间复杂度 O(n)

        进阶:空间复杂度 O(1),时间复杂度 O(n)

       解题方法:

        1、双指针

        1)设置左右两个指针,左指针指向链表头结点,右指针指向距头结点k-1个结点的结点
        2)当右指针在移动k-1个结点内到达尾节点null时,则链表长度小于k,返回null
        3)当链表长度不小于k时,同时一步一步的移动左右指针,直到右结点遍历到链表尾结点
        4)返回左指针,其刚好是倒数第k个结点

        为什么是k-1个,因为返回该链表中倒数第k个节点,即返回的结点个数为k,所以左右指针这段链表的结点数也为k,减去左指针结点,剩下的结点数k-1即为右结点移动的次数

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 *   public ListNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pHead ListNode类 
     * @param k int整型 
     * @return ListNode类
     */
    public ListNode FindKthToTail (ListNode pHead, int k) {
        // write code here
        //链表不为空并且k大于0
        if(pHead == null || k == 0){
            return null;
        }
        //设置左右指针
        ListNode left = pHead;
        ListNode right = pHead;
        //移动右指针到距头节点k-1的结点处
        for(int i = 0; i < k - 1; i++){
            //链表长度小于k
            if(right.next == null){
                return null;
            }
            right = right.next;
        }
        //链表总长度不小于k,所以同时移动左右指针,直到右指针指向尾结点
        while(right.next != null){
            left = left.next;
            right = right.next;
        }
        return left;
    }
}

        时间复杂度:遍历了两次链表,最坏情况下,两次遍历刚好遍历完整个链表n,所以为O(n)
        空间复杂度:只占用了两个常量结点的空间,所以为O(1)

       2、栈

        1)利用栈将链表所有结点存储
        2)因为在栈中链表节点刚好是反转的,所以只需要将栈顶的k个节点出栈,并组成链表即可

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 *   public ListNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pHead ListNode类 
     * @param k int整型 
     * @return ListNode类
     */

    public ListNode FindKthToTail (ListNode pHead, int k) {
        //链表不为空或者k大于0
        if(pHead == null || k == 0){
            return null;
        }
        //栈
        Stack<ListNode> stack = new Stack<>();
        //将链表所有结点存入栈
        while(pHead != null){
            stack.push(pHead);
            pHead = pHead.next;
        }
        //判断链表长度是否小于k
        if(stack.size() < k){
            return null;
        }
        //将尾结点出栈,便于连接链表
        ListNode index = stack.pop();
        //将剩余结点出栈,并组合链表
        for(int i = 0; i < k - 1; i++){
            ListNode cur = stack.pop();
            cur.next = index;
            index = cur;
        }
        return index;
    }
}

        时间复杂度:O(n)

        空间复杂度:O(n)

     问题2:删除链表的倒数第n个节点

      描述:

        给定一个链表,删除链表的倒数第 n 个节点并返回链表的头指针
        例如:

        给出的链表为: 1→2→3→4→5, n= 2.
        删除了链表的倒数第 n 个节点之后,链表变为1→2→3→5.

        数据范围: 链表长度 0≤n≤1000,链表中任意节点的值满足0≤val≤100

        要求:空间复杂度 O(1),时间复杂度 O(n)
        备注:题目保证 nn一定是有效的

        解题方法:

        1、栈

                1)将链表所有结点存入栈,此时栈顶是链表尾结点
                2)循环将结点弹出栈,组成链表
                3)当遍历到倒数第n个结点时,将其删除
                4)将栈中结点全部出栈,组成的链表即为删除倒数第 n 个节点的链表

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 
     * @param n int整型 
     * @return ListNode类
     */
    public ListNode removeNthFromEnd (ListNode head, int n) {
        //链表不为空
        if(head == null){
            return null;
        }
        //栈
        Stack<ListNode> stack = new Stack<>();
        //入栈
        while(head != null){
            stack.push(head);
            head = head.next;
        }
        //第一个结点,方便连接链表
        ListNode cur = null;
        //出栈
        int m = stack.size();
        for(int i = 1; i <= m; i++){
            //当结点为倒数第n个结点时,将其删除
            if(i == n){
                stack.pop();
                continue;
            }
            ListNode tmp = stack.pop();
            tmp.next = cur;
            cur = tmp;
        }
        //返回删除倒数第n个结点的新链表
        return cur;
    }
}

        时间复杂度:O(n)

        空间复杂度:O(n)

        2、双指针

                1)定义左右两个指针,其相距为n个结点,即右指针向前走n步
                2)同步一步一步的移动左右指针,直到右指针来到链表的尾结点,即right.next = null
                3)此时左指针刚好位于倒数第n个结点的前一个结点
                4)将该结点删除,返回头节点即可

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 
     * @param n int整型 
     * @return ListNode类
     */
    public ListNode removeNthFromEnd (ListNode head, int n) {
        if(head == null || n <  1){
            return null;
        }
        //虚拟头节点
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        //左右指针
        ListNode left = dummy;
        ListNode right = dummy.next;
        //将右指针移动n步
        while(--n > 0){
            if(right != null){
                right = right.next;
            }
            else{
                return null;
            }
        }
        //移动双指针,直到右指针来到链表尾结点
        while(right.next != null){
            left = left.next;
            right = right.next;
        }
        //删除倒数第n个结点
        left.next = left.next.next;
        return dummy.next;
    }
}

        时间复杂度:O(n),其中n为链表长度,最坏情况遍历整个链表1次

        空间复杂度:O(1),常数级指针,无额外辅助空间使用

        3、长度统计法

                1)统计链表长度

                2)遍历到倒数第n个结点的前一个结点

                3)删除该结点

                4)返回新链表

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 
     * @param n int整型 
     * @return ListNode类
     */
    public ListNode removeNthFromEnd (ListNode head, int n) {
        if(head == null || n <  1){
            return null;
        }
        //虚拟头节点
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        //获取链表长度
        int length = listNodeLenth(head);
        //获取倒数第n个结点的前一个结点
        ListNode pre = dummy;
        for(int i = 0; i < length - n; i++){
            pre = pre.next;
        }
        //删除倒数第n个结点
        pre.next = pre.next.next;
        return dummy.next;
    }
    //计算链表长度函数
    public int listNodeLenth(ListNode head){
        int length = 0;
        while(head != null){
            length++;
            head = head.next;
        }
        return length;
    }
}

        时间复杂度:O(n),其中n为链表长度,最坏情况遍历整个链表2次

        空间复杂度:O(1),常数级指针,无额外辅助空间使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值