常见七大排序算法
目录
一、简单排序
1.选择排序
1.1 选择排序思想
将数组中第一个数与后面的所有数进行比较,若大则交换,直到选出小的数放在第一个位置,依次往后循环。
1.2 时间复杂度
选择排序的时间复杂度为O(n^2)。
1.3 选择排序代码
//选择排序
public static int[] selectSort(int[] arr){
int temp;
for (int i=0;i<arr.length-1;i++){
for (int j=i;j<arr.length;j++){
if (arr[i]>arr[j]){
swap(arr,i,j);
}
}
}
return arr;
}
//交换方法(使用异或的方式,推荐使用)
public static void swap(int[] arr,int i,int j){
arr[i]=arr[i]^arr[j];
arr[j]=arr[i]^arr[j];
arr[i]=arr[i]^arr[j];
}
//交换方法(普通方法)
public static void swap(int[] arr,int i,int j){
int temp;
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
2.冒泡排序
2.1 冒泡排序思想
将相邻的两个数进行比较,按照大的拍后面的规则,一直将大的拍到最后一个,接下来继续按照这个规则拍前N-1的数组。
2.2 时间复杂度
冒泡排序的时间复杂度为O(n^2)。
2.2 冒泡排序代码
//冒泡排序代码
public static int[] bubbleSort(int[] arr){
int temp;
for (int i=arr.length-1;i>=0;i--){
for (int j=0;j<i;j++){
if (arr[j]>arr[j+1]){
swap(arr,j,j+1);
}
}
}
return arr;
}
//交换方法(使用异或的方式,推荐使用)
public static void swap(int[] arr,int i,int j){
arr[i]=arr[i]^arr[j];
arr[j]=arr[i]^arr[j];
arr[i]=arr[i]^arr[j];
}
//交换方法(普通方法)
public static void swap(int[] arr,int i,int j){
int temp;
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
3.插入排序
3.1 插入排序思想
当下标为0时,0之前没有数据不做排序;下标为1时,将下标为1的数据和前面所有的数据进行排序;下标为2时,将下标为2的数据和前面所有的数据进行排序…
其实就是第一个for控制第二个for的长度,第二个for实际就是冒泡
3.2 时间复杂度
插入排序的时间复杂度为O(n^2)。
3.3 插入排序代码
//插入排序
public static int[] insertSort(int[] arr){
for (int i=1;i<arr.length;i++){
for (int j=i;j>=0;j--){
if (arr[j]<arr[j-1]){
swap(arr,j,j-1);
}else {
break;
}
}
}
return arr;
}
//交换方法(使用异或的方式,推荐使用)
public static void swap(int[] arr,int i,int j){
arr[i]=arr[i]^arr[j];
arr[j]=arr[i]^arr[j];
arr[i]=arr[i]^arr[j];
}
4.希尔排序
4.1 希尔排序思想
先将整个待排记录分割成若干个子序列,分别进行直接插入排序,待整个序列的记录”基本有序“的时候,再对全体记录进行一次直接插入排序。
4.2 时间复杂度
希尔排序的时间复杂度为O(nlogn)。
4.3 希尔排序代码
//希尔排序
public static int[] shellSort(int[] arr){
int incr=arr.length/2;
//进行步长的变化,当小于等于0的时候就结束
for (int i=incr;i>0;i/=2){
//对数据进行分组
for (int j=i;j<arr.length;j++){
//此处的i就是本次循环的步长
for (int k=j;k>=i;k-=i){
if (arr[k]<arr[k-1]){
swap(arr,k,k-1);
}
}
}
}
return arr;
}
//交换方法(使用异或的方式,推荐使用)
public static void swap(int[] arr,int i,int j){
arr[i]=arr[i]^arr[j];
arr[j]=arr[i]^arr[j];
arr[i]=arr[i]^arr[j];
}
5.快速排序
5.1 快速排序思想
设置两个指针分别指向数组的最前边和最后边,始终将将数组的第一个数当作key,将数组拍成key前都是比key小的数,key后都是比key大的数。
5.2 时间复杂度
快排的时间复杂度为O(nlog2n)
5.3 快排代码
/**
* 快排算法
* @param arr 数组名称
* @param left 数组最初始的时候,最左边的下标,初始为0
* @param right 数组最初始的时候,最右边的下标,初始为arr.length-1
*/
public static void quick(int[] arr,int left,int right) {
//是递归结束的条件,当传来的数组长度小于等于1的时候,就没必要排序了,直接返回
if (arr==null ||arr.length <= 1 || left>=right) {
return;
}
int l = left;//从左边开始
int r = right;//从右边开始
int key = arr[left];//key始终是数组最左边的数字
//当l和r不相同的时候
//若l和r相同,那么说明第一次快排已经完成
while (l != r) {
//先从右边开始判断是否比key大
//右边大于等于key的可以进来,直到右边小于key的时候听下,得到此时的下标
while (arr[r] >= key & l<r) {
r--;
}
//找到右边的之后,再从左边开始找,找到大于key的下标
while (arr[l] <= key & l<r) {
l++;
}
//此时已经找到了右边小于key的小标和左边大于key的下标
//进行交换
if (l < r) {
arr[l] = arr[l] ^ arr[r];
arr[r] = arr[l] ^ arr[r];
arr[l] = arr[l] ^ arr[r];
}
}
//进行到这说明左指针和右指针已经重合,此时应该将指针的数字和key进行交换
//这块的arr[left]是数组最边的数组,也就是key
arr[left]=arr[l];
arr[l]=key;
//进行到这说明第一次快排已经完成
//可以直到key其实将数组分成了两半,左边是比key都小的,右边是比key都大的
//那么继续可以使用快排思想,将剩下的两半进行快排
quick(arr,left,l-1);
quick(arr,r+1,right);
}
6.归并排序
6.1 归并排序思想
将数组不断的拆分,直到拆分成两个不能再被拆分的最小单位(即只有两个数),然后将这两组中的数据进行排列并暂时存入到另外一个新数组中
6.2 时间复杂度
归并排序的时间复杂度为O(nlogn)
6.3 归并排序代码
/**
* 将数组拆分成两个数组一组的,方便比较,使用递归
* @param arr 数组
* @param low 数组最小位对应的下标
* @param high 数组最高位对应的下标
*/
public static void mergeSort(int[] arr,int low,int high){
//准备一个数组将排完序的数据进行存放
int[] temp=new int[arr.length];
//当数组被拆分成小的之后,最小拆分单位是两个数据
//当拆分成最小单位的时候就不会被拆分,return
if (low<high){
//每次拆分的时候都是一半一半拆分
int mid=(low+high)/2;
//拆分左边一半
mergeSort(arr,low,mid);
//拆分右边一半
mergeSort(arr,mid+1,high);
//拆分成最小单位之后,对其进行比较排序
merge(arr,low,mid,high,temp);
}
}
/**
* 对拆分之后的数据进行排序
* @param arr 数组
* @param mid 数组中间数据的下标
* @param low 数组最左边数据的下标
* @param high 数组最右边数据的下标
* @param temp 排序之后放的新数组
*/
public static void merge(int[] arr,int low,int mid,int high,int[] temp){
//给新数组中添加元素,作为元素下标
int k=0;
//指向左边数据的下标
int m=low;
//指向右边数据的下标
int n=mid+1;
//对两个小数组进行判断,并且将排序之后的数据放入新数组中
while (m<=mid && n<=high){
if (arr[m]<arr[n]){
temp[k++]=arr[m++];
}else {
temp[k++]=arr[n++];
}
}
//上面的while不一定将所有的数据全部都放入了新数组,只是将大的数组放入了新数组
//如果有排好的数据,左边全是小的,右边全是大的,那么就会导致有一部分数据没有存入新数组
while (m<=low){
temp[k++]=arr[m++];
}
while (n<=high){
temp[k++]=arr[n++];
}
//新的数组temp中存着排好的数据,接下来将这些数据重写存入arr中
for (int i = 0; i < k; i++) {
arr[low+i]=temp[i];
}
}
7.堆排序
7.1 堆排序思想
堆排序就是将数组换成树的形式,但不是二叉树,然后通过数组下标模拟树的位置,再通过树的思想去排序。
将数组换为树的形式
树的位置在数组下标上的体现
排序思想
7.2 时间复杂度
堆排序的时间复杂度为O(nlogn)
7.3 堆排序代码
/**
* 堆排序
* @param arr 数组名称
* @param start 数组起始下标
* @param end 数组最后一位数的下标,需要递归则end-1
*/
public static void heapSort(int[] arr,int start,int end){
//当头和尾相等的时候就直接返回,这时候只有两个数
if (start==end){
return;
}
//计算最后一个有孩子的节点,需要整型提升
int lastFather=((start+end)%2==0)?(start+end)/2-1:(start+end)/2;
//从最后一个有孩子的父节点向前遍历
for (int father=lastFather;father>=0;father--){
maxHeap(arr,father,end);
}
//进行到这说明最大的值已经在父节点上了,即最大的值在数组第一个位置上
//那么需要将第一个位置上的数与最后一位的数交换
arr[start]=arr[start]^arr[end];
arr[end]=arr[start]^arr[end];
arr[start]=arr[start]^arr[end];
//进行到这块说明最大的一个数已经在数组的最后一位
//那么需要将最后一位剔除出去,给其他的数据继续进行排序,得出最大的数
//这块使用递归,只需要将end-1即可,不包括最后一位
heapSort(arr,start,end-1);
}
/**
* 对一个节点的数据和其左右孩子进行比较,取出最大的值放在父节点中
* @param arr 数组
* @param father 父节点的数组下标
* @param end 这个数组未排序的最后一位下标
*/
public static void maxHeap(int[] arr,int father,int end){
//一个节点在数组中的下标:n
//一个节点的左子树在数组中的下标:2n+1
//一个节点的右子树在数组中的下标:2n+2
int leftChild=2*father+1;
int rightChild=2*father+2;
//判断父节点和右子树的数据大小
//如果右子树值比父节点大,并且右子树的下标位置>=最后一个位置,则交换
if (rightChild<=end && arr[rightChild]>arr[father]){
arr[rightChild]=arr[rightChild]^arr[father];
arr[father]=arr[rightChild]^arr[father];
arr[rightChild]=arr[rightChild]^arr[father];
}
//判断父节点和左子树的数据大小
if (arr[leftChild]>arr[father]){
arr[leftChild]=arr[leftChild]^arr[father];
arr[father]=arr[leftChild]^arr[father];
arr[leftChild]=arr[leftChild]^arr[father];
}
//进行到这块说明最大的值已经在父节点上了
}