LeetCode - 2. 链表

刷题顺序来自:代码随想录

目录

删除节点

203. 移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

public ListNode removeElements(ListNode head, int val) {
  ListNode pre = null;
  ListNode curr = head;
  while(curr != null) {
    if (curr.val == val) {
      // 判断删除的是否是头节点
      if (pre != null) {
        pre.next = curr.next;
      }
      else {
        head = curr.next;
      }
    }
    else {
      pre = curr;  // 当前节点没有被删除时,更新pre指针
    }

    curr = curr.next;
  }

  return head;
}

设计链表

707. 设计链表

设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。

在链表类中实现这些功能:

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
public class ListNode {
  int val;
  ListNode next;
  ListNode(int x) { val = x; }
}

class MyLinkedList {
  int size;
  ListNode head;  // sentinel node as pseudo-head
  public MyLinkedList() {
    size = 0;
    head = new ListNode(0);
  }

  /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
  public int get(int index) {
    // if index is invalid
    if (index < 0 || index >= size) return -1;

    ListNode curr = head;
    // index steps needed 
    // to move from sentinel node to wanted index
    for(int i = 0; i < index + 1; ++i) curr = curr.next;
    return curr.val;
  }

  /** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
  public void addAtHead(int val) {
    addAtIndex(0, val);
  }

  /** Append a node of value val to the last element of the linked list. */
  public void addAtTail(int val) {
    addAtIndex(size, val);
  }

  /** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
  public void addAtIndex(int index, int val) {
    // If index is greater than the length, 
    // the node will not be inserted.
    if (index > size) return;

    // [so weird] If index is negative, 
    // the node will be inserted at the head of the list.
    if (index < 0) index = 0;

    ++size;
    // find predecessor of the node to be added
    ListNode pred = head;
    for(int i = 0; i < index; ++i) pred = pred.next;

    // node to be added
    ListNode toAdd = new ListNode(val);
    // insertion itself
    toAdd.next = pred.next;
    pred.next = toAdd;
  }

  /** Delete the index-th node in the linked list, if the index is valid. */
  public void deleteAtIndex(int index) {
    // if the index is invalid, do nothing
    if (index < 0 || index >= size) return;

    size--;
    // find predecessor of the node to be deleted
    ListNode pred = head;
    for(int i = 0; i < index; ++i) pred = pred.next;

    // delete pred.next 
    pred.next = pred.next.next;
  }
}

翻转链表

206. 翻转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

public ListNode reverseList(ListNode head) {
	ListNode pre = null, curr = head;
  while(curr != null) {
    ListNode behind = curr.next;
    curr.next = pre;

    pre = curr;
    curr = behind;
  }

  return pre;
}

递归方法:

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

private ListNode reverse(ListNode prev, ListNode cur) {
  if (cur == null) {
    return prev;
  }
  ListNode temp = null;
  temp = cur.next;// 先保存下一个节点
  cur.next = prev;// 反转
  // 更新prev、cur位置
  // prev = cur;
  // cur = temp;
  return reverse(cur, temp);
}

交换节点

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

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

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

  ListNode pre = head, curr = head.next.next;
  head = head.next;
  pre.next = curr;
  head.next = pre;
  
  while(curr != null) {
    ListNode next = curr.next;
    if(next != null) {
      pre.next = next;
      curr.next = next.next;
      next.next = curr;
    }
    pre = curr;
    curr = curr.next;
  }

  return head;
}

虚拟头节点:

    public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
		
        ListNode dummy = new ListNode(0, head);
        ListNode pre = dummy, curr = head;
        while(curr != null && curr.next != null) {
            ListNode next = curr.next;

            pre.next = next;
            curr.next = next.next;
            next.next = curr;
            
            pre = curr;
            curr = curr.next;
        }

        return dummy.next;
    }

删除链表的倒数第n个节点

19. 删除链表的倒数第 N 个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

public ListNode removeNthFromEnd(ListNode head, int n) {        
    ListNode dummy = new ListNode(0, head);  // 添加虚拟头节点
    ListNode left = dummy, right = dummy;

    // right右移直到和left相差n
    for(int i = 0; i < n; i++) {
        right = right.next;
    }
	
    // left和right同时右移
    while(right.next != null) {
        left = left.next;
        right = right.next;
    }
	
    left.next = left.next.next;  // 删除节点

    return dummy.next;
}

链表相交

面试题 02.07. 链表相交

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    ListNode nodeA = headA, nodeB = headB;
    int lenA = getLen(headA), lenB = getLen(headB);
	
    // 使更长的链表起始位置右移,直到剩余长度和短链表相同
    if (lenA > lenB) {
        for(int i = 0; i < lenA-lenB; i++) {
            nodeA = nodeA.next;
        }
    }
    else if (lenA < lenB) {
        for(int i = 0; i < lenB-lenA; i++) {
            nodeB = nodeB.next;
        }
    }
	
    // 找到相交点
    while(nodeA != null && nodeA != nodeB) {
        nodeA = nodeA.next;
        nodeB = nodeB.next;
    }

    return nodeA;
}

// 求链表长度
public int getLen(ListNode head) {
    int len = 0;
    while(head != null) {
        len++;
        head = head.next;
    }

    return len;
}

环形链表

142. 环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。

注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

  • 使用HashSet存储访问过的元素,如果遇到元素已经被存储的情况,说明存在环。
public ListNode detectCycle(ListNode head) {
    if(head == null || head.next == null) {
        return null;
    }

    HashSet<ListNode> set = new HashSet<>();

    while(head != null) {
        if(set.contains(head)) {
            return head;
        }

        set.add(head);
        head = head.next;
    }

    return null;
}
  • 官方答案:快慢指针,slow每次移动1格,fast每次移动2格,如果存在环,它们会相遇(相遇位置不一定是环的起始点);相遇后,格外使用指针ptr指向头部,随后ptrslow每次移动1格,最后它们会在入环点相遇。
上一篇:回溯——第77题. 组合


下一篇:链表常见面试题:反转链表