这篇接着上一个说:直接上主题,不说废话。
**
5.希尔排序
**
传统的插入排序算法在某些场景中存在着一些问题,例如[2,3,4,5,1]这样的一个数组,当我们对其进行插入排序的时候,发现要插入的数字是1,而要想将1插入到最前面,需要经过四个步骤,分别将5、4、3、2后移。所以得出结论:如果较小的数是我们需要进行插入的数,那效率就会比较低。鉴于这种场景的缺陷,希尔排序诞生了,它是插入排序的一种更高效的版本。
先看看希尔排序的概念:
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。希尔排序是基于插入排序的以下两点性质而提出改进方法的:插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率;但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位;希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
稳定性
由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。
排序过程编辑
缩小增量
希尔排序属于插入类排序,是将整个有序序列分割成若干小的子序列分别进行插入排序。
排序过程:先取一个正整数d1<n,把所有序号相隔d1的数组元素放一组,组内进行直接插入排序;然后取d2<d1,重复上述分组和排序操作;直至di=1,即所有记录放进一个组中排序为止。
图片演示:
代码示例:
@Test
public void ShellSort() {
int[] arr = { 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 };
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
// 对数组元素进行分组
for (int i = gap; i < arr.length; i++) {
// 遍历各组中的元素
for (int j = i - gap; j >= 0; j -= gap) {
// 交换元素
if (arr[j] > arr[j + gap]) {
int temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
}
System.out.println(Arrays.toString(arr));
}
**
6.计数排序
**
计数排序
时间复杂度(平均、最坏、最好) O(n+k)
空间复杂度为O(n+k)
稳定性:稳定
n为数组元素个数,k为数据最大值
算法解析:
计数排序不是比较数值排序,是记录数据出现次数的一种排序算法
找出待排数组中最大值
额外一个数组记录待排数组值出现的次数
循环打印存储数值次数的数组下标值
图片演示:
代码示例:
import java.util.Arrays;
public class JiShu {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr={1,4,7,3,2,4};
int[] res=jishu(arr);
System.out.println(Arrays.toString(res));
}
public static int[] jishu(int[] arr){
int max=0;
int min=0;
for(int a:arr ){
if(a>max){
max=a;
}if(min>a){
min=a;
}
}
int[] newarr = new int[max-min+1];
for(int a :arr){
newarr[a-min]++;
}
int index = 0;
for(int i=0;i<newarr.length;i++){
for(int j=0;j<newarr[i];j++){
arr[index++]=i+min;
}
}
return arr;
}
}
**
7.归并排序
**
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并操作
归并操作(merge),也叫归并算法,指的是将两个顺序序列合并成一个顺序序列的方法。
如 设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;
第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;
第三次归并后:{1,6,8,38,100,202,301},比较次数:4;
总的比较次数为:3+4+4=11;
逆序数为14;
算法描述
归并操作的工作原理如下:
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
比较
归并排序是稳定的排序.即相等的元素的顺序不会改变.如输入记录 1(1) 3(2) 2(3) 2(4) 5(5) (括号中是记录的关键字)时输出的 1(1) 2(3) 2(4) 3(2) 5(5) 中的2 和 2 是按输入的顺序.这对要排序数据包含多个信息而要按其中的某一个信息排序,要求其它信息尽量按输入的顺序排列时很重要。归并排序的比较次数小于快速排序的比较次数,移动次数一般多于快速排序的移动次数。
归并排序和快速排序再大数据的底层代码里面应用的比较多可以多了解一下。
图片演示:
代码示例:
import java.util.Arrays;
public class GuiBing {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr1={1,2,3,5,685,81,5,7,9,15,35};
int[] arr2=new int[arr1.length];
caifeng(arr1, 0, arr1.length-1, arr2);
System.out.println(Arrays.toString(arr2));
System.out.println(Arrays.toString(arr1));
}
public static void caifeng(int[] arr,int left,int right,int[] newarr){
if(left<right){
int mid=(left+right)/2;
caifeng(arr, left, mid, newarr);
caifeng(arr, mid+1, right, newarr);
hebing(arr, left, mid, right, newarr);
}
}
public static void hebing(int[] arr,int left,int mid,int right,int[] newarr){
int m = left;
int x = mid;
int n = mid+1;
int y = right;
int index = 0;
while(m<=x&&n<=y){
if(arr[m]>=arr[n]){
newarr[index++]=arr[n++];
}else{
newarr[index++]=arr[m++];
}
}
while(m<=x){
newarr[index++]=arr[m++];
}
while(n<=y){
newarr[index++]=arr[n++];
}
for(int i=0;i<index;i++){
arr[left+i]=newarr[i];
}
}
}
**
8.排序之后需要掌握一个简单的二分法查找
查找过程
首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
算法要求
1.必须采用顺序存储结构。
2.必须按关键字大小有序排列。
比较次数
计算公式:
当顺序表有n个关键字时:
查找失败时,至少比较a次关键字;查找成功时,最多比较关键字次数是b。
注意:a,b,n均为正整数。
算法复杂度
二分查找的基本思想是将n个元素分成大致相等的两部分,取a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;如果x<a[n/2],则只要在数组a的左半部分继续搜索x,如果x>a[n/2],则只要在数组a的右半部搜索x.
时间复杂度无非就是while循环的次数!
图片演示:
代码示例:
public class ErFenFa {
public static void main(String[] args) {
int srcArray[] = {3,5,11,17,21,23,28,30,32,50,64,78,81,95,101};
System.out.println(binSearch(srcArray, 0, srcArray.length - 1, 81));
}
// 二分查找递归实现
public static int binSearch(int srcArray[], int start, int end, int key) {
int mid = (end - start) / 2 + start;
if (srcArray[mid] == key) {
return mid;
}
if (start >= end) {
return -1;
} else if (key > srcArray[mid]) {
return binSearch(srcArray, mid + 1, end, key);
} else if (key < srcArray[mid]) {
return binSearch(srcArray, start, mid - 1, key);
}
return -1;
}
// 二分查找普通循环实现
public static int binSearch(int srcArray[], int key) {
int mid = srcArray.length / 2;
if (key == srcArray[mid]) {
return mid;
}
int start = 0;
int end = srcArray.length - 1;
while (start <= end) {
mid = (end - start) / 2 + start;
if (key < srcArray[mid]) {
end = mid - 1;
} else if (key > srcArray[mid]) {
start = mid + 1;
} else {
return mid;
}
}
return -1;
}
}