[Code Random Record] Linked list brushing questions

A lot of repeated questions refer to: [Code Random Records] Double-pointer method for brushing questions

theoretical basis

Single list:

Double linked list:

Circular linked list:

Definition of singly linked list node:

public class ListNode {
    
    
    public int val; // 节点的值
    public ListNode next; // 指向的下一个节点

    public ListNode() {
    
    }

    public ListNode(int val) {
    
    
        this.val = val;
    }

    public ListNode(int val, ListNode next) {
    
    
        this.val = val;
        this.next = next;
    }
}

Remove linked list elements

Topic: 203. Remove Linked List Elements

Given a head node head of the linked list and an integer val, please delete all nodes Node.val == valsatisfying and return a new head node .

Remove linked list elements

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

recursion:

public ListNode removeElements(ListNode head, int val) {
    
    
    if (head == null) return head;
    head.next = removeElements(head.next, val);
    return head.val == val ? head.next : head;
}

Iteration: Find the previous node of the node to be deleted and perform the delete operation

class Solution {
    
    
    public ListNode removeElements(ListNode head, int val) {
    
    
        if (head == null) return head;
        ListNode node = head;
        while (node != null && node.next != null) {
    
    
            if (node.next.val == val) node.next = node.next.next;
            else node = node.next;
        }
        return head.val == val ? head.next : head;
    }
}

design linked list

Topic: 707. Designing Linked Lists

Dynamic Singly Linked List

Dynamic singly linked list : (virtual head node)

class ListNode {
    
    
    int val;
    ListNode next;

    ListNode() {
    
    }

    ListNode(int val) {
    
    
        this.val = val;
    }
}

class MyLinkedList {
    
    
    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 cur = head; // 从头节点开始查找
        // 包含虚拟头节点, 所以查找第 index+1 个节点
        while (index-- >= 0) cur = cur.next;
        return cur.val;
    }

     // 在链表最前面插入一个节点
     public void addAtHead(int val) {
    
    
        addAtIndex(0, val);
    }

     // 在链表的最后插入一个节点
     public void addAtTail(int val) {
    
    
        addAtIndex(size, val);
    }

    // 在第 index 个节点之前插入一个新节点
    // 如果 index 为 0,那么新插入的节点为链表的新头节点
    // 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点
    // 如果 index 大于链表的长度,则返回空
    public void addAtIndex(int index, int val) {
    
    
        if (index > size) return;
        if (index < 0) index = 0;
        size++;
        // 找到要插入节点的前驱
        ListNode pre = head;
        while (index-- > 0) pre = pre.next;
        // 插入操作
        ListNode newNode = new ListNode(val);
        newNode.next = pre.next;
        pre.next = newNode;
    }

    // 删除第 index 个节点
    public void deleteAtIndex(int index) {
    
    
        if (index < 0 || index >= size) return;
        size--;
        // 找到要删除节点的前驱
        ListNode pre = head;
        while (index-- > 0) pre = pre.next;
		// 删除操作
        pre.next = pre.next.next;
    }
}

Dynamic doubly linked list

Dynamic doubly linked list : (virtual head node and virtual tail node)

class ListNode {
    
    
    int val;
    ListNode next, prev;

    ListNode(int x) {
    
    
        val = x;
    }
}

class MyLinkedList {
    
    
    int size;
    ListNode head, tail;

    public MyLinkedList() {
    
    
        size = 0;
        head = new ListNode(0); // 虚拟头节点
        tail = new ListNode(0); // 虚拟尾节点
        head.next = tail;
        tail.prev = head;
    }

    public int get(int index) {
    
    
        if (index < 0 || index >= size) return -1;
        ListNode cur = head;

        // 索引 < 一半从前往后找, 索引 > 一半从后前找
        if (index < size / 2) {
    
    
            for (int i = 0; i <= index; i++) cur = cur.next;
        } else {
    
    
            cur = tail;
            for(int i = 0; i <= size - 1 - index; i++) cur = cur.prev;
        }
        return cur.val;
    }

    public void addAtHead(int val) {
    
    
        ListNode newNode = new ListNode(val);
        newNode.next = head.next;
        head.next.prev = newNode;
        head.next =  newNode;
        newNode.prev = head;
        size++;
        // addAtIndex(0, val);
    }

    public void addAtTail(int val) {
    
    
        ListNode newNode = new ListNode(val);
        newNode.next = tail;
        newNode.prev = tail.prev;
        tail.prev.next = newNode;
        tail.prev = newNode;
        size++;
        // addAtIndex(size, val);
    }

