二、链表类算法(力扣刷题)-Java (移除链表元素、设计链表、反转链表、两两交换链表中的节点、删除链表的倒数第N个节点、链表相交、环形链表 II)

二、链表类算法

ps:括号里的数字代表力扣上的题号
链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
链表的类型:单链表、双联表、循环链表
链表的存储方式:数组在内存中是连续分布的,而链表在内存中不是连续分布的,每个节点通过之后通过连接。

1、(203)移除链表元素

(1)直接使用原来的链表来进行删除:分为头节点等于目标值和不等于目标值两种情况,进行讨论并删除节点即可。

public static ListNode removeElements(ListNode head, int val) {
    ListNode temp;  //节点指针
    while (head!= null && head.val == val)
        head = head.next;
    if (head == null)
        return head;
    temp = head;
    while (temp.next != null){
        if (temp.next.val == val){
            temp.next = temp.next.next;
        }else
            temp = temp.next;
    }
    return head;
}

(2)设置一个虚拟头结点在进行删除操作:不需要单独讨论头节点的特殊情况,原链表的所有节点就都可以按照统一的方式进行移除

2、(707)设计链表

public class MyLinkedList {
    //size存储链表元素的个数
    int size;
    //虚拟头节点
    ListNode head;




    //初始化链表
    public MyLinkedList() {
        size = 0;
        head = new ListNode(0);
    }
    //获得第index个节点的数值
    public int get(int index) {
        if (index<0 || index>=size)
            return -1;
        ListNode currentNode = head;




        for(int i=0;i <= index;i++){
            currentNode = currentNode.next;
        }
        return currentNode.val;
    }
    //在链表最前面插入一个节点
    public void addAtHead(int val) {
        addAtIndex(0,val);
    }
    //在链表的最后插入一个节点
    public void addAtTail(int val) {
        addAtIndex(size,val);
    }
    //在链表中的第 index 个节点之前添加值为 val  的节点
    public void addAtIndex(int index, int val) {
        if(index>size)
            return;
        if (index<0) {
            index = 0;
        }
        size++;
        ListNode pre = head;
        for (int i=0;i<index;i++) {
            pre = pre.next;
        }
        ListNode toAddNode = new ListNode(val);
        toAddNode.next = pre.next;
        pre.next = toAddNode;
    }
    //删除第index个节点
    public void deleteAtIndex(int index) {
        if (index<0 || index>size-1)
            return;
        size--;
        ListNode pre = head;
        for (int i=0;i<index;i++) {
            pre = pre.next;
        }
        pre.next = pre.next.next;
    }
}

3、(206)反转链表

第一想法:设置双指针,left指针指向head,right移动链表尾部,left遍历节点链表头插建立新链表。
参考题解:
(1)双指针法
直接将链表中的每一个节点的指向改变,即得到新的反转链表

public static ListNode reverseList(ListNode head) {
    ListNode temp;
    ListNode cur = head;
    ListNode pre = null;
    while (cur!=null) {
        temp = cur.next;
        cur.next = pre;
        pre = cur;
        cur = temp;
    }
    return pre;
}

(2)递归法
递归的出口是cur指向null,当cur指向null的时候,返回pre,之前的每一层已经执行到最后一句 return reverse(cur,temp); ,故直接一层层地向上传递pre,直到退出递归。

//递归法 从后往前翻转
    public static ListNode reverse(ListNode pre, ListNode cur) {
        if (cur==null)  return pre;
        ListNode temp = cur.next;
        cur.next = pre;
        // 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步
        // pre = cur;
        // cur = temp;
        return reverse(cur,temp);
    }


    public static ListNode reverseList(ListNode head) {
        return reverse(null,head);
    }

4、(0024) 两两交换链表中的节点

第一想法:把节点拿下来重新组合节点排列顺序
参考题解:使用虚拟节点交换相邻两个节点的顺序
ps:此题代码随想录上有详细动画方便理解

