文章目录
本文记录学习七种排序(内排序)的代码,分别用C和python实现,必要时加图解释。
排序
内排序一览表
C语言准备
利用结构体传输这个线性表。
实现定义:
#include<stdio.h>
#include<time.h>
#define MAXSIZE 10
typedef struct // 线性表存储
{
int r[MAXSIZE];
int length;
}SqList;
void swap(SqList *L, int i, int j) // 交换函数
{
int temp=L->r[i];
L->r[i]=L->r[j];
L->r[j]=temp;
}
void ListPrint(SqList *L, int type) // 打印该列表
{
int i;
printf("[",type);
for(i=1;i<L->length;i++)
{
printf("%d,",L->r[i]);
}
printf("\b]\n");
}
void main()
{
SqList alist={
{0,9,1,5,8,3,7,4,6,2},
9
};
InsertSort(&alist); // 直接插入
ShellSort(&alist); // 希尔
BubbleSort(&alist); //冒泡
HeapSort(&alist); // 堆
SecletionSort(&alist); // 简单选择
QuickSort(&alist); // 快排
MergingSort(&alist); // 归并
}
python准备
if __name__ == '__main__':
alist = [9,1,8,4,6,7,3,5,2]
InsertionSort(alist)
ShellSort(alist)
BubbleSort(alist)
HeapSort(alist)
SelectionSort(alist)
QuickSort(alist)
MergingSort(alist)
1 插入排序
1.1 直接插入排序
要点:将数组第一个元素看作已经排好的序列,第二个到最后一个元素依次往这个序列里插,只要左边 < 该元素 < 右边。
C version
void InsertSort(SqList *L)
{
int i,j;
for(i=2;i<=L->length;i++) // 假设r[1]为拍好的序列,循环至最后一位
{
if(L->r[i]<L->r[i-1]) // 如果该位比左边小,执行以下,将小数移至左边
{
L->r[0]=L->r[i]; // 利用r[0]存储r[i]的值
for(j=i-1;L->r[j] > L->r[0];j--) // 从i-1向左找一个可以插入r[i]的值,这个r[j]需要大于r[i]
{
L->r[j+1]=L->r[j]; // 将这个比r[i]大的值向右移一位,此时j与j+1都相等,可以看作将j空出来了
}
L->r[j+1]=L->r[0]; // 此时j的值小于等于r[i]=r[0],插入右边
}
}
}
python version
def InsertionSort(blist):
length = len(blist)
for i in range(1, length):
if blist[i] < blist[i - 1]:
temp = blist[i]
j = i - 1
while (j >= 0) & (blist[j] > temp):
blist[j + 1] = blist[j]
j -= 1
blist[j + 1] = temp
print blist
1.2 希尔排序
要点:先分组,每个组利用插入排序先排好序,每个组排好之后整体插入排序。
优点:先使得整个序列基本有序,大的数集中在后面,小的数集中在前面,在插入排序的时候就不需要数据过大的移动。
难点:怎么分组效率最高?基本有序 ≠ 局部有序,如果是顺序选取组,很有可能组内达到有序,但整体效率还是很低,如图:
按三个为一组顺序分组,第三组的2插入左边排好的序列中,还是需要经过6次比较,性能并没有很大的提升。
但如果我们按下标一定的增量分组,则可以达到整个数组的基本有序。根据一个增量序列来分组排序,当增量为1时则是一次直接插入排序。数组长度计算出最大增量(最大增量必须小于数组长度),这里我们最大增量选,每次将增量变为之前的一半。即增量序列为
可以看到在第一次分组时,r[5]没有进行排序,降低了效率。我们看另一种增列序列,所有的元素在每一次分组都参与排序了,值得注意的是,我们的i是从increment+1开始循环,相当于把 i-increment看作是固定的,往左边插入元素。
以increment=2为例:
i=3时r[3]与r[1]比较,往前追溯发现没有同组的(1下标减去增量后小于0),i++;
i=4时r[4]与r[2]比较,往前追溯发现没有同组的(2下标减去增量后小于0),i++;
i=5时r[5]与r[3]比较,往前追溯同组的为r[1](3下标减去增量后等于1),将r[5]往该排好的序列插,完成后i++;
以此类推。
可以看出代码变动并不是很大,只是多加了一个循环,在插入排序的循环条件多加了j>0。这里可以学到很多编程技巧。
C version
void InsertSort(Shell *L)
{
int i,j;
int increment=L->hength;
do
{
increment=increment/2;
for(i=increment+1;i<=L->length;i++) //从第increment+1位开始,循环至最后一位
{
if(L->r[i]<L->r[i-increment]) //如果该位在组内比左边小,执行以下,将小数插至左边
{
L->r[0]=L->r[i]; //利用r[0]存储r[i]的值
for(j=i-increment;L->r[j] > L->r[0] && j>0;j-=increment) //从i-1向左找一个可以插入r[i]的值,这个r[j]需要大于r[i]
{
L->r[j+increment]=L->r[j]; //将这个比r[i]大的值向右移一位
}
L->r[j+increment]=L->r[0]; //此时j的值小于等于r[i]=r[0],插入右边
}
}
}
while(increment>1)
}
python version
def ShellSort(blist):
length = len(blist)
increment = length
increment_list = []
while increment > 1:
increment /= 2
increment_list.append(increment)
for incre in increment_list:
for i in range(incre, length):
if blist[i]< blist[i - incre]:
temp = blist[i]
j = i - incre
while (j >= 0) & (blist[j] > temp):
blist[j + incre] = blist[j]
j -= incre
blist[j + incre] = temp
print blist
2 选择排序
2.1 简单选择排序
简单选择排序的思路是对每一个i,在余下序列中找最小值,记录下标,一次交换。
void SelectionSort()
{
int i,j,min;
for(i=1;i<L->length;i++)
{
min=i;
for(j=i+1;j<=L->length;j++)
{
if(L->r[j]<L->r[min]) // 在余下序列中找比当前存储最小值还小的
{
min=j;
}
}
if(min!=i) // 经过查找后如果存在更小的,交换
{
swap(L,i,min);
}
}
}
2.2 快速排序
超级经典的排序算法,采用分治的思想,找一个数将数组分为两区,分区重复处理。
快排细分起来分为左右指针法、挖坑法、前后指针法,都是采用的递归实现;同时还可以使用非递归实现,将递归变为迭代。
临时查找的一篇博客:快速排序算法—左右指针法,挖坑法,前后指针法,递归和非递归
有空的时候再慢慢总结。
3 交换排序
3.1 冒泡排序
C version
冒泡排序优化前后一共有三个常见版本,第一个版本的思路不是严格意义的冒泡排序,而是普通的交换排序,因为不是两两比较相邻元素完成交换,思路是把与当前元素比较最小的放到前面。。
void ExchangeSort(SqList *L)
{
int i,j;
for(i=1;i<L->length;i++) // 从1到length-1
{
for(j=i+1;j<=L->length;j++) // 从i+1到length,i与j两两比较,区别于冒泡的相邻比较
{
if(L->r[j]<L->r[i])
{
swap(L,i,j);
}
}
}
}
第二个版本则是我们正宗的冒泡排序,从前往后扫描,依次填入当前序列最小的数值,这个找最小数的过程是从数组尾往前循环,两两比较,直至i+1,看起来就像最小的数字往数组头浮。
void BubbleSort(SqList *L)
{
int i,j;
for(i=1;i<L->length;i++) // 从1到length-1
{
for(j=L->length-1;j>=i;j--) // 从数组尾开始,因为要把小的数放到前面,交换的方向就是“浮”
{
//这里注意从length-1开始,我们需要将剩下最小的放到j=i处,如果从length开始则条件为j>i,比较为L->r[j]<L->r[j-1],交换为swap(L,j,j+1)
if(L->r[j]<L->r[j+1])
{
swap(L,j,j+1)
}
}
}
}
第三个版本是优化版本,目的是在数组在完成某次交换后已经有序,但i还是会继续循环,并对每个i寻找最小的值。如何告诉程序目前已经有序了?思路是设一个标记,当剩下的两两比较都是前小于后(完全有序)则告诉循环该停止了。
void BubbleSortOptimize(SqList *L)
{
int i,j;
int Check=1;
// C 语言把任何非零和非空的值假定为 true,把零或 null 假定为 false
for(i=1;i<L->length && Check;i++) // 从1到length-1,循环条件是i小于length且Check不为0
{
Check=0; // 如果退出时还是0,则判断为false,循环结束
for(j=L->length-1;j>=i;j--)
{
if(L->r[j]<L->r[j+1])
{
swap(L,j,j+1)
Check=1;
}
}
}
}
python version
def BubbleSort(blist):
length = len(blist)
check = True
n = 0
for i in range(length - 1):
for j in range(length - i - 1):
n += 1
if blist[length -1 - j] < blist[length - 2 - j]:
blist[length -1 - j], blist[length - 2 - j] = blist[length - 2 - j], blist[length -1 - j]
print blist