1. 选择排序
方法:首先找到数组中最小的那个元素,其次,将它和数组的第一个元素交换位置(如果第一个元素就是最小的元素那就和它自己交换)。再在剩下的元素中找到最小的元素,将它和数组的第二个元素交换;一直反复直到整个数组排序。
特点: 运行时间和输入无关交换共次数为N,算法的时间效率取决于比较的次数 数据移动是最少 时间复杂度:N^2 空间复杂度: 1
2. 插入排序
方法: 找到最小的元素放到数组第一位,索引右边的元素往右移,在找到第二小的放到第二位,其余的再往右移;依次类推,直到索引到达数组最右端。当前索引左边的元素都是有序的,但是最终位置不确定,可能会再被移动。
特点: 运行时间取决于输入中元素的初始顺序。 最坏情况是进行N2/2次比较和N2/2次交换;最好的情况是进行N-1次比较和0次交换 时间复杂度:介于N和N2之间 空间复杂度: 1
3. 希尔排序
方法: 希尔排序是在插入排序的基础上改进的,交换不相邻的元素以对数组进行局部排序,并最终用插入排序将局部有序的数组进行排序。使数组中任意间隔为h的元素都是有序的
代码实现:
public class Shell{
public static void main(String[] args) {
char a[] = {'S','H','E','L','L','S','O','R','T','E','X','A','M','P','L','E'};
int h = 1;
int N = a.length;
System.out.println(N);
while(h<N/3)
h = 3*h +1;
System.out.println(h);
while(h>=1)
{ //将数组变为h有序
for(int i = h; i<N; i++) {
for(int j = i; j>=h&&a[j]<=a[j-h];j -= h) {
char c = a[j];
a[j] = a[j-h];
a[j-h] = c;
}
}
h = h/3;
}
for (char c : a) {
System.out.print(c + " ");
}
}
}
特点: 希尔排序的次数不会超过N的若干倍乘以递增序列的长度 适用于中等数组 时间复杂度:NlogN 空间复杂度:1
4. 归并排序
归并: 将两个有序的数组归并成一个更大的有序数组 先递归地把它分成两半分别排序,然后再将结果归并起来。 先递归拆分排序,逐级归并结果最终完成排序。 例: 如果要将a[0…15]排序,排序方法会先调用自己将a[0…7]排序,再在其中调用自己将a[0…3]和a[0…1]排序。在将a[1]和a[0]分别排序之后,才最终开始将a[1]和a[0]归并。第二次归并是a[2]和a[3],然后是a[2…3]和a[0…1],依次类推。
代码实现:
public class Merge {
private static int[] b; // 归并所需要的辅助数组
public static void sort(int[] a) {
if(a==null)
throw new NullPointerException("数组为空");
b = new int[a.length];
sort(a,0,a.length-1);
}
public static void merge(int[] a, int start, int mid ,int end) {
int i = start, j = mid+1;
for(int k=start; k<=end; k++) { //将数组复制到辅助数组中
b[k] = a[k];
}
for(int k = start; k<=end ; k++) {
if(i>mid)
a[k] = b[j++];
else if(j>end)
a[k] = b[i++];
else if(b[j]<=b[i])
a[k] = b[j++];
else
a[k] = b[i++];
}
}
private static void sort(int[] a, int start, int end) {
// 将数组a排序
if (end <= start)
return;
int mid = (start + end) / 2;
// 递归
sort(a, start, mid);
sort(a, mid + 1, end);
merge(a, start, mid, end);
}
public static void main(String[] args) {
int[] array = {34,55,23,12,66,16,77,76,49,99,1};
sort(array);
System.out.println(Arrays.toString(array));
}
}
性质: 保证任意长度为N的数组排序所需时间和NlogN成正比; 缺点: 所需的额外空间和N成正比。 时间复杂度 NlgN 空间复杂度: N
5. 快速排序
排序: 将一个数组切分成两个数组,将两部分独立排序。 切分过程: 先随意的取a[lo]作为切分元素,即那个将会被排定的元素,然后我们从数组的左端开始向右扫描找到一个大于等于它的元素,再从数组右端开始向左扫描直到找到一个小于等于它的元素,然后交换两个元素的位置,保证左指针i左侧的元素都小于拆分元素,右指针j右侧的元素都不小于切分元素。当两个指针相遇时,我们只需要将切分元素a[lo]和左子数组最右侧元素交换然后返回j即可。
核心排序代码:
int partition(int[] a,int start, int end){
int i = start;
int j = end + 1;
int v = a[start];
while(true){
while(a[++i] >= v)
break;
while(a[--j] <= v)
break;
if(i>=j)
break;
exch(a, i,j);
}
exch(a,start,j);
return j;
}
void sort(int a[], int start, int end){
int vo = partirion(a,start,end);
sort(a,start,vo);
sort(a,vo+1,end);
}
性质: 是原地排序(只需要一个很小的辅助栈),且将长度为N的数组排序所需的时间和NlgN成正比。将一个N个数的数组排序,快速排序平均需要 2NlnN次比较。 时间复杂度:NlogN 空间复杂度:lgN
6. 堆排序
堆构造: 创建一个指针指向数组中的中间位置,然后从左至右遍历数组,用swim()函数保证指针的左侧是一颗堆有序的完全树即可。 具体实现是从右至左用sink()函数构造子堆。 如果一个结点的子节点都已经是堆了,那么在该节点上调用sink()函数就可以把他变成一个堆。
排序: 分为两个阶段
- 首先将原始数组重新组织安排进一个堆中(建立一个有序的数组并使最大元素位于数组的开头 ,有序指的是指针的左边)
- 然后在下沉排序阶段,从堆中按递减顺序取出所有元素并得到排序结果
代码实现:
public class MaxPQ<Key extends Comparable<Key>> {
private Key[] pq;
private int N = 0;
public MaxPQ(int maxN) { // 基于优先队列实现的二叉堆 根节点的位置是1
pq = (Key[]) new Comparable[maxN + 1];
}
public boolean isEmpty() {
return N == 0;
}
public int size() {
return N;
}
public void insert(Key v) { // 将新元素插入到队尾并让新元素上浮到合适位置
pq[++N] = v;
swim(pq,N);
}
public Key delMax() { // 从数组顶端删去最大元素并将数组的最后一个元素放到顶端,减小堆的大小并让这个元素下沉到合适位置
Key max = pq[1];
exch(pq, 1, N--);
pq[N + 1] = null;
sink(pq, 1, N);
return max;
}
public static void swim(Comparable[] a,int k) {
while(k>1 && less(k/2,k))
{
exch(a,k,k/2);
k=k/2;
}
}
public static void sort(Comparable[] a)
{
int N = a.length-1;
//构造大根堆
//跳过只有一个结点的堆即大小为1的堆,从数组的中间开始扫描,调用sink()方法,层层递减,最后在1位置上调用sink()方法后结束。
//此次扫描目的是构造一个堆有序的数组并使最大元素位于数组的开头(次大的元素在附近)
for(int k = N / 2; k >= 1; k--)
{
sink(a,k,N);
show(a);
}
System.out.println("下沉开始");
//下沉排序
//1.每次排序都先将最大的元素与最后一个元素交换位置,接着缩小数组,对除去最后一个元素的堆进行下沉排序
//2.对缩小后的数组进行下沉排序,若数组长度大于1,则跳转到第一步继续执行
while(N > 1)
{
exch(a,1,N--);
sink(a,1,N);
}
}
//下沉排序
private static void sink(Comparable[] a,int i,int len)
{
while(i*2 <= len)
{
int j = i * 2;
if(j < len && less(a[j],a[j+1]))
{
j++;
}
if(!less(a[i],a[j]))
{
break;
}
exch(a,i,j);
i = j;
}
}
private static boolean less(Comparable v,Comparable w)
{
return v.compareTo(w) < 0;
}
private static void exch(Comparable[] v,int i, int j)
{
Comparable temp = v[i];
v[i] = v[j];
v[j] = temp;
}
public static void show(Comparable[] a)
{
for(int i = 1; i < a.length; i++)
{
System.out.print(a[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int N = 12;
Integer[] a = new Integer[N];
for (int i = 1; i <= N - 1; i++) {
a[i] = (int) (Math.random() * 10 + 1);
}
MaxPQ mp = new MaxPQ<>(10);
for (int i = 1; i < mp.pq.length; i++) {
mp.pq[i] = (int) (Math.random() * 10 + 1);
}
//show(mp.pq);
//sort(mp.pq);
show(a);
sort(a);
show(a);
//show(mp.pq);
//mp.delMax();
//show(mp.pq);
}
}
各种排序算法的性能特点: