算法题解 —— 链表(15-19)

内容来源于自己的刷题笔记,对一些题目进行方法总结,用 java 语言实现。

15. 单链表的选择排序:
  1. 题目描述:

    给定一个无序单链表的头节点 head,实现单链表的选择排序。

    要求:额外空间复杂度为 O(1)

  2. 解题思路:

    1. 开始时默认整个链表都是未排序的部分,对于找到的第一个最小值节点,肯定是整个链表的最小值节点,将其设置为新的头节点,记为 newHead
    2. 每次在未排序的部分中找到最小值的节点,然后把这个节点从未排序的链表中删除,删除的过程当然要保证未排序部分的链表在结构上不至于断开
    3. 把删除的节点(也就是每次的最小值节点)连接到排好序部分的链表尾部
    4. 全部处理完成后,整个链表都已经有序,返回 newHead
  3. 代码实现:

    public class SelectionSort {
    
        private class Node{
            public int value;
            public Node next;
            public Node(int value){
                this.value = value;
            }
        }
    
        public Node selectionSort(Node head){
            Node cur = head;
            Node smallPre = null;
            Node small = null;
            Node tail = null;
            while (cur != null){
                small = cur;
                smallPre = getSmallPre(head);
                if (smallPre != null){
                    small = smallPre.next;
                    smallPre.next = small.next;
                }
                cur = cur == small ? cur.next : cur;
                if (tail == null){
                    head = small;
                }else {
                    tail.next = small;
                }
                tail = small;
            }
            return head;
        }
    
        private Node getSmallPre(Node head){
            Node cur = head.next;
            Node pre = head;
            Node small = head;
            Node smallPre = null;
            while (cur != null){
                if (cur.value < small.value){
                    smallPre = pre;
                    small = cur;
                }
                pre = cur;
                cur = cur.next;
            }
            return smallPre;
        }
    
    }
    
16. 一种怪异的节点删除方式:
  1. 题目描述:

    链表节点值类型为 int 型,给定一个链表中的节点 node,但不给定整个链表的头节点,在链表中删除 node。

    要求:时间复杂度为 O(1)

  2. 解题思路:

    思路很简单,就是把下一个节点的值也换成 node 的值,然后删除下一个节点,但这种做法存在很大的问题:

    1. 无法删除最后一个节点
    2. 这种方法本质不是删除节点,而是替换节点,工程上一个节点的复制可能很复杂,一般不考虑复制
  3. 代码实现:

    public class RemoveNodeWired {
    
        private class Node{
            public int value;
            public Node next;
            public Node(int value){
                this.value = value;
            }
        }
    
        public void removeNodeWired(Node node){
            if (node == null){
                return;
            }
            Node next = node.next;
            if (next == null){
                throw new RuntimeException("can not remove last Node.");
            }
            node.value = next.value;
            node.next = next.next;
        }
    }
    
17. 向有序的环形链表中插入新节点:
  1. 题目描述:

    一个环形单链表从头节点 head 开始不降序,同时由最后的节点指向头节点。给定这样一个环形单链表的头节点 head 和一个整数 num,请生成节点值为 num 的新节点,并插入到这个环形链表中,保证调整后的链表依然有效

  2. 解题思路:

    1. 生成节点值为 num 的新节点,记为 node
    2. 如果链表为空,让 node 自己组成环形链表,然后直接返回 node
    3. 如果链表不为空,令变量为 pre=head,cur=head.next,然后令 pre 和 cur 同步移动下去,如果遇到 pre 的节点值小于或等于 num,并且 cur 的节点值大于或等于 num,说明 node 应该在 pre 节点和 cur 节点之间插入,记为 node,然后返回 head 即可
    4. 如果 pre 和 cur 转了一圈,这期间都没有发生步骤3所说的情况,说明 node 应该插入到头节点的前面
  3. 代码实现:

    public class InsertNum {
    
        private class Node{
            public int value;
            public Node next;
            public Node(int value){
                this.value = value;
            }
        }
    
        public Node insertNum(Node head,int num){
            Node node = new Node(num);
            if (head == null){
                node.next = node;
                return node;
            }
            Node pre = head;
            Node cur = head.next;
            while (cur != null){
                if (pre.value <= num && cur.value >= num){
                    break;
                }
                pre = cur;
                cur = cur.next;
            }
            pre.next = node;
            node.next = cur;
            return head.value < num ? head : node;
        }
    }
    
