文章目录
选择排序的思路:
从头到尾遍历,找最大的元素的下标,找到之后把最大的元素和最后一个位置的元素交换
易错点:
- max作为保存最大元素下标的存在,每次使用完应该清零
- 找到值去交换,最大的元素位置是没有发生变化的
过程图示:
代码示例:
//普通的未经过优化的选择排序
void selectSort(int* array, int size)
{
int end = size - 1;
while (end != 0)
{
int max = 0;
int begin =0;
for (begin; begin <= end; ++begin)
{
if (array[max] < array[begin])
{
max = begin;
}
continue;
}
swap(array[max], array[end]);
end--;
}
}
优化后的选择排序:
将一次选择一个最大值变为一次选择一个最大值和最小值
最大值放在最后面的end位置,最小值放在最前面的begin位置
普通选择排序缺陷:
- 存在大量的重复比较
易错点:
- index的值每次遍历查找前要重置
- minpos和maxpos使用完之后的值也要重置
过程图示:
代码示例:
void selectSortD(int* array, int size)
{
int begin = 0;
int end = size - 1;
while (begin < end)
{
int index = begin + 1;
int minpos = begin;
int maxpos = begin;
for (index; index <= end; ++index)
{
if (array[index]>array[maxpos])
{
maxpos = index;
}
if (array[index] < array[minpos])
{
minpos = index;
}
}
//如果minpos在最后的位置,把minpos的位置与maxpos互换即可
if (minpos == end)
{
minpos = maxpos;
}
swap(array[begin], array[minpos]);
swap(array[end], array[maxpos]);
begin++;
end--;
}
}
归并排序(递归)的思路:
对一组数据,一直均分到一组只有一个元素,然后将其归并,逐步变为有序
易错点:
- 只有元素有序才可以归并
- begin2是以mid开始,如果以mid+1开始,会越界
- 先处理左半部分,在处理右半部分,都有序之后,归并,写回原数组
- 归并的时候申请辅助空间如果是在函数里,函数调用结束会释放,所以使用参数传递进去
- 空间只需要一份,所以不能当作mergeData的参数,因为递归进来每次都会创建
归并数据过程图示:
长短不一时,一方写完,另一方还未完,直接把另一方直接写入到后面
归并数据思路:
-
使用两个指针来依次对比左半部分和右半部分的第一个元素,先把小的放进去
-
放进去的那部分指针后移,继续比较,一直比到放完为止,长度不一致的情况循环结束单独处理
void mergeData(int* array,int left,int mid,int right,int* tmp)
{
int begin1 = left;
int end1 = mid;
int begin2 = mid;
int end2 = right;
int index = left;
while (begin1 < end1 && begin2<end2)
{
if (array[begin1] < array[begin2])
{
tmp[index] = array[begin1];
begin1++;
index++;
}
else
{
tmp[index] = array[begin2];
begin2++;
index++;
}
}
//处理长度不一致的情况
while (begin1 < end1)
{
tmp[index] = array[begin1];
begin1++;
index++;
}
while (begin2<end2)
{
tmp[index] = array[begin2];
begin2++;
index++;
}
}
为了方便调用,进行一下封装,其实不封装也无所谓,那时候还没学C++所以用malloc,现在自然用new
void mergeSort(int* array, int size)
{
int* tmp = new int[size];
_mergeSort(array, 0, size, tmp);
delete[] tmp;
}
说真的,我不是很喜欢递归的归并排序代码,我觉得memcpy那里需要加left真的很难理解
void _mergeSort(int* array, int left, int right,int* tmp)
{
if (right - left > 1)
{
int mid = left + ((right - left) >> 1);
_mergeSort(array, left, mid, tmp);
_mergeSort(array, mid, right, tmp);
mergeData(array, left, mid, right, tmp);
memcpy(array + left, tmp + left, sizeof(int)*(right - left));
}
}
不加left每次拷贝都会拷贝到左半部分,右半部分永远是空的
- 0-5这一整体,分组0-1 2-5,处理右边的部分的时候left是2,如果处理完了往回拷贝的时候不加left
- array默认指向数组第一个元素,等于2-5这边的数据没有接在前两个元素后面,而是有两个把之前的左半部分覆盖了
归并排序(递归)过程图示:
其实应该34一组,291一组,但是看思路就行了,这里画的严格按照代码来说是有问题的
归并排序(循环)的思路:
与递归不同的是,循环不需要先分组了,因为每个元素都是单独的,可以看作已经分好组了,直接开始归并
易错点:
- 需要在循环里手动赋予left的值
- i应该+=2*gap
- gap可以看作一组元素的个数,所以left,mid,right的值用gap表示
- mid和right有可能会超出size的范围造成越界,需要单独处理
归并排序(循环)过程图示:
归并排序(循环)代码:
void mergeSortN(int* array, int size)
{
int* tmp = new int[size];
int gap = 1;
while (gap < size)
{
for (int i = 0; i < size; i += 2 * gap)
{
int left = i;
int mid = left + gap;
int right = mid + gap;
if (mid > size)
{
mid = size;
}
if (right > size)
{
right = size;
}
mergeData(array, left, mid, right, tmp);
}
memcpy(array, tmp, sizeof(int)*size);
gap *= 2;
}
delete[] tmp;
}
至此,排序所有的内容基本都搞清楚了,短时间也不太可能会忘,忘了再过来看看就行了,开始新的篇章~