1、冒泡排序
void bubbleSort(int[] nums) {
int N = nums.length;
for (int i = 0; i < N - 1; i++) { // 外循环
for (int j = 0; j < N - i - 1; j++) { // 内循环
if (nums[j] > nums[j + 1]) {
// 交换 nums[j], nums[j + 1]
int tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
}
}
//每结束一次内循环,说明数组的后面已经排好的树量加一
}
}
时间复杂度恒为 O(N^2);
2、快速排序
void quickSort(int[] nums, int l, int r) {
// 子数组长度为 1 时终止递归
if (l >= r) return;
// 哨兵划分操作
int i = partition(nums, l, r);
// 递归左(右)子数组执行哨兵划分
quickSort(nums, l, i - 1);
quickSort(nums, i + 1, r);
}
int partition(int[] nums, int l, int r) {
// 以 nums[l] 作为基准数
int i = l, j = r;
while (i < j) {
while (i < j && nums[j] >= nums[l]) j--;
while (i < j && nums[i] <= nums[l]) i++;
swap(nums, i, j);
}
swap(nums, i, l);
return i;
}
void swap(int[] nums, int i, int j) {
// 交换 nums[i] 和 nums[j]
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
最佳时间复杂度:Ω(NlogN)
平均时间复杂度:Ω(NlogN)
最坏时间复杂度:O(N^2)
3、归并排序
void mergeSort(int[] nums, int l, int r) {
// 终止条件
if (l >= r) return;
// 递归划分
int m = (l + r) / 2;
mergeSort(nums, l, m);
mergeSort(nums, m + 1, r);
// 合并子数组
int[] tmp = new int[r - l + 1]; // 暂存需合并区间元素
for (int k = l; k <= r; k++)
tmp[k - l] = nums[k];
int i = 0, j = m - l + 1; // 两指针分别指向左/右子数组的首个元素
for (int k = l; k <= r; k++) { // 遍历合并左/右子数组
if (i == m - l + 1)
nums[k] = tmp[j++];
else if (j == r - l + 1 || tmp[i] <= tmp[j])
nums[k] = tmp[i++];
else {
nums[k] = tmp[j++];
}
}
}
时间复杂度: 最佳 Ω(NlogN) ,平均 Θ(NlogN) ,最差 O(NlogN)
4、选择排序
public static void sort(int[] array) {
System.out.println(Arrays.toString(array));
int temp;
for (int i = 0; i < array.length - 1; i++) {
int min = i;
for (int j = i + 1; j < array.length; j++) {
if (array[min] > array[j]) {
min = j;
}
}
if (min != i) {
temp = array[i];
array[i] = array[min];
array[min] = temp;
}
}
System.out.println(Arrays.toString(array));
}
5、插入排序
public class Solution {
public int[] sortArray(int[] nums) {
int len = nums.length;
// 把 nums[i] 插入有序数组 nums[0..i - 1]
for (int i = 1; i < len; i++) {
for (int j = i; j > 0; j--) {
// 注意:前面的数严格大于后面的数才交换
if (nums[j - 1] > nums[j]) {
swap(nums, j, j - 1);
} else {
break;
}
}
}
return nums;
}
private void swap(int[] arr, int index1, int index2) {
int temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
}
}
6、希尔排序
第 1 轮:把下标间隔为 5 的元素分成一组,一共 5 组,分别执行插入排序
此时数组比未排序的时候更接近有序了一点。
第 2 轮:把下标间隔为 2 的元素分成一组,一共 2 组,分别执行插入排序
此时数组比第 2 轮排序开始之前更接近有序了一点。
第 3 轮:把下标间隔为 1 的元素分成一组,其实就是标准的插入排序。
public class Solution {
public int[] sortArray(int[] nums) {
int len = nums.length;
for (int detal = len / 2; detal > 0; detal /= 2) {
for (int start = 0; start < detal; start++) {
insertionSortForDetal(nums, len, detal, start);
}
}
return nums;
}
private void insertionSortForDetal(int[] nums, int len, int detal, int start) {
for (int i = start + detal; i < len; i += detal) {
int temp = nums[i];
int j = i;
for (; j - detal >= 0; j -= detal) {
if (nums[j - detal] > temp) {
nums[j] = nums[j - detal];
} else {
break;
}
}
// 此时 nums[j - 1] <= temp
// nums[j] 的值被赋值到了 nums[j + 1]
nums[j] = temp;
}
}
}
7、堆排序
大根堆和小根堆
性质:每个结点的值都大于其左孩子和右孩子结点的值,称之为大根堆;每个结点的值都小于其左孩子和右孩子结点的值,称之为小根堆。如下图
我们对上面的图中每个数都进行了标记,上面的结构映射成数组就变成了下面这个样子
public class HeapSort {
public static void heapSort(int[] arr) {
if (arr == null || arr.length == 0) {
return;
}
int len = arr.length;
// 构建大顶堆,这里其实就是把待排序序列,变成一个大顶堆结构的数组
buildMaxHeap(arr, len);
// 交换堆顶和当前末尾的节点,重置大顶堆
for (int i = len - 1; i > 0; i--) {
swap(arr, 0, i);
len--;
heapify(arr, 0, len);
}
}
private static void buildMaxHeap(int[] arr, int len) {
// 从最后一个非叶节点开始向前遍历,调整节点性质,使之成为大顶堆
for (int i = (int)Math.floor(len / 2) - 1; i >= 0; i--) {
heapify(arr, i, len);
}
}
private static void heapify(int[] arr, int i, int len) {
// 先根据堆性质,找出它左右节点的索引
int left = 2 * i + 1;
int right = 2 * i + 2;
// 默认当前节点(父节点)是最大值。
int largestIndex = i;
if (left < len && arr[left] > arr[largestIndex]) {
// 如果有左节点,并且左节点的值更大,更新最大值的索引
largestIndex = left;
}
if (right < len && arr[right] > arr[largestIndex]) {
// 如果有右节点,并且右节点的值更大,更新最大值的索引
largestIndex = right;
}
if (largestIndex != i) {
// 如果最大值不是当前非叶子节点的值,那么就把当前节点和最大值的子节点值互换
swap(arr, i, largestIndex);
// 因为互换之后,子节点的值变了,如果该子节点也有自己的子节点,仍需要再次调整。
heapify(arr, largestIndex, len);
}
}
private static void swap (int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
8、桶排序
public class Solution {
// 桶排序
// 1 <= A.length <= 10000
// -50000 <= A[i] <= 50000
// 10_0000
private static final int OFFSET = 50000;
public int[] sortArray(int[] nums) {
int len = nums.length;
// 第 1 步:将数据转换为 [0, 10_0000] 区间里的数
for (int i = 0; i < len; i++) {
nums[i] += OFFSET;
}
// 第 2 步:观察数据,设置桶的个数
// 步长:步长如果设置成 10 会超出内存限制
int step = 1000;
// 桶的个数
int bucketLen = 10_0000 / step;
int[][] temp = new int[bucketLen + 1][len];
int[] next = new int[bucketLen + 1];
// 第 3 步:分桶
for (int num : nums) {
int bucketIndex = num / step;
temp[bucketIndex][next[bucketIndex]] = num;
next[bucketIndex]++;
}
// 第 4 步:对于每个桶执行插入排序
for (int i = 0; i < bucketLen + 1; i++) {
insertionSort(temp[i], next[i] - 1);
}
// 第 5 步:从桶里依次取出来
int[] res = new int[len];
int index = 0;
for (int i = 0; i < bucketLen + 1; i++) {
int curLen = next[i];
for (int j = 0; j < curLen; j++) {
res[index] = temp[i][j] - OFFSET;
index++;
}
}
return res;
}
private void insertionSort(int[] arr, int endIndex) {
for (int i = 1; i <= endIndex; i++) {
int temp = arr[i];
int j = i;
while (j > 0 && arr[j - 1] > temp) {
arr[j] = arr[j - 1];
j--;
}
arr[j] = temp;
}
}
}
9、二分查找
使用递归实现
/**
* 使用递归的二分查找
*title:recursionBinarySearch
*@param arr 有序数组
*@param key 待查找关键字
*@return 找到的位置
*/
public static int recursionBinarySearch(int[] arr,int key,int low,int high){
if(key < arr[low] || key > arr[high] || low > high){
return -1;
}
int middle = (low + high) / 2; //初始中间位置
if(arr[middle] > key){
//比关键字大则关键字在左区域
return recursionBinarySearch(arr, key, low, middle - 1);
}else if(arr[middle] < key){
//比关键字小则关键字在右区域
return recursionBinarySearch(arr, key, middle + 1, high);
}else {
return middle;
}
}
不适用递归,使用while
/**
* 不使用递归的二分查找
*title:commonBinarySearch
*@param arr
*@param key
*@return 关键字位置
*/
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
}