目录
ListNode代码
链表中倒数最后k个结点
描述
示例1
示例2
解题思路
AC代码
不借助指针
快慢指针
删除链表中重复的结点
描述
示例1
示例2
解题思路
AC代码
可测试运行代码(需要ListNode代码,在文章开头):
运行结果
链表一(基本概念和应用场景)+ 链表常见算法题-CSDN博客
ListNode代码
import java.util.Arrays;
public class ListNode {
    int val;
    ListNode next = null;
    public ListNode(int val) {
        this.val = val;
    }
    //遍历链表转换为字符串便于打印
    public String Traversal(ListNode listNode) {
        int[] arr = new int[getLength(listNode)];
        int i = 0 ;
        while (listNode != null){
            arr[i] = listNode.val;
            listNode = listNode.next;
            i++;
        }
        return Arrays.toString(arr);
    }
    // 获取长度
    public int getLength(ListNode head) {
        int length = 0;
        while (head != null) {
            length++;
            head = head.next;
        }
        return length;
    }
}
链表中倒数最后k个结点
描述
输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。
如果该链表长度小于k,请返回一个长度为 0 的链表。
数据范围:0≤n≤105,0≤ai≤109,90≤k≤109
要求:空间复杂度 O(n),时间复杂度O(n)
进阶:空间复杂度 O(1),时间复杂度 O(n)
例如输入{1,2,3,4,5},2时,对应的链表结构如下图所示:
其中蓝色部分为该链表的最后2个结点,所以返回倒数第2个结点(也即结点值为4的结点)即可,系统会打印后面所有的节点来比较。
示例1
输入:{1,2,3,4,5},2
返回值:{4,5}
说明:返回倒数第2个节点4,系统会打印后面所有的节点来比较。
示例2
输入:{2},8
返回值:{}
解题思路
- 先算出链表的长度
- 算出到倒数第k个节点需要多少步
- 将指针移动指定步数,到达指定的节点。返回当前结点。
AC代码
不借助指针
public ListNode FindKthToTail (ListNode pHead, int k) {
        // write code here
        // 求出链表的长度
        int length = getLength(pHead);
        if(length < k){
            return null;
        }
        // 算出链表要走多少步
        int step = length - k;
//        // 定义一个指针
//        ListNode cur = pHead;
        // 移动相应步数找到需要返回的节点
        while(step != 0){
            pHead = pHead.next;
            step--;
        }
        return pHead;
    }
    //
    public int getLength(ListNode head){
        int length = 0;
        while(head != null){
            length++;
            head = head.next;
        }
        return length;
    }快慢指针
public ListNode FindKthToTailByTwoPointer(ListNode pHead, int k){
        // 定义两个快慢指针
        ListNode fast = pHead;
        ListNode slow = pHead;
        // fast 先走k步
        while(k != 0){
            if (fast != null){
                // fast 往后走
                fast = fast.next;
                k--;
            }else {
                // 如果fast还没走到第k个位置 就是k大于链表长度,返回null
                return null;
            }
        }
        // 因为fast 先走k步,在保证两个指针一起走 他们的距离始终为k,
        // 当fast到链尾的时候 slow所在的位置就是倒数第k个节点
        while (fast != null){
            fast = fast.next;
            slow = slow.next;
        }
        // 
        return slow;
    }删除链表中重复的结点
描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表 1->2->3->3->4->4->5 处理后为 1->2->5
数据范围:链表长度满足 0≤n≤1000 ,链表中的值满足 1≤val≤1000
进阶:空间复杂度 O(n) ,时间复杂度 O(n)
例如输入{1,2,3,3,4,4,5}时,对应的输出为{1,2,5},对应的输入输出链表如下图所示:
示例1
输入:{1,2,3,3,4,4,5}
返回值:{1,2,5}
示例2
输入:{1,1,1,8}
返回值:{8}
解题思路
- 使用两个工作指针 一个前赴(pre)指针在前,负责构建新的建表,一个当前(cur)指针去找重复节点的值,便于pre去连接。
- 总的来说就是去遍历链表,去寻找链表中重复出现的值,然后跳过这些值,使用pre去连接下一个节点值。
- 具体步骤如下代码:
AC代码
public class Solution {
    public ListNode deleteDuplication(ListNode pHead) {
        // 定义一个新的头节点 构建删除重复节点后的链表
        ListNode nHead = new ListNode(0);
        // 前赴指针
        ListNode pre = nHead;
        // 当前结点
        ListNode cur = pHead;
        // 将新的表头与pHead相连
        nHead.next = pHead;
        // 循环条件 要么当前节点为空 需要跳出循环 或者 工作指针到链尾需要跳出循环
        while(cur != null && cur.next != null){
            // 如果当前节点的值和下一个节点的值相等
            if(cur.next !=null && cur.val == cur.next.val ){
                // 持续找到连续重复的值一直移动指针 直到相邻两个节点的值不相等
                while(cur != null && cur.next != null && cur.val == cur.next.val ){
                    // 后移指针 
                    cur = cur.next;
                }
                // 移动到下一个暂时没有重复的节点值的位置
                cur = cur.next;
                // 将pre连接到重复节点的最后一个节点的后一个节点 
                // 这个操作相当于删除当前的有重复值的连续节点 直接连接到下一个暂时不是重复的节点位置
                pre.next = cur;
            }else{
                // pre跟着cur一起移动
                pre = cur;
                cur = cur.next;
            }
        }
        return nHead.next;
    }
}可测试运行代码(需要ListNode代码,在文章开头):
public static void main(String[] args) {
        ListNode node = new ListNode(1);
        node.next = new ListNode(3);
        node.next.next = new ListNode(3);
        node.next.next.next = new ListNode(5);
        node.next.next.next.next = new ListNode(4);
        String before = node.Traversal(node);
        System.out.println("删除重复节点后的值为: " + before);
        ListNode listNode = deleteDuplication(node);
        String end = listNode.Traversal(listNode);
        System.out.println("删除重复节点后的值为: " + end);
    }
    public static ListNode deleteDuplication(ListNode pHead) {
        // 定义一个新的头节点 构建删除重复节点后的链表
        ListNode nHead = new ListNode(0);
        // 前赴指针
        ListNode pre = nHead;
        // 当前结点
        ListNode cur = pHead;
        // 将新的表头与pHead相连
        nHead.next = pHead;
        // 循环条件 要么当前节点为空 需要跳出循环 或者 工作指针到链尾需要跳出循环
        while(cur != null && cur.next != null){
            // 如果当前节点的值和下一个节点的值相等
            if(cur.val == cur.next.val ){
                // 持续找到连续重复的值一直移动指针 直到相邻两个节点的值不相等
                while(cur.next != null && cur.val == cur.next.val ){
                    // 后移指针
                    cur = cur.next;
                }
                // 移动到下一个暂时没有重复的节点值的位置
                cur = cur.next;
                // 将pre连接到重复节点的最后一个节点的后一个节点
                // 这个操作相当于删除当前的有重复值的连续节点 直接连接到下一个暂时不是重复的节点位置
                pre.next = cur;
            }else{
                // pre跟着cur一起移动
                pre = cur;
                cur = cur.next;
            }
        }
        return nHead.next;
    }运行结果
图解























