排序
一、插入类排序
插入类排序的基本思想:在一个已经排好序的记录子集的基础上,每一步将下一个待排序的记录有序的插入到已经排好序的记录子集中,知道将所有待排记录全部插入为止。
1、直接插入排序
算法思想:直接插入排序是一种最基本的插入排序方法,其基本操作是将第i个记录插入到前面i-1个已经排好序的记录中。
具体过程:将第i个记录的关键字Ki,顺次与前面记录的关键字Ki-1,Ki-2,...,K1进行比较,将所有关键字大于Ki的记录一次向后移动一个位置,直到遇见一个关键字小于或等于Ki的记录Kj,此时Kj后面必为空位置,将第i个记录插入空位置即可。直接插入是从i=2开始,默认将第一个记录视为已排好序的单元素集合。
该算法时间复杂度T(n)=O(N2),算法是稳定的。
package com.wyl.sort;
public class InsertSort<T extends Comparable<? super T>> {
/**
* 插入排序
* @param a
*/
public void inserSort(T[] a){
int j = 0;
for(int i=1;i<a.length;i++){ //外层循环控制比较的趟数
T temp = a[i];
/**
* 将此位置左边所有比它大的数向右边移动一位,将此值放在相应位置
* 每一轮只处理其左边的数据
*/
for(j=i;j>0 && (temp.compareTo(a[j-1])) < 0 ; j--) //内层循环控制元素移动的个数
a[j] = a[j-1];
a[j] = temp;
System.out.print("第" + i + "趟插入结果:");
for(T item:a){ //每进行插入排序一趟,打印出结果
System.out.print(item+"-->");
}
System.out.println();
}
}
public static void main(String[] args) {
InsertSort<Integer> insertSort = new InsertSort<Integer>();
Integer a[] = {12,24,53,21,33,12};
insertSort.inserSort(a);
}
}
2、希尔排序
算法:通过比较相距一定间隔的元素来工作;各趟比较所用的距离随着算法的进行而减小,知道比较相邻元素的最后一趟排序为止。由于这个原因希尔排序也叫作缩减增量排序。增量序列h1、h2...ht,只要h1=1即可。在使用增量hk的一趟排序之后,对于每一个i,我们都有a[i]<=a[i+hk];所有相隔hk的元素都被排序。
该算法时间复杂度T(n)=O(N1.5),算法是不稳定的。
package com.wyl.sort;
public class ShellSort<T extends Comparable<? super T>> {
/**
* 希尔排序
* @param a
*/
public void shellSort(T[] a){
if(a.length <= 0){
return;
}
int hk = a.length;
for(hk=hk/2;hk>0;){ //控制增量hk
for(int j=0; j<a.length-hk; j++){
if(a[j].compareTo(a[j+hk]) > 0){
//交换a[j]和a[j+hk]
T temp = a[j];
a[j] = a[j+hk];
a[j+hk] = temp;
}
}
System.out.print("增量为" + hk + "的希尔排序结果:");
for(T item:a){ //每进行希尔排序一趟,打印出结果
System.out.print(item+"-->");
}
System.out.println();
hk = hk/2; //每排序依次增量减半,h1=1
}
}
public static void main(String[] args) {
ShellSort<Integer> insertSort = new ShellSort<Integer>();
Integer a[] = {12,24,53,21,33,12};
insertSort.shellSort(a);
}
}
3、堆排序
算法:把待排序的记录关键字存放在数组r[1...n]中,将r看成是一颗完全二叉树的顺序表示,每个节点表示一个记录,第一个记录r[1]作为二叉树的根,以下各记录r[2]-r[n]依次逐层从左到右顺序排列,任意节点的左孩子r[2i],右孩子是r[2i+1],双亲是r[i/2]。对这颗完全二叉树调整建堆。
堆定义:节点的关键字值满足条件r[i].key >= r[2i].key且r[i].key >= r[2i+1].key的完全二叉树为大根堆。反之,如果这棵树的节点值小于或等于左孩子节点和右孩子节点值,为小根堆。
堆排序过程主要解决的问题:1、建初堆;2、重建堆。
1、建初堆
算法:将任意序列看成是对应的完全二叉树,由于叶节点可以视为单元素的堆,因而可以反复利用调整堆算法,自底向上逐层把所有子树调整为堆,直到将整个完全二叉树调整为堆。在完全二叉树中最后一个非叶节点位于n/2处,n为二叉树中节点个数。从n/2开始,逐层向上倒退,直到根节点。
2、重建堆
算法:首先将与堆相应的完全二叉树根节点的记录移出,该记录称为待调整记录。此时根节点相当于空节点,从空节点的左右子树中挑选一个关键字较大的记录,如果该记录的关键字大于待调整记录的关键字,则将该记录上移至空节点中。此时,原来那个关键字较大的节点相当于空节点,从空节点的左右子树中再选取较大的关键字值进行比较,重复上述过程,直到空节点左右子树的关键字均小于待调整记录的关键字。此时将待调整记录放入空节点即可。
package com.wyl.sort;
/**
* 堆排序
* @author wyl
*
* @param <T>
*/
public class HeapSort<T extends Comparable<? super T>> {
/**
* 1、创建初始堆
*/
public void createHeap(T[] a){
if(a.length <= 0){
return;
}
int n = a.length-1;
while(n>0){
for(int i = n/2 - 1;i>=0;i--){
//最后一个非叶节点的下标为n/2-1
a = rebuildHeap(a, i, n);
}
//每进行一次堆排序输出根节点,最后输出从大到小的顺序
System.out.print(a[0]+"、");
T temp = a[0];
a[0]=a[n]; //交换根节点和最后一个节点的值,并删除最后一个叶节点
a[n] = temp;
n--;
}
}
/**
* 2、重建堆
* 从最后一个非叶节点开始,从下至上依次调整堆
*/
public T[] rebuildHeap(T[] a, int i, int n){
if(n <= 0){
return null;
}
T temp = a[i]; //待调整节点
boolean finish = false; //调整完成标志
int li = 2 * i + 1; //左节点下标
while(li <= n - 1 && !finish){
if(li < n - 1 && a[li].compareTo(a[li+1])<0){ //节点存在右子树,且右子树值大于左子树
li = li+1;
}
if(temp.compareTo(a[li]) < 0){ //待调整节点值小于子树值,继续调整子树
a[i] = a[li];
i = li;
li = 2 * i + 1;
}else{
finish = true;
}
}
a[i] = temp;
return a;
}
public static void main(String[] args) {
HeapSort<Integer> insertSort = new HeapSort<Integer>();
Integer a[] = {12,24,53,21,33,27,3,58,4,14,5};
insertSort.createHeap(a);
}
}
4、快速排序
算法:从待排序记录序列中选取一个记录(通常选取第一个)为枢纽,其关键字设为K1,然后将其余关键字小于K1的记录移到前面,而将大于K1的移动到后面,结果将待排序记录分为两个子表,最后将关键字K1插入到其分界线的位置处。此过程为一趟快速排序。对分割后的子表继续按上述原则进行分割,直到所有的子表长度不超过1为止,此时待排序记录序列就变成一个有序表。
步骤:假设待划分序列为r[left],r[left+1]...r[right],具体实现上述划分过程可以设两个指针i,j分别为left,right。首先将基准记录r[left]移至变量x中,使得r[i]为空,然后反复进行如下扫描,直到i和j相遇。
(1)j从右向左扫描,直到r[j]<x,将r[j]移动到r[i]中,i++,此时r[j]为空;
(2)i从左向右扫描,直到r[i]>x,将r[i]移动到r[j]中,j--,此时r[i]为空;
当i和j相遇后,r[i]为空,r[i]左边所有记录小于r[i],右边所有记录大于r[i],将x放入r[i]中。对r[i]左边和右边的子表分别进行同样的方法进一步划分。
package com.wyl.sort;
/**
* 快速排序
* @author wyl
*
* @param <T>
*/
public class QuickSort<T extends Comparable<? super T>> {
/**
* 快速排序,
* @param a
*/
private void quickSort(T[] a) {
// TODO Auto-generated method stub
quickSort(a, 0, a.length-1);
}
private void quickSort(T[] a, int left, int right) {
if(left < right){
int pos = quickPaas(a, left, right); //分割点
quickSort(a, left, pos-1); //对左子表进行排序
quickSort(a, pos+1, right);//对右子表进行排序
}
}
/**
* 一趟快速排序
* @param a
* @param left
* @param right
*/
private int quickPaas(T[] a, int left, int right) {
T temp = a[left]; //存储第一个节点的值当作枢纽
int i = left;
int j = right;
while(i<j){
while(i<j && a[j].compareTo(temp) >= 0){
j--;
}
if(i<j){
a[i] = a[j];
i++;
}
while(i<j && a[i].compareTo(temp) <= 0){
i++;
}
if(i<j){
a[j] = a[i];
j--;
}
}
a[i] = temp;
return i;
}
public static void main(String[] args) {
QuickSort<Integer> insertSort = new QuickSort<Integer>();
Integer a[] = {12,24,53,21,33,27,3,58,4,14};
insertSort.quickSort(a);
System.out.print("快速排序后的结果:");
for(int k=0;k<a.length;k++){
System.out.print(a[k]+"、");
}
System.out.println();
}
}