面试必备--八大排序算法

冒泡排序(BubbleSort)

算法思路

比较两个相邻的元素,将值大的元素交换至右端。

  • 依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。重复第一趟步骤,直至全部排序完成。
  • 第一趟比较完成后,最后一个数一定是数组中最大的一个数,所以第二趟比较的时候最后一个数不参与比较;
  • 第二趟比较完成后,倒数第二个数也一定是数组中第二大的数,所以第三趟比较的时候最后两个数不参与比较;

N个数字只需要N-1躺。每趟比较N-1-i个数字,也就是最后一个数字不需要再比较。

算法图解

在这里插入图片描述

时间复杂度O(n^2)

冒泡代码:
package a_xlsort;

import java.util.Arrays;

public class BubbleSort {
    public static void main(String[] args) {
        int[] arr = new int[]{5, 7, 2, 9, 4, 1, 0, 5, 7};
        bubbleSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void bubbleSort(int[] arr) {
        // 需要N-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]){
                    int temp=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                }
            }
        }
    }

}


直接插入排序(InsertSort)

算法思路

从第二位开始,认为前面是有序的,将当前位和前面比较交换

  • 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
  • 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
算法图解

在这里插入图片描述

时间复杂度O(n^2)

直插代码:
package a_xlsort;

import java.util.Arrays;

public class InsertSort {
    public static void main(String[] args) {
        int[] arr = new int[]{5, 3, 2, 8, 5, 9, 1, 0};
        insertSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public 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;j--) {
                    if(temp<arr[j]){
                        //把前一个数字赋给后一个数字
                        arr[j+1]=arr[j];
                    }else {
						break;
					}
                }
                arr[j+1]=temp;
            }
        }
    }

}



归并排序(MergeSort)

算法思路

每趟从待排序的记录中选出关键字最小的记录,顺序放在已排序的记录序列末尾,直到全部排序结束为止。

  • 递归划分,化成单独一个元素为一个数组
  • 利用类似合并链表的方法,将每个单个的数组进行合并
  • 直到所有的子数组合并成一个数组
算法图解

在这里插入图片描述

时间复杂度O(nlogn)

归并代码:
package demo4;

import java.util.Arrays;

public class MergeSort {

	public static void main(String[] args) {
		int[] arr = new int[] {1,3,5,2,4,6,8,10};
		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=(high+low)/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++;
			}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];
		}
	}

}



快速排序(QuickSort)

算法思路

选择第0位坐标位,小在左大在右,两边分别递归(冒泡的优化)

  • 从数列中挑出一个元素,称为 “基准”(pivot);
  • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
  • 直到起始位置和结束位置相同时,递归结束
算法图解

在这里插入图片描述
在这里插入图片描述

在每一次比较后都会涉及某个值的覆盖,覆盖后再次判断进行移动,直到指到同一个位置,此时将标准数,赋值给该位置。 找左边第一个做基准的话必须先移动右边。(总之确保最后将标准数插入到正确位置,确保出现数据丢失问题)

时间复杂度O (nlogn)

快排代码:
package demo4;

import java.util.Arrays;

public class QuickSort {

	public static void main(String[] args) {
		int[] arr = new int[] {3,4,6,7,2,7,2,8,0,9,1};
		quickSort(arr,0,arr.length-1);
		System.out.println(Arrays.toString(arr));
	}
	
	public static void quickSort(int[] arr,int start,int end) {
		if(start<end) {
			//把数组中的第0个数字做为标准数
			int stard=arr[start];
			//记录需要排序的下标
			int low=start;
			int high=end;
			//循环找比标准数大的数和比标准数小的数
			while(low<high) {
				//右边的数字比标准数大
				while(low<high&&stard<=arr[high]) {
					high--;
				}
				//使用右边的数字替换左边的数
				arr[low]=arr[high];
				//如果左边的数字比标准数小
				while(low<high&&arr[low]<=stard) {
					low++;
				}
				arr[high]=arr[low];
			}
			//把标准数赋给低所在的位置的元素
			arr[low]=stard;
			//处理所有的小的数字
			quickSort(arr, start, low);
			//处理所有的大的数字
			quickSort(arr, low+1, end);
		}
	}

}


希尔排序(ShellSort)

算法思路

设置一个步长,将每组里面的数字比较交换,直到步长为1.

  • 选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
  • 按增量序列个数 k,对序列进行 k 趟排序;
  • 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
算法图解

在这里插入图片描述

在这里插入图片描述

希尔代码:
package demo4;

