1.希尔排序
排序原理:
具体实现方式是,先选定一个增量,通常为数组长度的一半,然后按照此增量将数组分为若干组(有的算法使用更复杂的增量序列)。对每一组使用插入排序进行排序。然后逐步减小增量,再次进行分组和排序,直到增量为1时,使用插入排序得到最终的有序数组。
自我理解:
恕作者菜,真不能简单以自己的话表达出来,当个物理公式半理解半背了。
以下基本属于“公式”,
利用数组长度确定初始增量h。
初始增量h确定:
while (h < len/2 )
{
h = 2 * h + 1;
}
然后从前往后以h个为间隔分组,每组数据分别进行插入排序(笔记一有积累),然后进行再次确定h以重新分组或判断结束,再次确定h=h/2,此为以此循环,如此反复便排序完成。
主要函数shell
static void shell(int* arr, int len)
{
int h = 1;
int temp = 0;
while (h < len)
{
h = h * 2 + 1;
}
for (; h >= 1; h = h / 2)
{
cout << h << endl;
for (int i = 0; i+h < len; i++) //插入排序起始
{
for (int j = i + h; j-h>=0; j = j - h)
{
if (arr[j-h] > arr[j])
{
temp = arr[j-h];
arr[j-h] = arr[j];
arr[j] = temp;
}
}
} //插入排序结束
}
}
判断大小函数greater,交换位置函数swap。
2.归并排序
排序原理:
1.递归地将序列分成左右两个子序列,直到每个子序列只有一个元素为止;
2.将左右两个有序子序列合并成一个有序序列,合并的过程中比较左右两个子序列的元素大小,将较小的元素放入新序列中;
3.重复步骤2,直到所有子序列合并为一个有序序列。
自我理解:
创建一个与被排列数组相等的辅助数组,先将全部(被排列)数组中的数据拆为一个个单独的个体,一个数为一段数组(序列),然后从前往后两个通过判断交换和为一个数组并放入对应的辅助数组上的位置,简称有序合成。如索引0,1和2,3上的数据交换完毕后,就用辅助数组上的0,1,2,3索引位依次接取。注意!辅助数组每次接取完,原数组都要借助辅助数组把更改的数据覆盖掉自己原来的数据。根据以上合成的长度,再进行相同长度的两段数组有序合成(从前往后)。
具体做法:
1.让数组不断细分成最低级,再逐步合成(1+1合,2+2合……)。
2.具体合成做法,得到当前数组索引最小值,最大值,再用两个型数据p1,p2接取最小值和加1的中间值,不断地比较p1,p2处位置数组数据大小,如果lo处的数据被比较出去并放在辅助数组对应位置,p1++,让前段有序数组段用向后一位数据继续比较,反之,p2++,让后端有序数组段用向后一位数据继续比较。
3.每次合成完后,把辅助数组复制给原数组。
4.如果此次合成hi<=lo,就结束合成。
主要函数代码块:
static void merge(int* arr, int len)
{
int *assit=new int[len];
for (int i = 0; i < len; i++)
{
assit[i] = arr[i];
}
sort(arr, 0, len - 1,assit);
}
protected:
static void sort(int* arr, int lo, int hi,int *assit)
{
if (hi <= lo)
{
return;
}
int mid = lo + (hi - lo) / 2;
sort(arr, lo, mid,assit); //对于当前数组段左合成
sort(arr, mid + 1, hi,assit); //对于当前数组段右合成
merge(arr, lo, mid, hi,assit); //对于当前数组段2合1
}
static void merge(int* arr, int lo, int mid, int hi,int *assit)
{
int i = lo; //为放入辅助数组的索引
int p1 = lo;
int p2 = mid + 1; //+1原因,调整正确的数组段初始索引
while (p1 <= mid && p2 <= hi)
{
if (greater(arr[p1], arr[p2])) //arr[p1]>arr[p2]时返回真
{
assit[i++] = arr[p2++];
}
else
{
assit[i++] = arr[p1++];
}
}
while (p1 <= mid)
{
assit[i++] = arr[p1++];
}
while (p2 <= hi)
{
assit[i++] = arr[p2++];
}
for (int index = lo; index <= hi; index++)
{
arr[index] = assit[index];
}
}
int p2 = mid + 1;
这里调整中间索引位置是为了对应上之前已排序好数组段,这样当一方数组段先向复制数组放入数据后,通过正确的索引结束,然后无脑将剩下的数组段输入辅助数组。
3.快速排序
排序原理:
-
选取一个基准元素pivot,将待排序序列分为两部分,左边的元素均小于等于pivot,右边的元素均大于等于pivot。
-
对左右两个部分重复步骤1,直到每个部分只剩下一个元素或是空序列为止。
-
将所有部分拼接起来,即得到排好序的序列。
个人理解:
其目的是将当前数组段基准元素放到一定位置,它的左边都是小于的它的元素,右边都是大于,以此基础不断细分至只有两个元素(或一个)的交换比较,那么数组就排序完成。可以以零索引处的数据作为第一次的基准元素,设定一个left为基准元素的后一位索引,right为当前数组段的最后一位,遍历right处的数据,right--,小于基准元素或等于left时停止,,再遍历left索引处的数据,left++,大于基准元素或等于right时停止,如果left<right,交换letf和right索引处的数据,反之,交换基准元素和right处的数据。为什么要交换right索引的数据?因为我们先遍历的是right索引的值,right会先后退等于letf,交换right处的值也没错,但是当right后退而letf前进满足另一种情况停止循环时,right处的值才为数组中最后一位小于基准元素的值,应该要与它交换。
主要代码块
static void sort(int* arr, int len)
{
sort(arr, 0, len - 1);
}
protected:
static int partition(int* arr, int lo, int hi)
{
int key = arr[lo];
int left = lo;
int right = hi+1;
while (true)
{
while (greater(arr[--right],key))
{
if (right == left)
{
break;
}
}
while (greater(key,arr[++left]))
{
if (left == right)
{
break;
}
}
if (left >= right)
{
break;
}
else
{
exch(arr, right, left);
}
}
exch(arr, lo, right);
return right;
}
static void sort(int* arr, int lo, int hi)
{
if (hi <= lo)
{
return;
}
int partition = Quick::partition(arr, lo, hi);
Quick::showArr(arr, 10);
sort(arr, lo, partition - 1);
sort(arr, partition+1,hi);
}
此时可能又有疑问,为什么int right = hi+1;看起来特殊,其实是为了迎合int left = lo;它直接等于了基准元素的索引,后面的代码中先++才避免了遍历到基准元素。那为什么right不单干呢?不+1,但我后--,但这样会导致遍历正确的数据时索引过时,交换数据时错乱。索引时right,交换时(right-1)。
最后,欢迎大佬指证错误啊,小弟属实菜。