【OfferX】链表

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.乱序链表

题目143. Reorder List(Medium)

变换链表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.旋转链表

题目61. Rotate List(Medium)

给定一个链表,将最后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.排序链表

题目148. Sort List(Medium)

使用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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值