147. 对链表进行插入排序

对链表进行插入排序。

img

插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。 每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。

插入排序算法:

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

示例 1:

1
2
输入: 4->2->1->3
输出: 1->2->3->4

示例 2:

1
2
输入: -1->5->3->4->0
输出: -1->0->3->4->5

看起来很简单的插入排序花了我一晚上.

插入排序不需要多讲,题面总结得很精辟,每一步将一个待排序的数据插入到前面已经排好序的有序序列中,直到插完所有元素为止。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。

插入排序的平均时间复杂度也是 O(n^2),空间复杂度为常数阶 O(1),具体时间复杂度和数组的有序性也是有关联的。

插入排序中,当待排序数组是有序时,是最优的情况,只需当前数跟前一个数比较一下就可以了,这时一共需要比较 N-1 次,时间复杂度为 O(N)。最坏的情况是待排序数组是逆序的,此时需要比较次数最多,最坏的情况是 O(n^2)

但是 链表就不能简单移动了,而是要做到插入(把节点插入相应的地方),遍历找到要插入的地方以后,还要实现交换操作(和数组最大的不同也是最难的地方)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    public static ListNode insertionSortList(ListNode head) {

        ListNode todo=head.next;
        /*最坑的地方是在画图,总结到一个经验,画图其实不如调试,调试起来看变化,再画图微调最好,傻乎乎画图最后会绕死进去
         * 添加一个表头节点非常有必要,这还是看题解发现的,不然插入时只能分情况讨论越来越复杂
         * pre节点防止断裂,注意要及时更新pre节点
         * todo这种变量名就不知所云,下次记得用cur
         * */

        ListNode dummyHead=new ListNode(0);
        dummyHead.next=head;
        ListNode todohead=dummyHead;
        ListNode todopre=dummyHead.next;
        while (todo!=null){

            while(todohead.next!=null&& todo.val>todohead.next.val){
                todohead=todohead.next;

            }
            if(todo==todohead.next){
/*不能用值的比较!!!!,这个是作为循环终止的条件(使用continue直接下一步,防止进行5步交换):等到todohead.next循环到todo时,让todo后移,注意不仅仅要使得todo后移,todopre也要后移,todohead要归零*/
                todo=todo.next;
                todopre=todopre.next;
                todohead=dummyHead;
                continue;
            }
            todopre.next=todo.next;
            todo.next=todohead.next;
            todohead.next=todo;
            todo=todopre.next;
            todohead=dummyHead;

            //这5步画图吧
        }
        return dummyHead.next;
    }