引言:
今天天气还是依然的冷,码字越来越不容易了,本来上次写了一个比较好的引言,但是因为电脑第二天没电,并且我没有保存,现在找不到了,所以今天我们的引言就这样吧!今天给大家介绍一下有关数据结构中的排序的内容,因为来不及一口气把所有的排序学完并且学明白,我们就把这些排序给分开进行讲解。所以今天我们学的是直接插入排序和希尔排序相关的知识分析和代码的实现。
1.排序的概念及其应用
1.1 排序的概念
排序:所谓的排序就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],并且r[i]在r[j]之前,而在排序后的序列中,r[i]任然在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的
内部排序:数据元素全部在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序的过程的要求不能在内存之间移动数据的排序
1.2 排序的应用(此时就可以去淘宝上截图一下,就是价格的排序之类的)(可以很好的充当一个筛选的作用)
1.3 常见的排序:插入排序(直接插入排序、希尔排序) 选择排序(选择排序、堆排序) 交换排序(冒泡排序、快速排序)归并排序
2.直接插入排序的实现及分析
2.1 基本思想:
直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按期关键码值的大小逐个插入到一个已经排好的有序序列中,直到所有的记录插入完为止,然后就得到一个新的有序序列。
2.2 生活中的实例:我们玩扑克牌的时候,就是用了插入排序的思想,如图:
2.3 具体的实现原理:当我们想要插入第i(i>=1)个元素时,前面的arr[0],arr[1],arr[2],……,arr[i-1]已经排好序,此时用arr[i]的排序码值与arr[i-1],arr[i-1],……,的排序码值进行比较,找到合适的位置将arr[i]插入,原来位置上的元素按照顺序向后移动
2.4 具体代码的实现(最详细的注释)
void InsertSort(int* arr, int n)
{
//此时这种排序方法是有要求的,比如一定是要要求这个数组中的元素已经是有序的了(所以此时我们排序是在默认这个数组有序的情况下进行的)
//此时将这个问题拆分一下(变成小问题来解决)
//拆分原理:(关键)此时假设[0,end]中的元素已经是有序的了,然后我们把我们想要插入的元素放在end+1的位置然后按照顺序插入进去,然后使[0,end+1]整个数组也有序(并且此时假设都是以升序来进行的排序)
int i = 0;
for (i = 0; i < n - 1; i++)//此时这个位置我们控制成了n-1,但我们要知道次数是到n-2就会结束的(并且此时因为下标的原理,此时这样写就是刚好,不然就会导致end+1的位置造成越界)
{
//这个for循环就是为了控制(end ,控制了end也就是控制了有顺序的元素的个数)插入的元素的个数(此时有了这个for循环就可以使我想插入几个元素就比较几次),显而易见,下面的那个while循环只是为了控制顺序插入一个数据(而此时有了这个for循环就可以使我顺序插入n个数据了)
//int end;//此时就是假设,我也不知道此时的end是什么东西,唯一知道的就是[0,end]此时是一个有序的数组
//此时想要控制插入的元素的个数,那么此时就可以知道end的值了,所以要对end进行初始化,所以就不可以按照上面那样写了,下成下面这个样子就行
int end = i;
int tmp = arr[end + 1];//此时为了进行排序,会进行位置的移动,所以为了防止end+1位置被覆盖,所以此时我可以把我的end+1的位置给先存起来(以便于待会可以更好的拿出来使用)
while (end >= 0)//此时进行码值的比较的时候(会有一个特殊的情况:就是我想要插入的元素的码值比数组中的元素的码值都要小),所以此时我就要比较很多次,假如比end=0元素的值都要小,此时end再减减,就会导致end=-1,所以此时就会导致越界访问,所以此时我们这个循环就一定要把这个比较的次数(也就是end--)给控制好,让其不为<0,所以此时这个循环的条件就是end>=0,才进行元素的调换
{
if (arr[end] > tmp)//此时的这个tmp就是表示我要插入的值(并且此时这个我要插入的值就是在end+1,这个数组的位置处),所以我此时就可以进行比较码值的大小,这样就可以把我想要排序的元素按照顺序从end+1的位置插入到他合适的位置处
{
arr[end + 1] = arr[end];//这个位置就是进行排序嘛,就是码值的比较,然后我要排序的元素此时就可以从数组中的最后一个位置慢慢的移动到我的合适的位置
end--;//这个end--的意思就是慢慢的向前走的意思,直到找到合适的位置或者走到数组的最前面(也就是end=0的位置)(这样我就可以跟数组中的所有元素进行比较了)
}
else
{
//此时这个else的意思是显而易见的:就是我想要插入的元素比数组中的元素的码值大了,此时就可以直接进行插入了
//但是此时这个直接插入在这个位置就有一个小细节(还是当我想要插入的元素的码值比我数组中的都小的时候出现的情况),因为当我想插入的数据是最小的时候,程序来到这个位置就会导致我数组中的元素每个都往后移动了一位,然后此时就导致end--,导致此时的end变成了-1,因为我刚刚的end还是0吗,符合循环的条件(但是最后一次减减才导致的-1),所以此时如果程序来到这个位置,此时的end就是还是-1,所以在这个位置插入元素就又会导致越界的问题了,所以此时这个else并不适合插入数据
break;//所以这个else只适合用break跳出去
}
}
//并且此时程序来到这个位置就是说明循环不满足条件或者是else中的break起了作用,所以此时这个位置就可以放心的把我的数据给插入到这个数组之中去了
arr[end + 1] = tmp;//因为end+1就可以防止end=-1的情况,并且也符合正常的元素的比较后的插入(就是在这个元素的后一个位置插入,也就是数组中的end+1的位置)
}
}
2.5 直接插入排序的测试
void PrintArr(int* arr, int sz)
{
int i;
for (i = 0; i < sz; i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
void TestInsertSort()
{
int arr[] = { 6,3,7,4,10,9,2,1,5,8 };
InsertSort(arr, sizeof(arr) / sizeof(arr[0]));
PrintArr(arr, sizeof(arr) / sizeof(arr[0]));
}
#include<stdio.h>
int main()
{
TestInsertSort();
return 0;
}
3.希尔排序的实现及分析
希尔排序又称为缩小增量法。
3.1 希尔排序的基本思想:先选定一个整数,把待排序文件中所有记录分成一个组,所有距离为1的记录在用一个组内,并对每一组的记录进行排序。然后,取,重复上述分组和排序的工作。当到达距离=1时,所有记录在统一组内排好序。
就是一个直接插入排序的优化(但是好像很高级)
此时这个希尔排序进行排序是将排序过程分为了两步
3.1.1.先进行预排序,让数组接近有序
3.1.2.然后再进行直接插入排序
3.2 此时就会涉及到一个叫gap的变量,我们可以对数组中间隔为gap的元素先进行一个预排序,gap由大变小。
特点:gap越大,大的数可以越快的到后面,小的数可以越快的到前面,gap越小,越接近有序,gap=1时就是我的直接插入排序
代码实现:(具体原理就是直接插入排序的原理,重点就是如何理解这个gap)
void ShellSort(int* arr, int n)
{
//int gap = 3;//因为我并不知道我需要排序的数据的个数,所以这个位置我们不可以将gap给固定掉
int gap = n;
while (gap > 1)
{
gap = gap / 2;
//gap = gap / 3 + 1;//这两种都可以
//此时这个位置中gap>1就都是预排序
//gap==1时就是直接插入排序
}
int i = 0;
//此时下面的代码就是为了把数组中间隔为gap的多组数据同时进行排序
for (i = 0; i < n - gap; i++)
{
int end = i;
int tmp = arr[end + gap];
while (end >= 0)
{
if (arr[end] > tmp)
{
arr[end + gap] = arr[end];
end = end - gap;
}
else
{
break;
}
}
arr[end + gap] = tmp;
}
}