目录
推荐:https://www.cnblogs.com/guoyaohua/p/8600214.html
【知识框架】:
稳定性:如果a原本在b前面,而a=b,排序之后a仍然在b的前面,则称这个算法是稳定的,否则成为不稳定。注意,算法是否具有稳定性并不能衡量一个算法的优劣,它主要是对于算法的性质进行描述。
1 插入排序
1.1 算法描述
每次讲一个待排序的记录,按其关键字的大小插入到前面已经排好序的子序列中,直到全部记录插入完成。
步骤:
有序序列L[1...i-1] | L(i) | 无序序列L[i+1...n] |
为了实现将元素L(i)插入到有序序列L[1...i-1]中,我们需要执行以下操作:
- 第一个位置的元素被认为是已经排好序;
- 取出下一个元素L(i);
- 从i-1位置往前扫描,找到第一个比L(i)小的数,插入其后面的位置k;
- 将L[k...i-1]全部后移一个位置;
- 将L(i)复制到L(k);
1.2 代码
//一、插入排序
static void InsetSort(int a[], int length) {
int i,j;
for(i=1; i<length ; i++) {
j=i-1;
int temp=a[i];
while(j>=0 && a[j]>temp ){
a[j+1]=a[j];
j--;
}
a[j+1]=temp;
}
}
1.3 复杂度分析
最佳情况:表中元素已经有序,每插入一个元素都只需比较一次而不用移动元素,时间复杂度 O(n) ,
最坏情况:逆序,总比较次数达到最大,为(2+3+4+...+n),时间复杂度 O(n2)
平均情况:O(n2)
2 希尔排序
2.1 算法描述
直接插入排序适用于基本有序的排序表和数据量不大的排序表。基于这两点,1959年D.L.Shell提出了希尔排序,又称缩小增量排序。
先将待排序表分成若干个形如L[i, i+d, i+2d,..., i+kd] 的特殊子表,再分别进行直接插入排序,当整个表基本有序时,再对全体记录进行一次直接插入排序。
- 先取一个小于n的步长d1,把表中全部记录分成d1组,所有距离为d1的倍数的记录放在同一组,对这些组分别进行直接插入排序;
- 然后取下一个更小的步长d2,重复上述操作,知道最后的步长dt=1,即所有记录分成一组,进行直接插入排序,由于此时已经具有良好的局部有序性,故可以很快得到最终结果。
- 步长:希尔提出的方法是d1=n/2,d2=(n/2)/2,...d(i+1)=di/2
- 步长的选择是希尔排序的重要部分。只要最终步长为1任何步长序列都可以工作。
2.2 代码
//二、希尔排序
static void ShellSort(int a[], int length) {
for (int i = length / 2; i >= 1; i /= 2) { //步长i
for(int j=i; j<length; j++) { //i组
int temp=a[j];
int m=j-i;
while(m>=0&&temp<=a[m]) {
a[m+i]=a[m];
m=m-i;
}
a[m+i]=temp;
}
}
}
2.3 复杂度分析
当n在某个特定范围时,希尔排序时间复杂度约为 O(n1.3),最坏情况:T(n) = O(n2)
稳定性:不稳定。
3 冒泡排序
3.1 算法描述
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
-----------维基百科冒泡排序
3.2 代码
//三、冒泡排序
static void BubbleSort(int a[], int length) {
int flag=1;
for(int i=1; flag==1 && i<length; i++) {
flag=0;
int temp;
for (int j = 0; j + 1 < length - i; j++) {
if (a[j] > a[j + 1]) {
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
flag = 1;
}
}
}
}
3.3 复杂度分析
最佳情况:T(n) = O(n) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(n2)
4 快排
4.1 算法描述
快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为较小和较大的2个子序列,然后递归地排序两个子序列。
步骤为:
- 挑选基准值:从数列中挑出一个元素,称为“基准”(pivot),
- 分割:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成,
- 递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。
递归到最底部的判断条件是数列的大小是零或一,此时该数列显然已经有序。
选取基准值有数种具体方法,此选取方法对排序的时间性能有决定性影响。
-----------维基百科快排
4.2 代码
//四、快排
static void QuickSort(int a[], int low, int high) {
//int pivot=a[0];
//int low=0;
//int high=length-1;
int pivot=0;
if(low<high) {
pivot=partition(a,low,high);
for(int i:a)
System.out.print(i+" ");
System.out.println();
//分治
QuickSort(a, low, pivot-1);
QuickSort(a, pivot+1, high);
//
}
}
static int partition(int a[], int low,int high) {
int pri=a[(low+high)/2];//选中点为基准点
while (low < high) {
while (low < high && a[high] >= pri) {//从右到左找到第一个小于pri的数
--high;
}
a[low] = a[high];
while (low < high && a[low] <= pri) {//从左到右找到第一个大于pri的数
++low;
}
a[high] = a[low];
}
a[low]=pri;
return low;
}
4.3 复杂度分析
最佳情况:T(n) = O(nlogn) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(nlogn)
5 简单选择排序
5.1 算法描述(维基百科选择排序)
- 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
- 然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的后面。
- 以此类推,直到所有元素均排序完毕。
5.2 代码
//五、简单选择排序
static void SelectSort(int a[], int n) {
for(int i=0;i<n-1;i++) {
int min=i;
for(int j=i+1;j<n-1;j++) {
if(a[j]<a[min])
min=j;
}
//System.out.println("a["+min+"]="+a[min]);
if(min!=i) {
int temp=a[i];
a[i]=a[min];
a[min]=temp;
}
}
}
5.3 复杂度分析
最佳情况:T(n) = O(n2) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(n2)
6 堆排序
6.1 算法描述(维基百科)
概述:
若以升序排序说明,把数组转换成大根堆(Max-Heap Heap),这是一种满足最大堆积性质(Max-Heap Property)的二叉树:对于除了根之外的每个节点i, A[parent(i)] ≥ A[i]。
重复从最大堆积取出数值最大的结点(把根结点和最后一个结点交换,把交换后的最后一个结点移出堆),并让残余的堆积维持最大堆积性质。
堆节点的访问:
通常堆是通过一维数组来实现的。在数组起始位置为0的情形中:(i=1,2,...n)
- 父节点 i 的左子节点在位置(2i)
- 父节点 i 的右子节点在位置(2i+1)
- 子节点 i 的父节点在位置floor(i/2)
堆的操作:
在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:
- 最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
- 创建最大堆(Build Max Heap):将堆中的所有数据重新排序
- 堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算
6.2 代码
//堆排序:建堆
static void BuildHeap(int a[], int len) {
for(int i=len/2;i>0;i--) {
AdjustDown(a,i,len);
}
}
static void AdjustDown(int a[], int k, int len) {
a[0]=a[k];
for(int i=2*k;i<=len;i=i*2) {
if(i<len && a[i]<a[i+1]) {
i++;
}
if(a[0]>=a[i])
break;
else{
a[k]=a[i];
k=i;
}
}
a[k]=a[0];
}
static void AdjustUp(int a[], int k, int len) {
a[0]=a[k];
for(int i=k/2; i>0 && a[i]<a[0]; i=i/2) {
a[k]=a[i];
k=i;
}
a[k]=a[0];
}
//六、堆排序
static void HeapSort(int a[], int len) {
BuildHeap(a,len);
for(int i=len; i>1; i--) {
a[0]=a[1];
a[1]=a[i];
a[i]=a[0];
AdjustDown(a,1,i-1);
}
}
6.3 复杂度分析
最佳时间:O(nlogn) 最坏时间:O(nlogn) 平均时间:O(nlogn)
空间复杂度:O(1)
7 归并排序
7.1 算法描述
采用分治法:
- 分割:递归地把当前序列平均分割成两半。两半分别执行归并排序
- 集成:在保持元素顺序的同时将上一步得到的子序列集成到一起(归并)。
7.2 代码
//七、归并排序
static void MergeSort(int a[], int low, int high) {
if(low<high) {
int mid=(low+high)/2;
MergeSort(a,low,mid);
MergeSort(a,mid+1,high);
Merge(a,low,mid,high);
}
}
static void Merge(int a[], int low, int mid, int high) {
int i,j,k;
int[] b = new int[high+1];
for(i=0; i<=high; i++) {
b[i]= a[i];
}
// for(int m:b) {
// System.out.print(m+" ");
// }
// System.out.println();
//
for(i=low,j=mid+1,k=i; i<=mid && j<=high; k++) {
if(b[i]<=b[j]) {
a[k]=b[i++];
}else {
a[k]=b[j++];
}
}
while(i<=mid) {
a[k++]=b[i++];
}
while(j<=mid) {
a[k++]=b[j++];
}
}
7.3 复杂度分析
最佳情况:T(n) = O(n) 最差情况:T(n) = O(nlogn) 平均情况:T(n) = O(nlogn)
空间复杂度:O(n)
8 基数排序
8.1 算法描述(维基百科基数排序)
将所有待比较数值(正整数)统一为同样的数字长度,数字较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
8.2 代码
//八、基数排序
static void RadixSort(int a[], int len) {
if(a!=null && len<2) {
return;
}
//算出最大数的位数
int max=a[0];
for(int i=1; i<len; i++) {
if(a[i]>max) {
max=a[i];
}
}
int maxDigit=0;
while(max!=0) {
max/=10;
maxDigit++;
}
int mod=10, div=1;
ArrayList<ArrayList<Integer>> list=new ArrayList<ArrayList<Integer>>();
for(int i=0; i<10; i++) {
list.add(new ArrayList<Integer>());
}
System.out.println("list.size="+list.size());
for(int i=0; i<maxDigit; i++,mod*=10,div*=10) {
for(int j=0; j<len; j++) {
int num=a[j]%mod/div; //算出第i位上的数num
list.get(num).add(a[j]); //将第i位上为num的数a[j]加入num所在的list
}
int n=0;
for(int j=0; j<10; j++) {
for(int m=0; m<list.get(j).size(); m++) {
a[n++]=list.get(j).get(m);
}
list.get(j).clear(); //清空第j个list
}
}
}
8.3 复杂度分析
时间复杂度:O(kn)
空间复杂度:O(k+n)
//9 计数排序
//10 桶排序
函数调用
package demo01;
import java.util.ArrayList;
import java.util.Queue;
public class Test01 {
public static void main(String[] args) {
int a[] = {0, 50, 32, 40, 90, 80, 5, 21, 69, 55, 70, 1, 10, 1, 100};
//int a[]= {27,3};
int length = a.length;
//InsetSort(a, length);
//QuickSort(a, 0, length-1);
//SelectSort(a,length);
//HeapSort(a,length-1);
MergeSort(a, 0, length-1);
RadixSort(a,length);
for(int i:a)
System.out.print(i+" ");
}
}