本文已参与「新人创作礼」活动,一起开启掘金创作之路。
排序算法是软件开发中非常重要的一环。本期就来讨论关于算法在面试中会遇到的一些问题,其中包含个人一些理解,由于本人学识有限,难免会有纰漏之处,还望读者能多多包涵。
1 冒泡排序
我们讨论n个数的数组从小到大排序,将第1个数和第2个数进行比较,如果前者更大则交换两者位置,否则不交换。再比较第2个数和第3个数,以此类推,最后是第n-1个数和第n个数比较,一趟下来,最大的数必然在数组最后的位置。这样就排好了一个数,第二趟又是将第1个数和第2个数进行比较,最后是第n-2个数和第n-1个数比较,一趟下来,第二大的数必然在数组倒数第二的位置。如此循环,最后只剩一个数时数组即为有序。
之所以叫冒泡排序,就是因为大的数会不断往后移动,就像冒泡一样。
时间复杂度,最后一趟只需比较一次,所以是1+2+...+n-1
代码Java版:
public static void bubbleSort(int[] array){
int k;
for(int i=0;i<array.length-1;i++){
for(int j=0;j<array.length-1-i;j++){
if(array[j]>array[j+1]){
k=array[j];
array[j]=array[j+1];
array[j+1]=k;
}
}
}
//从小到大输出排好序的数组
for(int i=0;i<array.length;i++){
System.out.print(array[i]+" ");
}
}
为了避免后面已经有序的情况下依然进行比较,例如数列:{1,-1,0,5,9,10,12},第一趟就完成了排序,但是算法还是会继续比较,第二轮,第三轮...,所以可以设置一个标志位,如果一趟下来没有进行排序,说明数列已经是有序状态,这样可以节省时间,对此进行了优化,代码如下:
public static void bubbleSort(int[] array){
int k;
for(int i=0;i<array.length-1;i++){
boolean isSorted = true;//有序标记,每一轮的初始是true
for(int j=0;j<array.length-1-i;j++){
if(array[j]>array[j+1]){
isSorted = false;//有元素交换,所以不是有序,标记变为false
k=array[j];
array[j]=array[j+1];
array[j+1]=k;
}
}
if(isSorted){
break;
}
}
//从小到大输出排好序的数组
for(int i=0;i<array.length;i++){
System.out.print(array[i]+" ");
}
}
2 选择排序
选择排序的方法主要有两种,分别是简单选择排序以及堆排序,它们都是从待排序的数据元素中选择合适的元素放到合适的位置来进行排序。
2.1 简单选择排序
我们讨论n个数的数组从小到大排序。基本思想就是:每一趟从待排序的元素中选择最小的一个元素作为首元素,直到所有元素排完为止。
具体一点就是:一开始从n个数中选择一个最小值,将其放在第一个位置上,然后从剩下的n-1个数中选取最小值放在第2个位置上。重复过程直到剩下最后一个元素,数组即为有序。代码如下;
public static void SimpleSelectSort(int[] array){
int temp,min;
for(int i=0;i<array.length-1;i++){
min=i;
for(int j=i+1;j<array.length;j++){
if(array[j]<array[min]){
min=j;
}
}
if(min!=i){
temp=array[i];
array[i]=array[min];
array[min]=temp;
}
}
//从小到大输出排好序的数组
for(int i=0;i<array.length;i++){
System.out.print(array[i]+" ");
}
}
2.2 堆排序
堆:一种数据结构,说白了就是完全二叉树。分为大顶堆和小顶堆。大顶堆是各节点的值都比左右子节点值大或相等;小顶堆是各节点值都比左右子节点小或相等。
二叉树一般采用链式结构存储,但因为是完全二叉树,所以可以采用顺序结构存储,也就是数组结构。 在数组中,节点array[i]的孩子节点为array[2i+1]和array[2i+2]。
那我们实现起来就是:
- 构造大顶堆
- 交换堆顶和末尾节点
- 重置大顶堆,再到第2步。
public static void HeapSort(int[] array){
int len=array.length; //数组长度
for(int i=len/2-1;i>=0;i--){ //构建大顶堆
HeapSort2(array,i,len);
}
for(int i=len-1;i>0;i--){
int temp = array[0];
array[0] = array[i];
array[i] = temp; //交换首位
len--;
HeapSort2(array,0,len); //重置大顶堆
}
//从小到大输出排好序的数组
for(int i=0;i<array.length;i++){
System.out.print(array[i]+" ");
}
}
public static void HeapSort2(int[] array,int i,int len){
int left = 2*i+1; //i的左子节点
int right = 2*i+2; //i的右子节点
int current = i; //默认父节点是最大值
if(left < len && array[left] > array[current]){ //如果有左子节点且更大,更新current的值
current = left;
}
if( right < len && array[right] > array[current]){ //如果有右子节点且更大,更新current的值
current = right;
}
if(current != i){
int temp = array[i]; //如果有子节点大于父节点则交换
array[i] = array[current];
array[current] = temp;
HeapSort2(array,current,len); // 互换之后,子节点的值变了,如果该子节点也有自己的子节点,仍需要再次调整。
}
}