视觉直观感受7种排序算法

1. 快速排序

介绍:

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来,且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性。

步骤:

  1. 从数列中挑出一个元素,称为 “基准”(Pivot),
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

排序效果:

视觉直观感受7种常用排序算法

 

举个例子

如无序数组[6 2 4 1 5 9]

a),先把第一项[6]取出来,

用[6]依次与其余项进行比较,

如果比[6]小就放[6]前边,2 4 1 5都比[6]小,所以全部放到[6]前边

如果比[6]大就放[6]后边,9比[6]大,放到[6]后边,//6出列后大喝一声,比我小的站前边,比我大的站后边,行动吧!霸气十足~

一趟排完后变成下边这样:

排序前 6 2 4 1 5 9

排序后 2 4 1 5 6 9


b),对前半拉[2 4 1 5]继续进行快速排序

重复步骤a)后变成下边这样:

排序前 2 4 1 5

排序后 1 2 4 5

前半拉排序完成,总的排序也完成:

排序前:[6 2 4 1 5 9]

排序后:[1 2 4 5 6 9]

排序结束

public class QuickSort {

    public static void main(String[] args)
    {
        int[] x = { 6, 2, 4, 1, 5, 9 };
        quick_sort1(x, 0, x.length - 1);
        for(int item : x)
        {
        	System.out.print(item+","); 
        }
    }
	 static int partition(int[] unsorted, int low, int high)
    {
        int pivot = unsorted[low];
        while (low < high)
        {
            while (low < high && unsorted[high] > pivot) high--;
            unsorted[low] = unsorted[high];
            while (low < high && unsorted[low] <= pivot) low++;
            unsorted[high] = unsorted[low];
        }
        unsorted[low] = pivot;
        return low;
    }

    static void quick_sort1(int[] unsorted, int low, int high)
    {
        int loc = 0;
        if (low < high)
        {
            loc = partition(unsorted, low, high);
            quick_sort1(unsorted, low, loc - 1); 
            quick_sort1(unsorted, loc + 1, high);
        }
    }
//low=0 high =5  pivot=6 first:5 2 4 1 6 9  
//low=0 high=3   pivot=5 second:1 2 4 5 6 9
//low=0 high=2   pivot=1 third:1 2 4 5 6 9
}


2. 归并排序

介绍:

归并排序(Merge sort,台湾译作:合并排序)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用

步骤:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4. 重复步骤3直到某一指针达到序列尾
  5. 将另一序列剩下的所有元素直接复制到合并序列尾

排序效果:

视觉直观感受7种常用排序算法

 

 归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。

//将有序数组a[]和b[]合并到c[]中
void MemeryArray(int a[], int n, int b[], int m, int c[])
{
	int i, j, k;

	i = j = k = 0;
	while (i < n && j < m)
	{
		if (a[i] < b[j])
			c[k++] = a[i++];
		else
			c[k++] = b[j++]; 
	}

	while (i < n)
		c[k++] = a[i++];

	while (j < m)
		c[k++] = b[j++];
}

可以看出合并有序数列的效率是比较高的,可以达到O(n)。

解决了上面的合并有序数列问题,再来看归并排序,其的基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?

可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递的分解数列,再合数列就完成了归并排序。

import java.io.Console;


public class MergeSort {
	static void merge(int[] unsorted, int first, int mid, int last, int[] sorted)
    {
        int i = first, j = mid;
        int k = 0;
        while (i < mid && j < last)
            if (unsorted[i] < unsorted[j])
                sorted[k++] = unsorted[i++];
            else
                sorted[k++] = unsorted[j++];

        while (i < mid)
            sorted[k++] = unsorted[i++];
        while (j < last)
            sorted[k++] = unsorted[j++];

        for (int v = 0; v < k; v++)
            unsorted[first + v] = sorted[v];
    }

    static void merge_sort(int[] unsorted, int first, int last, int[] sorted)
    {
        if (first + 1 < last)
        {
            int mid = (first + last) / 2; 
            //Console.class("{0}-{1}-{2}", first, mid, last);
            merge_sort(unsorted, first, mid, sorted); //左边有序 
            merge_sort(unsorted, mid, last, sorted);//右边有序
            merge(unsorted, first, mid, last, sorted);//再将二个有序数列合并  
        }
    }
    public static void main(String[] args)
    {
        int[] x = { 6, 2, 4, 1, 5, 9 };
        int[] sorted = new int[x.length];
        merge_sort(x, 0, x.length, sorted);
        for(int item : x)
        {
        	System.out.print(item+" "); 
        }
    }
    // 先merge_sort  1:first =0 last= 6 mid=3*** 2:first =0 last =3 mid=1**first =1 last =3 mid =2
    //1:i=0 j =2 k=0   2:i=2 j=2 k=1 sort[0]=2  3:i=2 j=3 k=2 sort[1]=4  4: v=1
}

