前言
昨晚参加了今日头条的笔试,大部分都是算法题,而算法恰恰就是我的弱项,所以被虐得不轻。于是为了提升自己的算法能力,我在博客整理一些常用的算法及原理,巩固一下基础知识,顺便为秋招做准备。先讲一下最基础的排序算法吧(昨晚就是有一道关于排序的,我居然忘了排序算法的实现,以此为戒)
插入排序
1.直接插入排序(稳定)
思路:在插入第i个记录时,R1、R2、......、Ri-1已经排好序,这时将Ri的关键字ki依次与关键字ki-1、ki-2等进行比较,从而找到应该插入的位置并将Ri插入,插入位置及其后的记录依次向后移动。
java实现:
public class Main{ public static void main(String[] args){ //待排序的数组 int[] numbers = {3,56,12,456,5,23,16,15,56,456}; int i,j,temp; for(i = 1;i < numbers.length;i++){ temp = numbers[i]; j = i - 1; //将当前数与排好序的序列逐个比较,寻找插入位置 while(j >= 0 && numbers[j] > temp){ //将大于当前数的元素后移 numbers[j+1] = numbers[j]; j--; } numbers[j+1] = temp; } //输出排序后的数组 for(int number : numbers){ System.out.print(number + " "); } } }
2.折半插入排序(稳定)
思路:折半插入排序是对直接插入排序的完善。直接插入排序将无序区中开头元素R[i](1 <= i <= n-1)插入到有序区R[0..i-1]中,此时可以采用折半查找方法先在R[0..i-1]中找到插入位置,再通过移动元素进行插入。这样的插入排序称为折半插入排序或二分插入排序。
Java实现:
public class Main{ public static void main(String[] args){ //待排序的数组 int[] numbers = {3,56,12,456,5,23,16,15,56,456}; int i,j,temp,low,high,mid; for(i = 1;i < numbers.length;i++){ temp = numbers[i]; j = i - 1; low = 0; high = j; //通过折半查找插入的位置 while(low <= high){ mid = (low + high)/2; if(numbers[mid] < temp) low = mid+1; else high = mid-1; } //元素后移 for(j = i - 1;j >= high + 1;j--){ numbers[j+1] = numbers[j]; } numbers[high + 1] = temp; } //输出排序后的数组 for(int number : numbers){ System.out.print(number + " "); } } }
3.希尔排序(不稳定)
思路:希尔排序实际上是一种分组插入方法。其基本思想是:先取定一个小于n的整数d1(一般为n/2)作为第一个增量,把表的全部元素分成d1个组,所有相互之间距离为d1的倍数的元素放在同一个组中,在各组内进行直接插入排序;然后,取第二个增量d2(<d1,一般为d1/2),重复上述的分组和排序过程,直至所取的增量dt=1(dt<dt-1<...<d2<d1),即所有元素放在同一组中进行插入排序。
java实现:
public class Main{ public static void main(String[] args){ //待排序的数组 int[] numbers = {3,56,12,456,5,23,16,15,56,456}; int i,j,temp,gap; //增量置初值 gap = numbers.length / 2; while(gap > 0){ //对所有相隔gap位置的元素组采用直接插入排序 for(i = gap;i < numbers.length;i++){ temp = numbers[i]; j = i - gap; //对相隔gap位置的元素组进行排序 while(j >= 0 && temp < numbers[j]){ numbers[j+gap] = numbers[j]; j -= gap; } numbers[j+gap] = temp; } //减小增量 gap = gap/2; } //输出排序后的数组 for(int number : numbers){ System.out.print(number + " "); } } }
交换排序
1.冒泡排序(稳定)
思路:通过无序区中相邻元素间关键字的比较和位置的交换,使关键字最小的元素如气泡一般逐渐往上“漂浮”直至“水面”。整个算法是从最下面的元素开始,对每两个相邻的关键字进行比较,且使关键字较小的元素换至关键字较大的元素之上,使得经过一趟冒泡排序后,关键字最小的元素到达上端。接着,再在剩下的元素中找关键字次小的元素,并把它换到第二个位置上。以此类推,一直到所有元素都有序为止。
java实现:
public class Main{ public static void main(String[] args){ //待排序的数组 int[] numbers = {3,56,12,456,5,23,16,15,56,456}; int i,j,temp; boolean exchange = false; //比较,找出关键字最小的元素 for(i = 0;i < numbers.length - 1;i++){ for(j = numbers.length - 1; j > i;j--){ if(numbers[j-1] > numbers[j]){ //将numbers[j-1]与numbers[j]进行交换,将关键字最小的往前移 temp = numbers[j-1]; numbers[j-1] = numbers[j]; numbers[j] = temp; exchange = true; } } //若本趟没有发生交换,中途结束算法 if(!exchange) break; } for(int number : numbers){ System.out.print(number + " "); } } }
2.快速排序(不稳定)
思路:快速排序是由冒泡排序改进而得的,他的基本思想是:在待排序的n个元素中任取一个元素(通常取第一个元素)作为基准,把该元素放入适当位置后,数据序列被此元素划分成两部分,所有关键字比该元素关键字小的元素放置在前一部分,所有关键字比它大的元素放置在后一部分,并把该元素排在这两部分的中间(称该元素归位),这个过程称做一趟快速排序,之后对所有划分出来的两部分分别重复上述过程,直至每部分内只有一个元素或为空为止。
java实现:
public class Main{ public static void main(String[] args){ //待排序的数组 int[] numbers = {3,56,12,456,5,23,16,15,56,456}; QuickSort(numbers,0,numbers.length-1); for(int number : numbers){ System.out.print(number + " "); } } public static void QuickSort(int[] numbers,int s,int t){ int i = s,j = t,temp; //区间内至少存在两个元素的情况 if(s < t){ //用区间的第一个元素作为基准 temp = numbers[s]; //从区间两端交替向中间扫描,直至i = j为止 while(i != j){ //从右向左扫描,找第一个小于temp的numbers[j] while(j > i && numbers[j] >= temp){ j--; } //找到这样的numbers[j],numbers[i]与numbers[j]交换 numbers[i] = numbers[j]; //从左向右扫描,找第一个大于temp的numbers[i] while(j > i && numbers[i] <= temp){ i++; } //找到这样的numbers[i],numbers[i]与numbers[j]交换 numbers[j] = numbers[i]; } numbers[i] = temp; //对左区间递归排序 QuickSort(numbers,s,i-1); //对右区间递归排序 QuickSort(numbers,i+1,t); } } }
参考文献:
数据结构教程/李春葆主编 -北京:清华大学出版社,2013.1
软件设计师教程/褚华主编 -北京:清华大学出版社,2014