题目要求:
对单链表进行从小到大排序,要求时间复杂度O(N*logN),空间复杂度O(1);
思路:
因时间复杂度要求为O(N*logN),简单排序(时间复杂度为O(N*N))均不可用,
故可以考虑归并排序的思想,归并排序对数组操作空间复杂度为O(n),但对链表为O(1),因为每次只在merge函数中创建了一个辅助的链表头结点ListNode temp=new ListNode(0);
归并排序的一般步骤为:
1)将待排序数组(链表)取中点并一分为二;
2)递归地对左半部分进行归并排序;
3)递归地对右半部分进行归并排序;
4)将两个半部分进行合并(merge),得到结果。
所以对应此题目,可以划分为三个小问题:
1)找到链表中点 (快慢指针思路,快指针一次走两步,慢指针一次走一步,快指针在链表末尾时,慢指针恰好在链表中点);
2)写出merge函数,即如何合并链表。
3)写出mergesort函数,实现上述步骤。
代码如下:
/*
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode sortList(ListNode head) {
//mergesort 空间复杂度为1;
if(head==null||head.next==null)
return head;
ListNode mid=findMid(head);
ListNode left=head;
ListNode right=mid.next;
mid.next=null;
left=sortList(left);
right=sortList(right);
head=merge(left,right);
return head;
}
//若链表节点为奇数,则找到中间节点;若链表节点为偶数,则找到两个中间节点的前一个;
public ListNode findMid(ListNode head)
{
ListNode prear=head;
ListNode pfront=head;
while(prear.next!=null&&prear.next.next!=null)
{
prear=prear.next.next;
pfront=pfront.next;
}
return pfront;
}
public ListNode merge(ListNode left,ListNode right)
{
if(left==null)
return right;
if(right==null)
return left;
//相较数组,并没有辅助数组的赋值操作,空间复杂度为O(1);
ListNode temp=new ListNode(0);
ListNode p=temp;
while(left!=null&&right!=null)
{
if(left.val<right.val)
{
p.next=left;
p=p.next;
left=left.next;
}else
{
p.next=right;
p=p.next;
right=right.next;
}
}
if(left!=null)
{
p.next=left;
}
if(right!=null)
{
p.next=right;
}
return temp.next;
}
}
对链表进行插入排序的正确方法是:新建一个辅助头节点help,遍历原来的链表,对原链表的每个节点找到新链表中适合插入位置的前指针,然后执行插入操作。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode insertionSortList(ListNode head) {
if(head == null||head.next ==null)
return head;
ListNode help=new ListNode(0);//辅助结点;
ListNode cur=head;
while(cur!=null)
{
ListNode next=cur.next;//记录下一个结点;
ListNode pre=help;//每次都遍历一次新链表,找到 cur结点所要插入的位置;
while(pre.next!=null&&pre.next.val<cur.val)
{
pre=pre.next;
}
cur.next=pre.next;
pre.next=cur;
cur=next;
}
return help.next;
}
}
链表的选择排序:
选择排序是从未排序的部分中找到最小值,然后放在排好序部分的尾部,逐渐将未排序的部分缩小,最后全部变为排好序的部分。
本题使用了两个辅助头节点。
- public class Solution {
- public ListNode insertionSortList(ListNode head) {
- if(head == null||head.next == null)
- return head;
- ListNode help=new ListNode(0);
- help.next=head;
- ListNode dump=new ListNode(0);
- ListNode temp=dump;
- while(help.next!=null)
- {
- ListNode min_pre=help;
- ListNode cur=help;
- while(cur.next!=null)
- {
- if(cur.next.val<min_pre.next.val)
- min_pre=cur;
- cur=cur.next;
- }
- temp.next=min_pre.next;
- min_pre.next=min_pre.next.next;
- temp=temp.next;
- }
- temp.next=null;
- return dump.next;
- }
- }