import java.util.Arrays;

public class ShellSort {

	public static void main(String[] args) {
		int[] arr = new int[] { 3, 5, 2, 7, 8, 1, 2, 0, 4, 7, 4, 3, 8 };
		System.out.println(Arrays.toString(arr));
		shellSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	
	public 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]) {
						int temp = arr[j];
						arr[j] = arr[j + d];
						arr[j + d] = temp;
					}
				}
			}
			System.out.println("第" + k + "次排序结果:" + Arrays.toString(arr));
			k++;
		}
	}

}


堆排序(HeapSort)

大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;

算法思路
  • 创建一个堆 H[0……n-1];

  • 把堆首(最大值)和堆尾互换;

  • 重新调整为一个大(小)顶堆(最后一个非叶子节点开始调整)

  • 重复步骤 2,直到堆的尺寸为 1。

算法图解

在这里插入图片描述

堆代码:
package demo4;

import java.util.Arrays;

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);
		}
	}

}

基数排序(RadixSort)

算法思路
  • 计算数组中最大元素的位数,设置为循环次数
  • 从个位开始,放入对应的数组(队列)
  • 按先入先出的顺序取出
  • 最后循环2 3操作直到最高位
算法图解

在这里插入图片描述

基数代码:
package demo4;

import java.util.Arrays;

import demo2.MyQueue;

public class RadixQueueSort {

	public static void main(String[] args) {
		int[] arr = new int[] {23,6,189,45,9,287,56,1,798,34,65,652,5};
		radixSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	
	public static void  radixSort(int[] arr) {
		//存最数组中最大的数字
		int max=Integer.MIN_VALUE;
		for(int i=0;i<arr.length;i++) {
			if(arr[i]>max) {
				max=arr[i];
			}
		}
		//计算最大数字是几位数
		int maxLength = (max+"").length();
		//用于临时存储数据的队列的数组
		MyQueue[] temp = new MyQueue[10];
		//为队列数组赋值
		for(int i=0;i<temp.length;i++) {
			temp[i]=new MyQueue();
		}
		//根据最大长度的数决定比较的次数
		for(int i=0,n=1;i<maxLength;i++,n*=10) {
			//把每一个数字分别计算余数
			for(int j=0;j<arr.length;j++) {
				//计算余数
				int ys = arr[j]/n%10;
				//把当前遍历的数据放入指定的队列中
				temp[ys].add(arr[j]);
			}
			//记录取的元素需要放的位置
			int index=0;
			//把所有队列中的数字取出来
			for(int k=0;k<temp.length;k++) {
				//循环取出元素
				while(!temp[k].isEmpty()) {
					//取出元素
					arr[index] = temp[k].poll();
					//记录下一个位置
					index++;
				}
			}
		}
	}
	
}


选择排序(SelectSort)

算法思路
  • 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。

  • 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。

  • 重复第二步,直到所有元素均排序完毕。

算法图解

在这里插入图片描述

选择代码:
package demo4;

import java.util.Arrays;

public class SelectSort {

	public static void main(String[] args) {
		int[] arr = new int[] {3,4,5,7,1,2,0,3,6,8};
		selectSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	
	//选择排序
	public 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[minIndex]>arr[j]) {
					//记录下最小的那个数的下标
					minIndex=j;
				}
			}
			//如果最小的数和当前遍历数的下标不一致,说明下标为minIndex的数比当前遍历的数更小。
			if(i!=minIndex) {
				int temp=arr[i];
				arr[i]=arr[minIndex];
				arr[minIndex]=temp;
			}
		}
	}

}


算法比较

在这里插入图片描述

总结

一、时间性能

平均情况下,记住一个口诀:快些以 n log2 n 的速度归队

快=快速排序,些=希尔排序,归=归并排序,队=堆排序

这四种排序算法,时间都是 n log2 n 的,除了这四个之外,其他的排序算法平均时间都为 n^2

记住一个特殊的排序算法:基数排序的时间复杂度

d(n+rd),其中 d 是分配和收集的趟数,n 是原始序列的数目(分配次数),rd 是桶的个数,也就是关键字最大位数(收集次数)

最坏的情况下,这四个中,快速排序为 n^2,其余的和平均时间相同,还是 n log2 n

二、稳定性

心情不稳定,快些选一堆好友来陪我

快=快速排序

些=希尔排序

选=简单选择排序

堆=堆排序

其余的排序算法都稳定


源码地址:https://github.com/Xxianglei/AC_Offer

  • 13
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值