本篇博客知识点
分别描述了 冒泡,选择,直接插入,二分插入,希尔,快速以及归并排序。同时还有Java实现代码,算法分析和示意图
算法描述
算法实例
经过五趟可以将 21 25 49 25 16 08 排为由小到大的升序
其中,里面的每一趟的排序示意如下,以第一天49如何配到最后为例
每一趟的结果都是把未排序的最大的那个数字排到最后。
算法代码实现—Java代码实现
工具方法,交换数组中的两个位置
private static void swap(int[] num, int i, int j) {
int temp = num[i];
num[i] = num[j];
num[j] = temp;
}
//优化版---冒泡排序
public void sort2(int [] num){
for(int i=0;itrue;
for(int j=0;j1;j++){
if(num[j]>num[j+1]){
//交换位子
swap(num, j, j+1);
isOK = false;
}
}
// 当某次没有交换时,说明已经是排好序了。后面就可以不用再比较了
if(isOK){
System.out.println("isOk:"+isOK);
break;
}
}
}
//冒泡排序
public void sort1(int [] num){
for(int i=0;i1;i++){
for(int j=0;j1-i;j++){
if(num[j]>num[j+1]){
//交换位子
swap(num,j,j+1);
}
}
}
}
算法评价
算法描述
算法代码实现—Java代码实现
//选择排序:每一趟选出最小的数前面的数交换
public void sort2(int a[]){
int k = 0;// k用来比赛当前最小的数的坐标
for(int i=0;i1;i++){
for(int j=i+1;jif(a[j]//一轮比较完后,最小的数的位子为k 应该放的位子最前面的位子i 交换~
swap(a, i, k);
}
}
算法评价
算法分析
算法描述:
记录存放在数组R[0….n-1]中,排序过程的某一中间时刻,R被划分成两个子区间R[0…i-1]和R[i….n-1],其中:前一个子区间是已排好序的有序区;后一个子区间则是当前未排序的部分。
基本操作:
将当前无序区的第1个记录R[i]插入到有序区R[0….i-1]中适当的位置,使R[0…i]变为新的有序区。
操作细节:
当插入第i(i≥1)个对象时, 前面的r[0], r[1], …, r[i-1]已经排好序。
用r[i]的关键字与r[i-1], r[i-2], …的关键字顺序进行比较(和顺序查找类似),如果小于,则将r[x]向后移动(插入位置后的记录向后顺移);找到插入位置即将r[i]插入。
实现代码
//3.1直接插入排序 ---原序列越有序排得越快 (逆序排得最慢)
public void sort3_2(int a[]){
//依次把每个元素拿来插入到 之前已经有序的子序列当中
for(int i=0; i1; i++){//趟数:n-1 ---除第1个元素,后面的每个元素都拿来插入一次
//前面i个数已经排好序,现在是准备插入第i+1个数
//待插入的数
int temp = a[i+1];
//找到j ----temp最终是坐在j+1的位置 j的情况:或者是-1,或者是从后往前找到的第一个没有比temp大的数
int j=i;//从第i个位置开始从后往前依次边查找边移位置
while(a[j]>temp){
a[j+1] = a[j];
j--;
if(j<0){
break;
}
}
//让temp坐在j+1的位置
a[j+1]=temp;
}
}
算法描述:
算法实现:
//3.2 加入二分查找的插入排序
private static void binaryInsertSort(int[] a) {
//依次把每个元素拿来插入到 之前已经有序的子序列当中
for(int i=0; i1; i++){//趟数:n-1 ---除第1个元素,后面的每个元素都拿来插入一次
//前面i个数已经排好序,现在是准备插入第i+1个数
//待插入的数
int temp = a[i+1];
//※※利用二分算法查找j ---j的定义同3.1
int low=0;
int high=i;
int mid;
while(low<=high){
//System.out.println(low+","+high);
mid =(low+high)/2;
if(a[mid]>temp){//左半区(所有右半区的数都会比temp大)
high=mid-1;
}else{//右半区
low = mid+1;
}
}
int j=high;//出循环后,high的位置即是我们想要找的j
//把[j,i]部分的元素全部往后移一个位置
for(int k=i;k>j;k--){
a[k+1]=a[k];
}
//让temp坐在j+1的位置
a[j+1]=temp;
}
}
希尔排序又称缩小增量排序,是1959年由D.L.Shell提出来的。
算法描述
1.先取定一个小于n的整数gap1作为第一个增量,把整个序列分成gap1组。所有距离为gap1的倍数的元素放在同一组中,在各组内分别进行排序(分组内采用直接插入排序或其它基本方式的排序)。
2.然后取第二个增量gap2
算法分析
实现代码
//希尔排序,最小增量排序
public void sort4_1(int a[]){
// gab---每次减半
for( int gab=(a.length+1)/2;gab>0;gab=(gab+1)/2){
//组内排序---简单冒泡排序
for(int i=0;ifor(int j=i;jif(a[j]>a[j+gab]){
swap(a, j, j+gab);
}
}
}
if(gab==1){
break;
}
}
}
算法描述
基准记录也称为枢轴(或支点)记录。取序列第一个记录为枢轴记录,其关键字为Pivotkey。指针low指向序列第一个记录位置,指针high指向序列最后一个记录位置。
算法特点:
算法分析
//快速排序
private static void sort5_1(int[] a, int p, int r) {
if(pint q = partition(a,p,r);//划分之后,a[q]的位置已经排好,a[p~q-1]中的元素全部比a[q]小,a[q+1~r]中的元素全部比a[q]大
sort5_1(a,p,q-1);//左子序列
sort5_1(a,q+1,r);//右子序列
}
}
private static int partition(int[] a, int p, int r) {
//优化,随机取一个数和第一个数交换
int rand = (int)(Math.random()*(r-p));
swap(a,p,p+rand); //把随机选中的元素换到首元素(枢轴)
//以下代码都是以第一个数为枢轴
int x=a[p];
int i=p+1;//把第一个元素定为枢轴
int j=r+1;
while(true){
/*
//i--在左区找比枢轴大的数(位置)
while(a[i]x){
j--;
}
*/
//i--在左区找比枢轴大的数(位置)
while(a[i]//j--在右区找比枢轴小的数(位置)
while(a[--j]>x);
if(i>=j){
break;
}
swap(a,i,j);
}
//把枢轴换中间位置
swap(a,p,j);
return j;
}
算法评价
最好情况(每次总是选到中间值作枢轴)T(n)=O(nlogn)
最坏情况(每次总是选到最小或最大元素作枢轴)T(n)=O(n²)
最坏情况:S(n)=O(n)
一般情况:S(n)=O(logn)
快速排序 优化—前面实现版本
可以证明,快速排序算法在平均情况下的时间复杂性和最好情况下一样,也是O(nlogn),这在基于比较的算法类中算是快速的,快速排序也因此而得名。
快速排序算法的性能取决于划分的对称性。因此通过修改partition( )方法,可以设计出采用随机选择策略的快速排序算法,从而使期望划分更对称,更低概率出现最坏情况。
算法描述:
设初始序列含有n个记录,则可看成n个有序的子序列,每个子序列长度为1。
两两合并,得到 n/2 个长度为2或1的有序子序列。
再两两合并,……如此重复,直至得到一个长度为n的有序序列为止。
实现代码
//6归并排序
//6.1 归并方法(要求掌握):把数组a[]当中 [left,mid] 和 [mid+1,right] 两个子序列归并,结果放在b[]
private static void merge(int[]a, int[]b, int left,int mid, int right ){
int p=left; //子序列a[left,mid]的遍历游标
int r=mid+1; //子序列a[mid+1,right]的遍历游标
int k=left; //归并结果序列b[left,right]的遍历游标
//该过程持续到其中一个子序列归并完为止
while( (p<=mid) && (r<=right) ){
if( a[p]else{
b[k++]=a[r++];
}
}
//把另一个序列中剩下的元素直接对拷到 b 中
if(p>mid){//左子序列归并完。把右子序列中剩下的元素对拷到b
for(int i=r;i<=right;i++){
b[k++]=a[i];
}
}else{//右子序列归并完。把左子序列中剩下的元素对拷到b
for(int i=p;i<=mid;i++){
b[k++]=a[i];
}
}
}
//6.2 对一个乱序序列进行用归并排序(了解即可)
private static void mergeSort(int[]a, int left, int right){
if (left//至少有2个元素才进行排序
//先分解
int mid = (left + right) / 2;
mergeSort(a,left,mid);
mergeSort(a,mid+1,right);
//对上面的两个子序列进行归并排序
int b[] = new int[a.length];//新开一个临时数组b,用于存放归并结果
merge(a,b,left,mid,right);//把当前分解层次的两个子序列归并到数组b中
copyArray(a,b,left,right);//把临时数组b中的数据复制到a中,由后续动作对a继续进行归并
}
}
private static void copyArray(int[] a, int[] b, int left, int right) {
for(int i=left;i<=right;i++){
a[i] = b[i];
}
}
算法分析:
合并排序法主要是将两笔已排序的资料合并和进行排序。
如果所读入的资料尚未排序,可以先利用其它的排序方式来处理这两笔资料,然后再将排序好的这两笔资料合并。
算法评价