1、选择排序
一种最简单的排序方式是这样的:首先,找到数组中的那个最小的元素;其次,将它和数组中的第一个元素进行交换位置(如果第一个最小,就和自己交换);再次,在剩下的元素中找到最小的元素,将它与数组的第二个元素进行交换位置。如此往复,直到将整个数组排序。这种方法叫做选择排序,因为它在不断地选择剩余元素中的最小者。
选择排序有两个很鲜明的特点是:①运行时间和输入无关;②数据移动是最少的。[每次交换都会改变两个数组元素的值,因此选择排序用了N次交换-----交换次数和数组的大小是线性关系。]
import java.util.Scanner;
//读取字符串,将他们排序并输出
public class Selection {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
while(sc.hasNext()){
String str=sc.nextLine();
String[] ss=str.split(" ");
sort(ss);//对字符进行排序
show(ss);//显示排序后的结果
}
sc.close();
}
private static void show(String[] ss) {
for (int i = 0; i < ss.length; i++) {
System.out.print(ss[i]+" ");
}
System.out.println();
}
private static void sort(String[] ss) {
//将a按升序排列
int N=ss.length;
for (int i = 0; i < N; i++) {
int min=i;
for (int j = i+1; j < N; j++) {
if (less(ss[j],ss[min])) {
min=j;
}
}
exch(ss,i,min);
}
}
private static void exch(Comparable[] a, int i, int j) {//交换两个元素
Comparable t=a[i];
a[i]=a[j];
a[j]=t;
}
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w)<0;
}
}
2、插入排序
为了给要插入的元素腾出空间,我们需要将其余所有元素在插入之前都向右移动一位,这种算法叫做插入排序。
与选择排序一样,当前索引左边的所有元素都是有序的,但他们的最终位置还是不确定的,为了给更小的元素腾出空间,他们可能会被移动。但当索引到达右端时,数组排序就完成了。
与选择排序不同的是,插入排序所需的时间取决于输入中元素的初始顺序。已经有序或者接近有序的数组进行排序将会比对随机顺序的数组或者逆序的数组进行排序要快得多。
插入排序对于实际应用中的某些类型的非随机数组很有效。
当倒置的数量很少时,插入排序很可能比其他任何算法都要快。
import java.util.Scanner;
//读取字符串,将他们排序并输出
public class Insertion {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
while(sc.hasNext()){
String str=sc.nextLine();
String[] a=str.split(" ");
sort(a);//对字符进行插入排序
show(a);//显示排序后的结果
}
sc.close();
}
private static void show(String[] a) {
for (int i = 0; i < a.length; i++) {
System.out.print(a[i]+" ");
}
System.out.println();
}
private static void sort(String[] a) {
//将a按升序排列
int N=a.length;
for (int i = 1; i < N; i++) {
//将a[i]插入到a[i-1],a[i-2]....之中
for (int j = i; j >0&&less(a[j], a[j-1]); j--) {
exch(a, j, j-1);
}
System.out.print("每次插入后的数组:");
show(a);
}
}
private static void exch(Comparable[] a, int i, int j) {//交换两个元素
Comparable t=a[i];
a[i]=a[j];
a[j]=t;
}
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w)<0;
}
}
对于随机排序额无重复主键的数组,插入排序和选择排序的运行时间是平方级别的,两者之比应该是一个较小的常数。(插入排序略快)
3、希尔排序
希尔排序是插入排序的一种又称“缩小增量排序”,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。希尔排序是把记录按下标的一定增强分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减到1时,整个文件恰被分成一组,算法便终止。
希尔排序为了加速简单地改进了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。
希尔排序的思想是使数组中任意间隔为h的元素都有序。这样的数组被称为h有序数组。
实现希尔排序的一种方法是对于每个h,用插入排序将h个子数组独立地排序。
-
(1)插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。
-
(2)但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
-
该方法实质上是一种分组插入方法。
希尔排序更高效的原因是它权衡了子数组的规模和有序性。排序之初,各个子数组都很短,排序之后子数组都是部分有序的,这两种情况都很适合插入排序。
和选择排序及插入排序形成对比的是,希尔排序也可以用于大型数组。它对任意排序(不一定是随机的)的数组表现也很好。实际上,对于一个给定的递增序列,构造一个使希尔排序运行缓慢的数组并不容易。通过比较,希尔排序比插入排序和选择排序要快得多,并且数组越大,优势越大。
import java.util.Scanner;
/**
* 希尔排序:思想是使数组中任意间隔为h的元素都是有序的。
*/
public class Shell {
public static void main(String[] args) {
//从标准输入读取字符串,将他们排序并输出
Scanner scanner=new Scanner(System.in);
while(scanner.hasNext()){
String str=scanner.nextLine();
String[] a=str.split(" ");
if (str!=null) {
sort(a);//排序算法
assert isSorted(a);//测试数组是否有序
show(a);//打印数组元素
}
}
scanner.close();
}
@SuppressWarnings("rawtypes")
private static void sort(Comparable[] a) {
int N=a.length;
int h=1;
while(h<N/3){
h=3*h+1;
}
while(h>=1){
//将数组变为h有序
for (int i = h; i < N; i++) {
//将a[i]插入到a[i-h],a[i-2*h],a[i-3*h]...之中
for (int j = i; j >=h&&less(a[j], a[j-h]); j-=h) {
exch(a, j, j-h);
}
}
h=h/3;
}
}
@SuppressWarnings("rawtypes")
private static boolean isSorted(Comparable[] a) {
for (int i = 1; i < a.length; i++) {
if (less(a[i],a[i-1])) {
return false;
}
}
return false;
}
@SuppressWarnings("rawtypes")
private static void show(Comparable[] a) {
for (int i = 0; i < a.length; i++) {
System.out.print(a[i]+" ");
}
System.out.println();
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w)<0;
}
@SuppressWarnings({ "rawtypes", "unused" })
private static void exch(Comparable[] a,int i,int j){
Comparable t=a[i];
a[i]=a[j];
a[j]=t;
}
}