无论是在面试中还是笔试的算法题中,或者竞赛的算法题中都会多多少少和这基本的8大排序有关,下列就这8大排序做一简单的介绍(Java实现),首先看一下基本的分类:

面试常问的8大排序总结

 

本文的顺序是按照本人理解的由简单到难而写的

1.直接插入排序

算法思想:这是一个向有序区间中插入一个元素的排序过程,设有n个数据的待排区间arr[0]~arr[n-1],则应该初始值arr[0]这一个元素作为一个有序的区间,arr[1]~arr[n-1]是无序的,然后从arr[1]开始遍历,拿到元素后和有序空间的值进行比较(注意这里和有序空间倒着比较会更好),比较后把该元素插入到合适的位置,然后有序数组有多一个元素,直至遍历完则排序结束。

例如:有数组arr[]={3,2,5,8,4,7,6},从第二个元素开始比较:

i=2时:2,3,5,8,4,7,6

i=3时:2,3,5,8,4,7,6

i=4时:2,3,5,8,4,7,6

i=5时:2,3,4,5,8,7,6

...以此类推

代码如下:

	public static void insertSort(int[] arr) {//直接插入排序函数
		int i,j;
		for(i=1;i<arr.length;i++) {//从第二个元素开始遍历
			int x=arr[i];//元素移动之前先把当前元素保存起来,防止移动时覆盖
			for(j=i-1;j>=0;j--) {//和当前遍历元素的前面的区间比较大小,直至找到合适的位置
				if(arr[j]>arr[i]) {
					arr[j+1]=arr[j];
				}
				else {
					break;
				}
			}
			arr[j+1]=x;//把当前遍历元素放入到合适的位置上
		}

	}
	public static void printArray(int[] arr) {//输出函数
		for(int i=0;i<arr.length;i++) {
			System.out.println(arr[i]);
		}
	}
	public static void main(String[] args) {
		int[] arr= {3,2,5,8,4,7,6};
		insertSort(arr);
		printArray(arr);
	}

时间复杂度:o(n^2),稳定性:稳定

2.直接选择排序法

算法思想:在一个乱序数组中,遍历数组,找到这个数组的最小值(或者最大值),然后和第一个元素进行交换(arr[0]=min/max),接着从数组中剩下的元素中继续找最小值(最大值),和第二个元素进行交换...以此类推。

例如:有数组arr[]={3,2,5,8,4,7,6}

第一次:2,3,5,8,4,7,6

第二次:2,3,5,8,4,7,6

第三次:2,3,4,8,5,7,6

......

代码如下:

	public static void selectSort(int[] arr) {
		for(int i=0;i<arr.length-1;i++) {
			int pmin=i;//初始化
			for(int j=i+1;j<arr.length;j++) {
				if(arr[j]<arr[pmin])pmin=j;//获得最小值得下标(即就得到了最小值)
			}
			if(i!=pmin) {//为了防止两个相同的数交换,加个判断
				int temp=arr[i];
				arr[i]=arr[pmin];
				arr[pmin]=temp;
			}
		}
	}
	public static void printSort(int[] arr) {
		for(int i=0;i<arr.length;i++) {
			System.out.println(arr[i]);
		}
	}
	public static void main(String[] args) {
		int[] arr= {3,2,5,8,4,7,6};
		selectSort(arr);
		printSort(arr);
	}

时间复杂度:o(n^2),稳定性:不稳定

3.冒泡排序法

算法思想:在一个无序的数组中,自上向下的相邻的两个数进行比较交换,让小的值冒上来,让大的值沉到底,即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。

例如:有数组arr[]={3,2,5,8,4,7,6},根据数组的特点我们可以尝试着从后往前跑(即7和6相比后交换,4和6相比交换...)

第一次:2,3,4,5,8,6,7

第二次:2,3,4,5,6,8,7

第三次:2,3,4,5,6,7,8

代码如下:

