内排序和外排序:
根据在排序过程中待排序的记录是否全部被放置在内存中,排序分为:内排序和外排序;
(1)内排序是在排序整个过程中,待排序的所有记录全部被放置在内存中;
(2)外排序是由于排序的记录个数太多,不能同时放置在内存,整个排序过程需要在内外存之间多次交换数据才能进行;
本文主要记录内排序的几种算法;
对于内排序来说,排序算法的性能主要受3个方面影响:
(1)时间性能:在内排序中主要进行两种操作:比较和移动;高效率的内排序算法应该是具有尽可能少的关键字比较次数和尽可能少的记录移动次数;
(2)辅助空间:辅助空间除了存放待排序所占用的存储空间外,执行算法所需要的其他存储空间;
(3)算法的复杂性:这里指的是算法本身的复杂度,而不是指算法的时间复杂度,显然算法过于复杂也会影响排序的性能;
根据排序过程中借助的主要操作,我们把内排序分为:插入排序、交换排序、选择排序、基数排序和归并排序;
(1)直接插入排序(升序排列)
基本思想:
先将前两个数排好序,然后拿第三个数依次和前两个比较(从后往前比较),比较完成后,然后将这个数插入到合适的地方,然后拿第四个数和前面已经排好序的三个数进行比较,结束后,将这个数插入到合适的地方,这样,依次进行下去,即在排好序的一组数中,把后面的数通过比较插入到前面的有序数中;
Java代码:
//列举几种直接插入排序的实现
public static void insertSort1(int data[])
{
int temp;
int i, j;
for (i = 0; i < data.length; i++)
{
temp = data[i];
for (j = i - 1; j >= 0; j--)
{
if (temp < data[j])
data[j + 1] = data[j];
else
break;
}
data[j + 1] = temp;
}
}
public static void insertSort2(int data[])
{
for (int i = 1; i < data.length; i++)
{
for (int j = i; j > 0; j--)
{
if (data[j] < data[j - 1])
{
int temp = data[j];
data[j] = data[j - 1];
data[j - 1] = temp;
}
}
}
}
public static void insertSort3(int data[])
{
int temp = 0;
for (int i = 1; i < data.length; i++)
{
int j = i - 1;
temp = data[i];
for (; j >= 0 && temp < data[j]; j--)
{
data[j + 1] = data[j]; // 将大于temp的值整体后移一个单位
}
data[j + 1] = temp;
}
}
(2)希尔排序法(升序排列):又称为缩小增量排序:
基本思想:先取一个小于n(元素的个数)的整数d1作为第一个增量,一般是n/2,然后分为d1个组,所有距离为d1的倍数的记录放在同一个组中,先在各组内进行直接插入排序,然后再以d2为增量,即d1/2,重复进行上述的分组和排序,直到所取的增量为1,即所有记录放在同一组中进行直接插入排序为止;
希尔排序的诞生是由于插入排序在处理大规模数组的时候会遇到需要移动太多元素的问题。希尔排序的思想是将一个大的数组“分而治之”,划分为若干个小的数组,以 d 来划分,比如数组 [1, 2, 3, 4, 5, 6, 7, 8] ,如果以 d = 2 来划分,可以分为 [1, 3, 5, 7] 和 [2, 4, 6, 8] 两个数组(对应的,如 d = 3 ,则划分的数组为: [1, 4, 7] 、 [2, 5, 8] 、 [3, 6] )然后分别对划分出来的数组进行插入排序,待各个子数组排序完毕之后再减小d 值重复进行之前的步骤,直至 d = 1 ,即对整个数组进行插入排序,此时的数组已经基本上快排好序了,所以需要移动的元素会很小很小,解决了插入排序在处理大规模数组时较多移动次数的问题。
希尔排序是直接插入排序的改进版,在数据量大的时候对效率的提升帮助很大,数据量小的时候建议直接使用插入排序就好了。
Java代码:
public static void shellSort1(int data[])
{
double d1 = data.length;
int temp = 0;
while (true)
{
d1 = Math.ceil(d1 / 2);
int d = (int) d1;
for (int x = 0; x < d; x++)
{
for (int i = x + d; i < data.length; i += d)
{
int j = i - d;
temp = data[i];
for (; j >= 0 && temp < data[j]; j -= d)
{
data[j + d] = data[j];
}
data[j + d] = temp;
}
}
if (d == 1)
break;
}
}
static void shellSort2(int data[])
{
int temp;
int i, j, d;
for (d = data.length / 2; d >= 1; d /= 2)
{
for (i = d; i < data.length; i++)
{
temp = data[i];
for (j = i - d; j >= 0; j -= d)
{
if (temp < data[j])
data[j + d] = data[j];
else
break;
}
data[j + d] = temp;
}
}
}
(3)直接选择排序:(升序排列)
基本思想:在要排序的一组数中,选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。
直接选择排序的时间复杂度为O(n2 ) ;Java代码:
public static void selectSort1(int data[])
{
int k = 0;
for (int i = 0; i < data.length; i++)
{
int j = i + 1;
k = i;
int temp = data[i];
for (; j < data.length; j++)
{
if (data[j] < temp)
{
temp = data[j];
k = j;
}
}
data[k] = data[i];
data[i] = temp;
}
}
public static void selectSort2(int data[])
{
int temp;
int i, j, k;
for (i = 1; i <= data.length - 1; i++)
{
k = i - 1;
for (j = i; j <= data.length - 1; j++)
{
if (data[j] < data[k])
k = j;
}
if (k != i - 1)
{
temp = data[i - 1];
data[i - 1] = data[k];
data[k] = temp;
}
}
}
(4)冒泡排序:(升序排列)
基本思想:第一次循环,第一个和第二个进行比较,若后面的较小,则交换,然后第二个和第三个进行比较,若后面的较小,则交换,依次,即相邻的进行比较,比较出最大的自然就沉到了最后;然后进行第二次循环,比较到倒数第二个;
第三次循环,比较到倒数第三个,依次进行;
Java代码:
public static void bubbleSort1(int data[])
{
int temp = 0;
for (int i = 0; i < data.length; i++)
{
for (int j = 0; j < data.length - 1 - i; j++)
{
if (data[j] > data[j + 1])
{
temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
}
}
}
}
static void bubbleSort2(int data[])
{
int temp;
int i, j, flag;
for (i = 1; i <= data.length - 1; i++)
{
flag = 0;
for (j = data.length - 1; j >= i; j--)
if (data[j] < data[j - 1])
{
temp = data[j - 1];
data[j - 1] = data[j];
data[j] = temp;
flag = 1;
}
if (flag == 0)
return;
}
}
(5)快速排序:(升序排列)
基本思想:选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分(具体的实现是从两边找,两边的数都和基准元素比较,找到一对后交换),一部分比基准元素小,一部分大于等于基准元素,然后再用同样的方法递归地排序划分的两部分。
快速排序是对冒泡排序的一种改进,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
快速排序作为一种高效的排序算法被广泛应用,SUN的JDK中的Arrays.sort 方法用的就是快速排序;
Java代码:
//推荐使用这种
public static void sort1(int[] data, int start, int end)
{
if (end - start <= 0)
{
return;
}
int last = start;
for (int i = start + 1; i <= end; i++)
{
if (data[i] < data[start])
{
int temp = data[++last];
data[last] = data[i];
data[i] = temp;
}
}
int temp = data[last];
data[last] = data[start];
data[start] = temp;
sort1(data, start, last - 1);
sort1(data, last + 1, end);
}
/*
从第二个数开始依次和45比较,小于45的和前面的标记的交换,大于的不变;
如:{45,53,18,36,72,30,48,15}
第一步:{45,18,53,36,72,30,48,15}
第二步:{45,18,36,53,72,30,48,15}
第三步:{45,18,36,30,72,53,48,15}
第四步:{45,18,36,30,15,53,48,72}
第五步:{15,18,36,30,45,53,48,72}
*/
static void sort2(int[] data, int left, int right)
{
int i, j;
int middle, temp;
i = left;
j = right;
middle = data[left];
do
{
// 从左扫描大于中值的数
while ((data[i] < middle) && (i < right))
i++;
// 从右扫描大于中值的数
while ((data[j] > middle) && (j > left))
j--;
if (i <= j) // 找到了一对值
{
// 交换
temp = data[i];
data[i] = data[j];
data[j] = temp;
i++;
j--;
}
} while (i <= j); // 如果两边扫描的下标交错,就停止(完成一次)
// 当左边部分有值(left<j),递归左半边
if (left < j)
sort2(data, left, j);
// 当右边部分有值(right>i),递归右半边
if (right > i)
sort2(data, i, right);
}
/*
如:{45,53,18,36,72,30,48,15}
第一步结束:{15,30,18,36,72,53,48,45} 然后依次;
*/
(6)堆排序(升序排列)
基本思想:堆排序是一种树形选择排序,是对直接选择排序的有效改进;
Java代码:
public static void heapSort(int data[], int n)
{
// 利用堆排序的方法对数组A中的n个元素进行排序
int temp;
int i;
for (i = n / 2 - 1; i >= 0; i--)
sift(data, n, i); // 建立初始堆
for (i = 1; i <= n - 1; i++) // 进行n-1次循环,完成堆排序
{
// 将树根结点的值同当前区间内最后一个结点的值对换
temp = data[0];
data[0] = data[n - i];
data[n - i] = temp;
sift(data, n - i, 0); // 筛data[0]结点,得到n-i个结点的堆
}
}
public static void sift(int data[], int n, int i)
{
// 对data[n]数组中的data[i]元素进行筛运算,形成以data[i]为根的堆
int temp = data[i]; // 把待筛结点的值暂存于x中
int j;
j = 2 * i + 1;
while (j <= n - 1) // 当data[i]的左孩子不为空时执行循环
{
// 若右孩子的排序码较大,则把j修改为右孩子的下标
if (j < n - 1 && data[j] < data[j + 1])
j++;
// 将data[j]调到双亲位置上,修改i和j的值,以便继续向下筛
if (temp < data[j])
{
data[i] = data[j];
i = j;
j = 2 * i + 1;
}
// 查找到x的最终位置,终止循环
else
break;
}
data[i] = temp; // 被筛结点的值放入最终位置
}
如:{46,79,56,38,40,84}
建堆:
交换,从堆中踢出最大数:
剩余结点再建堆,再交踢出最大数:
(7)归并排序:(升序排列)
基本思想:将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列;
归并排序是建立在归并操作上的一种有效的排序算法,例如有两个有序表:(7,10,13,15)和(4,8,19,20),归并后得到的有序表为:(4,7,8,10,13,15,19,20)。如:
Java代码:
public static void sort(int[] data, int left, int right)
{
if (left < right)
{
int center = (left + right) / 2; // 找出中间索引
sort(data, left, center); // 对左边数组进行递归
sort(data, center + 1, right); // 对右边数组进行递归
merge(data, left, center, right); // 合并
}
}
public static void merge(int[] data, int left, int center, int right)
{
int[] temp = new int[data.length];
int mid = center + 1;
int third = left; // third记录中间数组的索引
int tmp = left;
while (left <= center && mid <= right)
{
// 从两个数组中取出最小的放入中间数组
if (data[left] <= data[mid])
{
temp[third++] = data[left++];
}
else
{
temp[third++] = data[mid++];
}
}
// 剩余部分依次放入中间数组
while (mid <= right)
{
temp[third++] = data[mid++];
}
while (left <= center)
{
temp[third++] = data[left++];
}
// 将中间数组中的内容复制回原数组
while (tmp <= right)
{
data[tmp] = temp[tmp++];
}
System.out.println(Arrays.toString(data)); // 记录一下
}
(8)基数排序:(升序排列)
基本思想:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列;基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由键值的最右边开始,即从个位开始,而MSD则相反,由键值的最左边开始,从最高位开始;LSD的基数排序适用于位数小的数列,如果位数多的话,使用MSD的效率会比较好;
Java代码:
package sort;
public class Sort
{
// d表示最大的数有多少位
public static void sort(int[] number, int d)
{
int k = 0;
int n = 1;
int m = 1; // 控制键值排序依据在哪一位
int[][] temp = new int[10][number.length]; // 数组的第一维表示可能的余数0-9
int[] order = new int[10]; // 数组orderp[i]用来表示该位是i的数的个数
while (m <= d)
{
for (int i = 0; i < number.length; i++)
{
int lsd = ((number[i] / n) % 10);
temp[lsd][order[lsd]] = number[i];
order[lsd]++;
}
for (int i = 0; i < 10; i++)
{
if (order[i] != 0)
for (int j = 0; j < order[i]; j++)
{
number[k] = temp[i][j];
k++;
}
order[i] = 0;
}
n *= 10;
k = 0;
m++;
}
}
public static void main(String[] args)
{
int[] data = { 45, 53, 18, 36, 72, 30, 48, 112, 15 };
sort(data, 3);
for (int i : data)
{
System.out.print(i + " ");
}
}
}