内部排序算法:基数排序

基本思想 基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。 基数排序可以采用两种方式: LSD(Least Significant Digital):从待排序元素的最右边开始计算(如果是数字类型,即从最低位个位开始)。 MSD(Most Significant Digital):从待排序元素的最左边开始计算(如果是数字类型,即从最高位开始)。 我们以LSD方式为例,从数组R[1..n]中每个元素的最低位开始处理,假设基数为radix,如果是十进制,则radix=10。基本过程如下所示: 计算R中最大的元素,求得位数最大的元素,最大位数记为distance; 对每一位round<=distance,计算R[i] % radix即可得到; 将上面计算得到的余数作为bucket编号,每个bucket中可能存放多个数组R的元素; 按照bucket编号的顺序,收集bucket中元素,就地替换数

内部排序算法:归并排序

基本思想 n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果: 初始状态:无序区为R[1..n],有序区为空。 第1趟排序: 在无序区R[1..n]中选出关键字最小的记录R[k],将它与无序区的第1个记录R[1] 交换,使R[1..1]和R[2..n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。 …… 第i趟排序: 第i趟排序开始时,当前有序区和无序区分别为R[1..i-1]和R[i..n](1≤i≤n-1)。 该趟排序从当前无序区中选出关键字最小的记录R[k],将它与无序区的第1个记录R[i]交换,使R[1..i] 和R[i+1..n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。 这样,n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果。 算法实现 归并排序算法,Java实现,代码如下所示: public abstract class Sorter { public abstract void sort(int[] array); } public class MergeSorter extends Sorter { @Ov

内部排序算法:堆排序

基本思想 堆的定义 n个关键字序列kl,k2,…,kn称为堆,当且仅当该序列满足如下性质之一(简称堆性质): ki≤k2i且ki≤k2i+1 或 ki≥k2i且ki≥k2i+1(1≤i≤FLOOR(n/2)) 若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若 存在)结点的关键字。 小根堆:根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小的。 大根堆:根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大的。 我们可以选择大根堆或者小根堆中的任意一个来进行排序。 排序思想 用大根堆排序的基本思想: 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区。 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得 到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key。 由于交换后新的根R[1]可能违反堆

内部排序算法:快速排序

基本思想 设当前待排序的数组无序区为R[low..high],利用分治法可将快速排序的基本思想描述为: 分解: 在R[low..high]中任选一个记录作为基准(Pivot),以此基准将当前无序区划分为左、右两个较小的子区间R[low..pivotpos-1)和R[pivotpos+1..high],并使左边子区间中所有记录的关键字均小于等于基准记录(不妨记为pivot)的关键字pivot.key,右边的子区间中所有记录的关键字均大于等于pivot.key,而基准记录pivot则位于正确的位置(pivotpos)上,它无需参加后续的排序。 注意:划分的关键是要求出基准记录所在的位置pivotpos,划分的结果可以简单地表示为(注意pivot=R[pivotpos]): R[low..pivotpos-1].keys ≤ R[pivotpos].key ≤ R[pivotpos+1..high].keys 其中low≤pivotpos≤high。 求解: 通过递归调用快速排序对左、右子区间R[low..pivotpos-1]和R[pivotpos+1..high] 快速排序。 组合: 因为当“求解”步骤中的两个递归调用结束时,其左、右

内部排序算法:希尔排序

基本思想 先取一个小于n的整数d1作为第一个增量,把待排序的全部记录分成dx个组。所有距离为d1的倍数的记录放在同一个组中。 先在各组内进行直接插人排序。 然后,取第二个增量d2<d1重复上述的分组和排序。 直至所取的增量dt=1(dt<dt-x<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。 算法实现 希尔排序算法,Java实现,代码如下所示: public abstract class Sorter { public abstract void sort(int[] array); } public class ShellSorter extends Sorter { @Override public void sort(int[] array) { int d = array.length; do { d /= 2; shellPass(array, d); // 根据逐渐减小的间隔增量,循环调用一趟排序 } while (d > 1); } /** * 希尔一趟排序 * @param d 间隔增量 */ pr

内部排序算法:冒泡排序

基本思想 将被排序的记录数组R[0..n-1]垂直排列,每个记录R[i]看作是重量为R[i].key的气泡。根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R:凡扫描到违反本原则的轻气泡,就使其 向上”飘浮”。如此反复进行,直到最后任何两个气泡都是轻者在上,重者在下为止。 具体过程,如下所示: 初始状态:R[0..n-1]为无序区。 第一趟扫描:从无序区底部向上依次比较相邻的两个气泡的重量,若发现轻者在下、重者 在上,则交换二者的位置,即依次比较(R[n-1], R[n-2])、(R[n-2], R[n-3])、…、(R[1], R[0]);对于每对气泡(R[j+1], R[j]),若R[j+1].key第一趟扫描完毕时,”最轻”的气泡就飘浮到该区间的顶部,即关键字最小的记录被放在最高位置R[0]上。 第二趟扫描:扫描R[1..n-1]。扫描完毕时,”次轻”的气泡飘浮到R[1]的位置上……最后,经过n-1趟扫描可得到有序区R[0..n-1]。 注意: 第i趟扫描时,R[0..i-1]和R[i..n-1]分

内部排序算法:直接选择排序

基本思想 n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果: 初始状态:无序区为R[1..n],有序区为空。 第1趟排序:在无序区R[1..n]中选出关键字最小的记录R[k],将它与无序区的第1个记录R[1] 交换,使R[1..1]和R[2..n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。 …… 第i趟排序:第i趟排序开始时,当前有序区和无序区分别为R[1..i-1]和R[i..n](1≤i≤n-1)。 该趟排序从当前无序区中选出关键字最小的记录R[k],将它与无序区的第1个记录R[i]交换,使R[1..i] 和R[i+1..n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。 这样,n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果。 算法实现 直接选择排序算法,Java实现,代码如下所示: public abstract class Sorter { public abstract void sort(int[] array); } public class StraightSelectionSorter extends Sort

内部排序算法:直接插入排序

基本思想 假设待排序的记录存放在数组R[0..n-1]中。初始时,R[0]自成1个有序区,无序区为R[1..n-1]。 从i=1起直至i=n-1为止,依次将R[i]插入当前的有序区R[0..i-1]中,生成含n个记录的有序区。 算法实现 直接插入排序算法,Java实现,代码如下所示: public abstract class Sorter { public abstract void sort(int[] array); } public class StraightInsertionSorter extends Sorter { @Override public void sort(int[] array) { int tmp; for (int i = 1; i < array.length; i++) { tmp = array[i]; // array[i]的拷贝 // 如果右侧无序区第一个元素array[i] < 左侧有序区最大的array[i-1], // 需要将有序区比array[i]大的元素向后移动。 if (array[i] < array[i - 1]) { int j = i - 1;