举例

无序数组[6 2 4 1 5 9]

先看一下每个步骤下的状态,完了再看合并细节

第一步 [6 2 4 1 5 9]原始状态

第二步 [2 6] [1 4] [5 9]两两合并排序,排序细节后边介绍

第三步 [1 2 4 6] [5 9]继续两组两组合并

第四步 [1 2 4 5 6 9]合并完毕,排序完毕

输出结果[1 2 4 5 6 9]

合并细节

详细介绍第二步到第三步的过程,其余类似

第二步:[2 6] [1 4] [5 9]

两两合并,其实仅合并[2 6] [1 4],所以[5 9]不管它,

原始状态

第一个数组[2 6]

第二个数组[1 4]

--------------------

第三个数组[...]

 

第1步,顺序从第一,第二个数组里取出一个数字:2和1

比较大小后将小的放入第三个数组,此时变成下边这样

第一个数组[2 6]

第二个数组[4]

--------------------

第三个数组[1]

 

第2步,继续刚才的步骤,顺序从第一,第二个数组里取数据,2和4,

同样的比较大小后将小的放入第三个数组,此时状态如下

第一个数组[6]

第二个数组[4]

--------------------

第三个数组[1 2]

 

第3步,再重复前边的步骤变成,将较小的4放入第三个数组后变成如下状态

第一个数组[6]

第二个数组[...]

--------------------

第三个数组[1 2 4]

 

第4步,最后将6放入,排序完毕

第一个数组[...]

第二个数组[...]

--------------------

第三个数组[1 2 4 6]

 

[ 1 2 4 6 ]与[ 5 9 ]的合并过程与上边一样,不再分解



3. 堆排序

介绍:

堆积排序(Heapsort)是指利用这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

步骤:

(比较复杂,自己上网查吧)

排序效果:

视觉直观感受7种常用排序算法

 

经典排序算法 - 堆排序Heap sort

堆排序有点小复杂,分成三块

第一块,什么是堆,什么是最大堆

第二块,怎么将堆调整为最大堆,这部分是重点

第三块,堆排序介绍


第一块,什么是堆,什么是最大堆

什么是堆

这里的堆(二叉堆),指得不是堆栈的那个堆,而是一种数据结构。

堆可以视为一棵完全的二叉树,完全二叉树的一个“优秀”的性质是,除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示,每一个结点对应数组中的一个元素.

数组与堆之间的关系

Heap1

二叉堆一般分为两种:最大堆和最小堆。

什么是最大堆

堆中每个父节点的元素值都大于等于其孩子结点(如果存在),这样的堆就是一个最大堆

因此,最大堆中的最大元素值出现在根结点(堆顶)

节点与数组索引关系

对于给定的某个结点的下标i,可以很容易的计算出这个结点的父结点、孩子结点的下标,而且计算公式很漂亮很简约

Image(2)

第二块,怎么将堆调整为最大堆,这部分是重点

整个过程如下图所示

在4,14,7这个小堆里边,父节点4小于左孩子14,所以两者交换

在4,2,8这个小堆里边,父节点4小于右孩子8,所以两者交换

Heap3

上图展示了一趟调整的过程,这个过程递归实现,直到调整为最大堆为止

第三块,堆排序介绍

堆排序就是把堆顶的最大数取出,

将剩余的堆继续调整为最大堆,具体过程在第二块有介绍,以递归实现

剩余部分调整为最大堆后,再次将堆顶的最大数取出,再将剩余部分调整为最大堆,这个过程持续到剩余数只有一个时结束

下边三张图详细描述了整个过程

Heap4

Heap5

Heap6 

代码:

public class Demo {
public static void main(String[] args)
{
    int[] array=new int[]{1,2,3,4,7,8,9,10,14,16};
    MaxHeap heap=new MaxHeap(array); //y引用一个MaxHeap类
    System.out.println("执行最大堆化前堆的结构:");
    printHeapTree(heap.heap);
    heap.BuildMaxHeap();
    System.out.println("执行最大堆化后堆的结构:");
    printHeapTree(heap.heap);
    heap.HeapSort();
    System.out.println("执行堆排序后数组的内容");
    printHeap(heap.heap);
    
}
private static void printHeapTree(int[] array)
{
    for(int i=1;i<array.length;i=i*2)
    {
        for(int k=i-1;k<2*(i)-1&&k<array.length;k++)
        {
            System.out.print(array[k]+" ");
        }
        System.out.println();
    }    
}
private static void printHeap(int[] array)
{
    for(int i=0;i<array.length;i++)
    {
        System.out.print(array[i]+" ");
    }
}


}

