这篇文章仅仅为心中自证,不是算法教学,也不想误人子弟,谢谢各位。
第一章:一些感慨
我断断续续学习算法两年多了,这说起来是多么苦涩,是我笨嘛?一直不知道算法是什么东西。
从《算法导论》再到《C算法》不清楚看了多少遍,它们就是我过不去的坎吗?
不敢说什么大话,但是我有一个心得,学习算法,一定要理解,理解比会写更重要,会写,很有可能仅仅是记忆好,
但是过一段时间忘了, 就对这个算法完全没有印象了,我就是这样。
所以我以后学习算法,一定抱着理解的心态,理解了,就很好。
第二章:基本排序算法
2.1 冒泡排序
人们常说,冒泡排序是最初级的排序算法,人们说这句话的时候是从时间复杂度这个角度来说的,这么说或许没错,
但是,冒泡排序是相当漂亮的排序,这个“漂亮”是说,假如把排序算法可视化起来,x轴从小到大,依次对应0到n,y轴对应的是a[x],
然后排序开始了,你会发现冒泡很飘逸,看起来很赏心悦目,大概人们都喜欢美丽的泡泡吧。
冒泡排序的中心思想:不写中心思想至少也有十年了吧,记得高中之后语文老师就不搞什么中心思想之类的。
但是我还是要写一写冒泡排序的中心思想,而且,现在我不看资料,我想描述一下,看一看我到底能不能描述。
冒泡排序的思想是这样的,从数组a[0]开始,比较相邻的两项的大小顺序,假如顺序不对,则交换两项。
5 | 4 | 6 | 3 | 2 |
比如上面的数组,先比较a[0]和a[1],发现a[0]大于a[1],我们认为从小到大排,所以顺序就不对了,那就交换吧,交换之后就是
4 | 5 | 6 | 3 | 2 |
再然后就是比较a[1]和a[2]了,就这样,一次冒泡,最大项就冒到了顶部。
很明显,我们需要两层循环,可是第二次循环的时候,我们就没必要从0到n了,因为我们知道a[n-1]项已经是最大的了。
1 void BubbleSort(Item a[],int left,int right) 2 { 3 int i = 0,j = 0; 4 for(i = left; i < right; i++) 5 { 6 for(j = left;j < right -i - 1; j++) 7 { 8 compexch(a[j],a[j+1]); 9 } 10 } 11 } 12 void BubbleSort(Item a[],int left,int right) 13 { 14 int i = 0,j = 0; 15 for(i = left; i < right; i++) 16 { 17 for(j = right;j > i; j--) 18 { 19 compexch(a[j-1],a[j]); 20 } 21 } 22 }
2.2 快速排序
记得有谁说过,快速排序是在冒泡排序的基础上的,这个,我信了。
快速排序的中心思想:在我看来快速排序的中心思想是很简单的,我不知道快排的发明人是怎么想出来的。
快排的思想是,选择数组中的一个数,把它放到排序结束后它应该在的位置。
这是一句很有意思的话,我怎么知道一个数排序结束后它在哪呢?其实可以知道的,
我们希望一次排序结束后,这个数左边的所有数都比这个数小,这个数右边所有的数都比这个数大。
然后我们对它的左边用快排,然后对他的右边用快排。
最原始的写法是这样的。
1 int partition(int * a,int l,int r) 2 { 3 int i = l-1,j = r; 4 int v = a[r]; 5 for(;;) 6 { 7 while(a[++i] < v); 8 while(v < a[--j]) 9 if(j == l) 10 break; 11 if(i >= j) 12 break; 13 EXCH(a[i],a[j]); 14 } 15 EXCH(a[i],a[r]); 16 return i; 17 } 18 void QuickSort(int * a,int l,int r) 19 { 20 int i; 21 if(r <= l) 22 return; 23 i = partition(a,l,r); 24 QuickSort(a,l,i-1); 25 QuickSort(a,i+1,r); 26 }
partition函数的中心思想:partition函数的功能,就是从数组中选取一个数,然后让左边的全部小于它,右边的全部大于它,然后返回位置就行了。
但是上面的partition真是大有问题,在正序和逆序的时候,很多人都哭了,所以常见的做法是,选取的这个数是随机选取的。
一个或许可能的解决方案是这个,(我又空手敲代码了,我有可能写出来吗?)(没有,我又看了好多资料,调试了好久。)
int partition(int * a,int l,int r) { int i = l-1; int j = r; int m = l+rand()%(r-l+1); EXCH(a[m],a[r]); int v = a[r]; for(;;) { while(a[++i] < v); while(v < a[--j]) if(j == l) break; if(i >= j) break; EXCH(a[i],a[j]); PrintArray(a); } EXCH(a[i],a[r]); PrintArray(a); return i; }
2.3 插入排序
或许,我根本不适合学习算法,还是放弃吧,写完这一章。
插入排序的中心思想:我是这样理解的,待排序项一直左移,已经排好的一直右移,直到某一项小于待排序项。
从第二项开始一直执行这样的操作。
缺点:数组移动。
1 void InsertSort(int * a,int l,int r) 2 { 3 for(int i = l+1;i < r; i++) 4 { 5 int j = i; 6 int v = a[j]; 7 while(--j >= 0) 8 { 9 if(v < a[j]) 10 { 11 a[j+1] = a[j]; 12 } 13 else 14 { 15 break; 16 } 17 } 18 ++j; 19 a[j] = v; 20 PrintArray(a); 21 } 22 }
2.4 希尔排序
希尔排序是分组的插入排序,
划分公式可以是n*x+1,一个划分可能的划分是1 4 7 10 13 16,另一个可能的划分是1 5 9 13 17 21。
2.5 选择排序
选择排序,首先选取数组第一小元素放到第一项,数组第二小的元素放到第二项。
void Array::SelectSort() { int i = 0; int j = 0; for(i = 0; i < 10; i++) { int min = i; for(j = i+1;j < 10; j++) { if(first[j] < first[min]) { min = j; } } first[i].Exchange(first[min]); } }
2.6 归并排序
归并排序和快速排序的形式相反,两个递归之后跟一个归并函数。
归并函数的要点是,对已经排序过的数组进行排序。可以原位归并,或者借助一个数组。
下面的代码来自《C算法》不知道打错了没有。
1 void mergesort(Item * a,int l,int r) 2 { 3 int m = (r+l)/2; 4 if(r <= l) 5 { 6 mergesort(a,l,m); 7 mergesort(a,m+1,r); 8 merge(a,l,m,r); 9 } 10 } 11 merge(Item * a, int l, int m, int r) 12 { 13 int i,j,k; 14 for(i = m+1; i > l; i--) 15 aux[i-1] = a[i-1]; 16 for(i = m; j < r;j++) 17 aux[r+m-j] = a[j+1]; 18 //上面两行代码,为的是生成bitonic序列,下面才是排序开始。 19 for(k = l; k < r;k++) 20 if(less(aux[j],aux[i])) 21 a[k] = aux[j--]; 22 else 23 a[k] = aux[i++]; 24 }
第三章:总结
心有千千结,心思总难知,这篇文章仅仅为心中自证,不是算法教学,也不想误人子弟,谢谢各位。
来源:https://www.cnblogs.com/likeyiyy/p/3395871.html