内容来源于自己的刷题笔记,对一些题目进行方法总结,用 java 语言实现。
15. 单链表的选择排序:
-
题目描述:
给定一个无序单链表的头节点 head,实现单链表的选择排序。
要求:额外空间复杂度为 O(1)
-
解题思路:
- 开始时默认整个链表都是未排序的部分,对于找到的第一个最小值节点,肯定是整个链表的最小值节点,将其设置为新的头节点,记为 newHead
- 每次在未排序的部分中找到最小值的节点,然后把这个节点从未排序的链表中删除,删除的过程当然要保证未排序部分的链表在结构上不至于断开
- 把删除的节点(也就是每次的最小值节点)连接到排好序部分的链表尾部
- 全部处理完成后,整个链表都已经有序,返回 newHead
-
代码实现:
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. 一种怪异的节点删除方式:
-
题目描述:
链表节点值类型为 int 型,给定一个链表中的节点 node,但不给定整个链表的头节点,在链表中删除 node。
要求:时间复杂度为 O(1)
-
解题思路:
思路很简单,就是把下一个节点的值也换成 node 的值,然后删除下一个节点,但这种做法存在很大的问题:
- 无法删除最后一个节点
- 这种方法本质不是删除节点,而是替换节点,工程上一个节点的复制可能很复杂,一般不考虑复制
-
代码实现:
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. 向有序的环形链表中插入新节点:
-
题目描述:
一个环形单链表从头节点 head 开始不降序,同时由最后的节点指向头节点。给定这样一个环形单链表的头节点 head 和一个整数 num,请生成节点值为 num 的新节点,并插入到这个环形链表中,保证调整后的链表依然有效
-
解题思路:
- 生成节点值为 num 的新节点,记为 node
- 如果链表为空,让 node 自己组成环形链表,然后直接返回 node
- 如果链表不为空,令变量为 pre=head,cur=head.next,然后令 pre 和 cur 同步移动下去,如果遇到 pre 的节点值小于或等于 num,并且 cur 的节点值大于或等于 num,说明 node 应该在 pre 节点和 cur 节点之间插入,记为 node,然后返回 head 即可
- 如果 pre 和 cur 转了一圈,这期间都没有发生步骤3所说的情况,说明 node 应该插入到头节点的前面
-
代码实现:
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. 合并两个有序的单链表:
-
题目描述:
给定两个有序单链表的头节点 head1 和 head2,请合并两个有序链表,合并后的链表依然有序,并返回合并后链表的头节点。
-
解题思路:
- 如果两个链表中有一个为空,说明无须合并过程,返回另一个链表的头节点即可
- 比较 head1 和 head2 的值,小的节点也是合并后链表的最小节点,这个节点无疑应该是合并链表的头节点,记为 head;在之后的步骤中,哪个链表的头节点的值更小,另一个链表的所有节点都会依次插入到这个链表中
- 设 head 节点所在的链表为链表1,另一个链表为链表2.链表1和链表2都从头部开始一起遍历,比较每次遍历到的两个节点的值,记为 cur1 和 cur2,然后根据大小关系做出不同的调整,同时用一个变量 pre 表示上次比较时值比较小的节点
- 如果链表1先走完,此时 cur1=null,pre 为链表1的最后一个节点,那么就把 pre 的 next 指针指向链表2当前的节点(即 cur2),表示把链表2没遍历到的有序部分直接拼到最后,调整结束;链表2也是一样
- 返回合并后链表的头节点 head
-
代码实现:
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. 按照左右半区的方式重新组合单链表:
-
题目描述:
给定一个单链表的头部节点 head,链表长度为 N,如果 N 为偶数,那么前 N/2 个节点算作左半区,后 N/2 个节点算作右半区;如果 N 为奇数,那么前 N/2 个节点算作左半区,后 N/2 + 1 个节点算作右半区。左半区从左到右依次记为 L1->L2->…,右半区从左到右依次记为 R1->R2->…,请将单链表调整成 L1->R1->L2->R2->… 的形式
-
解题思路:
- 如果链表为空或者长度为1,不用调整,过程直接结束
- 链表长度大于1,遍历一遍找到左半区的最后一个节点,记为 mid
- 遍历一遍找到 mid 后,将左半区与右半区分离成两个链表(mid.next=null),分别记为 left(head) 和 right(原来的 mid.next)
- 将两个链表按照题目要求合并起来
-
代码实现:
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; } }