第三次博客:排序方法基本介绍(1):
稳定的
冒泡排序(bubble sort) — O(n^2)
鸡尾酒排序(Cocktail sort,双向的冒泡排序) — O(n^2)
插入排序(insertion sort)— O(n^2)
桶排序(bucket sort)— O(n); 需要 O(k) 额外空间
计数排序(counting sort) — O(n+k); 需要 O(n+k) 额外空间
合并排序(merge sort)— O(nlog n); 需要 O(n) 额外空间
原地合并排序— O(n^2)
二叉排序树排序 (Binary tree sort) — O(nlog n)期望时间; O(n^2)最坏时间; 需要 O(n) 额外空间
鸽巢排序(Pigeonhole sort) — O(n+k); 需要 O(k) 额外空间
基数排序(radix sort)— O(n·k); 需要 O(n) 额外空间
Gnome 排序— O(n^2)
图书馆排序— O(nlog n) with high probability,需要 (1+ε)n额外空间
不稳定的
选择排序(selection sort)— O(n^2)
希尔排序(shell sort)— O(nlog n) 如果使用最佳的现在版本
组合排序— O(nlog n)
堆排序(heapsort)— O(nlog n)
平滑排序— O(nlog n)
快速排序(quicksort)— O(nlog n) 期望时间,O(n^2) 最坏情况; 对于大的、乱数列表一般相信是最快的已知排序
Introsort— O(nlog n)
耐心排序— O(nlog n+ k) 最坏情况时间,需要 额外的 O(n+ k) 空间,也需要找到最长的递增子串行(longest increasing subsequence)
不实用的
Bogo排序— O(n× n!) 期望时间,无穷的最坏情况。
Stupid sort— O(n^3); 递归版本需要 O(n^2) 额外存储器
珠排序(Bead sort) — O(n) or O(√n),但需要特别的硬件
Pancake sorting— O(n),但需要特别的硬件
stooge sort——O(n^2.7)很漂亮但是很耗时
学习c、c++、java、数据结构中遇到的排序方法有插入排序
冒泡排序
选择排序
快速排序
插入排序
堆排序
归并排序
基数排序
希尔排序
今天介绍冒泡排序、选择排序、快速排序、插入排序这四个比较基础的排序:不过即使是基础的排序也是很博大精深,不仅仅只有表面看起来的四种,想要加深就得自己看资料学习了,本人只介绍最基本的四种。
万丈高楼平地起,一切都要从开始的说起:
冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
例子:
public static void bubbleSort(int []arr) {
int[] arr = {12,23,34,56,56,56,78};
for(int i =0;i<arr.length-1;i++) {
for(int j=0;j<arr.length-i-1;j++) {//-1为了防止溢出
if(arr[j]>arr[j+1]) {
int temp = arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
选择排序
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法。
public static void selectSort(int[] a) {
if((a == null) || (a.length == 0))
return ;
for(int i = 0;i < a.length - 1;i ++){
int minIndex = i; // 无序区的最小数据数组下标
for(int j = i + 1;j < a.length;j ++){
// 在无序区中找到最小数据并保存其数组下标
if(a[j] < a[minIndex]){
minIndex = j;
}
}
// 将最小元素放到本次循环的前端
int temp = a[i];
a[i] = a[minIndex];
a[minIndex] = temp;
}
}
快速排序算法
快速排序(Quicksort)是对冒泡排序的一种改进。
快速排序由C. A. R. Hoare在1960年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
class Quick {
public void sort(int arr[],int low,int high) {
int l=low;
int h=high;
int povit=arr[low];
while(l<h) {
while(l<h&&arr[h]>=povit)
h--;
if(l<h){
arr[l]=arr[h];
l++;
}
while(l<h&&arr[l]<=povit)
l++;
if(l<h){
arr[h]=arr[l];
h--;
}
}
arr[l]=povit;
print(arr);
System.out.print("l="+(l+1)+"h="+(h+1)+"povit="+povit+"\n");
if(l-1>low)sort(arr,low,l-1);
if(h+1<high)sort(arr,h+1,high);
}
}
///方式二/
更高效点的代码:
public<TextendsComparable<?superT>>
T[]quickSort(T[]targetArr,intstart,intend)
{
inti=start+1,j=end;
Tkey=targetArr[start];
SortUtilsUtil=newSortUtil();
if(start=end)return(targetArr);
/*从i++和j–两个方向搜索不满足条件的值并交换
*
*条件为:i++方向小于key,j–方向大于key
*/
while(true)
{
while(targetArr[j].compareTo(key)>0)j–;
while(targetArr[i].compareTo(key)<0&&i<j)i++;
if(i>=j)break;
sUtil.swap(targetArr,i,j);
if(targetArr[i]==key)
{
j–;
}else{
i++;
}
}
/关键数据放到‘中间’/
sUtil.swap(targetArr,start,j);
if(start<i-1)
{
this.quickSort(targetArr,start,i-1);
}
if(j+1<end)
{
this.quickSort(targetArr,j+1,end);
}
returntargetArr;
}
///方式三:减少交换次数,提高效率//
private<TextendsComparable<?superT>>
voidquickSort(T[]targetArr,intstart,intend)
{
inti=start,j=end;
Tkey=targetArr[start];
while(i<j)
{
/按j–方向遍历目标数组,直到比key小的值为止/
while(j>i&&targetArr[j].compareTo(key)>=0)
{
j–;
}
if(i<j)
{
/targetArr[i]已经保存在key中,可将后面的数填入/
targetArr[i]=targetArr[j];
i++;
}
/按i++方向遍历目标数组,直到比key大的值为止/
while(i<j&&targetArr[i].compareTo(key)<=0)
/此处一定要小于等于零,假设数组之内有一亿个1,0交替出现的话,而key的值又恰巧是1的话,那么这个小于等于的作用就会使下面的if语句少执行一亿次。/
{
i++;
}
if(i<j)
{
/targetArr[j]已保存在targetArr[i]中,可将前面的值填入/
targetArr[j]=targetArr[i];
j–;
}
}
/此时i==j/
targetArr[i]=key;//应加判断
/递归调用,把key前面的完成排序/
this.quickSort(targetArr,start,i-1);
/递归调用,把key后面的完成排序/
this.quickSort(targetArr,j+1,end);
//两个递归应加判断
}
插入排序
插入排序(Insertion sort)是一种简单直观且稳定的排序算法。如果有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法——插入排序法,插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。插入算法把要排序的数组分成两部分:第一部分包含了这个数组的所有元素,但将最后一个元素除外(让数组多一个空间才有插入的位置),而第二部分就只包含这一个元素(即待插入元素)。在第一部分排序完成后,再将这个最后元素插入到已排好序的第一部分中。
插入排序的基本思想是:每步将一个待排序的记录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。
分类
包括:直接插入排序,二分插入排序(又称折半插入排序),链表插入排序,希尔排序(又称缩小增量排序)。属于稳定排序的一种(通俗地讲,就是两个相等的数不会交换位置) 。
本次介绍的是基础的直接插入排序和二分插入排序:
直接插入排序
直接插入排序(Straight Insertion Sort)是一种最简单的排序方法,其基本操作是将一条记录插入到已排好的有序表中,从而得到一个新的、记录数量增1的有序表。
public class InsertSort{
public void insertSort(int[] array){
for(int i=1;i<array.length;i++)//第0位独自作为有序数列,从第1位开始向后遍历
{
if(array[i]<array[i-1])//0~i-1位为有序,若第i位小于i-1位,继续寻位并插入,否则认为0~i位也是有序的,忽略此次循环,相当于continue
{
int temp=array[i];//保存第i位的值
int k = i - 1;
for(int j=k;j>=0 && temp<array[j];j--)//从第i-1位向前遍历并移位,直至找到小于第i位值停止
{
array[j+1]=array[j];
k--;
}
array[k+1]=temp;//插入第i位的值
}
}
}
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++) {
System.out.print(array[i]);
if (i != array.length - 1) {
System.out.print(",");
}
}
}
}
二分法插入排序(又称折半插入排序)
二分法插入排序,简称二分排序,是在插入第i个元素时,对前面的0~i-1元素进行折半,先跟他们中间的那个元素比,如果小,则对前半再进行折半,否则对后半进行折半,直到left<right,然后再把第i个元素前1位与目标位置之间的所有元素后移,再把第i个元素放在目标位置上。public static void advanceInsertSortWithBinarySearch(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int temp = arr[i];
int low = 0, high = i - 1;
int mid = -1;
while (low <= high) {
mid = low + (high - low) / 2;
if (arr[mid] > temp) {
high = mid - 1;
} else { // 元素相同时,也插入在后面的位置
low = mid + 1;
}
}
for(int j = i - 1; j >= low; j–) {
arr[j + 1] = arr[j];
}
arr[low] = temp;
}
}