单链表的归并排序和插入排序

男娘i 2022-06-13 04:21 319阅读 0赞

由于最近在学习数据结构和算法,在牛客网 的在线编程题上遇到了对链表的相关排序操作,发现自己对链表这块还是理解不够深入,以前做过对数组进行排序,但链表的操作要比数组复杂一些,毕竟不能进行随机访问了,单链表得从头开始找。
下面是我通过在线编程(OJ)代码并附有注释。


1.单链表的归并排序

1.题目

Sort a linked list in O(n log n) time using constant space complexity.
对一个链表进行排序,要求时间复杂度为O(n log n)

2.解题思路

题目要求时间复杂度为nlog(n),所以选择了归并排序。归并排序最坏时间复杂度、平均时间复杂度、最快时间复杂度都为nlog(n)。它是一种稳定的排序算法。
链接:思路来源
来源:牛客网
这里写图片描述

2.1 拆分链表,找到中间节点

快慢指针的方式,快指针移动两位,慢指针移动一位。

  1. /** 查找中间结点 */
  2. public ListNode findMidNode(ListNode head){
  3. ListNode p= head;
  4. ListNode q=head.next;
  5. while (q != null && q.next != null) {
  6. p = p.next;
  7. q = q.next.next;
  8. }
  9. return p;
  10. }

2.2分治,分别处理每个链表

  1. ListNode midNode =findMidNode(head);
  2. ListNode record = midNode.next;
  3. midNode.next = null; //断开两个链表的连接
  4. ListNode left = MySort(head, midNode);
  5. ListNode right = MySort(record, end);

2.3合并

  1. /** 合并左右链表 */
  2. private ListNode merge(ListNode left, ListNode right) {
  3. if(left==null)return right;
  4. if(right==null)return left;
  5. ListNode newhead = null;
  6. newhead=new ListNode(0);
  7. ListNode temp = newhead;
  8. while (left != null && right != null) {
  9. if (left.val > right.val) {
  10. temp.next= right;
  11. right= right.next;
  12. } else {
  13. temp.next= left;
  14. left = left.next;
  15. }
  16. temp=temp.next;
  17. }
  18. temp.next=(left==null)?right:left;
  19. return newhead.next;
  20. }

3. 完整代码

  1. /** * Definition for singly-linked list. * class ListNode { * int val; ListNode next; * ListNode(int x) { * val = x; next = null; * } *} //排序算法选择归并排序 */
  2. public class Solution {
  3. private ListNode indexNode =new ListNode(Integer.MAX_VALUE);
  4. public ListNode sortList(ListNode head) {
  5. if (head == null) {
  6. return head;
  7. }
  8. return MySort(head, null);
  9. }
  10. private ListNode MySort(ListNode head, ListNode end) {
  11. if(head==end){
  12. return head;
  13. }
  14. ListNode midNode =findMidNode(head);
  15. ListNode record = midNode.next;
  16. midNode.next = null;
  17. ListNode left = MySort(head, midNode);
  18. ListNode right = MySort(record, end);
  19. return merge(left, right);
  20. }
  21. /** 查找中间结点 */
  22. public ListNode findMidNode(ListNode head){
  23. ListNode p= head;
  24. ListNode q=head.next;
  25. while (q != null && q.next != null) {
  26. p = p.next;
  27. q = q.next.next;
  28. }
  29. return p;
  30. }
  31. /** 合并左右链表 */
  32. private ListNode merge(ListNode left, ListNode right) {
  33. if(left==null)return right;
  34. if(right==null)return left;
  35. ListNode newhead = null;
  36. newhead=new ListNode(0);
  37. ListNode temp = newhead;
  38. while (left != null && right != null) {
  39. if (left.val > right.val) {
  40. temp.next= right;
  41. right= right.next;
  42. } else {
  43. temp.next= left;
  44. left = left.next;
  45. }
  46. temp=temp.next;
  47. }
  48. temp.next=(left==null)?right:left;
  49. return newhead.next;
  50. }
  51. public static void main(String[] args) {
  52. ListNode list = new ListNode(4);
  53. ListNode head = list;
  54. head.next = new ListNode(2);
  55. head.next.next = new ListNode(1);
  56. head.next.next.next = new ListNode(3);
  57. Solution solution = new Solution();
  58. ListNode result = solution.sortList(head);
  59. while (result != null) {
  60. System.out.print(result.val + "\t");
  61. result = result.next;
  62. }
  63. }
  64. public void display(ListNode result) {
  65. while (result != null) {
  66. System.out.print(result.val + "\t");
  67. result = result.next;
  68. }
  69. System.out.println();
  70. }
  71. }
  72. class ListNode {
  73. int val;
  74. ListNode next;
  75. ListNode(int x) {
  76. val = x;
  77. next = null;
  78. }
  79. }

2.单链表的直接插入排序

1.题目

Sort a linked list using insertion sort.

2.注意事项

每次的插入操作时,需要保留下一结点。
然后用伪首结点该结点值为最小值,将他们串联起来。

完整代码

  1. public class Solution {
  2. public ListNode insertionSortList(ListNode head) {
  3. if (head == null || head.next == null) // 链表为空或者仅一个结点
  4. return head;
  5. ListNode saveNode = new ListNode(Integer.MIN_VALUE);
  6. // ListNode current =head;
  7. ListNode previous = saveNode;
  8. while (head != null) {
  9. ListNode next = head.next;
  10. previous = saveNode; // 每次都从新链表的头结点进行遍历
  11. while (previous.next != null && previous.next.val < head.val) {
  12. previous = previous.next;
  13. }
  14. if (previous == saveNode) { // 链表为空时
  15. head.next = previous.next;
  16. previous.next=head;
  17. } else if(previous.next==null){ //尾部结点时
  18. head.next = previous.next;
  19. previous.next=head;
  20. }
  21. else { //中间插入
  22. ListNode nextnext=previous.next;
  23. head.next=nextnext;
  24. previous.next=head;
  25. }
  26. head = next;
  27. }
  28. return saveNode.next;
  29. }
  30. public void display(ListNode node) {
  31. while (node != null) {
  32. System.out.print(node.val + "\t");
  33. node = node.next;
  34. }
  35. }
  36. }
  37. class ListNode {
  38. int val;
  39. ListNode next;
  40. ListNode(int x) {
  41. val = x;
  42. next = null;
  43. }
  44. }

发表评论

表情:
评论列表 (有 0 条评论,319人围观)

还没有评论,来说两句吧...

相关阅读

    相关 插入排序算法

    如果数据存储在一段连续的内存上,比如数组中,插入排序算法的实现相信大家都已经非常熟悉,如果要对一个单链表进行插入排序,将会牵扯到大量指针操作。 同时,如果在实现的过

    相关 归并排序插入排序

    由于最近在学习数据结构和算法,在牛客网 的在线编程题上遇到了对链表的相关排序操作,发现自己对链表这块还是理解不够深入,以前做过对数组进行排序,但链表的操作要比数组复杂一些,毕竟

    相关 排序--归并排序

    要求在空间复杂度为O(1)的情况下对链表进行排序,在不考虑时间复杂度的情况下可以考虑冒泡排序,只对链表中的值进行操作,这样时间复杂度为O(n^2)。用归并排序,时间复杂度为O(

    相关 排序-归并

    链表排序,可以插入排序,我就不写了。 实现归并排序 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Con