排序算法的基本分类
1.插入排序
插入排序 时间复杂度是O(n^2) 不稳定
插入排序介绍:对于欲排序的元素以插入的方式寻找该元素的适当位置,以达到排序的目的 * 插入排序思想:把n个待排序的元素看成未一个有序表和一个无序表,开始时有序表中只包含一个元素, * 无序表中取出第一个元素,把它的排序码一次与有序表中的排序码进行比较, * 将它插入到有序表中的适当位置,使之成为新的有序表 * 插入排序的缺点:当需要插入的数是较小的数时,数据向后移动的次数明显增多
public static void insertSort(int[] array) {
for (int i = 1; i < array.length; i++) {
int insertValue = array[i];
int insertIndex = i - 1;
while (insertIndex >= 0 && insertValue < array[insertIndex]) {
//将当前被比较过的有序表中的元素向后移动一位
array[insertIndex + 1] = array[insertIndex];
//继续往前找
insertIndex--;
}
//如果待插入的数insertValue小于有序列表中的所有数,
// 或者待插入的数大于当前数组下标为insertIndex的数
//当退出循环时说明待插入的位置找到了,为insertIndex+1
if (insertIndex + 1 != i) {
array[insertIndex + 1] = insertValue;
}
//System.out.println("第"+(i+1)+"轮后的顺序是:"+ Arrays.toString(array));
}
System.out.println("插入排序后的顺序是:" + Arrays.toString(array));
}
2.希尔排序
希尔排序 时间复杂度是O(n log n) 不稳定
希尔排序是简单插入排序的更高效版本,也称为缩小增量排序 * 希尔排序的思想:就是把记录数按下标的一定增量分组,对每组使用世界插入排序算法排序, * 随着增量逐渐减少,每组包含的关键词越来越多,当增量减少至一时, * 整个文件恰被分成一组,算法便终止 * 希尔排序时,对有序序列在插入时采用交换法,ShellSortExchange() * 希尔排序时,对有序序列在插入时采用交换法,ShellSortMove()
public static void shellSortExchange(int[] array) {
int temp = 0;//用于临时存放交换的元素
int count = 0;//用于记录当前是第几轮排序
//gap即数组被分为gap数个小组,
for (int gap = array.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < array.length; i++) {
//按照插入排序的方法 遍历各个组中的所有元素,共有gap组
for (int j = i - gap; j >= 0; j -= gap) {
//如果当前元素大于加上步长后的那个元素,说明交换
if (array[j] > array[j + gap]) {
temp = array[j];
array[j] = array[j + gap];
array[j + gap] = temp;
}
}
}
// System.out.println("希尔排序第"+(++count)+"轮排序后的顺序是:"+Arrays.toString(array));
}
System.out.println("希尔交换式排序后的顺序是:" + Arrays.toString(array));
}
public static void shellSortMove(int[] array) {
//增量gap,并逐步缩小增量
for (int gap = array.length / 2; gap > 0; gap /= 2) {
//从第gap个元素,逐个对其所在的组进行直接插入排序
for (int i = gap; i < array.length; i++) {
int insertIndex = i;
int insertValue = array[insertIndex];
if (array[insertIndex] < array[insertIndex - gap]) {
while (insertIndex - gap >= 0 && insertValue < array[insertIndex - gap]) {
//移动
array[insertIndex] = array[insertIndex - gap];
insertIndex -= gap;
}
//当退出while后,就给insertValue找到了插入的位置
array[insertIndex] = insertValue;
}
}
}
System.out.println("希尔移位式排序后的顺序是:" + Arrays.toString(array));
}
3.选择排序
选择排序 时间复杂度是O(n^2) 不稳定
* 选择排序介绍:选择式排序是从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置后达到选择排序的目的 * 选择排序思想:第一次从arr[0]~ar[n-1]中选取最小值与arr[0]交换, * 第i次从arr[i-1]~arr[n-1]中选最小值与arr[i-1]交换, * 第n-1次从arr[i-2]~arr[n-1]中选最小值与arr[n-2]交换, * 得到一个按从小到大排序的有序序列
public static void selectSort(int[] array) {
for (int i = 0; i < array.length; i++) {
int minIndex = i;//每次存放找到的最小值的下标
int min = array[i];//存放最小值
for (int j = i + 1; j < array.length; j++) {
if (min > array[j]) {
min = array[j];//重置min
minIndex = j;//重置minIndex
}
}
//将最小值与当前遍历到的值交换
if (minIndex != i) {
array[minIndex] = array[i];
array[i] = min;
}
//System.out.println("第"+(i++)+"轮后");
}
System.out.println("选择排序排序后的顺序是:" + Arrays.toString(array));
}
4.冒泡排序
冒泡排序 时间复杂度是O(n^2) 稳定
冒泡排序思想:通过对待排序序列从前向后,依次比较相邻元素的值, * 如发现逆序则交换,使值较大的元素从前移动到后面, * 就像水底下的气泡一样组件向上冒 * 冒泡排序优化:如果一趟比较下来没有进行过交换,说明序列有序, * 因此再排序过程中设置一个标志flag,判单是否进行过交换, * 从而减少不必要的比较
public static void bubbleSort(int[] array) {
for (int i = 0; i < array.length - 1; i++) {//这里注意 -1因为每趟排序都是把最大的数交换到了当前排序数的最后面
int temp = 0;
boolean flag = true;
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j] > array[j + 1]) {
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
flag = false;
}
}
//System.out.println("第"+(i+1)+"趟排序后端数组:"+Arrays.toString(array));
if (flag = true) {
//代表没有发生交换,数组有序,就不用交换了
break;
}
}
System.out.println("冒泡排序排序后的顺序是:" + Arrays.toString(array));
}
5.快速排序
快速排序 时间复杂度是O(n log n) 不稳定
* 快速排序是对冒泡排序的一种改进,也属于交换排序 * 快速排序思想:通过一趟排序将要排序的数据分割成独立的两部分, * 其中一部分的所有数据都比另外一部分的所有数据都要小, * 然后再按照此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行, * 以此达到整个数据编程有序序列
public static void quickSort(int[] array, int left, int right) {
int l = left;//左下标
int r = right;//右下标
int pivot = array[(left + right) / 2];//中轴值
int temp = 0;//临时变量
//while循环的目的是让比pivot值小的都在pivot的左边,比pivot值大的都在pivot的右边
while (l < r) {
//从左向右找大于pivot值
while (array[l] < pivot) {
l += 1;
}
//从右向左找小于pivot的值
while (array[r] > pivot) {
r -= 1;
}
//左右两边的值符合规则
if (l >= r) {
break;
}
//交换找到的值
temp = array[l];
array[l] = array[r];
array[r] = temp;
//如果arr[l]==pivot 前移 r--
if (pivot == array[l]) {
r--;
}
//如果arr[r]==pivot 后移 l++
if (pivot == array[r]) {
l++;
}
}
if (l == r) {
//防止栈溢出
l += 1;
r -= 1;
}
//向左递归
if(left<r){
quickSort(array,left,r);
}
//向右递归
if(l<right){
quickSort(array,l,right);
}
}
6.桶排序
桶排序 时间复杂度O(n+k) 稳定
* 桶排序思想:将数据 分组放到桶中,递归排序桶中的数据, * 当桶中的bucketSize为1时将数据加入到返回集合中,bucketSize为大桶中每个小桶的大小 * 当桶中的bucketCount为1时 将bucketSize的值减1.bucketCount为大桶中小桶的个数
public static List<Integer> bucketSort(List<Integer> list, int bucketSize){
//递归的出口
if(list==null||list.size()<2){
return list;
}
//遍历list找到最大与最小的值
int max=list.get(0);
int min=list.get(0);
for (int num: list) {
if(num<min){
min=num;
}
if(num>max){
max=num;
}
}
//定义返回结果集
List<Integer> retList = new ArrayList<Integer>();
//求出bucketCount
int bucketCount=(max-min)/bucketSize+1;
//定义大桶
List<List<Integer>> buckets = new ArrayList<>(bucketCount);
//初始化小桶
for (int i = 0; i < bucketCount; i++) {
buckets.add(new ArrayList<>());
}
//遍历原始的集合将数据放入到桶中
for (int i = 0; i < list.size(); i++) {
int value=list.get(i);
int index=(value-min) /bucketSize;
buckets.get(index).add(value);
}
//依次将桶中的数据进行排序
for (int i = 0; i < buckets.size(); i++) {
if(bucketSize==1){
//说明要么只有一个元素,要么桶中的元素都相同
for (int j = 0; j < buckets.get(i).size(); j++) {
retList.add(buckets.get(i).get(j));
}
}else {
if(bucketCount==1){
bucketSize--;
}
//当bucketSize不等于1说明存在两个以上不同种类的数据
//将小桶中的集合看成原集合再次遍历
List<Integer> temp = bucketSort(buckets.get(i), bucketSize);
//依次将递归返回的集合存放到结果集中
for (Integer num : temp) {
retList.add(num);
}
}
}
return retList;
}
7.归并排序
归并排序 时间复杂度O(n log n) 稳定
* 归并排序介绍:一开始先把数组从中间划分成两个子数组,一直递归地把子数组划分成跟小的数组, * 直到子数组里面只有一个元素,才开始排序 * 归并排序思想:把一个复杂问题分成两个或多个相同或相似的子问题,然后把子问题分成更小的子问题, * 直到子问题可以简单的直接请求,最原问题的解就是子问题的合并, * 归并排序将分治的思想体现得淋漓尽致
public static void mergeSort(Integer[] array, int lo, int hi){
//判断是否只剩下最后一个元素
if(lo>=hi){
return;
}
//从中间将数组分成两部分
int mid=lo+(hi-lo)/2;
//分别递归的将左右两半排好序
mergeSort(array,lo,mid);
mergeSort(array,mid+1,hi);
//将排好序的两半合并
merge(array,lo,mid,hi);
}
private static void merge(Integer[] array, int lo, int mid, int hi) {
//复制一份原来的数组
Integer[] copy=array.clone();
//定义k指针表示从什么位置开始修改原来的数组
int k=lo;
//i指针表示左半边的起始位置
int i=lo;
//j指针表示右半边的起始位置
int j=mid+1;
while (k<hi){
if(i>mid){
//当左半边的数都处理完毕,只需要将右半边的数逐个拷贝过去
array[k++]=copy[j++];
}else if(j>hi){
//当右半边的数都处理完毕,只需要将左半边的数逐个拷贝过去
array[k++]=copy[i++];
}else if(copy[j]<copy[i]){
//当右半边的数小于左半边的数,将右半边的数拷贝,j指针向后移动一位
array[k++]=copy[j++];
}else {
//当左半边的数小于右半边的数,将左半边的数拷贝,i指针向后移动一位
array[k++]=copy[i++];
}
}
}