public static ListNode swapPairs(ListNode head) {
    ListNode dummyNode = new ListNode();    //设置一个虚拟头节点
    dummyNode.next = head;  //将虚拟头节点指向head节点,方便后面返回头节点
    ListNode cur = dummyNode;
    ListNode temp;
    ListNode temp1;
    while (cur.next!=null && cur.next.next!=null) {
        temp = cur.next;    //记录临时节点
        temp1 = cur.next.next.next; //记录临时节点

        cur.next = cur.next.next;   //第一步
        cur.next.next = temp;   //第二步
        cur.next.next.next = temp1; //第三步

        cur = cur.next.next;    //将cur向右移动两格
    }
    return dummyNode.next;
}

5、(19)删除链表的倒数第N个节点

第一想法:利用快满指针,快指针先走n步,然后快慢指针同时向后移动,快指针到达链表尾部时,满指针到达链表倒数第n个节点

public static ListNode removeNthFromEnd(ListNode head, int n) {
    ListNode dummyNode = new ListNode();    //设置虚拟节点,避免讨论头节点删除问题
    dummyNode.next = head;
    ListNode left = dummyNode;  //快指针
    ListNode right = dummyNode; //慢指针
    for (int i=0;i<n+1;i++) { //快指针 先走n+1步
        right = right.next;
    }
    while (right != null) { //快慢指针同时移动,直到快指针移到表尾
        left = left.next;
        right = right.next;
    }
    left.next = left.next.next; //删除倒数第n个节点
    return dummyNode.next;
}

6、(面试题02.07)链表相交

第一想法:计算两个不同头节点链表的长度,但没想到怎么利用这个长度;将两个链表的节点放在两个数组中,但只能判断节点的值相等,无法判断节点指针是否相等。
参考题解:求出两个链表的长度,并求出两个链表长度的差值,然后让curA(长的那一个链表)移动到,和curB(短的那一个链表) 末尾对齐的位置,然后同时向后移动,每移动一次判断节点是否相等(包含节点值和指针)

public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    ListNode curA = headA;  //A链表当前指针
    ListNode curB = headB;  //B链表当前指针
    int lenA=0, lenB=0;
    int chaAB;
    while (curA != null) {  //得到A链表的长度
        ++lenA;
        curA = curA.next;
    }
    while (curB != null) {  //得到B链表的长度
        ++lenB;
        curB = curB.next;
    }
    curA = headA;   //A,B链表当前指针回到各自头节点位置
    curB = headB;
    chaAB = lenA-lenB;  //求出两链表长度差值
    if (chaAB > 0) {    //A链表长
        while (chaAB != 0) {
            curA = curA.next;
            --chaAB;
        }
    } else {            //B链表长
        chaAB = -chaAB;
        while (chaAB != 0) {
            curB = curB.next;
            --chaAB;
        }
    }
    while (curA != null) {
        if (curA == curB)   //判断A,B链表当前指针是否相等
            return curA;
        curA = curA.next;   //A,B当前指针都向后移动一步
        curB = curB.next;
    }
    return null;
}

7、(142)环形链表 II

第一想法:new一个跟链表一样长的数组,初始化为0,从头节点开始遍历链表,若是当前节点的对应下标的数组元素值为0并且当前节点的下一个节点对应下标的数组元素值为0,那么就把当前节点对应下标的数组元素值变为1,退出循环的点即为循环入口点。
运行结果:错误;
参考题解:先利用快慢指针判断该链表是否有环,快指针每次移动两步,慢指针每次移动一步,若两指针相遇则代表该链表有环。(因为若有环,则代表每次快指针在向慢指针靠近一步,只要有环,最终一定会相遇)
再利用index1和index2,让一个指针指向头节点,另一个指针指向相遇节点,两指针每次移动一步,则他们最终相遇的位置就是循环入口点。
二、链表类算法(力扣刷题)-Java (移除链表元素、设计链表、反转链表、两两交换链表中的节点、删除链表的倒数第N个节点、链表相交、环形链表 II)_第1张图片
特点:代码简洁,主要在于数学证明。

public ListNode detectCycle(ListNode head) {
    ListNode fast = head, low = head;
    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        low = low.next;
        if (fast == low) {  //如果快慢指针相遇,让index1指向相遇节点,index2指向头节点
            ListNode index1 = fast;
            ListNode index2 = head;
            while (index1 != index2) {  //当index1==index2相等时,index1此时即为循环入口点
                index1 = index1.next;
                index2 = index2.next;
            }
            return index1;
        }
    }
    return null;
}

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