这里写目录标题
- 交换算法之冒泡排序
- 交换算法之快速排序
- 插入算法之插入排序
- 插入排序之希尔排序
- 选择排序之简单选择排序
- 排序算法之归并排序
- 排序算法之基数排序
- 常用排序算法之堆排序
交换算法之冒泡排序
冒泡排序:冒泡排序是一种简单的排序算法,也是笔试题中出现最多的一种排序算法题。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。(‘排序动画’)
算法描述:
- 从一个元素开始,两两比较,如果左边元素比右边大,就交换他们的位置。
- 对每一个相邻元素做相同的工作,这样第一轮比较之后最大的元素就会在最右边。
- 下一轮做重复的操作,但是不用比较最后一个元素。
- 重复以上操作,知道排序完成。
//冒泡排序
public class BubbleSort {
public static void main(String[] args) {
int[] arr = new int[] {2,9,7,5,1,8,4,7,4,9};
System.out.println(Arrays.toString(arr));
BubbleSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void BubbleSort(int[] arr) {
//比较length - 1轮
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]) {
//交换位置
arr[j] = arr[j] ^ arr[j + 1];
arr[j + 1] = arr[j] ^ arr[j + 1];
arr[j] = arr[j] ^ arr[j + 1];
}
}
}
}
}
交换算法之快速排序
快速排序:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。(‘排序动画’)
算法描述:
- 设置数组第一个数为标准数,设置一个开始下标start和结束下标end
- 拿结束下标指向的最后一个元素和标准数相比,如果比标准数大,下标就减一end–,指向倒数第二个元素,如果比标准数小,就用end指向的元素,替换掉start指向的元素
- 拿开始下标指向的第一个元素和标准数相比,如果比标准数小,下标就加一start++,指向第二个元素,如果比标准数大,就用start指向的元素,替换掉end指向的元素
- 重复23,开始下标和结束下标都重合,把标准数赋值到重合的位置,这样就得到了前部分的所有数比标准数小,后一部分的所有数比标准数大
- 然后分别将前后的作为整体,再进行1234步骤,一次循环递归就会得到顺序数组
- 注意:如果只有以上步骤没有条件判断就会死循环,所有需要加一个判断即,当开始下标小于结束下标时,才能比较排序
//快速排序
public class QuickSort {
public static void main(String[] args) {
int[] arr = new int[] {3,6,9,8,4,7,1,0,7,5,9,4};
quickSort(arr,0,arr.length - 1);
System.out.println(Arrays.toString(arr));
}
private static void quickSort(int[] arr, int start, int end) {
//递归结束的条件,开始的位置和结束的位置之间要有元素
if(start < end) {
//把数组中第0个数作为标准数
int pivot = arr[start];
//记录需要排序的下标
int low = start;
int high = end;
//循环找出比标准数大的数和比标准数小的数
while(low < high) {
//如果右边的数字比标准数大
while(low < high && arr[high] >= pivot) {
high--;
}
//如果右边的数字比标准数的小,就把左边的数字替换成右边的数字
arr[low] = arr[high];
//如果左边的数字比标准数小
while(low < high && arr[low] <= pivot) {
low++;
}
//如果左边的数字比标准数大,就把右边的数字替换成左边的数字
arr[high] = arr[low];
}
//最后低位和高位重合,一轮循环结束,把标准数赋个重合的这个位置数
arr[low] = pivot;
//处理所有比标准数小的数字
quickSort(arr, start, low);
//处理所有比标准数大的数字
quickSort(arr, low + 1, end);
}
}
}
插入算法之插入排序
插入排序:插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。(‘排序动画’)
算法描述:
- 从一个元素开始遍历(从前往后),如果当前元素比前一个元素大就继续遍历
- 如果当前元素比前一个元素小,记录当前元素
- 遍历当前元素前的所有元素(从后往前),如果当前元素比前一个元素小,那么前一个元素往后移一位,继续往前比较知道当前元素比前一个元素大,就把记录的元素放在当前位置
- 知道第一步遍历结束,排序就完成了
//插入排序
public class InsetSort {
public static void main(String[] args) {
int[] arr = new int[] {2,7,9,4,7,3,8,1,0,5,8};
insertSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void insertSort(int[] arr) {
//遍历所有数字
for (int i = 1; i < arr.length; i++) {
//如果当前数字比前一个小
if(arr[i] < arr[i - 1]) {
//把当前遍历的数字存起来
int temp = arr[i];
int j;
//遍历当前数字前面所有数字
for (j = i - 1; j >= 0 && temp < arr[j]; j--) {
//把前一个数字赋给后一个数字
arr[j + 1] = arr[j];
}
//把临时变量(外层for循环的当前元素)赋给不满足条件的后一个元素
arr[j + 1] = temp;
}
}
}
}
插入排序之希尔排序
前言:假设有一个数组,数组元素为[3,4,5,6,7,2],如果这个数组用插入排序的话,排序的效率比较低。(‘排序动画’)
希尔排序:1959年Shell发明,第一个突破O(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
算法描述:
- 将元素组的长度除以2得到步长d,根据步长将数组分组(例如:第一个元素是第一个arr[1],那么和第一个元素同组的第二个元素就是arr[1 + d],依次分组)
- 分组的组内元素进行插入排序,第一次分组结束
- 第二次分组再将步长除以2,再将组内的元素进行插入排序
- 重复123知道步长为0,在进行插入排序后,排序完成
//希尔排序
public class ShellSort {
public static void main(String[] args) {
int[] arr = new int[] {1,7,9,3,7,9,0,3,5,7,2};
shellSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void shellSort(int[] arr) {
int k = 1;
//遍历所有步长
for (int d = arr.length / 2; d > 0; d /= 2) {
//遍历所有元素
for (int i = d; i < arr.length; i++) {
//遍历本组所有元素
for (int j = i - d; j >= 0; j -= d) {
//如果当前元素大于加上步长后的那个元素
if(arr[j] > arr[j + d]) {
arr[j] = arr[j] ^ arr[j + d];
arr[j + d] = arr[j] ^ arr[j + d];
arr[j] = arr[j] ^ arr[j + d];
}
}
}
System.out.println("第" + k + "次排序后的结果:" + Arrays.toString(arr));
k++;
}
}
}
选择排序之简单选择排序
选择排序:选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
算法描述:
- 先假设第一个为最小,依次遍历比较,如果比标记的数小就交换位置
- 重复第一步知道排序结束
//选择排序
public class SelectSort {
public static void main(String[] args) {
int[] arr = new int[] {1,6,8,4,5,8,2,9,3};
selectSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void selectSort(int[] arr) {
//遍历所有数
for (int i = 0; i < arr.length; i++) {
int minIndex = i;
//把当前的数依次后后面的数比较,记录最小的数
for (int j = i + 1; j < arr.length; j++) {
//如果后面的数比记录的数小
if(arr[j] < arr[minIndex]) {
minIndex = j;
}
}
//如果最小的数和当前遍历数的下标不一致,说明下表为minIndex的数比当前遍历的数更小
if(i != minIndex) {
arr[i] = arr[i] ^ arr[minIndex];
arr[minIndex] = arr[i] ^ arr[minIndex];
arr[i] = arr[i] ^ arr[minIndex];
}
}
}
}
排序算法之归并排序
归并排序:归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
算法描述:
- 把长度为n的输入序列分为两个长度为n/2的子序列;
- 对这两个子序列采用归并排序;
- 将两个排序好的子序列合并成一个最终的排序序列。
public class MertgeSort {
public static void main(String[] args) {
int[] arr = new int[] {1,3,5,2,4,6,8};
System.out.println(Arrays.toString(arr));
mergeSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
//归并排序
public static void mergeSort(int[] arr,int low ,int high) {
int middle = (low + high) / 2;
if (low < high) {
//处理左边
mergeSort(arr, low, middle);
//处理右边
mergeSort(arr, middle + 1, high);
//归并
merge(arr, low, middle, high);
}
}
public static void merge(int[] arr,int low,int middle,int high) {
//用于存储归并后的临时数组
int[] temp = new int[high - low + 1];
//记录第一个数组中需要遍历的下标
int i = low;
//记录第二个数组中需要遍历的下标
int j = middle + 1;
//用于记录在临时数组中存放的下标
int index = 0;
//遍历两个数字取出小的数字,放在临时数组中
while(i <= middle && j <= high) {
//第一个数组的数据更小
if(arr[i] < arr[j]) {
//把小的数字放入临时数组中
temp[index] = arr[i];
i++;
index++;
}else {
temp[index] = arr[j];
j++;
index++;
}
}
//处理多余的数据
while(j <= high) {
temp[index] = arr[j];
j++;
index++;
}
while(i <= middle) {
temp[index] = arr[i];
i++;
index++;
}
//把临时数组中的值传到原数组
for (int k = 0; k < temp.length; k++) {
arr[k + low] = temp[k];
}
}
}
排序算法之基数排序
基数排序:基数排序是按照低位(个位)先排序,然后收集;再按照高位(十位百位等取决于最大数)排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。
算法描述:
- 取得数组中的最大数,并取得位数;
- arr为原始数组,从最低位开始每个位组成radix数组;
- 对radix进行计数排序(利用计数排序适用于小范围数的特点)。
public class RadixSort {
public static void main(String[] args) {
int[] arr = new int[] {23,6,189,45,9,287,56,1,789,34,65,652,5};
radixSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void radixSort(int[] arr) {
//存数组中最大的数字
int max = Integer.MAX_VALUE;
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
//计算最大数字是几位数
int length = (max + "").length();
//用于临时存储数据的数组
int[][] temp = new int[10][arr.length];
int[] counts = new int[arr.length];
//根据最大长度的数决定比较的次数
for (int i = 0,n = 1; i < length; i++,n*=10) {
//把每一个数字分别计算余数
for (int j = 0; j < arr.length; j++) {
//计算余数
int ys = arr[j]/n%10;
//把当前遍历的数据放入指定二维数组
temp[ys][counts[ys]] = arr[j];
//记录数量
counts[ys]++;
}
//记录取的元素需要放的位置
int index = 0;
//把数字取出来
for (int k = 0; k < counts.length; k++) {
//记录数量的数组中当前余数记录的数量不为0
if(counts[k] != 0) {
//循环取出元素
for (int l = 0; l < counts[k]; l++) {
//取出元素
arr[index] = temp[k][l];
//记录下一个位置
index++;
}
//把数量设置为0
counts[k] = 0;
}
}
}
}
}
常用排序算法之堆排序
大顶堆:父节点都大于子节点
小顶堆:父节点都小于子节点
升序排序用大顶堆,降序排序用小顶堆
public class HeapSort {
public static void main(String[] args) {
int[] arr = new int[] {9,6,8,7,0,1,10,4,2};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void heapSort(int[] arr) {
//开始位置是最后一个非叶子节点,即最后一个节点的父节点
int start = (arr.length - 1)/2;
//调整为大顶堆
for (int i = start; i >= 0; i--) {
maxHeap(arr, arr.length, i);
}
//先把数组中的第0个和堆中的最后一个数交换位置,再把前面的处理为大顶堆
for (int i = arr.length - 1; i > 0; i--) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
maxHeap(arr, i, 0);
}
}
public static void maxHeap(int[] arr,int size,int index) {
//左子节点
int leftNode = 2*index + 1;
//右子节点
int rightNode = 2*index + 2;
int max = index;
//和两个子节点分别对比,找出最大节点
if (leftNode < size && arr[leftNode] > arr[max]) {
max = leftNode;
}
if (rightNode < size && arr[rightNode] > arr[max]) {
max = rightNode;
}
//交换位置
if(max != index) {
int temp = arr[index];
arr[index] = arr[max];
arr[max] = temp;
//交换位置以后,可能会破坏之前拍好的堆,所以,之前的拍好的堆需要重新调整
maxHeap(arr, size, max);
}
}
}