二分法(使用<、>和=)
一、使用mid=(l+r)/2,<、>和=判断
二、由于是start=mid+1和end=mid-1,所以数组只有一个元素的时候,如果不相等下一次就会满足不了left<=right而返回-1
二分法可运行完整代码:
public class 二分法 {
public static void main(String[] args) {
int[] arr = new int[] { 12, 23, 34, 45, 56, 67, 77, 89, 90 };
System.out.println(search(arr, 12));
System.out.println(search(arr, 45));
System.out.println(search(arr, 67));
System.out.println(search(arr, 89));
System.out.println(search(arr, 99));
}
private static int search(int[] arr, int i) {
int start = 0;
int end = arr.length - 1;
while (start <= end) {
int mid = (start + end) / 2;
if (i < arr[mid]) {
end = mid - 1;
} else if (i > arr[mid]) {
start = mid + 1;
} else {
return mid;
}
}
return -1;
}
}
快速排序(使用<、>)
一、选择基准元素为最左边的元素tem=arr[l],最外层要有if(l<r)因为quickSort(arr, l+1, right)如果首个元素被排序到了最右边,那么就会有l+1=right。【都应该在if(l<r)里面,递归的函数也是】
二、此后后面每个地方也都要有l<r的判断,里面while循环找到tem元素在数组中该在的位置(while循环退出条件l=r)
轮换:tem=arr[l],先右边arr[l++] = arr[r](此处l++了就方便直接进行下一个while),再左边arr[r--]=arr[l],while循环外面arr[l]=tem 或 arr[r]=tem
三、不稳定排序,只有严格意义上>、<才不会移动,=也会移动位置
while (l < r && arr[l] < tem) { // 不稳定排序,只有<才不会移动,=也会移动位置
++l;
}
if (l < r) arr[r--] = arr[l];
快排的时间复杂度
核心点:最好情况下,每一次划分分区都正好将数组分成长度相等的两半(nlgn)
核心点:最坏情况下,每一次划分分区都将数组分成了0和n-1两部分(n的平方)
快速排序可运行完整代码:
public class 快排 {
private void quickSort(int[] arr, int l, int r) {
int left = l; // 保存边界值方便在递归的时候使用(因为后面l、r值变化了)
int right = r;
if (l < r) {
int tem = arr[l]; // 选择基准元素为 tem=arr[l]
while (l < r) { // 截止条件 l=r
while (l < r && arr[r] > tem) { //不稳定排序,只有>才可能保持位置不变,=可能移动到前面(使用<>不用=)
--r;
}
if (l < r) arr[l++] = arr[r];
while (l < r && arr[l] < tem) { // 不稳定排序,只有<才不会移动,=也会移动位置
++l;
}
if (l < r) arr[r--] = arr[l];
}
arr[l] = tem; // 找到移动到的位置 l=r
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
}
System.out.println();
quickSort(arr, left, l);
quickSort(arr, l + 1, right);
}
}
public static void main(String[] args) {
快排 main = new 快排();
int[] arrays = { 9, 8, 7, 6, 5, 4, 3, 2, 1 };
main.quickSort(arrays, 0, arrays.length - 1);
System.out.println("Over!");
}
}
归并排序(<=)
该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
- 把长度为n的输入序列分成两个长度为n/2的子序列;
- 对这两个子序列分别采用归并排序;
- 将两个排序好的子序列合并成一个最终的排序序列。
一、递归sort函数,要满足l<r
二、使用mergeSort函数将两个排序好的子序列合并成一个最终的排序序列(内部三个while循环将ans排序好,再一个while循环添加给arr)
类似的计算过程:
设置变量i、j、t,最后再t=0填充一下arr
归并排序可运行完整代码:
public class 归并排序 {
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 l, int r, int[] ans) {
if (l < r) {
int mid = (l + r) / 2;
sort(arr, l, mid, ans);
sort(arr, mid + 1, r, ans);
mergeSort(arr, l, mid, r, ans);
}
}
private static void mergeSort(int[] arr, int l, int mid, int r, int[] ans) {
int i = l; // 三个变量表示下标
int j = mid + 1;
int t = 0;
while (i <= mid && j <= r) { // 三个while循环将ans排序好
if (arr[i] <= arr[j]) { // 稳定排序,相等的话先添加左边的
ans[t++] = arr[i++];
} else {
ans[t++] = arr[j++];
}
}
while (i <= mid) ans[t++] = arr[i++]; // while的简写
while (j <= r) ans[t++] = arr[j++];
t = 0;
while (l <= r) arr[l++] = ans[t++];
}
}
堆排序(<)
堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。
堆
下面的问题是我们怎么样在计算机中存储这个堆呢?也许有人会想到树的存储,确实,刚看这个堆我也是这么想的。然而事实并非如此,这个堆可以看成是一个一维数组A[6]={9,8,5,3,1,2}
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了
一、从第一个非叶子节点 arr.length/2 - 1从下至上,一个一个添加元素进去构建大顶堆
二、adjust函数需要arr、i节点和length参数
三、下标和长度关联:交换下标length-1的元素之后,调整长度为length-1的大顶堆。(到下标为2的时候,长度为2,构建大顶堆交换后就可以结束了)
堆排序完整可运行代码:
public class 堆排序 {
public static void main(String[] args) {
int[] arr = {9, 8, 7, 6, 5, 4, 3, 2, 1};
for (int i = arr.length / 2 - 1; i >= 0; i--) { // 1.构建大顶堆(从第一个非叶子节点从下至上,从右至左调整结构)
adjust(arr, i, arr.length); // 从底元素逐渐一个一个添加元素进去构建大顶堆
}
for (int j = arr.length - 1; j > 0; j--) { // 2.调整堆结构+交换堆顶元素和末尾元素(到下标为2的时候,长度为2,构建大顶堆交换后就可以结束了)
int tem = arr[j];
arr[j] = arr[0];
arr[0] = tem;
adjust(arr, 0, j); // 从顶元素开始重新调整为长度为j的大顶堆
}
for (int i : arr) System.out.print(i + " ");
}
private static void adjust(int[] arr, int i, int length) {
int temp = arr[i];
for (int k = 2 * i + 1; k < length; k = 2 * k + 1) { // 从i结点的左子结点开始自顶往下,也就是2i+1处开始
if (k + 1 < length && arr[k] < arr[k + 1]) k++;
if (arr[k] > temp) { // 如果子节点值大于父节点,将子节点值赋给父节点(不用进行交换)
arr[i] = arr[k];
i = k; // 哪个地方变化i就移动到哪个地方
} else break;
}
arr[i] = temp; // temp赋给arr[i]这个位置上 //将temp值放到最终的位置
}
}