链表
链表实现栈
在链表头部插入和删除节点时间复杂度O(1),所以可以构建栈。
对链表增加一个tail指针,可以构造队列。
removeElements
反转链表
反转链表关键点是需要3个指针:pre、cur和next。最基本的接口有两个:反转全部链表和反转某一个节点后面的n个节点。
// 反转第from个到第to个节点
ListNode reverseBetween(ListNode head, int from, int to){
ListNode dummy = new ListNode();
dummy.next = head;
ListNode pre = dummy; // 最终落到第from个节点的前一个
while(--from > 0) pre = pre.next; // pre移动from-1次
ListNode post = null; // 最终落到第to个节点的下一个节点
ListNode tail = pre.next; // 反转后反转部分尾部节点
pre.next = reverse(pre.next, to-from, post);
tail.next = post;
return dummy.next;
}
// 反转 head 后面 index 个节点
// 并将最后一个节点的下一个节点保存到post中
// 返回反转后的头结点,即原来的尾结点
ListNode reverse(ListNode head, int index, ListNode post){
if(index==0){
post = head.next;
return head;
}
ListNode tail = head.next;
ListNode ret = reverse(head.next, index-1, post);
tail.next = head;
return ret;
}
// 边界条件考虑的比较齐全的写法,无需dummyNode
ListNode reversePart(ListNode head, int from, int to){
if(head==null || from >= to) return head;
int len=0;
ListNode fPre=null, tPost=null;
ListNode node1=head;
while(node1!=null){
len++;
fPre = len==from-1 ? node1 : fPre;
tPost = len==to+1 ? node1 : tPost;
node1 = node1.next;
}
if(from>to || from < 1 || to > len)
return head;
// 反转部分的第一个节点
node1 = fPre==null ? head : fPre.next;
node1.next = tPost; // 提前指向tPost
ListNode node2 = node1.next;
while(node2!=tPost){
ListNode next = node2;
node2.next = node1;
node1 = node2;
node2 = next;
}
// node1变为了部分链表反转后的第一个
if(fPre!=null){
fPre.next = node1;
return head;
}
return node1;
}