    public void addAtIndex(int index, int val) {
    
    
        if (index > size) return;
        if (index < 0) index = 0;
        // 找到要添加的前驱
        ListNode pre = head;
        while (index-- > 0) pre = pre.next;
        // 添加操作
        ListNode newNode = new ListNode(val);
        newNode.next = pre.next; // 新节点的 next 指向
        newNode.prev = pre; // 新节点的 prev 指向
        pre.next.prev = newNode; // 后继的 prev 指向新
        pre.next = newNode; // 前驱的 next 指向新
        size++;
    }

    public void deleteAtIndex(int index) {
    
    
        if(index >= size || index < 0) return;
        // 找到要删除的前驱
        ListNode pre = head;
        while (index-- > 0) pre = pre.next;
        // 删除操作
        pre.next.next.prev = pre;
        pre.next = pre.next.next;
        size--;
    }
}

static singly linked list

Static singly linked list : (virtual head node)

class MyLinkedList {
    
    
    final int N = 1010;
    int[] e = new int[N];
    int[] ne = new int[N];
    int h = 0; // 头节点
    int idx = 1; // 有虚拟头节点
    int size = 0; // 长度

    public MyLinkedList() {
    
    }
    
    public int get(int k) {
    
    
        if (k < 0 || k >= size) return -1;
        int t = ne[h];
        while (k -- > 0) t = ne[t];
        return e[t];
    }
    
    public void addAtHead(int val) {
    
    
        e[idx] = val;
        ne[idx] = ne[h];
        ne[h] = idx++;
        size++;
    }
    
    public void addAtTail(int val) {
    
    
        int t = h;
        while (ne[t] != 0) t = ne[t];
        e[idx] = val;
        ne[idx] = ne[t];
        ne[t] = idx++;
        size++;
    }
    
    public void addAtIndex(int k, int val) {
    
    
        if (k <= 0) addAtHead(val);
        else if (k == size) addAtTail(val);
        else if (k > 0 && k < size) {
    
    
            k --;
            int t = ne[h];
            while (k -- > 0) t = ne[t];
            e[idx] = val;
            ne[idx] = ne[t];
            ne[t] = idx++;
            size++;
        }
    }
    
    public void deleteAtIndex(int k) {
    
    
        if (k < 0 || k >= size) return;
        int t = h;
        while (k -- > 0) t = ne[t];
        ne[t] = ne[ne[t]];
        size--;
    }
}

reverse linked list

Title: 206. Reversing a Linked List

Double pointer:

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

recursion:

public ListNode reverseList(ListNode head) {
    
    
    if (head == null || head.next == null) return head;
    ListNode node = reverseList(head.next);
    head.next.next = head;
    head.next = null;
    return node;
}

Head interpolation: space O(n)

public ListNode reverseList(ListNode head) {
    
    
    ListNode res = null;
    for (ListNode x = head; x != null; x = x.next)
        res = new ListNode(x.val, res);
    return res;
}

Exchange the nodes in the linked list two by two

Topic: 24. Exchange nodes in a linked list pairwise

Given a linked list, exchange the adjacent nodes in it two by two, and return the head node of the exchanged linked list. You must complete this exercise without modifying the values ​​inside the nodes (i.e. only node swaps).

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

Iteration + virtual head node:

public ListNode swapPairs(ListNode head) {
    
    
	ListNode vn = new ListNode(0, head); // 虚拟头节点
	ListNode cur = vn, tmp = null;
	while (cur != null && cur.next != null && cur.next.next != null) {
    
    
		tmp = cur.next;
		cur.next = cur.next.next;
		tmp.next = cur.next.next;
		cur.next.next = tmp;
		cur = cur.next.next;
	}
	return vn.next;
}

recursion:

public ListNode swapPairs(ListNode head) {
    
    
	if (head == null || head.next == null) return head;
	ListNode next = head.next;
	head.next = swapPairs(next.next);
	next.next = head;
	return next;
}

Delete the last N node of the linked list

Topic: 19. Delete the last N node of the linked list

Given a linked list, delete the npenultimate , and return the head node of the linked list.

Fast and slow pointer:

  • The fast pointer moves n steps first, and then starts to move at the same time as the slow pointer
  • When the fast pointer points to the last node, the slow pointer points to the node to be deleted (the previous node)
