1.排序链表
题目
给定一个链表,其第1,3,5,7 …位元素,即奇数位元素升序,2,4,6,8…位元素,即偶数位元素降序,返回排序后的链表
解:首先将链表的偶数位元素提取出来,然后进行reverse操作,再进行归并排序
2.寻找链表的中间元素
题目
给定一个链表,求中间元素,如果链表有偶数个元素,返回中间偏右的那个
解1:递归计数
int mid = -1;
Node middle(Node node,int i){
if(node==null){
mid = i/2;
return null;
}
Node p = middle(node.next,i+1);
if(i==mid){
return node;
}
return p;
}
解2:使用fast和slow指针,fast移动两步,slow移动一步
Node middle(Node node){
Node fast = node,slow = node;
while(fast!=null && fast.next!=null){
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
删除链表的中间元素
解法同上。
3.将链表扁平化
题目
链表的节点有right和bottom两个指针,如下所示
1 3 4 6
2 5 7 10
9
1的right指针是3,bottom指针是2。只有第一层的right指针有值
解:使用分治的思想,首先将right指针进行flatten,然后进行merge
代码略
4.旋转链表
将链表的前k个节点移动到链表尾部。
5.从链表中移除重复元素
解:此题需要注意代码的细节,否则容易出bug
void removeDuplicate(Node head){
if(head==null)return null;
Node last = head;
Node p = head.next;
last.next = null;
while(p!=null){
if(p.val!=last.val){
last = last.next = p;
p = p.next;
last.next = null;// IMPORTANT!!
}else{
p = p.next;
}
}
}
6.复制random节点
题目:138. Copy List with Random Pointer
一个链表有random和next指针,复制这个链表使得random和next保持相同的结构
解:先将两个链表通过next连接起来,然后设置第二个链表的random,然后恢复第一个链表的next
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
public Node copyRandomList(Node head) {
if(head==null)return null;
Node p = head;
Node tmp = null;
// invariant: p is head[i]
// p.next = copied node
// copied node.next = head[i].hext
while(p!=null){
tmp = p.next;
p.next = new Node(p.val);
p.next.next = tmp;
p = tmp;
}
Node newHead = head.next;
p = head;
while(p!=null){
if(p.random!=null){
p.next.random = p.random.next;
}
p = p.next.next;
}
p = head;
Node last = null;
// invariant: p.next = t, t,p needs to adjust
// in the end:
while(p!=null){
tmp = p.next;
p.next = tmp.next;
if(last!=null){
last.next = tmp;
}
last = tmp;
p = tmp.next;
}
return newHead;
}
}
7.乱序链表
变换链表L0->L1->…->Ln-1->Ln,为 L0->Ln->L1->Ln-1->…
解1:使用双端队列,空间复杂度O(N)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public void reorderList(ListNode head) {
// A->B C->D
//
Deque<ListNode> que = new ArrayDeque<>();
ListNode p = head;
while(p!=null){
que.addFirst(p);
p = p.next;
}
while(que.size()>2){
ListNode back = que.removeLast();
ListNode front = que.removeFirst();
front.next = back.next;
back.next = front;
}
ListNode back = que.isEmpty()?null:que.removeLast();
ListNode front = que.isEmpty()?null:que.removeFirst();
if(back!=null){
back.next = front;
if(front!=null){
front.next = null;
}
}
}
}
解2:先找到链表的中间位置(偶数个则是偏右侧),然后将后面的部分逆序,然后进行拼接。
代码略。
8.旋转链表
给定一个链表,将最后k个元素移动到首部
解:使用递归,在(i+k+1)%n==0,表明是最后k个元素的父节点。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if(k==0||head==null)return head;
ListNode kth = lastKth(head,head,0,k);
return kth==null?head:kth;
}
int n;
ListNode lastKth(ListNode node,ListNode head,int i,int k){
if(node==null){
n = i;
return null;
}
ListNode kth = lastKth(node.next,head,i+1,k);
if(node.next==null){
node.next = head;
}
if(((i+k+1)%n)==0){
head = node.next;
node.next = null;
return head;
}
return kth;
}
}
删除倒数第k个节点
题目:19. Remove Nth Node From End of List
删除链表的第k个节点
解:和上面的思路一致,使用递归
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if(head==null)return null;
ListNode r=removeKth(head,0,n);
if(r==null){
r = head.next;
head.next = null;
return r;
}
return head;
}
int n;
ListNode removeKth(ListNode node,int i,int k){
if(node==null){
n=i;
return null;
}
ListNode r = removeKth(node.next,i+1,k);
if((i+k+1)%n==0){
r = node.next;
if(r!=null){
node.next = r.next;
r.next = null;
}
}
return r;
}
}
9.移除重复
题目:82. Remove Duplicates from Sorted List II
移除所有出现次数大于1次的元素
解:使用指针维持相同元素的区间
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode last = null;
ListNode p = head;
head = null;
ListNode t = null;
while(true){
int c = 0;
while(p!=null && p.next!=null && p.val==p.next.val){
p = p.next;
++c;
}
if(p==null)break;
t = p.next;
if(c==0){
if(last==null){
last = head = p;
}else{
last = last.next = p;
}
last.next = null;
}
p =t;
}
return head;
}
}
10.倒转链表
题目:92. Reverse Linked List II(Medium)
给定一个链表,倒转第m~n之间的节点
解:找到边界点 m.prev, m, n,n.next, 然后进行倒序即可。
class Solution {
public ListNode reverseBetween(ListNode head, int m, int n) {
int i = 0;
ListNode p = head;
ListNode prev = null;
ListNode mPrev = null,mNode = null;
ListNode nNodeNext=null,nNode = null;
ListNode t = null;
while(p!=null){
t = p.next;
if(i==m-1){
mPrev = prev;
mNode = p;
}
if(i==n-1){
nNode = p;
nNodeNext = t;
}
// . Y X X X .
// mPrev
if(i>m-1 && i<=n-1){
p.next = prev;
}
++i;
prev = p;
p = t;
}
if(mNode!=null)mNode.next = nNodeNext;
if(mPrev!=null) mPrev.next = nNode;
return mPrev!=null?head:nNode;
}
}
11.排序链表
使用O(nlogn)时间复杂度,O(1)空间复杂度对链表进行排序
解:分治法,先将奇数位指针和偶数位指针拆开,然后进行分别排序后归并
class Solution {
public ListNode sortList(ListNode head) {
return sort(head);
}
ListNode sort(ListNode node){
if(node==null||node.next==null)return node;
ListNode p = node;
ListNode a = node, b = node.next;
ListNode lastq = null;
while(p!=null){
if(lastq!=null){
lastq.next = p.next;
}
lastq = p.next;
p.next = lastq==null? null : lastq.next;
p = p.next;
}
a = sort(a);
b = sort(b);
return merge(a,b);
}
ListNode merge(ListNode a,ListNode b){
ListNode c = new ListNode(0);
ListNode h = c;
while(a!=null && b!=null){
if(a.val>b.val){
h.next = b;
b = b.next;
}else{
h.next = a;
a = a.next;
}
h = h.next;
}
if(a!=null)h.next=a;
if(b!=null)h.next=b;
return c.next;
}
}