18. 合并两个有序的单链表:
  1. 题目描述:

    给定两个有序单链表的头节点 head1 和 head2,请合并两个有序链表,合并后的链表依然有序,并返回合并后链表的头节点。

  2. 解题思路:

    1. 如果两个链表中有一个为空,说明无须合并过程,返回另一个链表的头节点即可
    2. 比较 head1 和 head2 的值,小的节点也是合并后链表的最小节点,这个节点无疑应该是合并链表的头节点,记为 head;在之后的步骤中,哪个链表的头节点的值更小,另一个链表的所有节点都会依次插入到这个链表中
    3. 设 head 节点所在的链表为链表1,另一个链表为链表2.链表1和链表2都从头部开始一起遍历,比较每次遍历到的两个节点的值,记为 cur1 和 cur2,然后根据大小关系做出不同的调整,同时用一个变量 pre 表示上次比较时值比较小的节点
    4. 如果链表1先走完,此时 cur1=null,pre 为链表1的最后一个节点,那么就把 pre 的 next 指针指向链表2当前的节点(即 cur2),表示把链表2没遍历到的有序部分直接拼到最后,调整结束;链表2也是一样
    5. 返回合并后链表的头节点 head
  3. 代码实现:

    public class Merge {
    
        private class Node{
            public int value;
            public Node next;
            public Node(int value){
                this.value = value;
            }
        }
    
        public Node merge(Node head1,Node head2){
            if (head1 == null || head2 == null){
                return head1 != null ? head1 : head2;
            }
            Node head = head1.value < head2.value ? head1 : head2;
            Node cur1 = head == head1 ? head1 : head2;
            Node cur2 = head == head2 ? head2 : head1;
            Node pre = null;
            Node next = null;
            while (cur1 != null && cur2 != null){
                if (cur1.value <= cur2.value){
                    pre = cur1;
                    cur1 = cur1.next;
                }else{
                    next = cur2.next;
                    pre.next = cur2;
                    cur2.next = cur1;
                    pre = cur2;
                    cur2 = next;
                }
            }
            //尾部连接
            pre.next = cur1 == null ? cur2 : cur1;
            return head;
        }
    }
    
19. 按照左右半区的方式重新组合单链表:
  1. 题目描述:

    给定一个单链表的头部节点 head,链表长度为 N,如果 N 为偶数,那么前 N/2 个节点算作左半区,后 N/2 个节点算作右半区;如果 N 为奇数,那么前 N/2 个节点算作左半区,后 N/2 + 1 个节点算作右半区。左半区从左到右依次记为 L1->L2->…,右半区从左到右依次记为 R1->R2->…,请将单链表调整成 L1->R1->L2->R2->… 的形式

  2. 解题思路:

    1. 如果链表为空或者长度为1,不用调整,过程直接结束
    2. 链表长度大于1,遍历一遍找到左半区的最后一个节点,记为 mid
    3. 遍历一遍找到 mid 后,将左半区与右半区分离成两个链表(mid.next=null),分别记为 left(head) 和 right(原来的 mid.next)
    4. 将两个链表按照题目要求合并起来
  3. 代码实现:

    public class Relocate {
    
        private class Node{
            public int value;
            public Node next;
            public Node(int value){
                this.value = value;
            }
        }
    
        public void relocate(Node head){
            if (head == null || head.next == null){
                return;
            }
            Node mid = head;
            Node right = head.next;
            while (right.next != null || right.next.next != null){
                mid = mid.next;
                right = right.next.next;
            }
            right = mid.next;
            mid.next = null;
            mergeLR(head,right);
        }
    
        private void mergeLR(Node left,Node right){
            Node next = null;
            while (left.next != null){
                next = right.next;
                right.next = left.next;
                left.next = right;
                left = right.next;
                right = next;
            }
            left.next = right;
        }
    }
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值