对于链表的操作大多有迭代和递归两种解决方法:
迭代法:首先创建一个临时的节点p用于遍历链表,其开始可以指向头节点,也可以让其next节点指向头节点((如果p指向头节点则while循环的判断条件是p!=null,反之则是p.next!=null),随后p不断地向后移动,在这个过程中进行要求的操作。如果结果要返回头指针,可以实现创建一个节点让其next指向头指针。
- 如果是要删除元素,那么需要拥有前一个节点的指针,让其指向要删除的元素的下一个元素,所以此时则不能让p指向头节点,而应该是让next去指向,即判断的是下一个元素的值,这样才能够实现删除。
- 如果是要翻转链表,那么需要不断改变指针的方向,即让next等于之前的元素,所以需要一个变量prev记录之前的元素,在循环内先将下一个元素保存到next变量之后再改变指针的指向,指向prev,然后将prev更改为p,将p更改为next,这样就能顺利遍历链表。
- 递归法:链表的递归实际上就是将链表拆为一个一个的子链表,不断缩小链表的长度,当到链表的最后一位(即p或p.next=null时),递归就到达了最底层,这时直接返回null或head。每一层递归传递给下一层的参数一般时子链表的头指针,拿到头指针后进行操作,之后根据题目要求进行操作并且返回上一层所需要的结果,一般是处理后新的头指针,用于给上一层的尾元素进行连接(比如进行交换顺序头指针发生改变时),或者一直返回最底层的指针(进行翻转头指针没有发生改变时(由于子链表的顺序没有改变,上一层可以直接拿到正确的下一个元素所以不需要下一层返回))
一、移出链表元素
题目链接:203.移出链表元素
/** * <pre> * <p>可以采用迭代,不断判断下一个节点是否要删除;或者采用递归,每次获取子链表的head,如果头节点的值如果等于val就说明返回的是子链表的头</p> * </pre> * * @author <a href="https://github.com/kil1ua">Ken-Chy129</a> * @date 2023/1/5 10:45 */ public class 移出链表元素203 { // 迭代 public ListNode removeElements(ListNode head, int val) { ListNode res = new ListNode(); res.next = head; ListNode p = res; while (p.next != null) { if (p.next.val == val) { p.next = p.next.next; } else { p = p.next; } } return res.next; } // 递归 public ListNode removeElements2(ListNode head, int val) { if (head == null) { return null; } head.next = removeElements2(head.next, val); // 每次删除子链表,返回子链表的头 return head.val == val ? head.next : head; } } class ListNode { int val; ListNode next; ListNode() {} ListNode(int val) { this.val = val; } ListNode(int val, ListNode next) { this.val = val; this.next = next; } }
二、设计链表
题目链接:707.设计链表
/** * <pre> * <p>链表的设计:单链表和双链表</p> * </pre> * * @author <a href="https://github.com/kil1ua">Ken-Chy129</a> * @date 2023/1/5 11:29 */ // 单链表 class MyLinkedList { private int size; private MyListNode head; public MyLinkedList() { size = 0; head = new MyListNode(0); } public int get(int index) { if (index < 0 || index >= size) { return -1; } MyListNode p = head; for (int i=0; i<=index; i++) { p = p.next; } return p.val; } public void addAtHead(int val) { addAtIndex(0, val); } public void addAtTail(int val) { addAtIndex(size, val); } public void addAtIndex(int index, int val) { if (index < 0 || index > size) { return; } MyListNode p = head; for (int i=0; i<index; i++) { p = p.next; } MyListNode newNode = new MyListNode(val); newNode.next = p.next; p.next = newNode; size++; } public void deleteAtIndex(int index) { if (index < 0 || index >= size) { return; } MyListNode p = head; for (int i=0; i<index; i++) { p = p.next; } p.next = p.next.next; size--; } } class MyListNode { int val; MyListNode next; public MyListNode() { } public MyListNode(int val) { this.val = val; } public MyListNode(int val, MyListNode next) { this.val = val; this.next = next; } } // 双链表 class BiLinkedList { private int size; private BiListNode head; private BiListNode tail; public BiLinkedList() { size = 0; head = new BiListNode(0); tail = new BiListNode(0); head.next = tail; tail.prev = head; } public int get(int index) { if (index < 0 || index >= size) { return -1; } BiListNode p; if (index < size/2) { p = head; for (int i=0; i<=index; i++) { p = p.next; } } else { p = tail; for (int i=0; i<size-index; i++) { p = p.prev; } } return p.val; } public void addAtHead(int val) { addAtIndex(0, val); } public void addAtTail(int val) { addAtIndex(size, val); } public void addAtIndex(int index, int val) { if (index < 0 || index > size) { return; } BiListNode p; if (index < size/2) { p = head; for (int i=0; i<index; i++) { p = p.next; } } else { p = tail; for (int i=0; i<=size-index; i++) { p = p.prev; } } BiListNode newNode = new BiListNode(val); newNode.next = p.next; newNode.prev = p; p.next.prev = newNode; p.next = newNode; size++; } public void deleteAtIndex(int index) { if (index < 0 || index >= size) { return; } BiListNode p; if (index < size/2) { p = head; for (int i=0; i<index; i++) { p = p.next; } } else { p = tail; for (int i=0; i<=size-index; i++) { p = p.prev; } } p.next = p.next.next; p.next.prev = p; size--; } } class BiListNode { int val; BiListNode next; BiListNode prev; public BiListNode() { } public BiListNode(int val) { this.val = val; } }
三、翻转链表
题目链接:206.翻转链表
/** * <pre> * <p>翻转链表:迭代或递归</p> * </pre> * * @author <a href="https://github.com/kil1ua">Ken-Chy129</a> * @date 2023/1/5 13:39 */ public class 翻转链表206 { // 迭代法 public ListNode reverseList(ListNode head) { ListNode prev=null, p=head, next; while (p != null) { next = p.next; p.next = prev; prev = p; p = next; } return prev; } // 递归法 public ListNode reverseList2(ListNode head) { if (head == null || head.next == null) { // head == null 是用于0长度的链表直接返回 return head; // 遍历到最后一个节点则返回,这个就是翻转后链表的头 } ListNode newHead = reverseList2(head.next); // 如果是1->2->3->...->k->k+1->...->k+n // 执行到k时变为1->2->3->...->k->k+1<-...<-k+n // 那么k的next是k+1,需要将k+1的next改为k head.next.next = head; // 将k的next置为null,不然会成环,k指向k+1而k+1指向k head.next = null; return newHead; } }
四、两两交换链表中的节点
题目链接:24.两两交换链表中的节点
/** * <pre> * <p>可以采用迭代也可以采用递归</p> * </pre> * * @author <a href="https://github.com/kil1ua">Ken-Chy129</a> * @date 2023/1/5 14:58 */ public class 两两交换链表中的节点24 { public ListNode swapPairs(ListNode head) { ListNode p = new ListNode(), res = p; p.next = head; while (p.next != null && p.next.next != null) { ListNode first = p.next, second = p.next.next; first.next = second.next; second.next = first; p.next = second; p = first; } return res.next; } // 递归 public ListNode swapPairs2(ListNode head) { if (head == null || head.next == null) { return head; } ListNode next = swapPairs(head.next.next); ListNode second = head.next; head.next = next; second.next = head; return second; } }