二分算法
在有序序列中快速找到某个数
时间复杂度O(logn) 空间复杂度O(1)
public static int commonBinarySearch(int[] arr,int key){
int low = 0;
int high = arr.length - 1;
int middle = 0; //定义middle
if(key < arr[low] || key > arr[high] || low > high){
return -1;
}
while(low <= high){
middle = (low + high) / 2;
if(arr[middle] > key){
//比关键字大则关键字在左区域
high = middle - 1;
}else if(arr[middle] < key){
//比关键字小则关键字在右区域
low = middle + 1;
}else{
return middle;
}
}
return -1; //最后仍然没有找到,则返回-1
}
排序算法
快速排序
时间复杂度O(nlogn) 空间复杂度O(logn) 为栈所需辅助空间 不稳定排序
思想:
- 确定轴点。对无序序列首先找到一个轴点,一般选序列的第一个元素。
- 寻找轴点的正确位置。low指向序列的第一个元素,height指向最后一个元素。先寻找右边第一个比轴点小的元素,如果找不到就height–; 否则把这个比轴点小的值赋给low位置的元素。接着再从左边找第一个比轴点大的元素,找不到就low++;否则就把这个大元素赋值给height位置的元素。直到low>=height; 这样通过轴点把无序序列一分为二,左边都是比轴点小的,右边都是比轴点大的,然后把轴点插入到这个“正确的位置”。
- 对轴点左边的无序序列重复1和2操作,直到low>=height;
代码:
public class Test {
public static void main(String[] args) {
try {
//49 38 65 97 76 13 27 49
int[] array = new int[]{49,38,65,97,76,13,27,49};
Test test = new Test();
test.quickSort(array, 0, array.length - 1);
System.out.println(array);
}catch (Exception e){
e.printStackTrace();
}
}
//快排
private void quickSort(int[] array, int low, int height){
//当前array只有一个元素 无需排序
if (low < height){
//获取轴点
int pivotPoint = pivotPoint(array, low, height);
//对轴左边快排
quickSort(array, low, pivotPoint - 1);
//对轴右边快排
quickSort(array, pivotPoint + 1, height);
}
}
/**
* 寻找轴点
* @param array
* @param low
* @param height
* @return
*/
private static int pivotPoint(int[] array, int low, int height){
int pivotPointValue = array[low];
while (low < height){
while (low < height && array[height] >= pivotPointValue){
height--;
}
//把高的值赋给低
array[low] = array[height];
while (low < height && array[low] <= pivotPointValue){
low++;
}
//把低的值赋给高
array[height] = array[low];
}
array[low] = pivotPointValue;
return low;
}
}
归并排序
时间复杂度O(nlogn) 空间复杂度O(n) 所占空间较大 稳定排序
思想:
把一组无序序列先两两一对组成若干个有序序列,然后对这若干个有序序列,再两两组对合并成一个有序序列,直到这若干个有序序列只有一个为止。
实现:
public class MergeSort {
public static void merge(int[] a, int low, int mid, int high) {
int[] temp = new int[high - low + 1];
int i = low;// 左指针
int j = mid + 1;// 右指针
int k = 0;
// 把较小的数先移到新数组中
while (i <= mid && j <= high) {
if (a[i] < a[j]) {
temp[k++] = a[i++];
} else {
temp[k++] = a[j++];
}
}
// 把左边剩余的数移入数组
while (i <= mid) {
temp[k++] = a[i++];
}
// 把右边边剩余的数移入数组
while (j <= high) {
temp[k++] = a[j++];
}
// 把新数组中的数覆盖nums数组
for (int k2 = 0; k2 < temp.length; k2++) {
a[k2 + low] = temp[k2];
}
}
public static void mergeSort(int[] a, int low, int high) {
int mid = (low + high) / 2;
if (low < high) {
// 左边
mergeSort(a, low, mid);
// 右边
mergeSort(a, mid + 1, high);
// 左右归并
merge(a, low, mid, high);
System.out.println(Arrays.toString(a));
}
}
public static void main(String[] args) {
int a[] = { 51, 46, 20, 18, 65, 97, 82, 30, 77, 50 };
mergeSort(a, 0, a.length - 1);
System.out.println("排序结果:" + Arrays.toString(a));
}
}
堆排序
时间复杂度O(nlogn) 空间复杂度O(n²) 不稳定排序
思想:
利用大根堆和小根堆的特点,根结点总是数组里最小的那个,因此每次选择堆顶元素,输出到一个新的有序序列即可。
两个核心问题:
-
输出堆顶元素后,如何重建小根堆?
-
一开始的无序序列如何变成小根堆?
实现:
/**
* 堆排序演示
*
* @author Lvan
*/
public class HeapSort {
public static void main(String[] args) {
// int[] arr = {5, 1, 7, 3, 1, 6, 9, 4};
int[] arr = {16, 7, 3, 20, 17, 8};
heapSort(arr);
for (int i : arr) {
System.out.print(i + " ");
}
}
/**
* 创建堆,
* @param arr 待排序列
*/
private static void heapSort(int[] arr) {
//创建堆
for (int i = (arr.length - 1) / 2; i >= 0; i--) {
//从第一个非叶子结点从下至上,从右至左调整结构
adjustHeap(arr, i, arr.length);
}
//调整堆结构+交换堆顶元素与末尾元素
for (int i = arr.length - 1; i > 0; i--) {
//将堆顶元素与末尾元素进行交换
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
//重新对堆进行调整
adjustHeap(arr, 0, i);
}
}
/**
* 调整堆
* @param arr 待排序列
* @param parent 父节点
* @param length 待排序列尾元素索引
*/
private static void adjustHeap(int[] arr, int parent, int length) {
//将temp作为父节点
int temp = arr[parent];
//左孩子
int lChild = 2 * parent + 1;
while (lChild < length) {
//右孩子
int rChild = lChild + 1;
// 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
if (rChild < length && arr[lChild] < arr[rChild]) {
lChild++;
}
// 如果父结点的值已经大于孩子结点的值,则直接结束
if (temp >= arr[lChild]) {
break;
}
// 把孩子结点的值赋给父结点
arr[parent] = arr[lChild];
//选取孩子结点的左孩子结点,继续向下筛选
parent = lChild;
lChild = 2 * lChild + 1;
}
arr[parent] = temp;
}
}
其他排序的主要思想:
插入排序
打牌时为了保证抓过的牌有顺序,则每抓一张牌,需要插入合适的位置。
- 直接插入排序
从无序数组中拿取第一个,插入到新的有序数组中。 - 折半插入排序
直接插入排序中,寻找合适位置插入,这个寻找的过程是顺序的。折半插入就把这个插入过程优化为“折半查找”。 - 希尔排序
分组插入。
选择排序
- 简单选择排序
每次遍历无序数组,从中选择最小的元素插入到有序数组的最后一位。 - 堆排序
不断选择堆顶元素输出到有序数组中的最后一位。
交换排序
- 冒泡排序
就像湖水中的泡泡从湖底慢慢浮向水面。
从第一个元素开始,一直向上寻找当前元素和下一个元素中的较大值,并把较大值留下继续于下一个进行比较,直到数组末尾。
再从第一个元素开始,再次重复上述过程。 - 快速排序
利用pivot,low,height对整个无序数组进行递归排序。