算法-代码随想录-链表

移除链表元素

移除链表元素

迭代法

注意:

  1. head可能为null,也可能是要删除的val;因此要设置一个虚拟头结点指向头结点,再通过迭代一个一个删除。
  2. 删除方式为:循环,判断下一节点是否是目标节点,是则指向目标节点的下一节点(这里不要移动指针,因为删除后的下一节点仍然有可能是目标节点)。不是则将指针后移。

代码:

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode remo = new ListNode(0);
        remo.next = head;
        ListNode tmp = remo;
        while(tmp.next != null){
            if(tmp.next.val == val){
                tmp.next = tmp.next.next;
            }else{
                tmp = tmp.next;
            }
        }
        // 这里不能直接返回head结点
        return remo.next;
    }
}

设计链表

设计链表

单链表

  • 自定义链表节点
  • 设计链表中,包含链表大小及虚拟头结点
  • MyLinkedList() 初始化链表,设置初始值
  • void addAtIndex(int index, int val) 此处index与其他添加方法不同,index可以等于size,这种情况就是插入尾节点。
public class design {
//测试主方法
    public static void main(String[] args) {
        MyLinkedList obj = new MyLinkedList();
        obj.addAtHead(7);
        obj.addAtHead(2);
        obj.addAtHead(1);
        obj.addAtIndex(3, 0);
        obj.deleteAtIndex(2);
        obj.addAtHead(6);
        obj.addAtTail(4);
        int a = obj.get(4);
        System.out.println(a);//4
        obj.addAtHead(4);
        obj.addAtIndex(5, 0);
        obj.addAtHead(6);
    }
}
//自定义节点类
class LinkedNode{
    int val;
    LinkedNode next;
    LinkedNode(){}
    LinkedNode(int val){
        this.val = val;
    }
}
//设计链表
class MyLinkedList{
    int size;//链表大小
    LinkedNode head;//虚拟头结点
    //初始化链表
    public MyLinkedList(){
        size = 0;
        head = new LinkedNode(0);
    }
    //获取指定下标元素
    public int get(int index){
        if(index < 0 || index > size - 1){
            return -1;
        }
        
        LinkedNode aHead = head;
        for(int i = 0; i <= index; i++){
        	aHead = aHead.next;
        }
        return aHead.val;
    }
	//在头部插入节点
    public void addAtHead(int val){
        LinkedNode node = new LinkedNode(val);
        LinkedNode aHead = head;
        node.next = aHead.next;
        aHead.next = node;
        size++;
    }
	//在尾部插入节点
    public void addAtTail(int val) {
        LinkedNode aHead = head;
        LinkedNode node = new LinkedNode(val);
        while(aHead.next != null){
            aHead = aHead.next;
        }
        aHead.next = node;
        size++;
    }
	//在指定索引插入元素
    public void addAtIndex(int index, int val) {
        //index可以等于size
        if(index < 0 || index > size){
            return;
        }
        int i = 0;
        LinkedNode aHead = head;
        LinkedNode node = new LinkedNode(val);
        while(i < index){
            aHead = aHead.next;
            i++;
        }
        node.next = aHead.next;
        aHead.next = node;
        size++;
    }
	//删除索引处元素
    public void deleteAtIndex(int index) {
        if(index < 0 || index > size - 1){
            return;
        }
        int i = 0;
        LinkedNode aHead = head;
        while(i < index){
            i++;
            aHead = aHead.next;
        }
        aHead.next = aHead.next.next;
        size--;
    }
}

双链表

  • 节点包含next、prev双向指针
  • 创建链表时,创建虚拟头结点和虚拟尾节点
class LinkedNode{
    int val;
    LinkedNode next, prev;
    LinkedNode(){}
    LinkedNode(int val){this.val = val;}
}

class MyLinkedList{
    int size;
    LinkedNode head, tail;
    MyLinkedList(){
        //初始化
        size = 0;
        head = new LinkedNode(0);
        tail = new LinkedNode(0);
        head.next = tail;
        tail.prev = head;
    }

    public int get(int index){
        if (index < 0 || index >= size){
            return -1;
        }
        LinkedNode aHead = head;
        LinkedNode aTail = tail;
        if(index < size / 2){
            for (int i = 0; i <= index; i++) {
                aHead = aHead.next;
            }
            return aHead.val;
        } else{//4, 3
            for (int i = size; i > index; i--) {
                aTail = aTail.prev;
            }
            return aTail.val;
        }
    }

    public void addAtIndex(int index, int val){
        if(index > size){
            return;
        }
        LinkedNode node = new LinkedNode(val);
        LinkedNode aHead = head;
        //找到前驱
        for (int i = 0; i < index; i++) {
            aHead = aHead.next;
        }
        node.next = aHead.next;
        aHead.next.prev = node;
        node.prev = aHead;
        aHead.next = node;
        size++;
    }

    public void addAtHead(int val) {
        //等价于在第0个元素前添加
        addAtIndex(0,val);
    }

    public void addAtTail(int val) {
        //等价于在最后一个元素(null)前添加
        addAtIndex(size,val);
    }

    public void deleteAtIndex(int index) {
        //判断索引是否有效
        if(index<0 || index>=size){
            return;
        }
        //删除操作
        size--;
        LinkedNode pre = this.head;
        for(int i=0; i<index; i++){
            pre = pre.next;
        }
        pre.next.next.prev = pre;
        pre.next = pre.next.next;
    }


}

反转链表

双指针法

cur指针指向head结点,pre指针指向null。将cur.next 指向null。再将两个指针向右平移。

img

