排序算法
在面试过程中,排序算法可能不问,但必须得会,所以今天整理了一边主要的排序算法,有冒泡排序、选择排序、插入排序、快速排序、归并排序。
冒泡排序
冒泡排序的时间复杂度是O(n2)。核心相邻两个元素比较,找到最大值后将其交换。
算法描述
- 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
- 针对所有的元素重复以上的步骤,除了最后一个;
- 重复步骤1~3,直到排序完成。
代码块
public class MaoPaoSort {
public static void main(String[] args) {
int[] array = {10, 9, 19, 28, 37, 56, 34};
// 循环轮数 第几轮
for (int i = 1; i < array.length; i++) {
// 每轮循环比较次数
for (int j = 0; j < array.length - i; j++) {
if (array[j] > array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
}
动态展示
选择排序
选择排序的时间复杂度是O(n2)。核心是找到最小值下标,然后交换位置。
算法描述
- 初始状态:无序区为R[1…n],有序区为空;
- 第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1…i-1]和R(i…n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1…i]和R[i+1…n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
- n-1趟结束,数组有序化了。
代码块
// 选择排序
public class SimpleSelectSort {
public static void main(String[] args) {
int[] array = {10, 9, 19, 28, 37, 56, 34};
for (int i = 1; i < array.length; i++) {
int min = array[i - 1];
int index = i - 1;
for (int j = i; j < array.length; j++) {
if (array[j] < min) {
min = array[j];
index = j;
}
}
if (array[i - 1] > array[index]) {
int temp = array[i - 1];
array[i - 1] = array[index];
array[index] = temp;
}
}
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
}
动态展示
插入排序
插入排序的时间复杂度是O(n2)。核心是暂存要插入的值,然后将之前的值向右移动,最后插入。
算法描述
- 从第一个元素开始,该元素可以认为已经被排序;
- 取出下一个元素,在已经排序的元素序列中从后向前扫描;
- 如果该元素(已排序)大于新元素,将该元素移到下一位置;
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
- 将新元素插入到该位置后;
- 重复步骤2~5
代码块
// 插入排序
public class InsertionSort {
public static void main(String[] args) {
int[] array = {10, 9, 19, 19, 28, 37, 56, 34};
// 插入次数
for (int i = 1; i < array.length; i++) {
// 暂存当前值
int current = array[i];
int preIndex = i - 1;
while (preIndex >= 0 && current < array[preIndex]) {
// 较大值向后移一位 (因为已暂存当前值,所以原有数据可以放心向后移动)
array[preIndex + 1] = array[preIndex];
preIndex--;
}
// 如果跳出循环,表示找到插入位置
array[preIndex + 1] = current;
}
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
}
动态展示
快速排序
快速排序的时间复杂度是O(nlog2n)。核心是找到基准值位置,然后递归。
算法描述
- 假设最开始的基准数据为数组第一个元素23,则首先用一个临时变量去存储基准数据,即tmp=23;然后分别从数组的两端扫描数组,设两个指示标志:left指向起始位置,right指向末尾.
- 如果扫描到的值大于基准数据就让right减1,如果发现有元素比该基准数据的值小,就将right位置的值赋值给left位置
- 如果扫描到的值小于基准数据就让left加1,如果发现有元素大于基准数据的值,就再将left位置的值赋值给right位置的值,
- 重复步骤2~3
代码块
// 快速排序
public class QuickSort {
public static void main(String[] args) {
int[] array = {10, 8, 2, 9, 6, 9, 15, 11, 7};
quickSort(array, 0, array.length - 1);
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
}
public static void quickSort(int[] array, int left, int right) {
if (left < right) {
int index = getIndex(array, left, right);
quickSort(array, left, index);
quickSort(array, index + 1, right);
}
}
public static int getIndex(int[] array, int left, int right) {
// 暂存基准值
int temp = array[left];
while (left < right) {
// 先从右向左寻找比基准值小的位置
while (left < right && array[right] >= temp) {
right--;
}
// 将从右边找到的比基准值小的数赋值给左边 (已暂存,可以放心赋值)
array[left] = array[right];
// 从左向右寻找比基准值大的位置
while (left < right && array[left] <= temp) {
left++;
}
// 将从左边找到的比基准值大的数赋值给右边 (右边的值已经赋值给做百年,可以放心赋值)
array[right] = array[left];
}
// 将基准值赋值
array[left] = temp;
// 此时left和right相等,返回left和返回right都一样
return left;
}
}
归并排序
归并排序的时间复杂度是O(nlog2n)。用到的方法是分治法,归并算法合并时的核心是创建一个临时数组,并且注意左边数组和右边数组的结束条件,最后将临时数组的值赋值给原有数组。
算法描述
- 把长度为n的输入序列分成两个长度为n/2的子序列;
- 对这两个子序列分别采用归并排序;
- 将两个排序好的子序列合并成一个最终的排序序列。
- 重复步骤2~3
代码块
// 归并排序
public class MergeSort {
public static void main(String[] args) {
int[] array = {10, 8, 2, 6, 9, 15, 11, 7};
chaiFen(array, 0, array.length - 1);
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
public static void chaiFen(int[] array, int left, int right) {
if (left < right) {
int mid = (left + right) / 2;
chaiFen(array, left, mid);
chaiFen(array, mid + 1, right);
mergeSort(array, left, mid, right);
}
}
public static void mergeSort(int[] array, int left, int mid, int right) {
// 创建临时数组 (可变)
int[] temp = new int[right - left + 1];
// 左边数组起始下标
int i = left;
// 右边数组起始下标
int j = mid + 1;
// 临时数组起始下标
int index = 0;
// 左边数组循环终止条件 右边数组终止循环条件
while (i <= mid && j <= right) {
if (array[i] < array[j]) {
temp[index++] = array[i++];
} else {
temp[index++] = array[j++];
}
}
// 左边数组有剩余元素
while (i <= mid) {
temp[index++] = array[i++];
}
// 右边数组有剩余元素
while (j <= right) {
temp[index++] = array[j++];
}
// 此时temp数组已经有序,再把它赋值给array数组
for (int k = 0; k < temp.length; k++) {
array[left + k] = temp[k];
}
}
}