开始写博客的第一天
痛定思痛,经过几场面试的敲打,笔者稍稍有些疲累了。决定开始写博客,边学习边记录自己的生活。
先说经历,笔者是普通二本科班出身。学习成绩属于中下游,为什么呢?因为我喜欢逃课。但是代码能力和思维逻辑能力稍稍比大部分同学要强一些。
在校期间没拿过任何奖项,任何证书。为什么我这么垃圾的人还来写博客呢?因为不甘心。我一直认为成功是不断的努力得来的,并不是谁天生就厉害。当然这里可能不包括那些知名的神童。记得以前的我还是可以和清华大学的学生一较高下的人,转眼间自己就成了被压榨的打工人,或许有些自我感觉良好,但当时的我确实是比较厉害的,是从什么时候开始不喜欢学习了呢?记不清了,浑浑噩噩过了快8年,我每次都是勉强上了学校,期间还被学校劝退过,因为逃课。我不喜欢学校,是真的,我非常不喜欢上课,这抑制住了我的天性,特别是上那种必须让你听他讲课的老师的课,他并不信任你,认为你不听就一定学不会,所以就产生矛盾,导致学习兴趣下降。我不是排斥老师,但我想每个人都有他的选择,非让人去做相同的事,我认为只能有反效果。我喜欢研究,喜欢去思考,听课让我的学习效率下降,就像比起看视频,我更喜欢看文档,自己去思考代码逻辑,或者框架逻辑。言尽于此,是非成败皆是过去,未来是在自己脚下,选择什么样的路可能最终目的并不是自己想要的,但想必也不会相差太远。
这次就说说排序吧。
众所周知,排序算法比较出名的有八种。分别是 冒泡排序, 选择排序, 插入排序, 希尔排序, 归并排序, 快速排序, 堆排序, 基数排序。或许还有桶排序,计数排序这些。
冒泡排序
冒泡排序的思想无非是交换交换再交换。什么意思呢?先说我们排序要排的是什么,一般来说都是数字,那么势必要涉及到升序或者降序,我们以升序为例。在一组尚未排序的一维数组A={7,15,1,3,9,7,11}中,对于冒泡排序来说,它比较的是两个相邻的两个数,对于升序来说,大的数在后,小的数在前。
第一趟
7和15比较,7比15小,不交换,然后15和1比较,1小,交换,然后15和3比,交换,直到15排到最后一个元素,这里是不是比较清晰明了呢?意思就是把最大的值一直冒泡,把他排到最后,这样就是升序,反之则是降序。
第二趟
初始状态{7,1,3,9,7,11,15}
结束状态{1,3,7,7,9,11,15}
这里就已经排序好了,但是由于冒泡排序的特性,还是需要再排序,直到第N趟,N为元素的个数。当然我们可以做一个状态值,比如设定一个布尔值flag,初始状态为false,若没有发生交换,则置换为ture,结束循环。对于排序大致完成的数组,能够有效提升时间复杂度。
// 冒泡排序
@Override
public Integer[] bubbleSort(Integer[] array) {
for(int i = 0;i<array.length-1;i++){
for(int j = 0;j<array.length-1;j++){
if(array[j]>array[j+1]){
swap(array,j,j+1);
}
}
}
return array;
}
选择排序
什么是选择排序?选择排序顾名思义,选择二字已经说出了一切。即将当前数组的最大或者最小值排在某一端的最末端。同样的,排序升序或者降序。以升序为例,选择排序,自然就是选择最大的排在最后或者选择最小的排在最前,一般来说,两种都可实现,遍历反转了而已,我们这里取最小值排最前。
同样的一维数组A={7,15,1,3,9,7,11},在选择排序下。设置一个迭代值temp,记录当前数组最大值得下标。完成一趟则将其指向元素和当前数组第一个元素交换。
第一趟遍历整个数组,记录1的下标,将1和数组元素第一位7置换,则得到{1,15,7,3,9,7,11},这时标志位上升1,已排序的1不参与后续的排序。也可视为当前数组元素减一。
第二趟的结果是不是就是{1,3,7,15,9,7,11}呢?
时间复杂度比冒泡排序稍好是nlogn,但每次都要遍历剩下的数组。同样的,也可以设置一个标志位,如果迭代值temp未发生交换,则结束遍历。而且请注意,例子里的一维数组可能会误让你认为选择排序是稳定的,其实不然,选择排序是不稳定的。举个简单的例子,有三个数,15A-15B-14C,第一趟我们将AC对换,得到排好序的数组,CBA,但是稳定的应该是CAB,所以选择排序是不稳定的。
// 选择排序
@Override
public Integer[] selectSort(Integer[] array) {
int min;
for(int i = 0;i<=array.length-1;i++){
min = i;
for (int j = i+1;j<=array.length-1;j++){
if(array[j]<array[min]){
min = j;
}
}
if (i!=min)
swap(array,i,min);
}
return array;
}
插入排序
插入排序比较直观的一种方法是,它会开辟一个相同的数组空间,每次取原数组的一个元素,进行插入,插入的过程从前往后进行对比,直到新数组的某个后续元素大于他,而前驱元素小于或者等于它,满足这个条件,就插入进去。当然,这只是插入排序的一种比较容易理解的方式,事实上并不需要开辟一个新空间,插入排序的另一种实现方法,就是从当前数组第一个元素起,遍历第二个元素时,将其和第一个元素比较,如果是升序且第二个元素小于第一个元素,则将他们交换。这是,可以把这两个已经排好序的元素看做是一个数组,后续元素只需要再经历上述插入过程就行。插入排序的时间复杂度是O(n2),并且是稳定的。
// 插入排序
@Override
public Integer[] insertSort(Integer[] array) {
int k;
for(int i = 1;i<array.length;i++){
k = i;
for(int j = 0;j<i;j++){
if(array[i]<array[j]){
k = j;
break;
}
}
if (k!=i) {
for (int n = i; n > k; n--)
swap(array, n, n - 1);
}
}
return array;
}
希尔排序
归并排序
快速排序
// 快速排序
@Override
public Integer[] quickSort(Integer[] array,int left,int right) {
int k = 0;
if(left>right){
return null;
}
for(int m = left+1;m<right;m++){
if(array[m]<=array[left+k]){
for(int n = m;n>left+k;n--){
swap(array,n,n-1);
}
k++;
}
}
if(k!=0){
quickSort(array,left,left+k-1);
quickSort(array,left+k+1,right);
}
return array;
}
堆排序
基数排序
桶排序
计数排序