import java.util.Scanner;

public class reverse {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        ListNode head = new ListNode();
        //fprint(head);
        ListNode aHead = head;
        String str = scan.nextLine();
        String[] s = str.split(" ");
        for (int i = 0; i < s.length; i++) {
            int num = Integer.valueOf(s[i]);
            ListNode newNode = new ListNode(num);
            aHead.next = newNode;
            aHead = aHead.next;
        }
        fprint(head.next);
        head = reverseList(head);
        fprint(head);
    }
	//打印链表方法
    static void fprint(ListNode head){
        while(head != null){
            System.out.print(head.val + " ");
            head = head.next;
        }
    }
    //反转链表***
    static ListNode reverseList(ListNode head) {
        ListNode tmp;//临时记录cur.next
        ListNode cur = head.next;//双指针-前指针
        ListNode pre = null;//双指针-后指针
        while(cur != null){
            tmp = cur.next;
            cur.next = pre;
            //先后顺序不能变
            pre = cur;
            cur = tmp;
        }
        return pre;
    }

}
//节点定义
class ListNode {
      int val;
      ListNode next;
      ListNode() {}
      ListNode(int val) { this.val = val; }
      ListNode(int val, ListNode next) { this.val = val; this.next = next; }

}

递归法

思路与双指针法类似,写法不同。

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

static ListNode reverse(ListNode pre, ListNode cur){
        if (cur == null) return pre;
        ListNode tmp = cur.next;
        cur.next = pre;
        return reverse(cur, tmp);
    }

两两交换链表中的节点

题目链接:24. 两两交换链表中的节点 - 力扣(LeetCode)

思路:

设置虚拟头结点,交换后两个节点,再将虚拟头节点向后移动两位继续做交换。

24.两两交换链表中的节点3

代码:

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode cur = new ListNode();
        cur.next = head;
        ListNode aHead = cur;

        while(aHead.next != null && aHead.next.next != null){
            //临时记录
            ListNode tmp = aHead.next;
            ListNode tmp1 = aHead.next.next.next;

            aHead.next = aHead.next.next;//步骤一
            aHead.next.next = tmp;//步骤二
            aHead.next.next.next = tmp1;//步骤三

            aHead = aHead.next.next;

        }
        ListNode result = cur.next;
        return result;
    }
}

删除链表倒数第n个节点

思路:

  1. 虚拟头结点,方便删除
  2. 双结点,设置fast和slow两个结点,要找到倒数第n个数,即与最后一个结点的后一位空结点相差n个距离。那么在最开始,先让fast移动n个位置,使得fast与slow相差n个结点,再让fast与slow一起移动,当fast移动到空结点,slow就是需要删除的结点。

img

  • 移动n+1个单位,是为了让slow指向目标结点的前一结点,方便后续删除

img

img

img

代码:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode val = new ListNode(0);
        val.next = head;
        ListNode fast = val;
        ListNode slow = val;
        //移动n+1个单位
        for(int i = 0; i <= n; i++){
            fast = fast.next;
        }
        //fast移动到空结点以前,fast与slow一起移动
        while(fast != null){
            fast = fast.next;
            slow = slow.next;
        }
        //删除目标结点
        slow.next = slow.next.next;
        //返回头结点
        return val.next;
    }
}

链表相交

思路:

  1. 如果链表相交,则链表A与链表B的相交部分必然在尾部,且长度相等。
  2. 将两个链表的尾部对齐,将长链表头部多出来的部分直接跳过,再依次判断链表A与链表B的结点是否相等,相等则返回结点,如果没有符合的结点,则返回null。

代码:

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode curA = headA;
        ListNode curB = headB;
        int lenA = 0;
        int lenB = 0;
        //A链表长度
        while(curA != null){
            curA = curA.next;
            lenA++;
        }
        //B链表长度
        while(curB != null){
            curB = curB.next;
            lenB++;
        }
        curA = headA;
        curB = headB;
        //如果B长度>A长度
        if(lenB > lenA){
            //交换len
            int tmpLen = lenB;
            lenB = lenA;
            lenA = tmpLen;
			//交换cur结点
            ListNode tmpNode = curB;
            curB = curA;
            curA = tmpNode;
        }
		//gap是A与B相差的长度,使A与B对齐
        int gap = lenA - lenB;
        while(gap != 0){
            curA = curA.next;
            gap--;
        }
        while(curA != null){
            if(curA == curB){
                return curA;
            }
            curA = curA.next;
            curB = curB.next;
        }
        return null;

    }
}

环形链表

思路:

  • 判断是否有环,找到环的入口
  • 如何判断是否有环:设置快慢双指针,快指针每次移动两个结点,而慢指针每次移动一个结点,如果有环的话,快慢指针终会在环中的某个位置相遇,说明有环。
  • 如何找到环的入口

img

那么相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。

因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:

(x + y) * 2 = x + y + n (y + z)

两边消掉一个(x+y): x + y = n (y + z)

因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。

所以要求x ,将x单独放在左面:x = n (y + z) - y ,

再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z 注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。

这个公式说明什么呢?

先拿n为1的情况来举例,意味着fast指针在环形里转了一圈之后,就遇到了 slow指针了。

当 n为1的时候,公式就化解为 x = z

这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点

也就是在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。

让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。

142.环形链表II(求入口)

代码:

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(slow == fast){//有环
                ListNode index1 = head;
                ListNode index2 = fast;
                while(index1 != index2){
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        return null;
        
    }
}

posted @ 2024-05-08 08:46  forest-pan  阅读(6)  评论(0编辑  收藏  举报