算法
二分查找
又叫折半查找,要求待查找的序列有序。每次取中间位置的值与待查关键字比较,如果中间位置的值比待查关键字大,则在前半部分循环这个查找的过程,如果中间位置的值比待查关键字小,则在后半部分循环这个查找的过程。直到查找到了为止,否则序列中没有待查的关键字。
public int binarySearch(int[] arr, int target){
int left = 0;//定义左、右指针
int right = arr.length - 1;
while (left <= right){//循环遍历
int mid = left + (right -left)/2;//中间为左指标加一半间距
if (target == arr[mid]){//等于返回下标
return mid;
} else if (target < arr[mid]) {//小于,右指标为中间-1
right = mid -1 ;
} else {//大于,左指标为中间+1
left = mid +1;
}
}
return -1;//找不到目标值
}
冒泡排序
它的时间复杂度为O(logN),相比于顺序查找的时间复杂度O(N),效率更高。
- 比较前后相邻的二个数据,如果前面数据大于后面的数据,就将这二个数据交换。
- 这样对数组的第 0 个数据到 N-1 个数据进行一次遍历后,最大的一个数据就“沉”到数组第N-1 个位置。
- N=N-1,如果 N 不为 0 就重复前面二步,否则排序完成。
public int[] sort(int[] arr){
for (int i = 0; i < arr.length - 1; i++){
for (int j = 0; j < arr.length - 1 -i;j++){
if (arr[j] > arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+ 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
插入排序
是一种简单的排序算法,它的时间复杂度为O(N^2),适用于小规模数据的排序。插入排序的基本思想是,将数组分成两部分,一部分为已排序的元素,一部分为未排序的元素。每次从未排序的部分取出一个元素,将它插入到已排序的部分的合适位置,使得已排序的部分仍然保持有序。
public static void insertionSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
快速排序
是一种基于分治法的排序算法,它的时间复杂度为O(N*logN),是最常用的排序算法之一。快速排序的基本思想是,通过一趟排序将待排序的数据分成两部分,一部分小于基准值,一部分大于等于基准值,然后再对这两部分数据分别进行排序。
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
private static int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] < pivot) {
i++;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return i + 1;
}
希尔排序
希尔排序(Shell Sort)是插入排序的一种改进算法,也称作缩小增量排序。它的时间复杂度为O(N1.3),相比于插入排序的O(N2),效率更高。希尔排序的基本思想是,将待排序的数据分成若干个子序列,对子序列进行插入排序,然后逐步缩小子序列的间隔,直到间隔为1,最后对整个序列进行一次插入排序。
public static void shellSort(int[] arr) {
int n = arr.length;
int gap = n / 2;
while (gap > 0) {
for (int i = gap; i < n; i++) {
int temp = arr[i];
int j = i;
while (j >= gap && arr[j - gap] > temp) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
gap /= 2;
}
}
归并排序
归并排序(Merge Sort)是一种基于分治法的排序算法,它的时间复杂度为O(NlogN),相比于快速排序的O(NlogN),效率略低,但是它的稳定性更好,适用于对稳定性要求较高的场景。归并排序的基本思想是,将待排序的数据分成两部分,分别对这两部分数据进行排序,然后将两部分数据归并到一起。
public static void mergeSort(int[] arr, int left, int right) {
if (left < right) {
int mid = (left + right) / 2;
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
}
private static void merge(int[] arr, int left, int mid, int right) {
int[] temp = new int[right - left + 1];
int i = left, j = mid + 1, k = 0;
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
while (i <= mid) {
temp[k++] = arr[i++];
}
while (j <= right) {
temp[k++] = arr[j++];
}
for (int p = 0; p < temp.length; p++) {
arr[left + p] = temp[p];
}
}
桶排序
桶排序(Bucket Sort)是一种线性排序算法,它的时间复杂度为O(N),适用于排序范围较小的整数。桶排序的基本思想是,将待排序的数据分到若干个桶中,对每个桶中的数据进行排序,然后按照桶的顺序将数据取出来,得到有序的序列。
public static void bucketSort(int[] arr, int bucketSize) {
if (arr.length < 2) {
return;
}
int minValue = arr[0];
int maxValue = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] < minValue) {
minValue = arr[i];
} else if (arr[i] > maxValue) {
maxValue = arr[i];
}
}
int bucketCount = (maxValue - minValue) / bucketSize + 1;
List<List<Integer>> buckets = new ArrayList<>(bucketCount);
for (int i = 0; i < bucketCount; i++) {
buckets.add(new ArrayList<>());
}
for (int i = 0; i < arr.length; i++) {
int bucketIndex = (arr[i] - minValue) / bucketSize;
buckets.get(bucketIndex).add(arr[i]);
}
int currentIndex = 0;
for (int i = 0; i < bucketCount; i++) {
List<Integer> bucket = buckets.get(i);
Collections.sort(bucket);
for (int j = 0; j < bucket.size(); j++) {
arr[currentIndex++] = bucket.get(j);
}
}
}
基数排序
基数排序(Radix Sort)是一种线性排序算法,它的时间复杂度为O(N*K),其中K是数字位数,适用于对数字进行排序的场景。基数排序的基本思想是,将待排序的数字从低位到高位依次排序,得到有序的序列。
public static void radixSort(int[] arr) {
if (arr.length < 2) {
return;
}
int maxDigit = getMaxDigit(arr);
for (int i = 1; i <= maxDigit; i++) {
int[][] buckets = new int[10][arr.length];
int[] counts = new int[10];
for (int j = 0; j < arr.length; j++) {
int digit = getDigit(arr[j], i);
buckets[digit][counts[digit]++] = arr[j];
}
int k = 0;
for (int j = 0; j < counts.length; j++) {
for (int l = 0; l < counts[j]; l++) {
arr[k++] = buckets[j][l];
}
}
}
}
private static int getMaxDigit(int[] arr) {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
int digit = 0;
while (max != 0) {
digit++;
max /= 10;
}
return digit;
}
private static int getDigit(int num, int digit) {
return (num / (int)Math.pow(10, digit - 1)) % 10;
}
剪枝算法
剪枝算法(Pruning)是一种在搜索或枚举过程中,通过排除无用的分支来减少搜索或枚举的时间和空间开销的优化技术。剪枝算法常常用于求解最优解或近似最优解的问题,它可以避免不必要的计算,提高求解效率。
public static int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
// 剪枝:如果左右子树深度之和不大于当前最大深度,则停止搜索
if (leftDepth + rightDepth <= max) {
return leftDepth + rightDepth + 1;
}
return Math.max(leftDepth, rightDepth) + 1;
}