public class MaxHeap {
int[] heap;
int heapsize;
public MaxHeap(int[] array)
{
    this.heap=array;    
    this.heapsize=heap.length;
}
public void BuildMaxHeap()
{
    for(int i=heapsize/2-1;i>=0;i--)
    {
        Maxify(i);//依次向上将当前子树最大堆化
    }
}
public void HeapSort()
{
    for(int i=0;i<heap.length;i++)
    {
        //执行n次,将每个当前最大的值放到堆末尾
        int tmp=heap[0];
        heap[0]=heap[heapsize-1];
        heap[heapsize-1]=tmp;
        heapsize--;
        Maxify(0);
    }
}
public void Maxify(int i)
{
    int l=Left(i);
    int r=Right(i);
    int largest;
    
    if(l<heapsize&&heap[l]>heap[i])
        largest=l;
    else
        largest=i;
    if(r<heapsize&&heap[r]>heap[largest])
        largest=r;
    if(largest==i||largest>=heapsize)//如果largest等于i说明i是最大元素 largest超出heap范围说明不存在比i节点大的子女
        return ;
    int tmp=heap[i];//交换i与largest对应的元素位置,在largest位置递归调用maxify
    heap[i]=heap[largest];
    heap[largest]=tmp;
    Maxify(largest);
}
public void IncreaseValue(int i,int val)
{
    heap[i]=val;
    if(i>=heapsize||i<=0||heap[i]>=val)
        return;
    int p=Parent(i);
    if(heap[p]>=val)
        return;
    heap[i]=heap[p];
    IncreaseValue(p, val);
}

private int Parent(int i)
{
    return (i-1)/2;
}
private int Left(int i)
{
    return 2*(i+1)-1;
}
private int Right(int i)
{
    return 2*(i+1);
}
}
结果
执行最大堆化前堆的结构:
1 
2 3 
4 7 8 9 
10 14 16 
执行最大堆化后堆的结构:
16 
14 9 
10 7 8 3 
1 4 2 
执行堆排序后数组的内容
1 2 3 4 7 8 9 10 14 16 




4. 选择排序

介绍:

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。

排序效果:

视觉直观感受7种常用排序算法

经典排序算法 - 选择排序Selection sort

顾名思意,就是直接从待排序数组里选择一个最小(或最大)的数字,每次都拿一个最小数字出来,

顺序放入新数组,直到全部拿完

再简单点,对着一群数组说,你们谁最小出列,站到最后边

然后继续对剩余的无序数组说,你们谁最小出列,站到最后边

再继续刚才的操作,一直到最后一个,继续站到最后边,现在数组有序了,从小到大

举例

先说看每步的状态变化,后边介绍细节,现有无序数组[6 2 4 1 5 9]

第一趟找到最小数1,放到最前边(与首位数字交换)

交换前:| 6 | 2 | 4 | 1 | 5 | 9 |

交换后:| 1 | 2 | 4 | 6 | 5 | 9 |

第二趟找到余下数字[2 4 6 5 9]里的最小数2,与当前数组的首位数字进行交换,实际没有交换,本来就在首位

交换前:| 1 | 2 | 4 | 6 | 5 | 9 |

交换后:| 1 | 2 | 4 | 6 | 5 | 9 |


第三趟继续找到剩余[4 6 5 9]数字里的最小数4,实际没有交换,4待首位置无须交换

第四趟从剩余的[6 5 9]里找到最小数5,与首位数字6交换位置

交换前:| 1 | 2 | 4 | 6 | 5 | 9 |

交换后:| 1 | 2 | 4 | 5 | 6 | 9 |

第五趟从剩余的[6 9]里找到最小数6,发现它待在正确的位置,没有交换

排序完毕输出正确结果[1 2 4 5 6 9]

第一趟找到最小数1的细节

当前数组是| 6 | 2 | 4 | 1 | 5 | 9 |

先把6取出来,让它扮演最小数

当前最小数6与其它数一一进行比较,发现更小数就交换角色

当前最小数6与2比较,发现更小数,交换角色,此时最小数是2,接下来2与剩余数字比较

当前最小数2与4比较,不动

当前最小数2与1比较,发现更小数,交换角色,此时最小数是1,接下来1与剩余数字比较

当前最小数1与5比较,不动

当前最小数1与9比较,不动,到达末尾

当前最小数1与当前首位数字进行位置交换,如下所示

交换前:| 6 | 2 | 4 | 1 | 5 | 9 |

交换后:| 1 | 2 | 4 | 6 | 5 | 9 |

完成一趟排序,其余步骤类似

public class SelectSort {
	static void selection_sort(int[] unsorted)
    {
        for (int i = 0; i < unsorted.length; i++)
        {
            int min = unsorted[i], min_index = i;
            for (int j = i; j < unsorted.length; j++)
            {
                if (unsorted[j] < min)
                {
                    min = unsorted[j];
                    min_index = j;
                }
            }
            if (min_index != i)
            {
                int temp = unsorted[i];
                unsorted[i] = unsorted[min_index];
                unsorted[min_index] = temp;
            }
        }
    }