public static void bubbleSort(int[] arr) {
		int tag=1;
		for(int i=0;tag==1&&i<arr.length-1;i++) {//使用tag的目的就是是,这个操作比较3次就排好序了,就可以结束了,不让程序在在哪来两两比较
			tag=0;
			for(int j=arr.length-1;j>i;j--) {
				if(arr[j-1]>arr[j]) {//当排序完成或者j率先等于-1时,这段不执行,然后tag=0;结果上层的for循环直接结束
					int temp=arr[j-1];
					arr[j-1]=arr[j];
					arr[j]=temp;
					tag=1;
				}
			}
		}
		//下面这段代码是我常用的排序的,思想类似于冒泡排序法(更重要的是好记)
		/*for(int i=0;i<arr.length-1;i++) {
			for(int j=i+1;j<arr.length;j++) {
				if(arr[i]>arr[j]) {
					int temp=arr[i];
					arr[i]=arr[j];
					arr[j]=temp;
				}
			}
		}*/
	}
	public static void printSort(int[] arr) {
		for(int i=0;i<arr.length;i++) {
			System.out.println(arr[i]);
		}
	}
	public static void main(String[] args) {
		int[] arr= {3,2,5,8,4,7,6};
		bubbleSort(arr);
		printSort(arr);
	}

时间复杂度:o(n^2),稳定性:稳定

4.堆排序

算法思想:首先应该明确大根堆(大顶堆),小根堆(小顶堆);大根堆是建立在二叉树的一种结构,即每个根节点的值比左右子女的值大就是大根堆,每个根节点的值比左右子女的值小就是小根堆。第一步:将一个无序数组要构成一个大根堆,此时根节点的值是最大的;第二步:把最大值与二叉树中最后一个叶子节点进行交换,此时最后一个叶子节点就是最大值;第三歩:把剩下的n-1个元素继续用这种办法不断调整,直至排成有序的序列。如下图:

面试常问的8大排序总结对于一个无序的数组arr[]={4,6,8,5,9};并且应该熟知二叉树的一个性质:假设arr[i]是根节点,arr[2*i+1]即他的左子女,arr[2*i+2]是他的右子女。大根堆要满足:arr[i]>arr[2*i+1]&&arr[i]>arr[2*i+2]

步骤一:调整成大根堆

1.假定无序数组是这样的:

面试常问的8大排序总结

2.我们从最后一个非叶子节点进行调整,直至调整至根节点结束(arr.length/2-1=1)

面试常问的8大排序总结

面试常问的8大排序总结

面试常问的8大排序总结

此时一个大根堆已经构建成功

步骤二:将最后一个节点和根节点进行交换,继续重建大根堆,交换....

面试常问的8大排序总结

面试常问的8大排序总结

直至调整成:

面试常问的8大排序总结

简单的总结一下思路:

1.将一个无序数组构建成一个大根堆;

2.将根节点和最后一个叶子节点进行交换,将最大的元素“沉底”;

3.重新构建成一个大根堆,然后交换根节点元素和当前末尾元素,不断调整、交换直至有序。

代码如下:

public class HeapSort {
	public static void Sort(int[] arr) {
		//1.构建大顶堆
		for(int i=arr.length/2-1;i>=0;i--) {
			//从第一个非叶子结点从下至上,从右至左调整结构
			adjustHeap(arr,i,arr.length);
		}
		//调整堆结构+交换堆顶元素与末尾元素
		for(int j=arr.length-1;j>=0;j--) {
			swap(arr,0,j);//将堆顶元素与末尾元素进行交换
			adjustHeap(arr,0,j);//重新对堆进行调整
		}
	}
	//调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
	public static void adjustHeap(int[] arr,int i,int length) {
		int temp=arr[i];//先取出当前元素i
		for(int k=2*i+1;k<length;k=k*2+1) {//从i结点的左子结点开始,也就是2i+1处开始
			if(k+1<length&&arr[k]<arr[k+1]) {//如果左子节点小于右子节点,k指向右子结点
				k++;
			}
			if(arr[k]>temp) {//如果子节点大于父节点,将子节点赋值给父节点(不用进行交换)
				arr[i]=arr[k];
				i=k;
			}
			else {
				break;
			}
		}
		arr[i]=temp;
	}
	//交换堆顶元素和末尾元素
	public static void swap(int[] arr,int x,int y) {
		int temp=arr[x];
		arr[x]=arr[y];
		arr[y]=temp;
	}
	public static void main(String[] args) {
		int[] arr= {3,2,5,8,4,7,6,9,1,0};
		Sort(arr);
		System.out.println(Arrays.toString(arr));
	}
}

时间复杂度:o(nlogn),稳定性:不稳定

未完待续。。。

 

 

相关文章: