算法复杂度
相关概念:
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数
手写快排:
先选择第一个数字作为标尺,然后分别从第二个数字往右找,找到比第一个数大的数,和从倒数第一个数字往左找,找到比第一个数小的数,然后将找到的两个数进行交换,一直下去。
- 从数列中挑出一个元素,称为 “基准”(pivot);
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
public class quickSort {
private static int[] quickSort(int[] arr, int left, int right) {
if (left < right) {
int p = patition(arr, left, right);//基数
quickSort(arr, left, p - 1);
quickSort(arr, p + 1, right);
}
return arr;
}
private static int patition(int[] arr, int left, int right) {// 分区操作
int temp = left;
while (left < right) {
//从右边开始找到比主键值a[0]小的值,移到左边
while (left < right && arr[right] >= arr[temp])
right--;
while (left < right && arr[left] <= arr[temp])
//从左边开始找到比主键值a[0]大的值,移到右边
left++;
swap(arr,left,right);
}
//最终将基准数归位
swap(arr, temp, right);
return right;
}
private static void swap(int[] arr, int left, int right) {
int temp=arr[left];
arr[left]=arr[right];
arr[right]=temp;
}
}
手写归并:
该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
- 把长度为n的输入序列分成两个长度为n/2的子序列;
- 对这两个子序列分别采用归并排序;
- 将两个排序好的子序列合并成一个最终的排序序列。
public class MergeSort {
public static void main(String[] args) {
int[] arr = { 9, 8, 7, 6, 5, 4, 3, 2, 1 };
int[] temp = new int[arr.length];
sort(arr, 0, arr.length - 1, temp);
System.out.println(Arrays.toString(arr));
}
private static void sort(int[] arr, int left, int right, int[] temp) {
if (left < right) {
int mid = (left + right) / 2;
sort(arr, left, mid, temp);
sort(arr, mid + 1, right, temp);
MergeSort(arr, left, mid, right, temp);
}
}
private static void MergeSort(int[] arr, int left, int mid, int right, int[] temp) {
int i = left;
int j = mid + 1;
int t = 0;
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[t++] = arr[i++];
} else {
temp[t++] = arr[j++];
}
}
while (i <= mid) {// 将左边剩余的元素填充进temp
temp[t++] = arr[i++];
}
while (j <= right) {
temp[t++] = arr[j++];
}
t = 0;
while (left <= right) {
arr[left++] = temp[t++];
}
}
}
手写堆排序:
堆排序
堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。
堆
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了
用简单的公式来描述一下堆的定义就是:
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
public class HeapSort {
private static void sort(int[] arr) {
// 1、构建大顶堆
for(int i=arr.length/2-1;i>=0;i--){
//从第一个非叶子节点从下至上,从右至左调整结构
adjustHeap(arr,i,arr.length);
}
//2、调整堆结构+交换堆顶元素与末尾元素
for(int j=arr.length-1;j>0;j--){
swap(arr,0,j);//将堆顶元素与末尾元素进行交换
adjustHeap(arr, 0, j);//重新对堆进行调整
}
}
private static void swap(int[] arr, int a, int b) {
int temp=arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
private static void adjustHeap(int[] arr, int i, int length) {
int temp=arr[i];
for(int k=i*2+1;ktemp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
arr[i]=arr[k];
i=k;
}else{
break;
}
}
arr[i]=temp;//将temp值放到最终的位置
}
}
参考博文:
十大经典排序算法(动图演示)https://www.cnblogs.com/onepixel/articles/7674659.html
https://www.cnblogs.com/chengxiao/p/6103002.html