目录
排序:
将一组任意顺序的数据元素或记录,按照其关键字排列成递增或递减的新序列的操作过程。
一.简单选择排序
基本思路:每次从待排序的序列中选取关键字最小的记录,与当前排序序列的第一个记录交换位置,直到整个序列有序为止。
例:将5、8、2、4、6、1按升序排序。
排序 趟数 | 1 | 2 | 3 | 4 | 5 | 6 |
初始 | 5 | 8 | 2 | 4 | 6 | 1 |
1 | 1 | 8 | 2 | 4 | 6 | 5 |
2 | 1 | 2 | 8 | 4 | 6 | 5 |
3 | 1 | 2 | 4 | 8 | 6 | 5 |
4 | 1 | 2 | 4 | 5 | 6 | 8 |
5 | 1 | 2 | 4 | 5 | 6 | 8 |
过程说明:
一.排序1趟数过程说明:
首先将关键字一(5)与关键字二(8)比较大小,关键字一小,再将关键字一(5)与关键字三(2)比较大小,关键字三(2)小,再把关键字三与后面的关键字进行比较,最后把数值最小的关键字与关键字一交换数字,结果如排序趟数1所示。
排序2趟数过程说明:
二.经过趟数1的排序,关键字一的数值已经是最小的,所以从关键字二(8)开始排序;
将关键字二(8)与后序的关键字进行比较大小,将后序最小值的关键字与关键字二的数值交换位置。结果如趟数2所示:数值2为最小值,将数值2与数值8进行了交换位置。
三.趟数三的过程说明:
与上面的排序过程相同,前两个关键字的大小已经确定了,将关键字三(8)与后序全部的关键字进行比较大小,将最小值与关键字三(8)的位置进行互换。
四.
后序的排序同上述一样。最后结果如趟数5所示。
五.代码
void SelectSort(SqList *L) { /*对顺序表L作简单选择排序*/ RecordType temp; for(i=1; i<L->length; ++i) { /*选择第i个小的记录,并交换到位*/ j=i; /* j用于记录最小元素的位置*/ for(k=i+1; k<=L->length; k++) /*在L.r[i..L.length]中选择key最小的记录*/ if(L->r[k].key<L->r[j].key) //遍历i后面的所有数,选择最小的数 j=k; //用j记录较小的那个数 if(i!=j) /*第i个小的记录L->r[j]与第i个记录交换*/ { temp=L->r[j]; //temp此时记录了较小的数 L->r[j]=L->r[i]; //把较大的数赋值给后面的那个数 L->r[i]=temp; //较小的赋值给i } } }/*SelectSort
六.算法评价
二.直接插入排序
将一条待排序得记录按照其关键字的大小插入到已经有序的记录中,直到所有记录插入完成并且成为一个有序序列为止。
例:将 8、5、9、4、3、6、5* 按升序排序。
待插入元素位置序号 | 排序 趟数 | R[0] | R[1] | R[2] | R[3] | R[4] | R[5] | R[6] | R[7] |
初始 | 8 | 5 | 9 | 4 | 3 | 6 | 5* | ||
i=2 | 1 | 5 | 5 | 8 | 9 | 4 | 3 | 6 | 5* |
i=3 | 2 | 9 | 5 | 8 | 9 | 4 | 3 | 6 | 5* |
i=4 | 3 | 4 | 4 | 5 | 8 | 9 | 3 | 6 | 5* |
i=5 | 4 | 3 | 3 | 4 | 5 | 8 | 9 | 6 | 5* |
i=6 | 5 | 6 | 3 | 4 | 5 | 6 | 8 | 9 | 5* |
i=7 | 6 | 5* | 3 | 4 | 5 | 5* | 6 | 8 | 9 |
过程说明:
第一趟过程:
首先将R[2]赋值给R[0],然后把R[0]的值与R[1]进行比较,R[0]<R[1],将R[2]的值与R[1]交换,完成第一趟排序。
第二趟排序:
将R[3]的值赋值给R[0],将R[0]的值与前两个值进行比较,R[0]>R[2],所以R[3]>R[2]排列顺序不变。
第三趟排序:
将R[4]的值赋值给R[0],R[0]与前三个值进行,首先从R[1]进行比较,R[0]
<R[1],所以R[1]后面的值往后移动,R[0]的值赋值给[1];
第四趟排序:
将R[5]的值赋值给R[0],然后从R[0]开始依次比较大小,R[0]<R[1],所以R[1]以后的值都往后移动,将R[5]的移动到[1]的位置。
后序的排序方法重复以上操作。
代码:
void InsertSort(SqList *L)
{ /*对顺序表L作插入排序*/
for(i=2; i<=L->length; ++i) //首先用第二个开始,与第一个数r[1]比较
if(L->r[i].key<L->r[i-1].key) //小于前一个数
{ /*当“<”时,才需将L->r[i]插入有序表*/
L->r[0]=L->r[i]; /*将待插入记录复制为哨兵 r[0]此时是较小的那个数*/
j=i-1; //j为i的前一个数
while(L->r[0].key<L->r[j].key) //哨兵小于j的值
{ L->r[j+1]=L->r[j]; /*把大的那个值赋值给后面的位置*/
j--;
}
//循环后,经j--后,j的位置移动到了j的前一个位置,所以下面需要j++,把j恢复到原来的位置。
L->r[j+1]=L->r[0]; /*插入到正确位置*/
} /* if */
} /* InsertPass */
算法分析:
三.希尔排序
基本思路:
将整个待排序记录序列分割成若干子序列,对每个子序列进行直接插入排序,待整个序列中纪录“基本有序”时,再对全体记录进行一次直接插入排序。
具体操作:
选择一个增量序列T1......Tn,增序列是一个递减序列,且最后一个增量一定是1,即Tn=1;
按照增量序列个数n,进行n趟排序。
例:对关键字序列 T=(49,38,65,97, 76, 13, 27, 49*,55, 04)对应的记录进行希尔排序。
r[i] | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
初态: | 49 | 38 | 65 | 97 | 76 | 13 | 27 | 49* | 55 | 04 | |
第一趟(t=5) | 13 | 27 | 49* | 55 | 04 | 49 | 38 | 65 | 97 | 76 | |
第二趟(t=3) | 13 | 04 | 49* | 38 | 27 | 49 | 55 | 65 | 97 | 76 | |
第三趟(t=1) | 04 | 13 | 27 | 38 | 49* | 49 | 55 | 65 | 76 | 97 | |
过程说明:
增量序列t=5时,具体分组情况为:49,13;38,27;65,49*;97,55;76,04;
将上述每个小组的数据进行直接插入排序,结果得到第一趟的排序结果。
增量序列t=3时,具体分组情况为:13,38;04,27;49*,49;38,55;27,65;49,97;55,76;
将上述每个小组中的数据进行直接插入排序,结果得到第二趟的排序结果。
第三趟的分组结果:13,04;04,49*;49*,38;38,27;27,49;49,55;55,65;
65,76;76,97;各个小组的直接插入排序同步进行,然后把各个小组的排序结果整合在一起。结果就得出第三趟的排序结果。
代码:
void ShellInsert(SqList *L, int delta)
{ /*对L->r[ ]做一趟希尔插入排序,delta为增量*/
for(i=1+delta; i <= L->length; i++) /* 1+delta为第一个子序列的第二个元素的下标*/
if(L->r[i].key < L->r[i-delta].key)
{ L->r[0] = L->r[i]; /*备份r[i] */
for(j = i-delta; j > 0&&L->r[0].key < L->r[j].key; j-=delta)
L->r[j+delta] = L->r[j];
L->r[j+delta] = L->r[0];
}
}
void ShellSort(SqList *L)
/*对记录数组r做希尔排序,delta[0..t-1]为增量序列数组*/
{ for(i=0; i<t; ++i)
ShellInsert(L,delta[i]);
}
算法分析:
四.冒泡排序
基本思路:
依次将待排序的记录相邻的关键字两两进行比较,如果后面的关键字小于前面的关键字的值,则进行交换记录的位置,没趟排序结束都会确定一个最大值在最后的位置。
例:使用冒泡排序方法将9、5、2、4按升序排序。
第一趟:
第二趟:
第三趟:
代码:
void BubbleSort(SqList *L) { /*对记录数组L->r做冒泡排序*/ RecordType temp; n=L->length; change=TRUE; /*设置交换标识为TRUE,便于进入循环*/ for(i=1; i<=n-1&&change; ++i) { change=FALSE; /*在第i趟中先设置记录交换标识为FALSE */ for(j=1; j<=n-i; ++j) if(L->r[j].key > L->r[j+1].key) /*若相邻两记录逆序,交换 大的数字与下面小的数字互换位置*/ { temp = L->r[j]; //依次执行冒泡原则,先是12比较 ;再23 ;之后34比较。。。。 ; 如此循环 ;把大的那个数,向下排序 ; L->r[j] = L->r[j+1]; L->r[j+1] = temp; change = TRUE; //第一次循环结束,true进入下一次循环 } } } /* BubbleSort */ //当i=1;进入第一次循环嵌套,j=1;对顶部的数进行冒泡排序,第一个数到达底部; //当i=2;进入第二次循环嵌套,j=1; 同时i=2;(第一次循环后,已经有一个数排序完成,所以只需要比较n-i次)继续对顶部的数进行冒泡排序。。。; //剩下最后一个数,不需要进行排序,已经是有序的;
算法分析:
五.快速排序
基本思路:
先从数列中选取一个数作为基数(该记录称为枢轴)。
2.将比基准大的数全部放到它的右边,小于或等于基准数的全放到它的左边。
3.再对左右两部分分重复第二步,直到各个区间只有一个数,达到整个序列有序。
轴元素的选取:
通常选取序列中的第一个元素,
选取原则:
1.首元素R[s]或者尾元素R[t]。
2.中值元素,取的是首元素,尾元素,中间位置元素三者中“中间大小”的元素。
3.随机元素R[i],i是s~t之间的一个随机整数。
例题:
第一趟排序结束
第一趟排序详解:
这里统一选取最左边的数为枢轴元素,以把49取出作为基准,左边low位置为空,所以这里先从high位置开始排序,49*=49,所以49*位置不动,默认如果当前位置未移动,则当前下标继续移动,所以high移动到27的位置,27<49,移动到low的位置。low与high交替移动,所以此时移动low的位置,low移动到38的位置,38<49,不需要移动,此时继续移动low的位置,low移动到65的位置,65>49,所以把65移动到high的位置。这时,移动high的位置,high移动到13的位置,13<49所以把13移动到low的位置,(交替移动位置)所以移动low的位置到97,97>49,所以移动到high的位置,接下来移动high的位置到76,76>49,所以不移动。此时再移动high位置,high位置与low位置重合,此时把基准数填入当前位置。
第一趟排序结束。
第二趟排序结束
第二趟排序详解:
经过第一趟排序,数组被分为两部分;49左边为都小于49的数,49右边为都大于49的数。
再分别对这两部分数进行快速排序。
首先对左部分进行排序。
选取左部分数组中,最左边的数作为基准,high位于13的位置,13<27,所以移动到low的位置,此时移动low的位置,38大于27,把38移动到high的位置。再移动high的位置,此时high的位置与low的位置重合,把27填入此时的位置。
这时,27左右两边的数组分别只有一个数(默认为有序),所以左部分数组排序结束。
第三趟排序结束
第三趟排序详解:
同样选取最左边的数作为基准数,然后按照以上原理进行移动。
第四趟排序结束
第四趟排序详解:
第三趟排序后,76把右部分数组分为两部分,做部分有两个数,所以还需要进行排序,选取最左边的数作为基准,high位于65的位置,65大于49,所以不需要进行排序,此时继续移动high的位置,high与low重合,所以填入基准数,排序结束。
代码:
void QSort(RecordType R[], int s, int t) { /*对记录序列R[s..t]进行快速排序*/ if(s<t-1) /*长度大于1 */ { pivotloc=Partition(R, s, t); /*对R[s..t]进行一次划分,并返回枢轴位置*/ QSort(R, s, pivotloc-1); /*对低端子序列递归排序*/ QSort(R, pivotloc+1, t); /*对高端子序列递归排序*/ }//if }//Qsort
算法分析:
六.归并排序
七.堆排序
八.基数排序
九.小结:
后序内容持续完善更新。。。。。。。。