链表——复杂的穿针引线

例题:两两交换链表中的节点

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

使用四个指针,node1node2分别指向要交换的两个结点,pre指向node1前一个结点,next指向node2后一个结点。

交换node1和node2的操作:

  • node2.next = node1;
  • node1.next = next;
  • pre.next = node2;

然后,pre = node1,再根据p重新定位其余三个指针。
这个题指针这么多,一定要严防空指针

class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head==null || head.next==null) return head;
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode pre = dummyHead;
        ListNode node1 = pre.next;
        ListNode node2 = node1.next;
        ListNode next = node2.next;
        while(node1!=null && node2!=null){
            node2.next = node1;
            node1.next = next;
            pre.next = node2;
            pre = node1;
            node1 = pre.next;
            node2 = node1==null?null:node1.next;    //链表问题要严防空指针
            next = node2==null?null:node2.next;
        }
        return dummyHead.next;
    }
}

1.K 个一组翻转链表

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

思路:

  • 使用递归, reverseKGroup(ListNode head, int k)的含义是:返回k个一组的翻转链表头结点
  • 所以,在reverseKGroup(ListNode head, int k)函数中,要做的就是翻转前k个结点,然后将k个结点之后的链表进行k个一组翻转(即调用everseKGroup),接到已经反转好的链表后面,返回新链表的头结点。

尤其要注意链表最后部分的空指针问题

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode cur = head;
        for (int i = 0; i < k; i++) {
            if(cur == null) return head;
            cur = cur.next;
        }
        
        cur = head;
        ListNode pre = null;
        ListNode next = cur.next;
        for (int i = 0; i < k; i++) {
            cur.next = pre;
            pre = cur;
            cur = next;
            if(next!=null) next = next.next;
        }
        head.next = reverseKGroup(cur,k);
        return pre;
    }
}

2. 对链表进行插入排序

插入排序算法:
1.插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
2.每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
3.重复直到所有输入数据插入完为止。

思路:

  • 使用一个cur来遍历链表,需要一个pre指向cur前面的一个结点。
  • 每一个cur都比较从head到cur所有结点的结点值,遇到值大于cur.val的结点,便把cur插在它的前面。
class Solution {
    public ListNode insertionSortList(ListNode head) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode pre = dummyHead;
        ListNode cur = head;
        while(cur!=null){
            ListNode node = dummyHead;
            while(node.next!=cur && node.next.val<cur.val){
                node  = node.next;
            }
            if(node.next==cur){	//当node==cur说明cur是目前为止最大的
                pre = pre.next;
                cur = cur.next;
            }else{
                pre.next = cur.next;
                cur.next = node.next;
                node.next = cur;
                cur = pre.next;
            }
        }
        return dummyHead.next;
    }
}

优化:

  • 每次不一定从dummyHead开始扫描,可以通过和上一次插入的结点值进行比较。如果值比上一次插入的大,那么就从上一次插入的位置开始扫描。否则从头扫描。
class Solution {
    public ListNode insertionSortList(ListNode head) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode pre = dummyHead;
        ListNode cur = head;
        ListNode lastInsert = head;
        while(cur!=null){
            ListNode node = null;
            if(cur.val>lastInsert.val) node = lastInsert;
            else node = dummyHead;
            while(node.next!=cur && node.next.val<cur.val){
                node  = node.next;
            }
            if(node.next==cur){
                pre = pre.next;
                cur = cur.next;
            }else{
                pre.next = cur.next;
                cur.next = node.next;
                node.next = cur;
                lastInsert = cur;
                cur = pre.next;
            }
        }
        return dummyHead.next;
    }
}

3.排序链表

在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

使用归并排序可以完美的解决问题,但是,不能用自顶向下的方式,也就是不可以使用递归,因为空间复杂度不允许。所以要采用自底向上,这里先给出自顶向下的代码:

class Solution {
    public ListNode sortList(ListNode head) {
        if(head==null || head.next==null) return head;
        //第一步,找到中点,断开链表
        ListNode fast = head;
        ListNode slow = head;
        while(fast.next!=null && fast.next.next!=null){
            slow = slow.next;
            fast = fast.next.next;
        }
        //此时slow就是左半段末尾
        //第二步归并排序好的链表
        ListNode right = sortList(slow.next);
        slow.next = null;
        ListNode left = sortList(head);
        ListNode newList = new ListNode(0);
        ListNode cur = newList;
        while(left!=null && right!=null){
            if(left.val<right.val){
                cur.next = left;
                left = left.next;
            }else{
                cur.next = right;
                right = right.next;
            }
            cur = cur.next;
        }
        cur.next = left==null?right:left;
        return newList.next;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值