    public static void main(String[] args)
    {   long startTime = System.nanoTime(); 
        int[] x = { 6, 2, 4, 1, 5, 9 };
        selection_sort(x);
        for(int item : x)
        {
        	System.out.print(item+" "); 
        }
        long estimatedTime = System.nanoTime() - startTime; 
        System.out.println(estimatedTime);
    }
}

5. 冒泡排序

介绍:

冒泡排序(Bubble Sort,台湾译为:泡沫排序或气泡排序)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

步骤:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

排序效果:

视觉直观感受7种常用排序算法

 

public class BubbleSort {
	static void bubble_sort(int[] unsorted)
    {
        for (int i = 0; i < unsorted.length; i++)
        {
            for (int j = i; j < unsorted.length; j++)
            {
                if (unsorted[i] > unsorted[j])
                {
                    int temp = unsorted[i];
                    unsorted[i] = unsorted[j];
                    unsorted[j] = temp;
                }
            }
        }
    }

    public static void main(String[] args)
    {   
    	long startTime = System.nanoTime();  
        int[] x = { 6, 2, 4, 1, 5, 9 };
        bubble_sort(x);
        for(int item : x)
        {
        	System.out.print(item+" "); 
        }
        long estimatedTime = System.nanoTime() - startTime; 
        System.out.println(estimatedTime);
    }
}

例子为从小到大排序,

原始待排序数组| 6 | 2 | 4 | 1 | 5 | 9 |


第一趟排序(外循环)

第一次两两比较6 > 2交换(内循环)

交换前状态| 6 | 2 | 4 | 1 | 5 | 9 |

交换后状态| 2 | 6 | 4 | 1 | 5 | 9 |

 

第二次两两比较,6 > 4交换

交换前状态| 2 | 6 | 4 | 1 | 5 | 9 |

交换后状态| 2 | 4 | 6 | 1 | 5 | 9 |

 

第三次两两比较,6 > 1交换

交换前状态| 2 | 4 | 6 | 1 | 5 | 9 |

交换后状态| 2 | 4 | 1 | 6 | 5 | 9 |

 

第四次两两比较,6 > 5交换

交换前状态| 2 | 4 | 1 | 6 | 5 | 9 |

交换后状态| 2 | 4 | 1 | 5 | 6 | 9 |

 

第五次两两比较,6 < 9不交换

交换前状态| 2 | 4 | 1 | 5 | 6 | 9 |

交换后状态| 2 | 4 | 1 | 5 | 6 | 9 |

 

第二趟排序(外循环)

第一次两两比较2 < 4不交换

交换前状态| 2 | 4 | 1 | 5 | 6 | 9 |

交换后状态| 2 | 4 | 1 | 5 | 6 | 9 |

 

第二次两两比较,4 > 1交换

交换前状态| 2 | 4 | 1 | 5 | 6 | 9 | 
交换后状态| 2 | 1 | 4 | 5 | 6 | 9 |

 

第三次两两比较,4 < 5不交换

交换前状态| 2 | 1 | 4 | 5 | 6 | 9 | 
交换后状态| 2 | 1 | 4 | 5 | 6 | 9 |

 

第四次两两比较,5 < 6不交换

交换前状态| 2 | 1 | 4 | 5 | 6 | 9 |

交换后状态| 2 | 1 | 4 | 5 | 6 | 9 |

 

第三趟排序(外循环)

第一次两两比较2 > 1交换

交换后状态| 2 | 1 | 4 | 5 | 6 | 9 |

交换后状态| 1 | 2 | 4 | 5 | 6 | 9 |

 

第二次两两比较,2 < 4不交换

交换后状态| 1 | 2 | 4 | 5 | 6 | 9 | 
交换后状态| 1 | 2 | 4 | 5 | 6 | 9 |

 

第三次两两比较,4 < 5不交换

交换后状态| 1 | 2 | 4 | 5 | 6 | 9 | 
交换后状态| 1 | 2 | 4 | 5 | 6 | 9 |

 

第四趟排序(外循环)无交换

第五趟排序(外循环)无交换


排序完毕,输出最终结果1 2 4 5 6 9


稍微改进一点:

for (int i = 0; i < unsorted.length; i++)
        {
            for (int j = i; j < unsorted.length-i; j++)
            {

快很多

6. 插入排序

介绍:

插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

步骤:

  1. 从第一个元素开始,该元素可以认为已经被排序
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  5. 将新元素插入到该位置中
  6. 重复步骤2

排序效果:

(暂无)

 

7. 希尔排序

介绍:

希尔排序,也称递减增量排序算法,是插入排序的一种高速而稳定的改进版本。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

1、插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率

2、但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位>

排序效果:

视觉直观感受7种常用排序算法

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值