常见的算法实现
二分查找法
冒泡排序
选择排序
插入排序
希尔排序
快速排序(单边循环)
快速排序(双边循环)
1、二分查找法
/**
* (二分查找算法)
* 定义左边界、右边界、中间数
* while循环比较左右边界(条件是小于等于)
* 中间数为左右边界想加除 2
* 判断:相等返回中间树,不相等目标数大则左边界为中间数 + 1,小则右边界 - 1。
*
* @param arr 数组
* @param target 目标数
* @return 返回的是目标索引位置
*/
public static int binarySearch(int[] arr, int target) {
int right = arr.length - 1, left = 0, middle;
while (left <= right) {
middle = (left + right) / 2;
if (target == arr[middle]) {
return middle;
} else if (target > arr[middle]) {
left = middle + 1;
} else {
right = middle - 1;
}
}
return -1;
}
2、冒泡排序
/**
* (冒泡排序)
* 定义一个用于交换的中间数 temp和判断是否发生交换的布尔值 swapped。
* 双重循环,内循环比较相邻两数大小,前者比后者大就交换
* 内循环减去外循环 j 来减少循环次数
* 内外都做是否发生交换的判断,没有发生直接跳出循环,以减少循环次数
*
* @param arr 需要排序的数组
*/
public static void bubbleSort(int[] arr) {
int temp;
boolean swapped;
for (int j = 0; j < arr.length; j++) {
swapped = false; // 是否发生了交换
for (int i = 0; i < arr.length - 1 - j; i++) {
if (arr[i] > arr[i + 1]) {
temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
swapped = true;
}
if (!swapped) { //内循环判断没有发生交换直接退出内循环
break;
}
}
if (!swapped) { //外循环判断没有发生交换直接退出外循环
break;
}
}
}
3、选择排序
/**
* (选择排序)
* 定义一个用于交换的中间变量和最小索引位置变量
* 双重循环,内循环找出最小位置的索引(比如:第一个数依次比较后面的数,将最小数索引位置找到)
* 外循环将最小位置索引的数交换到前排位置
*
* @param arr
*/
public static void selectSort(int[] arr) {
int temp, minIndex;
for (int i = 0; i < arr.length - 1; i++) {
minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[minIndex] > arr[j]) {
minIndex = j;
}
}
if (minIndex != i) {
temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
4、插入排序
/**
* (插入排序)
* 定义一个用于交换的临时变量和插入元素的索引
* 双重循环,内循环插入元素与前面已排好序元素进行比较,小则交换
* 外循环则将开始保存的插入元素插入到内循环找到的索引位置
* @param arr
*/
public static void insertSort(int[] arr) {
int temp, j;
for (int i = 1; i < arr.length; i++) {
temp = arr[i];
j = i;
while (j >= 1) {
if (temp < arr[j - 1]) {
arr[j] = arr[j - 1];
j--;
} else { //跳出循环不需要进行交换
break;
}
}
arr[j] = temp;
}
}
5、希尔排序
/**
* (希尔排序)
* 希尔排序是解决插入排序较大元素在前排导致这个前排的较大元素进行多少交换的问题。
* 主要就是在插入排序基础之上再继续嵌套一层循环来解决问题
* 三重循环
* @param arr
*/
private static void shellSort(int[] arr) {
int n = arr.length;
for (int gap = n / 2; gap > 0; gap /= 2) {
// i 代表待插入元素的索引
for (int i = gap; i < n; i++) {
int t = arr[i]; // 代表待插入的元素值
int j = i;
while (j >= gap) {
// 每次与上一个间隙为 gap 的元素进行插入排序
if (t < arr[j - gap]) { // j-gap 是上一个元素索引,如果 > t,后移
arr[j] = arr[j - gap];
j -= gap;
} else { // 如果 j-1 已经 <= t, 则 j 就是插入位置
break;
}
}
arr[j] = t;
}
}
}
6、快速排序(单边循环)
/**
* (快速排序)单边循环
* 主要是利用的分区自治的思想
* 递归循环调用,将基准点作为分区分隔点
* 这个方法是单边循环快排
* 以数组最后一个元素为基准点的实现
*
* @param arr 数组
* @param low 低位元素位置
* @param high 高位元素位置
*/
public static void quickSort(int[] arr, int low, int high) {
if (low >= high) {
return;
}
int pv = partition(arr, low, high);
// 左边界递归调用
quickSort(arr, low, pv - 1);
// 右边界递归调用
quickSort(arr, pv + 1, high);
}
private static int partition(int[] arr, int low, int high) {
int pv = arr[high]; // 基准点元素
int i = low, temp;
for (int j = low; j < high; j++) {
if (arr[j] < pv) {
if (i != j) {
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
i++;
}
}
if (i != high) {
temp = arr[high];
arr[high] = arr[i];
arr[i] = temp;
}
// 返回值代表了基准点元素所在的正确索引,用它确定下一轮分区的边界
return i;
}
7、快速排序(双边循环)
/**
* (快速排序)双边循环
* 主要是利用的分区自治的思想
* 递归循环调用,将基准点作为分区分隔点
* 这个方法是双边循环快排
* 以数组最后左边的元素为基准点的实现
* 外循环加两个平级内循环实现
*
* @param arr 数组
* @param low 低位元素位置
* @param high 高位元素位置
*/
private static void quickSortDouble(int[] arr, int low, int high) {
if (low >= high) {
return;
}
int pv = partition2(arr, low, high);
quickSortDouble(arr, low, pv - 1);
quickSortDouble(arr, pv + 1, high);
}
private static int partition2(int[] arr, int low, int high) {
int pv = arr[low];
int i = low, temp;
int j = high;
while (i < j) {
// j 从右找小的
while (i < j && arr[j] > pv) {
j--;
}
// i 从左找大的
while (i < j && arr[i] <= pv) {
i++;
}
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
temp = arr[low];
arr[low] = arr[j];
arr[j] = temp;
return j;
}