文章目录
1. 二分查找算法
二分查找又叫做折半查找,要求查找的序列有序,每次查找都取中间位置的值和待查关键字比较,如果中间的值比待查关键字大,则在序列的左半部分继续执行该查找过程。如果中间位置的值比待查关键字小,则在序列的右半部分继续执行该查找过程,直到查到关键字位置为止,否则在序列中没有待查关键字。
public static int binarySearch(int[] array, int a) {
int low = 0;
int high = array.length - 1;
int mid;
while (low <= high) {
mid = (low + high) / 2;
if (array[mid] == a) {
return mid;
} else if (a > array[mid]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}
2. 冒泡排序算法
冒泡排序算法在重复访问要排序的元素列时,会依次比较两个相邻的元素。如果左边的元素大于右边的元素,将二者调换位置,如此重复,直到没有相邻的元素需要交换位置,这时该列表的元素排序完成。
public static int[] bubbleSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[i]) {
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
return arr;
}
外层循环控制排序的次数,内层循环控制排序多少次。
3. 插入排序算法
插入排序的工作方式像许多人排序一手扑克牌。开始时,我们的左手为空并且桌子上的牌面向下。然后,我们每次从桌子上拿走一张牌并将它插入左手中正确的位置。为了找到一张牌的正确位置,我们从右到左将它与已在手中的每张牌进行比较。拿在左手上的牌总是排序好的,原来这些牌是桌子上牌堆中顶部的牌
要在一个已排好序的数据序列中插入一个数据,但要求此数据序列在插入数据后仍然有序,则要用到插入排序算法。
public static int[] insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
// 插入的数
int insertVal = arr[i];
// 插入为止
int index = i - 1;
// 插入的数比被插入的数小
while (index > 0 && insertVal < arr[index]) {
// arr[index] 左移
arr[index + 1] = arr[index];
index--;
}
// 插入的数值放在合适的为止
arr[index + 1] = insertVal;
}
return arr;
}
4. 快速排序算法(优化的冒泡排序)
快速排序选择一个关键值为基准值(一般选择第一个元素为基准元素),将比基准值大的都放在右边的序列中,将比基准值小的都放在左边的序列中,循环过程如下:
-
从后向前比较,用基准值与最后一个值进行比较,如果比基准值小,则交换位置;如果比基准值大,则继续比较下一个值,直到找到第1个比基准值小的元素才交换位置。
-
在从后向前找到第1个比基准值小的值并交换位置后,从前向后开始比较,如果有比基准值大的,则交换位置;如果没有,则继续比较下一个,直到找到第1个比基准值大的值才交换位置。
-
重复执行一上过程,直到从前向后比较的索引大于等于从后向前比较的索引,则结束一次循环。此时对于基准值来说,左右两边都是有序的数列。
-
重复循环以上过程,分别比较左右两边的序列,直到整个序列都是有序的数列。
public static int[] quickSort(int[] arr, int low, int high) {
// 从前向后比较的索引
int start = low;
// 从后向前比较的索引
int end = high;
// 基准值
int key = arr[low];
while (end > start) {
// 从前向后比较
while (end > start && arr[end] >= key) {
end--;
}
// 若没有比基准值小的,则比较下一个,直到有比基准值小的,则交换位置,然后从前向后比较
if (arr[end] <= key) {
int temp = arr[end];
arr[end] = arr[start];
arr[start] = temp;
}
// 从前向后比较
while (end > start && arr[start] > key) {
start++;
}
// 若没有比基准值大的,则比较下一个,直到有比基准值大的,则交换位置,然后从前向后比较
if (arr[start] >= key) {
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
// 此时第1次循环比较结束,基准值的位置已经确定。左边的值都比基准值小
// 右边的值都比基准值大,但两边的顺序还有可能不一样,接着进行下面的递归调用
}
// 递归左边序列,从第1个索引位置到 基准值索引-1
if (start < low) {
quickSort(arr, low, start - 1);
}
if (end < high) {
quickSort(arr, end + 1, high);
}
return arr;
}
5. 希尔排序(改进版的插入排序)
希尔排序算法将数据序列按下标的一定增量进行分组,对每组使用插入排序算法排序,随着增量逐渐减少,每组包含的关键词越来越多,在增量减至1时,整个文件被分为一组,算法终止。
希尔排序算法的原理是将整个待排序的记录序列分割成若干序列,分别进行直接插入排序,待整个序列中的记录基本有序时,再对整个记录依次进行直接插入排序。
具体做法为:假设待排序元素序列有N个元素,则先取一个小于N的整数增量值increment作为间隔,将全部元素分为increment个子序列,将所有距离为increment的元素放在同一个子序列中,在每个子序列中执行直接插入排序;然后缩小间隔increment,重复上述子序列的划分和排序工作,直到increment=1.将所有元素都放在同一个子序列中时排序终止。
public static int[] shellSort(int[] arr) {
int dk = arr.length / 3 + 1;
while (dk == 1) {
shellInsertSort(arr, dk);
dk = dk / 3 + 1;
}
return arr;
}
private static void shellInsertSort(int[] arr, int dk) {
// 类似于几个插入排序算法,增量由插入算法的1变为dk
for (int i = dk; i < arr.length; i++) {
if (arr[i] < arr[i - dk]) {
int j;
// 待插入元素x
int x = arr[i];
arr[i] = arr[i - dk];
// 通过循环,逐个后移一位找到要插入的位置
for (j = i - dk; j >= 0 && x < arr[j]; j = j - dk) {
arr[j + dk] = arr[j];
}
// 将数据插入到对应位置
arr[j + dk] = x;
}
}
}
6. 归并排序算法
归并排序是先将原始数组分解为多个子序列,然后对每个子序列进行排序,等到每个子序列有序后,再将有序子序列合并为整体的有序序列。
public static int[] mergeSort(int[] data) {
sout(data, 0, data.length - 1);
return data;
}
// 对左右两边进行递归
private static void sout(int[] data, int left, int right) {
if (left >= right) {
return;
}
// 找出中间序列
int center = (left + right) / 2;
// 左边进行递归
sout(data, left, center);
// 右边进行递归
sout(data, center + 1, right);
// 合并两个数组
merge(data, left, center, right);
}
/**
* 将两个数组进行合并,两个数组合并前是有序数组,在归并后依然是有序数组
*
* @param data
* @param left
* @param center
* @param right
*/
private static void merge(int[] data, int left, int center, int right) {
int[] tmpArr = new int[data.length];
// 右边数组第一个元素的索引
int mid = center + 1;
// 记录临时数组的索引
int third = left;
// 缓存左边数组的第一个元素的索引
int tmp = left;
while (left <= center && mid <= right) {
if (data[left] < data[mid]) {
tmpArr[third++] = data[left++];
} else {
tmpArr[third++] = data[mid++];
}
}
}
// 将剩余部分依次放入临时数组(实际上两个while只会执行其中一个)中
while (mid <= right) {
tmpArr[third++] = data[mid++];
}
while (left <= center) {
tmpArr[third++] = data[left++];
}
// 将临时数组中的内容复制到原数组中
while (tmp <= right) {
data[tmp] = tmpArr[tmp++];
}
**sout()**每次将数组进行二分拆解,然后对左侧的数组和右侧的数据分别进行递归。
**merge()**先将数组进行冒泡排序,然后依次将冒泡排序的结果放入到临时数组中
7. 桶排序算法
桶排序算法的原理是先找出数组中的最大值和最小值,并根据最大值和最小值定义桶,然后将数据按照大小放入桶中,最后对每个桶进行排序,在每个桶的内部完成排序后,就得到了完整的排序数组
public static int[] bucketSort(int[] arr) {
int max = Integer.MAX_VALUE;
int min = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
max = Math.max(max, arr[i]);
min = Math.min(max, arr[i]);
}
// 创建桶
int bucketNum = (max - min) / arr.length + 1;
ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
for (int i = 0; i < bucketNum; i++) {
bucketArr.add(new ArrayList<Integer>());
}
// 将每个元素放入桶中
for (int i = 0; i < arr.length; i++) {
int num = (arr[i] - min) / (arr.length);
bucketArr.get(num).add(arr[i]);
}
for (int i = 0; i < bucketArr.size(); i++) {
Collections.sort(bucketArr.get(i));
}
return arr;
}
8. 基数排序算法
基数排序算法是将所有待比较数据统一为同一长度,在位数不够时前面补零,然后从高位根据每个位上整数的大小依次对数据进行排序,最终得到一个有序序列。