常规唠嗑
- 算法的稳定性和正确性:有时候排序算法会涉及到稳定性,首先稳定性是在算法具备正确性的前提下,对于元素的位置是否发生变更的一个说法。假如一个算法运算结束,在最终的序列里,保持两个元素之间初始位置不变,则属于稳定。
- 有木有以前看过《啊哈,算法》的小伙伴,来来来,交流交流思想
归并
- 思路: 1,归并老被叫做二路归并,为什么是二路,不是一路,三路?就是说啊,这个算法,每次都有两条线去排序。 2,什么叫做归并?我不管你几路,最终我需要一个结果,所以,各路人马必须并拢。
- 重点:2条路同时排序;合并这两条路;先逐步分解一路,合并时,通过对参数的调用(分解第二路)
- 意会路线图:天下分久必合合久必分,二路归并完整提现。假设原始序列有5个小国元素,我现在要排出强弱顺序,你不能让我1号国家一个一个去打吧,一个一个打搞车轮战术么?而且这种拉风的排行榜,当然是越快越好。 所以,先划分组,组再划分组,然后排出强弱,最终合并。
- 解题注意点:1,分合同步:未排序的可以分,合的只能是有序子集;2,合并时注意子集大小判分之后的剩余元素;3,开辟新空间接收合并集;
二路代码 —思想核心
public static int[] sort(int[] sourceArray) {
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
//只有一个元素
if (arr.length < 2) {
return arr;
}
// 取出将整体划分为 2 路的中间节点
int middle = (int) Math.floor(arr.length / 2);
// 递归划分子集—逐步裂变
int[] left = sort(Arrays.copyOfRange(arr, 0, middle));
int[] right = sort(Arrays.copyOfRange(arr, middle, arr.length));
// 合并,通过改变递归数组的长度,逐步收拢子集
return merge(left,right);
}
归并代码
public static int[] merge(int[] left, int[] right) {
int[] result = new int[left.length + right.length];
int i = 0;
// 归并已排序两个子集
while (left.length > 0 && right.length > 0) {
if (left[0] <= right[0]) {
result[i++] = left[0];
left = Arrays.copyOfRange(left, 1, left.length);
} else {
result[i++] = right[0];
right = Arrays.copyOfRange(right, 1, right.length);
}
}
// 假如两个数组大小比对排序合并之后,left数组还存有剩余比之前元素大的,直接将剩余元素添加到合并集
while (left.length > 0) {
result[i++] = left[0];
left = Arrays.copyOfRange(left, 1, left.length);
}
// 假如两个数组大小比对排序合并之后,right数组还存有剩余比之前元素大的,直接将剩余元素添加到合并集
while (right.length > 0) {
result[i++] = right[0];
right = Arrays.copyOfRange(right, 1, right.length);
}
return result;
}
快速排序
- 思路:归并之所以快,是因为多路同时搞;快速排序为什么快速?我没有多条路走,但我可以一条路A—>B,也可以B—>A呀。我两头同时开搞,就不像冒泡,插入之类的,我只能是A—>B . 在通信传输里边,这好像是单工吧。优化通信信道时,会说将单工,变成半双工或者全双工。我理解,快速排序,就是单工—>双工的优化。
- 意会路线图:因为是左右两头同时开搞,所以,需要一个规则,左边亲们搞啥,右边亲们搞啥。因为是排序的大前提,所以是:选定一个基数,左边搞比基数大的,右边搞比基数小的。然后,先有个大致范围:即一趟下来能根据既定基数获取所有比之较大或者较小的子集。PS:哪边搞大哪边搞小,其实都阔以,看升序或者是降序。然后在各自子集里,重复相同步骤。
- 重点:需要基数、子集再排序(递归)
- 关键点:原数组中子集的临界点维护;未开辟新空间,注意原始数组的维护
代码
public static int[] quickSort(int[] arr,int l,int r){
// 确定一次运算的基准值
int base = arr[l];
int lindex = l,rindex=r;
while (lindex<rindex){
// 从右至左找到第一个小于基准值的位置
while (arr[rindex]>base && lindex<rindex){
rindex--;
}
// 从左至右找到第一个大于基准值的位置
while (arr[lindex]<base && lindex<rindex ){
lindex++;
}
// 处于中间位置,一轮运算暂未结束,进行交换
if(lindex<rindex && arr[lindex]!=arr[rindex]){
int temp = arr[rindex];
arr[rindex]=arr[lindex];
arr[lindex]=temp;
}
//向右移动左值指针,或者向左移动右指针都阔以
if(lindex<rindex && arr[lindex]==arr[rindex]){
lindex++;
}
}
// 分治前一轮的再排序
if(l<lindex-1){
arr = quickSort(arr,l,lindex-1);
}
if(rindex+1<r){
arr = quickSort(arr,rindex+1,r);
}
return arr;
}
一点不成熟的想法
- 快排和归并在分治的思想下,有着异曲同工之妙。都会划分子集,不同的地方在于,子集的划分规则以及被划分子集的特点,另外是否有新空间的申请。
- 我当年应该是被吓傻了。