​welcome to my blog​

程序员代码面试指南第二版 27.单链表的选择排序

题目描述

给定一个无序单链表,实现单链表的选择排序(按升序排序)。

输入描述:
第一行一个整数 n,表示单链表的节点数量。
第二行 n 个整数 val 表示单链表的各个节点。

输出描述:
在给出的函数内返回给定链表的头指针。

示例1

输入
5
1 3 2 4 5

输出
1 2 3 4 5

第一次做; 选择排序思想: 从未排序区域找出最小值, 将最小值放到排序区域的最后, 当未排序区域为空时, 元素全部有序; 这道题最麻烦的地方在于指针的操作以及循环条件, 考验代码功底, 要小心不能写错

import java.util.Scanner;

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = Integer.parseInt(sc.nextLine());
//input check
if(n<1)
return;
String[] str = sc.nextLine().split(" ");
//创建链表
ListNode head = new ListNode(Integer.parseInt(str[0]));
ListNode curr = head;
for(int i=1; i<n; i++){
curr.next = new ListNode(Integer.parseInt(str[i]));
curr = curr.next;
}
//execute; 链表的选择排序
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode dummy2 = new ListNode(0);
ListNode p = dummy2;

ListNode minLeft = dummy, min = head;

ListNode currLeft = dummy;
curr = head;
while(dummy.next!=null){
while(curr!=null){
if(curr.val < min.val){
minLeft = currLeft;
min = curr;
}
currLeft = curr;
curr = curr.next;
}
//从原链表中删除值最小的节点
minLeft.next = min.next;
//细节; 尾结点会用到, 但不知道哪个是尾结点, 所以就全加上这句即可
min.next = null;
//将选出的最小节点放到有序部分的最后
p.next = min;
//有序部分扩
p = p.next;
//更新minLeft, min
minLeft = dummy;
min = dummy.next;
//更新curr
curr = dummy.next;
}
//打印结果
curr = dummy2.next;
while(curr!=null){
System.out.print(curr.val + " ");
curr = curr.next;
}
}
public static class ListNode{
int val;
ListNode next;
ListNode(int val){
this.val = val;
}
}
}

左神的解法; 将未排序部分值最小的节点的获取单独写成一个函数, 这样就不用将一堆指针变量写在一块了, 看着整洁些; 没有像我一样创建两个dummy节点; 最需要注意的地方是如何维护未排序部分的头结点: ​​curr = curr == small ? curr.next : curr​​, 只有curr恰好指向small时才更新

import java.util.Scanner;

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = Integer.parseInt(sc.nextLine());
//input check
if(n<1)
return;
String[] str = sc.nextLine().split(" ");
//创建链表
ListNode head = new ListNode(Integer.parseInt(str[0]));
ListNode curr = head;
for(int i=1; i<n; i++){
curr.next = new ListNode(Integer.parseInt(str[i]));
curr = curr.next;
}
//execute; 链表的选择排序
head = selectionSort(head);
//打印结果
curr = head;
while(curr!=null){
System.out.print(curr.val + " ");
curr = curr.next;
}
}
//选择排序
public static ListNode selectionSort(ListNode head){
//排序部分的尾结点
ListNode tail = null;
//未排序部分的头结点
ListNode curr = head;
//未排序部分值最小的节点的前一个节点
ListNode smallPre = null;
//未排序部分值最小的节点
ListNode small = null;
while(curr != null){
small = curr;
smallPre = getSmallestPreNode(curr);
//值最小的节点前一个节点为空的话, 说明值最小的节点是当前的头结点, 不用执行断链操作
if(smallPre != null){
small = smallPre.next;
smallPre.next = small.next;
}
//只有curr是small时才更新curr; 否则保持不变; 这算是个技巧吧
curr = small == curr ? curr.next : curr;
if(tail == null)
head = small;
else
tail.next = small;
tail = small;
}
return head;
}
//获取未排序区域中值最小的节点的前一个节点
public static ListNode getSmallestPreNode(ListNode head){
ListNode smallPre = null;
ListNode small = head;
ListNode pre = head;
ListNode curr = head.next;
while(curr != null){
if(curr.val < small.val){
smallPre = pre;
small = curr;
}
pre = curr;
curr = curr.next;
}
return smallPre;
}
public static class ListNode{
int val;
ListNode next;
ListNode(int val){
this.val = val;
}
}
}