简单的排序方法:冒泡排序、选择排序、插入排序、希尔排序。
先进的排序方法:归并排序、快速排序、堆排序、基数排序。
(一)冒泡排序
1.算法思想
每次在未排序的元素中两两比较找最大值,边找边从后往前存储(未排序...第i大、第2大、第1大)。
- 第 1 趟在 n 个元素里找最大,放在第 n 位;
- 第 2 趟在前 n-1 个元素里找最大,放在第 n-1 位;
- 第 i 趟在前 n-i+1 个元素里找最大,放在第 n-i+1 位。依次类推。
一般情况,整个冒泡排序只需进行 k(1<=k<n)趟冒泡操作,冒泡排序结束的条件是“在某一趟排序过程中没有进行记录交换的操作”,我们这里设置个标识符 flag 来控制。
2.代码实现
void bubbleSort(int[] array) {
int temp;
int n = array.length;
boolean flag = true;
for(int i=1; i<=n-1&&flag; i++) { //第 i 趟冒泡
flag = false; //每趟先设置标识符为false,表示未交换
for(int j=0; j<n-i; j++) { //遍历n-i+1个元素
if(array[j] > array[j+1]) { //若前大于后,则交换
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
flag = true;
}
}
}
}
3.效率分析
时间复杂度 O(n平方),空间复杂度 O(1)。
冒泡排序是一种 稳定的 排序方法。
(二)选择排序
1.算法思想
每次从前往后在未排序的元素中找最小,边找边从前往后存储(第1小、第2小...未排序)
- 第 1 趟:在 n 个元素里从前往后找比第 1 位小的元素,有则交换位置;
- 第 2 趟:在后 n-1 个元素里从前往后找比第 2 位小的元素,有则交换位置;
- 第 i 趟:在后 n-i+1 个元素里从前往后找比第 i 位小的元素,有则交换位置。
2.代码实现
void selectSort(int[] array) {
int j,temp;
int n = array.length;
for(int i=0; i<n-1; i++) { //经过 n-1趟选择,数据表才能有序
j = i;
for(int k=i+1; k<n; k++) { //在后 n-i+1 个元素里从前往后找比第 i 位小的元素
if(array[k] < array[i]) j=k; //改变 j 值,表示有比第i位小的
if(i != j) { //有则交换位置
temp = array[j];
array[j] = array[i];
array[i] = temp;
j = i;
}
}
}
}
3.效率分析
时间复杂度 O(n平方),空间复杂度 O(1)。
选择排序是一种 不稳定的 排序方法。
(三)直接插入排序
1.算法思想
从第二个元素开始,从前往后遍历将每位元素插入到已排序序列合适的位置。(可以看做打扑克的插入方法)
2.代码实现
void insertSort(int[] array) {
int n = array.length;
int temp,j;
for(int i=1; i<n; i++) { //将第一个元素视为已排好的序列,从第2个元素开始插入排序
if(array[i] < array[i-1]) { //与前一个比较,小的话就往前插
temp = array[i]; //临时变量存储要进行插入的元素
j = i - 1;
while((j != -1) && (temp < array[j])) { //比它大的挨个往后移动,空出一个位置
array[j+1] = array[j];
j--;
}
array[j+1] = temp; //在空出来的位置插入
}
}
}
3.效率分析
时间复杂度 O(n平方),空间复杂度 O(1)。
插入排序是一种 稳定的 排序方法。
4.算法优化
- 折半插入排序:采用折半查找在已排序的序列中确定插入位置。
- 表插入排序:采用链表存储结构,插入只修改指针,不移动记录。
(四)希尔排序
1.算法思想
希尔排序又称“缩小增量排序”。
- 先将整个待排序列分割成为若干子序列(这里不是简单的“逐段分割”,而是将相隔某个“增量”的元素组成一个子序列);
- 然后对每个子序列分别进行 直接插入排序;
- 缩小增量,重复(1)(2)步,使整个序列中元素“基本有序”;
- 最后一趟,增量为1,再对全体记录进行一次直接插入排序。
2.代码实现
static void shellSort(int[] array) {
//对数组array做希尔排序,数组delta[]为增量序列数组,增量(数组值)是不断缩小的,且最后一个增量一定是1。
int[] delta = {5,3,1};
for(int i=0; i<delta.length; i++) {
shellInsert(array, delta[i]);
}
}
static void shellInsert(int[] arr, int delta) {
int temp; //临时变量
//对数组array做一趟希尔排序,delta为增量
for(int i=delta; i<arr.length; i++) { //i初始为第一个子序列的第二个元素的下标
//如果相邻一个增量的两个元素,后小于前面的元素,则使子序列向后移动一个增量,空出一个位置来放后面那个小的
if(arr[i] < arr[i-delta]) {
temp = arr[i]; //备份后面那个小的元素
for(int j=i-delta; j>=0&&temp<arr[j]; j-=delta) {
arr[j+delta] = arr[j];
arr[j] = temp;
}
}
}
}
3.效率分析
由于关键字的比较次数与元素的移动次数依赖于增量数组中增量的选取,目前也没有 给出最好的选取增量的办法,所以时间复杂度也无法确定。
希尔排序是一种 不稳定的 排序方法。
(五)归并排序
1.算法思想
递归实现:先递归拆分成两个两个的,再两两合并。
非递归实现:
2.代码实现
3.效率分析
(六)快速排序
1.算法思想
- 从数组(子数组)中取出一个元素作为基准数(该元素称为枢轴)。
- 将比基准数大的数全放到它的右边,小于或等于基准数的全放到它的左边。
- 再对左右两部分重复第(1)(2)步,直到各区间只有一个数,达到整个数组有序。
2.具体操作过程
- 取数组或数组的子序列的首元素作为枢轴pivotkey;
- 之后检测 arr[high]的值,若大于等于pivotkey,则 high 减1,否则将 arr[high] 移动到 下标为 low 的位置;
- 之后检测 arr[low] 的值,若小于等于pivotkey,则 low 加1,否则将arr[low] 移至 数组下标为 high 的位置;
- 重复进行上述两个方向的检测,直至 high 和 low 是同一个下标为止。
3.代码实现
static void qSort(int[] array, int low, int high) {
int pivotloc;
if(low < high) { //数组(子数组)长度大于1
pivotloc = partition(array, low, high); //对数组array进行一次划分,并返回枢纽位置
qSort(array, low, pivotloc-1); //对左半部分数组元素序列(左子数组)递归排序
qSort(array, pivotloc+1, high); //对右半部分数组元素序列(右子数组)递归排序
}
}
static int partition(int[] arr, int low, int high) {
int pivotkey = arr[low]; //取数组或子数组的首元素作为枢轴
while(low < high) { //从表的两端交替向中间扫描
while(low<high && arr[high]>=pivotkey) { //从右向左扫描
high--;
}
arr[low] = arr[high]; //将比枢轴小的移到左端
while(low<high && arr[low]<=pivotkey) { //从左向右扫描
low++;
}
arr[high] = arr[low]; //将比枢轴大的移到右端
}
arr[low] = pivotkey; //将枢轴移动到正确位置
return low; //返回枢轴位置
}
4.效率分析
平均时间复杂度 O(n lb n),空间复杂度 O(lb n) ~ O(n)。
插入排序是一种 不稳定的 排序方法。
5.算法优化
枢轴的选取是影响算法性能的关键。
(七)堆排序
1.算法思想
2.代码实现
3.效率分析
(八)基数排序
1.算法思想
2.代码实现
3.效率分析
如有错误,欢迎留言指正 * _ *
如有错误,欢迎留言指正 * _ *