在数据结构与算法的学习中,排序算法有着举足轻重的地位,其实自己之前也学过,但是由于时间较长,也忘得差不多了,也才想着趁着假期的时间来系统的学习一下。但是理解的不够细致,还需要在花费点时间。
八种基本排序算法的实现
冒泡排序
冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
时间复杂度: O(n^2)
步骤如下:
比较相邻的元素。如果第一个比第二个大,就交换他们两个,直到把最大的元素放到数组尾部。
遍历长度减一,对剩下的元素从头重复以上的步骤。
直到没有任何一对数字需要比较时完成。
冒泡排序的java实现:
package demo4;
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
int [] arr=new int[]{5,7,2,9,4,1,0,5,7};
System.out.println(Arrays.toString(arr));
bubbleSort(arr);
System.out.println(Arrays.toString(arr));
}
/*
* 冒泡排序
* 共需要比较length-1次
*/
public static void bubbleSort(int[] arr) {
//控制共比较多少轮
for(int i=0;i<arr.length-1;i++) {
//控制比较次数
for(int j=0;j<arr.length-1;j++) {
if(arr[j]>arr[j+1]) {
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
}
快速排序
快速排序(Quicksort)是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
时间复杂度:O(N*logN)
步骤:
从数列中挑出一个元素,称为 “基准”(pivot),
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
快速排序的java代码实现:
package demo4;
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int [] arr=new int[]{5,7,2,9,4,1,0,5,7};
quickSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
/*
* 快速排序
*/
public static void quickSort(int [] arr,int start,int end) {
if(start<end) {
//把数组中第0 个数作为基准数
int stard=arr[start];
//记录需要排序的下标
int low=start;
int high=end;
//循环找比标准数大的数和比标准数小的数
while(low<high) {
//右边的数字比标准数大
while(low<high&&stard<=arr[high]) {
high--;
}
//使用右边的数字替换左边的数字
arr[low]=arr[high];
//如果左边的数字比标准数小
while(low<high&&arr[low]<=stard) {
low++;
}
arr[high]=arr[low];
}
//把标准数赋给所在的位置的元素
arr[low]=stard;
//处理所有的小的数字
quickSort(arr,start,low);
//处理所有大的数字
quickSort(arr,low+1,end);
}
}
}
插入排序(直接插入)
插入排序(Insertion Sort)在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。
时间复杂度: O(n^2)
步骤如下:
①从第一个元素开始,该元素可以认为已经被排序
②取出下一个元素,在已经排序的元素序列中从后向前扫描
③如果该元素(已排序)大于新元素,将该元素移到下一位置
④重复步骤3,直到找到已排序的元素小于或者等于新元素的位置5. 将新元素插入到该位置中
⑤重复步骤2
插入排序的java代码实现:
package demo4;
import java.util.Arrays;
public class insertSort {
public static void main(String[] args) {
int [] arr=new int[] {5,2,3,6,1,8,7};
insertSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void insertSort(int[] arr) {
//遍历所有的数字
for(int i=1;i<arr.length;i++) {
//如果当前数字比前一个数字更小
if(arr[i]<arr[i-1]) {
//把当前边遍历数字存起来
int temp=arr[i];
int j;
//遍历当前数字前面的所有数字
for(j=i-1;j>=0&&temp<arr[j];j--) {
//把前一个数字赋值给后一个数字
arr[j+1]=arr[j];
}
//把临时变量(外层for循环的元素)赋值给不满足条件的后一个元素
arr[j+1]=temp;
}
}
}
}
插入排序之希尔排序
针对直接插入排序的效率问题,有人对此进行了改进与升级,这就是现在的希尔排序。希尔排序又称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位
希尔排序的java实现:
package demo4;
import java.util.Arrays;
public class shellSort {
public static void main(String[]args) {
int [] arr=new int[] {5,2,3,6,1,8,7,0};
System.out.println(Arrays.toString(arr));
shellSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void shellSort(int [] arr) {
int k=1;
//遍历所有的步长
for(int d=arr.length/2;d>0;d/=2) {
//遍历所有元素
for(int i=0 ;i<arr.length;i++) {
//遍历本组中所有的元素
for(int j=i-d;j>=0;j-=d) {
if(arr[j]>arr[j+d]) {
int temp=arr[j];
arr[j]=arr[j+d];
arr[j+d]=temp;
}
}
}
System.out.println("第"+k+"次排序结果:"+Arrays.toString(arr));
k++;
}
}
}
排序后的结果为:
选择排序
选择排序法的思路是:
1、找出一个最小数,交换到最前面。
2、在剩下的数里面,再找一个最小的,交换到剩下数的最前面
3、重复步骤2 ,直到所有数都已排好。
显然,对于含有N个数的数组来说,其过程也要进行N-1趟 ( 0 <= i < N-1 )。
找出一个最小数,交换到最前面的方法是:
先将剩下数中的第一个数(序号是i)作为基数,用变量k记下其序号,后面的数依次与该基数比较,若比基数还小,则用k记下其序号(注意:此时不要交换),当所有数都与基数比较后,k中存放的就是最小数的序号,然后将它交换到最前面(现在才交换)。在上面的过程中,数据只交换了一次,即每趟只交换一次数据。
选择排序的java实现:
package demo4;
import java.util.Arrays;
public class selectSort {
public void main(String[]args) {
int [] arr=new int[] {6,1,4,3,6,1,8,7,0};
selectSort(arr);
System.out.println(Arrays.toString(arr));
}
/*
* 选择排序
*/
public static void selectSort(int []arr) {
//遍历所有的数
for(int i=0;i<arr.length;i++) {
int minIndex=i;
//把当前遍历的数和后面所有的数依次进行比较
for(int j=0;j<arr.length;j++) {
//如果后面比较的数比记录的最小的数小
if(minIndex<arr[j]) {
//记录下最小的那个数的下标
minIndex=j;
}
}
//如果最小的数和当前遍历的数的下标不一致,说明下标为Index的数比当前遍历的数更小
if(i!=minIndex) {
int temp=arr[i];
arr[i]=arr[minIndex];
arr[minIndex]=temp;
}
}
}
}
归并排序
归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
时间复杂度:O(N*logN)
步骤如下:
申请空间,创建两个数组,长度分别为两个有序数组的长度
设定两个指针,最初位置分别为两个已经排序序列的起始位置
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针达到序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
归并排序的java代码实现:
package demo4;
import java.util.Arrays;
public class MergeSort {
public static void main(String[]args) {
int [] arr=new int[] {1,3,5,2,4,6,8,10};
System.out.println(Arrays.toString(arr));
mergeSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
/*
* 归并排序
*/
public static void mergeSort(int[]arr,int low,int high) {
int middle=(high+low)/2;
if(low<high) {
//处理左边
mergeSort(arr,low,middle);
//处理右边
mergeSort(arr,middle+1,high);
//归并
merge(arr,low,middle,high);
}
}
public static void merge(int [] arr,int low,int middle,int high) {
//用于存储归并后的临时数组
int [] temp=new int[high-low+1];
//记录第一个数组中需要遍历的数组
int i=low;
//记录第二个数组中需要遍历的数组
int j=middle+1;
//用于记录在临时数组中存放的下标值
int index=0;
//遍历两个数组,取出小的数组放入临时文件中
while(i<=middle&&j<=high) {
//第一个数组中的数据较小
if(arr[i]<=arr[j]) {
//把小的数据放到临时数组中
temp[index]=arr[i];
//让下标下移一位
i++;
}else {
temp[index]=arr[j];
j++;
}
index++;
}
//处理多余的数据
while(j<=high) {
temp[index]=arr[j];
j++;
index++;
}
while(i<=middle) {
temp[index]=arr[i];
i++;
index++;
}
//把临时数组中的数据重新加入原数组
for(int k=0;k<temp.length;k++) {
arr[k+low]=temp[k];
}
}
}
基数排序
基数排序(英语:Radix sort)是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。基数排序的发明可以追溯到1887年赫尔曼·何乐礼在打孔卡片制表机(Tabulation Machine)上的贡献[1]。
它是这样实现的:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。
基数排序的java代码实现:
package demo4;
import java.util.Arrays;
public class RadixSort {
public static void main(String[]args) {
int [] arr=new int[] {23,6,189,45,278,56,78,980,51};
System.out.println(Arrays.toString(arr));
radixSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void radixSort(int [] arr) {
//存储素数组中最大的数字
int max=Integer.MIN_VALUE;
for(int i=0;i<arr.length ;i++) {
if(arr[i]>max) {
max=arr[i];
}
}
//System.out.println(max);
//求最大数的位数
int maxLength=(max+"").length();
//用来临时存储数据的数组
int[][] temp=new int [10][arr.length];
//用于记录在temp中相应的数组中存放的数组
int[] counts=new int[10];
//根据最大长度的数决定比较的次数
for(int i=0,n=1;i<maxLength;i++,n*=10) {
//把一个数分别计算余数
for(int j=0;j<arr.length;j++) {
//计算余数
int ys=arr[j]/n%10;
temp[ys][counts[ys]]=arr[j];
//记录数量
counts[ys]++;
}
//记录取的元素要放的位置
int index=0;
//把数字取出来
for(int k=0;k<counts.length;k++) {
//记录数量的数组中当前余数记录的数量部位0
if(counts[k]!=0) {
//循环取出数组
for(int l=0;l<counts[k];l++) {
//取出元素
arr[index]=temp[k][l];
//记录下一个位置
index++;
}
//把数量置为0
counts[k]=0;
}
}
}
}
}
堆排序
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,可以利用数组的特点快速定位指定索引的元素。它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]]>=A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。
堆排序的java代码实现:
package demo4;
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int [] arr=new int[] {9,6,8,7,0,1,10,4,2};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
/*
* 堆排序
*/
public static void heapSort(int []arr) {
//开始位置是最后一个非叶子节点,即最后一个节点的父节点
int start=(arr.length-1)/2;
for(int i=start;i>=0;i--) {
maxheap(arr,arr.length,i);
}
//把数组中的第0 个和堆中的最后一个交换位置,再把前面的处理为大顶堆
for(int i=arr.length-1;i>0;i--) {
int temp=arr[0];
arr[0]=arr[i];
arr[i]=temp;
maxheap(arr,i,0);
}
}
public static void maxheap(int [] arr,int size,int index) {
//左子节点
int leftNode=2*index+1;
//右子节点
int rightNode=2*index+2;
int max=index;
//和两个子节点进行对比,找出最大的节点
if(leftNode<size && arr[leftNode]>arr[max]) {
max=leftNode;
}
if(rightNode<size && arr[rightNode]>arr[max]) {
max=rightNode;
}
//交换位置
if(max!=index) {
int temp=arr[index];
arr[index]=arr[max];
arr[max]=temp;
//交换位置后需要重新调整位置
maxheap(arr,size,max);
}
}
}
排序算法的比较
在以上这些排序算法的学习中,自己花费了太多的时间。可是最终理解得还是不够透彻,以上算法采用了比较基础的方法实现,考虑的不是很全面,自己也会在之后的学习中多加思索。