#常用算法学习# #java运行#
快速排序法
快速排序是一种比较快速的排序算法,它的平均运行时间是 O(nlogn),之所以特别快是由于非常精练和高度优化的内部循环,最坏的情形性能为 O(n^2)。像归并一样,快速排序也是一种分治的递归算法。从空间性能上看,快速排序只需要一个元素的辅助空间,但快速排序需要一个栈空间来实现递归,空间复杂度也为O(logn)。
每趟排序过后,基于选定的基准值,小于基准值的放左边,大于基准值的放右边。
快速排序法可以有挖坑和左右指针法两种实现方法
挖坑法
/**
* 挖坑法
* @param arrays
* @param low
* @param high
*/
public static void pothlingSort(int[] arrays , int low , int high){
if(low < high){
//求每次分治的分割线
int divideIndex = getDivideIndex(arrays,low,high);
//再递归分别对分割的俩个子数组进行递归排序
pothlingSort(arrays,low,divideIndex -1);
pothlingSort(arrays,divideIndex + 1, high);
}
}
private static int getDivideIndex(int[] arrays, int low, int high) {
// 将数组最左端arrays[0]作为默认的基准值,将最左端的值放至基准值的坑内。
// 此时arrays[0]没有值了,需要从最右端找到一个比基准值小的数填至[0]这个坑。
// 再从左到右找到一个比基准值大的数填到刚才的坑。循环进行直到low=high
// 将基准值填至刚才的low位置。再进行分治
int baseValue = arrays[low];
arrays[low] = 0 ;
while (low < high){
while(low < high && arrays[high] >= baseValue){
high--;
}
arrays[low] = arrays[high] ;
arrays[high] = 0 ;
while(low < high && arrays[low] <= baseValue){
low++;
}
arrays[high] = arrays[low] ;
arrays[low] = 0 ;
}
if(low == high){
arrays[low] = baseValue;
}
return low;
}
左右指针法
/**
* 快速排序
* 选择一个基准值,所有小于基准值的放左边,大于基准值的放右边
* 1、挖坑法 2、左右指针法
* @param arr
*/
public void quickSortPoint(int[] arr, int low, int high) {
if (low < high) {
// 获取分区后的枢纽位置
int pivotIndex = partitionPoint(arr, low, high);
// 分别对枢纽左右两边的子数组进行递归排序
quickSortPoint(arr, low, pivotIndex - 1);
quickSortPoint(arr, pivotIndex + 1, high);
}
}
private int partitionPoint(int[] arr, int low, int high) {
// 选择数组的最后一个元素作为枢纽值
int pivot = arr[high];
int i = (low - 1);
//所有的小于i+1的都小于pivot
// 遍历数组,将小于枢纽值的元素放到左边,大于枢纽值的元素放到右边
for (int j = low; j < high; j++) {
if (arr[j] < pivot) {
i++;
// 交换 arr[i] 和 arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// 将枢纽元素放到正确的位置
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
// 返回枢纽位置
return i + 1;
}
直接插入排序
插入排序的平均时间复杂度也是 O(n^2),空间复杂度为常数阶 O(1),具体时间复杂度和数组的有序性也是有关联的。
插入排序中,当待排序数组是有序时,是最优的情况,只需当前数跟前一个数比较一下就可以了,这时一共需要比较 N-1 次,时间复杂度为 O(N)。最坏的情况是待排序数组是逆序的,此时需要比较次数最多,最坏的情况是 O(n^2)
/**
* 直接插入排序
* 将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增 1 的有序表
* @param arr
*/
private void insertSort(int [] arr){
//从第二个元素开始,都是排序过的列表
for(int i = 1; i < arr.length ;i++){
for(int j = i ; j > 0 ; j --){
if(arr[j] < arr[ j - 1]){
int tem = arr[j];
arr[j] = arr[j-1];
arr[j-1] = tem;
}else{
break;
}
}
}
}
冒泡排序
从数据的第一个元素开始,比较相邻的两个元素,前一个大于后一个的交换为只,一轮排序下来,最大的元素排在最后。
在最坏的情况下,即序列本来就是逆序的情况下,需要进行n-1次遍历,每次遍历需要比较和交换n-i次,因此总的比较和交换次数是n(n-1)/2,故冒泡排序的时间复杂度是O(n^2)。
/**
* 冒泡排序
* @param arr
*/
private void sort(int [] arr){
for(int i = 0 ; i < arr.length - 1 ; i++){
boolean change = false;
for(int j = 0 ; j < arr.length - 1 - i ; j++){
if(arr[j] > arr [ j + 1]){
int temp = arr[j+1];
arr[j+1] = arr[j];
arr[j]=temp;
change = true;
}
}
if(!change)
return;
}
}
/**
* 双冒泡排序
* @param arr
*/
private void sortT(int[] arr){
int left = 0;
int right = arr.length - 1;
while (left < right){
for(int i = 0 ;i < right ;i++){
if(arr[i] > arr [ i + 1]){
int temp = arr[i+1];
arr[i+1] = arr[i];
arr[i]=temp;
}
}
left ++;
for(int j = right - left ; j > 0 ; j--){
if(arr[j-1] > arr [ j]){
int temp = arr[j-1];
arr[j-1] = arr[j];
arr[j]=temp;
}
}
right --;
}
}