目录
一 、冒泡排序
二、选择排序
三、插入排序
四、希尔排序(分为移位法和交换法)
五、归并排序
六、快速排序
七、基数排序(桶排序的扩展)
八、堆排序
一 、冒泡排序
冒泡排序(Bubble Sorting)的基本思想是:通过对待 排序序列从前向后(从下标较小的元素开始),依次比较 相邻元素的值,若发现逆序则交换,使值较大 的元素逐渐从前移向后部,像水底下的气泡一样逐渐 向上冒。冒泡排序属于稳定排序算法的范畴,其实较为简单基础的排序算法 ,时间复杂度为O(n²),空间复杂度为O(1)。
动图演示
以下是具体的java代码实现:
public static void bubbleSort(int[] arr){
int temp = 0;
boolean flag = false; // 标识
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]) {
flag = true;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (!flag){
break;
}else{
flag = false; // 重置flag,进行下次判断
}
}
}
用冒泡排序算法测试80000个随机数
二、选择排序
选择排序是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中找出最小(最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。选择排序属于不稳定排序算法的范畴。其时间复杂度为O(n²),空间复杂度为O(1)。
动图演示
以下是具体的java代码实现:
public static void selectSort(int[] arr){
for (int i = 0; i < arr.length-1 ; i++){
int minIndex = i;
int min = arr[i];
for (int j = i + 1; j < arr.length; j++) {
if (min > arr[j]) { // 说明我们假定的最小值,并不是最小
min = arr[j]; // 重置min
minIndex = j; // 重置minIndex
}
}
// 将最小值,放在arr[0],即交换
if (minIndex != i) {
arr[minIndex] = arr[i];
arr[i] = min;
}
}
}
用选择排序算法测试80000个随机数
三、插入排序
插入排序(Insertion Sorting)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。插入排序属于稳定排序的范畴,其时间复杂度为O(n²),空间复杂度为O(1)。
动图演示
以下是具体的java代码实现:
public static void insertSort1(int[] arr) {
int insertVal = 0;
int insertIndex = 0;
for (int i = 0; i < arr.length - 1; i++) {
// 定义带插入的数
insertVal = arr[i + 1];
insertIndex = i;
// i 即arr[i+1] 前面这个数的下标
// 给insertVal 找到插入的位置
while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
arr[insertIndex + 1] = insertVal;
}
}
用插入排序算法测试80000个随机数
在进行插入排序算法测试的时候发现一个奇妙的事情,如果将insertIndex用i代替,它的测试时间会消耗更多,有兴趣的小伙伴可以去试一试看看。
四、希尔排序(分为移位法和交换法)
希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。 希尔排序法基本思想 希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个数据恰被分成一组,算法便终止。其属于不稳定排序算法的范畴,其时间复杂度为O(n log n),空间复杂度为O(1)。
动图演示
具体例子:
以下是具体的java代码实现:
// 希尔排序 (交换法)
public static void shellSort(int[] arr) {
int temp = 0;
int count = 0;
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
// 遍历各组所有数据(共gap组,每组个元素),步长为gap
for (int j = i-gap; j >= 0; j-=gap) {
// 如果当前这个元素大于加上步长后的那个元素,说明交换
if (arr[j] > arr[j+gap]){
temp = arr[j];
arr[j] = arr[j+gap];
arr[j+gap] = temp;
}
}
}
}
}
移位法的希尔排序算法测试80000个随机数
另一种是移位法,其改进使排序时间得到了大大的缩短 ,极大的提升了排序速度。
// 希尔排序(移位法)
public static void shellSort1(int[] arr){
int temp = 0;
// 增量gap,并逐步的缩小增量
for (int gap = arr.length/2 ; gap > 0 ; gap /= 2) {
//从第gap个元素,逐个对其所在的组进行直接插入排序
for (int i = gap; i < arr.length; i++) {
int j = i;
temp = arr[j];
if (arr[j] < arr[j - gap]){ // 优化
while (j - gap >= 0 && temp < arr[j - gap]) {
// 移动
arr[j] = arr[j - gap];
j -= gap;
}
}
// 当退出while循环后,就给temp找到了插入的位置
arr[j] = temp;
}
}
}
交换法的希尔排序算法测试80000个随机数
五、归并排序
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案连接在一起)。其属于稳定排序算法的范畴,其时间复杂度为O(n log n),空间复杂度为O(n),亦是空间换时间的一种。
动图演示
// 分+合并方法(归并排序)
public static void mergedSort(int[] arr,int left,int right,int[] temp){
if (left < right){
int mid = (left + right) / 2; // 中间索引
// 向左递归
mergedSort(arr,left,mid,temp);
// 向右递归
mergedSort(arr,mid+1,right,temp);
// 到合并
merge(arr,left,mid,right,temp);
}
}
// 合并的方法
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left; // 初始化i, 左边有序序列的初始索引
int j = mid+1; // 初始化j, 右边有序序列的初始索引
int t = 0; // 指向temp数组的当前索引
// 先把左右两边的数据按照规则填充到temp数组
// 直到左右两边的有序序列,有一边处理完毕为止
while(i <= mid && j <= right){ // 继续
// 如果左边的有序序列的当前元素,小于右边有序序列的当前元素
// 即将左边的当前元素拷贝到temp数组
// 然后 t++ , i++
if (arr[i] <= arr[j]){
temp[t++] = arr[i++];
}else{
temp[t++] = arr[j++];
}
}
// 把有剩余数据的一边的数据依次全部填充到temp
while(i <= mid){ // 说明左边的有序序列还有剩余元素,就全部填充到temp中
temp[t++] = arr[i++];
}
while(j <= right){ // 说明右边的有序序列还有剩余元素,就全部填充到temp中
temp[t++] = arr[j++];
}
// 将temp数组的元=元素拷贝的arr
// 注意,并不是每次都拷贝所有
t = 0;
int tempLeft = left; //
while(tempLeft <= right){ // 第一次合并 tempLeft = 0; right = 1;
// 最后一次 tempLeft = 0 right = 7;
arr[tempLeft] = temp[t++];
}
}
用归并排序算法测试80000个随机数
六、快速排序
快速排序(Quicksort)是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。其属于不稳定排序算法的范畴,其时间复杂度为O(n log n),空间复杂度为O(log n)。
动图演示
以下是具体的java代码实现:
public static void quickSort(int[] arr, int left, int right) {
int l = left;
int r = right;
int mid = arr[(left + right) / 2];
int temp = 0; // temp是临时变量,作为交换时使用
// while循环的目的是让比mid值小的放到左边
// 让比mid值大的放到右边
while (l < r) {
// 在mid的左边一直找,找到大于等于mid值,才退出
while (arr[l] < mid) {
l += 1;
}
// 在mid的右边一直找,找到小于等于mid值,才退出
while (arr[r] > mid) {
r -= 1;
}
// 如果l >= r成立,说明mid的左右两边的值,已经按照左边全部是小于等于mid的值,右边是大于等于mid的值
if (l >= r) {
break;
}
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
// 如果交换完后,发现这个arr[l] =mid值 相当于 r-- 前移一下
if (arr[l] == mid) {
r -= 1;
}
// 如果交换完后,发现这个arr[l] = mid值 相当于 l++ 后移一下
if (arr[r] == mid) {
l += 1;
}
}
// 如果 l == r,必须l++,r--,否则会出现栈溢出
if (l == r) {
l += 1;
r -= 1;
}
// 向左递归
if (left < r){
quickSort(arr,left,r);
}
// 向右递归
if (right > l){
quickSort(arr,l,right);
}
}
用快速排序算法测试80000个随机数
七、基数排序(桶排序的扩展)
基数排序是对传统桶排序的扩展,速度很快. 基数排序是经典的空间换时间的方式,占用内存很大, 当对海量数据排序时,容易造成 OutOfMemoryError(内存溢出)。 其属于稳定排序算法的范畴,其时间复杂度为O(n k),空间复杂度为O(n+k)。
动图演示
以下是具体的java代码实现:
public static void radixSort(int[] arr){
int max = arr[0]; // 假设第一个个数就是最大数
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max){
max = arr[i];
}
}
// 得到最大数是几位数
int maxLength = (max + "").length();
// 定义一个二维数组,表示10个桶,每个桶就是一个一维数组
// 1 二维数组包含10个一维数组
// 2 为了防止在放入数的时候,数据溢出,则每一个一维数组(桶),大小定为arr.length
// 3 很明确,基数排序是空间换时间的经典算法
int[][] bucket = new int[10][arr.length];
// 为了记录每个桶中,实际存放多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数
int[] bucketElementCounts = new int[10];
// 这里我们使用循环将这个代码处理
for (int i = 0, n = 1 ; i < maxLength; i++, n *= 10) {
// 每一轮(针对每个元素的对应位排序处理,第一次个位,第二个是十位,第三个是百位。。。)
for (int j = 0; j < arr.length; j++) {
// 取出每个元素的对应位的值
int digitOfElement = arr[j] / n % 10;
// 放入到对应的桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++;
}
// 按照这个桶的顺序(一维数组的下标依次取出数据,放入到原来数组)
int index = 0;
// 遍历每一个桶,并将每一个桶中的数据放到原数组
for (int k = 0; k < bucketElementCounts.length; k++) {
// 如果桶中有数据,我们才放入到原数组
if (bucketElementCounts[k] != 0 ){
// 循环该桶即第k个桶(即第k个一维数组),放入
for (int l = 0; l < bucketElementCounts[k]; l++) {
// 取出元素放入到arr中
arr[index++] = bucket[k][l];
}
}
// 第i+1轮处理后,需要将每个bucketElementCounts[k] = 0;
bucketElementCounts[k] = 0;
}
}
}
用基数排序算法测试80000个随机数
八、堆排序
堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆,反之称为小顶堆, 注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系。其属于不稳定排序算法的范畴,其时间复杂度为O(n log n),空间复杂度为O(1)。
动图演示
以下是具体的java代码实现:
public static void heapSort(int[] arr) {
int temp = 0;
System.out.println("堆排序");
for (int i = arr.length/2 - 1; i >= 0; i--) {
adjustHeap(arr,i,arr.length);
}
for (int j = arr.length-1; j > 0 ; j--) {
// 交换
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
adjustHeap(arr,0,j);
}
}
public static void adjustHeap(int[] arr, int i , int length){
int temp = arr[i];
// 开始调整
// k = i * 2 +1 k是i节点的左子节点
for (int k = i * 2 +1; k < length ; k = k * 2 + 1) {
if (k+1 < length && arr[k] < arr[k+1]){ // 说明左子节点的值小于右子节点的值
k++; // k 指向右子节点
}
if (arr[k] > temp){ // 如果子节点大于父节点
arr[i] = arr[k]; // 把较大的值赋给当前节点
i = k; // !!! i指向k, 继续循环比较
}else{
break;
}
}
// 当for循环结束后,我们已经将i为父节点的树的最大值,放在了最顶(局部)
arr[i] = temp; // 将temp值放到调整后的位置
}
用堆排序算法测试测试80000个随机数
谢谢您的浏览,小白一个,如有不足,请多包涵 ^_^ ^_^ ^_^ ^_^