总结回顾下几个排序算法:
参考文章:
选择排序
- 思想:在序列中找到最小(最大)的元素,存到排序序列的起始位置,然后从剩余未排序的
- 元素中继续寻找最小(最大)元素,放在已排序的序列的起始位置
- 思考:为甚么叫做选择排序?因为每次都是从剩余的序列有意识选择最值元素。(从后面序列拿元素是需要比较的)
- 算法描述
- 初始状态:无序区为R[1…n],有序区为空
- 第i(i=1,2,3…n-1)趟排序开始是,分为有序区R[1…i-1]和R[i…n)。
- 该趟排序从当前无序区中选出关键字最值的记录R[K],与无序区的第一个交换
- 新有序区为R[1…i] 新无序区R[i+1…n)
- n-1趟结束,数组有序化了
public static int[] selectionSort(int[] array){
if(array.length==0){
return array;
}
for (int i = 0; i < array.length; i++) {
int minIndex=i;
for (int j = i; j < array.length; j++) {
if(array[j]<array[minIndex]){ //遍历找到最小的值
minIndex=j;
}
}
int temp=array[minIndex]; //将在后面搜寻的数列最小值放入array[i];
array[minIndex]=array[i];
array[i]=temp;
}
return array;
}
插入排序Insertion Sort
- 思想:要排序的一堆数组中,假设前n-1是排好序的,现在将第n数组插入到前面已经排好序的数列中。
- 使得这n个数组也是排好序的,如此循环,直到全部排好
- 思考:为什么叫做插入排序,是按顺序在后面数列(未排序)拿一个数, 有意识的插入到前面已经排序的数列中(这个插入过程是需要比较的)
- 算法描述
- 1 从第一个元素开始,默认该元素是已经排序的
- 2 取出下一个元素i,在已经排序的元素序列从后向前扫描
- 3 如果某元素大于新元素i,则将该元素移到下一个位置
- 4 重复步骤3 直到已排序的元素小于或者等于新元素i的位置
- 5 将新元素i插入到该位置后
- 重复2-5
public static int[] insertionSort(int[] array){
if(array.length==0){
return array;
}
int needsort;
for (int i = 0; i < array.length; i++) {
needsort=array[i+1];
int preIndex=i;
while(preIndex>=0&&needsort<array[preIndex]){//为新元素寻找合适的位置
array[preIndex+1]=array[preIndex];//若某个元素大于新元素的值,则将该元素向后挪位置
preIndex--;
}//退出while循环时,已经为新元素找到合适的位置,即preIndeex+1
array[preIndex+1]=needsort;
}
return array;
}
希尔排序Shell Sort
- 思想:希尔排序时插入排序的一种更高效的改进版本,将整个待排序的序列分割成若干个子序列进行插入排序,
- 待整个序列中的记录基本有序,再进行直接插入排序。
- 思考总结:每次先将序列分成di组 ,每间距为i的所有元素看成一个小序列,然后将若干个小序列进行组内插入排序
- 重复以上步骤,直到di为1,此时就是直接插入排序。是再插入排序的基础上做的一些改进。
- 算法描述:
- 1 先取一个人正整数d1(d1<n),把全部记录分成d1个组,所有距离为d1的倍数的记录看成一个组
- 然后再各组内进行插入排序
- 2 然后取d2(d2<d1) 重复上述分组和排序操作;直到去di=1(i>1)位置,即所有记录成为一个组
- 3 最后对这个组进行排序 这时候就是一个简单插入排序了
- 一般选取d1为n/2 d2为d1/2…
public static int[] shellsort(int[] array){
if(array.length==0){
return array;
}
int len=array.length;
int needsort,gap=len/2; // 12 3 4 5 6 7 8 2 1 9
while(gap>0){ //对每次按照gap分组,进行简单直接排序
for(int i=gap; i<len;i++){ //与简单直接排序比较:gap为1 且i=0 而这里i一定为gap 因为要分脆弱干个小序列,每个小序列进行简单排序,每 个小序列开头索引并不是0
needsort=array[gap];
int preIndex=gap-i;
while(preIndex>=0&&array[preIndex]>needsort){
array[preIndex+gap]=array[preIndex];
preIndex-=gap;
}
array[preIndex+gap]=needsort;
}
gap/=2;
}
return array;
}
归并排序(Merge Sort)
*思想:分治法,思想是分而治之,归并排序是将两个有序序列合并成一个有序序列,那么对于一个无序的长序列
*分解成若干有序的子序列(直到每个子序列只有一个元素的时候),然后依次进行归并
*
*思考:就像一个完满二叉树 从下往上排序开始排序
*算法描述
*1将原始序列从中间分为左 右两个子序列,此时序列为2
*2再将左右序列再分别从中间分为左右两个子序列,此时序列数为4
*3重复以上步骤,直到每个子序列都只有一个元素,可以认为每个子序列都是有序的
*4 最后依次进行归并操作,直到序列数变为1
*
//递归法
public static int[] MergeSort(int[] array){ // length=10;
if(array.length<2) return array; // return array 0
int mid=array.length/2;//mid=5 2 1
int[] left=Arrays.copyOfRange(array, 0, mid);//left 0-4 lleft=0-1 lll=0
int[] right=Arrays.copyOfRange(array, mid, array.length);//right 5-10 lright=2-4 llr=1
return merge(MergeSort(left),MergeSort(right)); //MergeSort(left) M(lleft)=2 M(llleft):0
//递归排序 MeraeSort(left)就是不断递分割序列,直到序列不可分割为止
}
public static int[] merge(int[] left,int[] right){
int[]result =new int[left.length+right.length];
for (int index=0,i = 0,j=0;index < result.length; index++) {
if (i >= left.length) //如果left长度小于i,就是left这边比较完了(或者left的1长度为0),将right的下一个元素直接纳入到数组中
result[index] = right[j++];
else if (j >= right.length)
result[index] = left[i++];
else if (left[i] > right[j])
result[index] = right[j++];
else
result[index] = left[i++];
}
return result;
}
快速排序
*思想:快速排序使用分治的思想
*
*思考:递归分区排序
*算法描述
- 1在待排序列中,选择一个元素,作为基准
- 2以该基准在序列中的实际位置,把序列分成左右两个子序列,左序列比基准小,右序列比基准大
- 3递归的对两个序列进行快速排序,直到序列为空或者只有一个元素
private static int partition(int[] arr, int low, int high) {
//指定左指针i和右指针j
int i = low;
int j= high;
//将第一个数作为基准值。挖坑
int x = arr[low];
//使用循环实现分区操作
while(i<j){//5 8
//1.从右向左移动j,找到第一个小于基准值的值 arr[j]
while(arr[j]>=x && i<j){
j--;
}
//2.将右侧找到小于基准数的值加入到左边的(坑)位置, 左指针想中间移动一个位置i++
if(i<j){
arr[i] = arr[j];
i++;
}
//3.从左向右移动i,找到第一个大于等于基准值的值 arr[i]
while(arr[i]<x && i<j){
i++;
}
//4.将左侧找到的打印等于基准值的值加入到右边的坑中,右指针向中间移动一个位置 j--
if(i<j){
arr[j] = arr[i];
j--;
}
}
//使用基准值填坑,这就是基准值的最终位置
arr[i] = x;//arr[j] = y;
//返回基准值的位置索引
return i; //return j;
}
private static void quickSort(int[] arr, int low, int high) {//???递归何时结束 递归条件当不满足low<high时
if(low < high){
//分区操作,将一个数组分成两个分区,返回分区界限索引
int index = partition(arr,low,high);
//对左分区进行快排
quickSort(arr,low,index-1);
//对右分区进行快排
quickSort(arr,index+1,high);
}
}
public static void quickSort(int[] arr) {
int low = 0;
int high = arr.length-1;
quickSort(arr,low,high);
}
*堆排序
- 算法思想:堆排序是选择排序的一种。是将数据看成完全二叉树,根据完全二叉树来排序的一种算法
-
要求其节点的数值都大于或小于左右子节点的值。
- 思考:堆映射的数组并不是有序数组,所以利用大小根堆的性质对数组排序。实际上是一种选择排序。
- 算法描述:
- 当前节点位置为i 所以父节点位置(i-1)/2 左孩子索引2i+1 右孩子索引2i+2
- 大根堆arr(i)>arr(2i+1)&&arr(i)>arr(2i+2)
- 小根堆arr(i)<arr(2i+1)&&arr(i)<arr(2i+2)
- 1首先将待排序的数组构造成一个大根堆,此时,整个数组1的最大值就是堆结构的顶端
- 2将顶端的数与末尾的数进行交换,末尾数为最大值,剩余待排序数组个数为n-1
- 3将剩余n-1个数在构造成大根堆,再将顶端数与n-1位置的数进行交换,如此反复便能得到有序数组
//建堆 arrays看作完全二叉树 currentRootNode当前父节点位置 size节点综述
public static void heapify(int[] arrays,int currentRooNode,int size){
if(currentRooNode<size){
//左子树和右子树的位置
int left =2*currentRooNode+1;
int right=2*currentRooNode+2;
//把当前父节点看成式最大的
int max=currentRooNode;
//开始比较父节点和左右子节点的大小
if(left<size){ //先决条件
//如果比当前根元素大,记录位置
if(arrays[max]<arrays[left]){
max=left;
}
}
if(right<size){ //先决条件
//如果比当前根元素大,记录为位置
if(arrays[max]<arrays[right]){
max=right;
}
}
//如果最大不是根元素的位置,那么就交换
if(max!=currentRooNode){
//max位置为左节点或者右节点的位置
int temp=arrays[max];
arrays[max]=arrays[currentRooNode];
arrays[currentRooNode]=temp;
//继续比较,直到完成一次建堆
//一般此处会有疑问,若max位置为左节点位置,右节点以及一下要不要排序了?
//因为未比较时除了当前节点,左右子节点以及后续子节点都满足大根堆的性质,
//若将左节点和当前节点位置交换,左节点以及后续节点可能不满足大根堆性质,而右节点仍然满足,所以不用调整。
heapify(arrays,max,size);
}
}
}
//完成一次建堆,最大值在堆的顶部
public static void maxHeapify(int[] arrays,int size){
//从数组的尾部开始,直到第一个元素(角标为0)
for (int i = size-1; i >=0; i--) {
heapify(arrays,i,size);//将剩余数组再构造成大根堆
}
}
//堆排序算法,不断建堆,让数组最后一位与当前堆顶(数组第一位)交换即可排序
public static void heapifySort(int[] arrays){
for(int i=0;i<arrays.length;i++){
//每次建堆就可以排除一个元素了
maxHeapify(arrays,arrays.length-i);
//交换
int temp=arrays[0];
arrays[0]=arrays[(arrays.length-1)-i];
arrays[(arrays.length-1)]=temp;
}
}