public ListNode removeNthFromEnd(ListNode head, int n) {
    
    
	if (head == null || head.next == null) return null;
	ListNode fast = head, slow = head;
	while (n-- > 0) fast = fast.next; // 快指针先走 n 步
	if (fast == null) return head.next; // 删除第一个节点
	while (fast.next != null) {
    
    
		fast = fast.next;
		slow = slow.next;
	}
	slow.next = slow.next.next;
	return head;
}

Fast and slow pointer + virtual head node:

Generally using virtual head nodes does not need to deal with special cases

public ListNode removeNthFromEnd(ListNode head, int n) {
    
    
	ListNode vn = new ListNode(0, head); // 虚拟头节点
	ListNode slow = vn, fast = vn;
	while (n-- > 0) fast = fast.next; // 快指针先走 n 步
	while (fast.next != null) {
    
    
		fast = fast.next;
		slow = slow.next;
	}
	slow.next = slow.next.next;
	return vn.next;
}

recursion:

class Solution {
    
    
    int cur = 0;
    public ListNode removeNthFromEnd(ListNode head, int n) {
    
    
        if (head == null) return null;
        head.next = removeNthFromEnd(head.next, n);
        cur++;
        if (cur == n) return head.next;
        return head;
    }
}

linked list intersect

Topic: Interview Question 02.07. Linked List Intersection

Given the head nodes headAand headB, please find and return the starting node where the two singly linked lists intersect. If there is no intersection between the two linked lists, return null.

Double pointer:

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    
    
	ListNode curA = headA, curB = headB;
	int lenA = 0, lenB = 0;
	// 求链表 A 的长度
	while (curA != null) {
    
     
		lenA++; 
		curA = curA.next; 
	}
	// 求链表 B 的长度
	while (curB != null) {
    
     
		lenB++; 
		curB = curB.next; 
	}
	curA = headA;
	curB = headB;
	// 链表 A 更长则让 A 多走, B 更长则让 B 多走,保证 A B 从同一位置开始比较
	if (lenA > lenB) for (int i = 0; i < lenA - lenB; i++) curA = curA.next;
	else for (int i = 0; i < lenB - lenA; i++) curB = curB.next;
	// 逐个比较 A 和 B 
	while (curA != null) {
    
    
		if (curA == curB) return curA;
		curA = curA.next;
		curB = curB.next;
	}
	return curA;
}

Clever approach:

  • a goes to b, b goes to a, if there is an intersection point, it will inevitably meet
  • If there is no intersection point, it will end up meeting atnull
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    
    
	ListNode h1 = headA, h2 = headB;
	while (h1 != h2) {
    
    
		h1 = (h1 == null) ? headB : h1.next;
		h2 = (h2 == null) ? headA : h2.next;
	}
	return h1;
}

hash:

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    
    
	Set<ListNode> set = new HashSet<>();
	ListNode p = headA;
	while (p != null) {
    
    
		set.add(p);
		p = p.next;
	}
	p = headB;
	while (p != null) {
    
    
		if (set.contains(p)) return p;
		p = p.next;
	}
	return null;
}

Ring linked list: fast and slow pointer

Topic: 141. Ring Linked List - LeetCode

Given the head node of a linked list head, determine whether there is a ring in the linked list.

Fast and slow pointer:

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

hash:

public boolean hasCycle(ListNode head) {
    
    
	Set<ListNode> set = new HashSet<>();
	while (head != null && !set.contains(head)) {
    
    
		set.add(head);
		head = head.next;
	}
	return head != null;
}

Ring Linked List II**

Topic: 142. Circular Linked List II - LeetCode

Given the head node of a linked list head, return the first node where the linked list starts entering the ring. Returns if the linked list is acyclic null.

Fast and slow pointer judgment ring + find ring entry:

public ListNode detectCycle(ListNode head) {
    
    
	ListNode slow = head, fast = head;
	// 快慢指针判断是否有环
	while (fast != null && fast.next != null) {
    
    
		slow = slow.next;
		fast = fast.next.next;
		if (slow == fast) {
    
     // 有环, 找环的起始点
			fast = head; // 快指针从头开始
			while (fast != slow) {
    
    
				fast = fast.next;
				slow = slow.next;
			}
			return fast;
		}
	}
	return null;
}

hash:

public ListNode detectCycle(ListNode head) {
    
    
	Set<ListNode> set = new HashSet<>();
	while (head != null && !set.contains(head)) {
    
    
		set.add(head);
		head = head.next;
	}
	return head;
}

Guess you like

Origin blog.csdn.net/weixin_43734095/article/details/126788369