排序
1.概念
排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或
递减的排列起来的操作。平时的上下文中,如果提到排序,通常指的
是排升序(非降序)。通常意义上的排序,都是指的原地排序(in
place sort)。
稳定性:两个相等的数据,如果经过排序后,排序算法能保证其
相对位置不发生变化,则我们称该算法是具备稳定性的排序算法
2.七大基于比较的排序
插入排序 :
1. 直接插入排序
2. 希尔排序
选择排序 :
3. 选择排序
4. 堆排序
交换排序 :
5. 冒泡排序
6. 快速排序
归并排序 :
7. 递归排序
2.1插入排序
2.1.1直接插入排序
a.原理:
整个区间被分为1.无序区间和 2.有序区间 每次选择无序区间的第
一个元素,在有序区间内选择合适的位置入。
b.实现:
每次选择无序区间的第一个元素,在有序区间内选择合适的位置
插入。(保持稳定性) 有序 [ 0,i ) 无序[ i, array.length
)
public static void insertSort(int[] arrya){
for (int i=1;i<arrya.length;i++){
int key = arrya[i];
int j;
for(j=i-1;j>=0;j--){
if(arrya[j]<=key){
break;
}else{
arrya[j+1]=arrya[j];
}
}
arrya[j+1]=key;//此时的j已经经过--,减一
}
}
c.性能分析:
稳定性:稳定
插入排序,初始数据越接近有序,时间效率越高。
2.1.2 折半插入排序
a.原理:
在有序区间选择数据应该插入的位置时,因为区间的有序性,可以利用折半查找的思想。
b.实现:
public static void bsInsertSort(int[] array) {
for (int i = 1; i < array.length; i++) {
int v = array[i];
int left = 0;
int right = i;
// [left, right)
// 需要考虑稳定性
while (left < right) {
int m = (left + right) / 2;
if (v >= array[m]) {
left = m + 1;
} else {
right = m;
}
}
// 搬移
for (int j = i; j > left; j--) {
array[j] = array[j - 1];
}
array[left] = v;
}
}
c.性能分析
2.2希尔排序
a.原理
希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上分组和排序的工作。当到达gap==1时,所有记录在统一组内排好序。
希尔排序是对直接插入排序的优化。 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap ==1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比
b.实现
public static void shellSort(int[] array){
int gap = array.length;
while(true){
gap=(gap/3)+1; //或者gap=gap/2;
insertSortWithGap(array,gap);
if(gap==1){
break;
}
}
}
public static void insertSortWithGap(int[] array,int gap) {
for(int i = gap;i<array.length;i++){
int key = array[i];
int j ;
for(j = i - gap;j >=0&&array[j]>key;j-=gap){
array[j + gap] = array[j];
}
array[j+gap] = key;
}
}
c.性能分析
2.2选择排序
2.2.1直接选择排序
a.原理:
每一次从无序区间选出最大(或最小)的一个元素,存放在无序区间的最后(或最前),直到待全部排序的数据元素排完 。
b.实现
//有序 [0, array.length-i)
//无序 [array.length-i, array.legth)
public static void selectSort(int [] array){
for(int i=0;i<array.length-1;i++){
int maxIndex = 0;
for(int j = 1; j < array.length-i;j++){
if(array[j]>array[maxIndex]){
maxIndex = j;
}
}
swap(array,maxIndex,array.length-i-1);
}
}
c.性能分析
2.2.2双向选择排序
a.原理
每一次从无序区间选出最小 + 最大的元素,存放在无序区间的最前和最后,直到全部待排序的数据元素排完 。
b.实现
c.性能分析
2.2.3堆排序
a.原理
基本原理也是选择排序,只是不在使用遍历的方式查找无序区间的最大的数,而是通过堆来选择无序区间的最大的数。
注意:排升序建大堆,排降序建小堆。
b.实现
public static void heapSort(int[]array){
creatHeapBig(array);
for(int i = 0 ; i < array.length-1; i++){
swap(array,0,array.length-i-1);
shiftDownBig(array ,0 ,array.length -i- 1);
}
}
private static void creatHeapBig(int[] array) {
for ( int i = (array.length-2)/2;i >=0;i--){
shiftDownBig(array , i ,array.length);
}
}
c.性能分析
2.3交换排序
2.3.1冒泡排序
a.原理
在无序区间,通过相邻数的比较,将最大的数冒泡到无序区间的最后,持续这个过程,直到数组整体有序
b.实现
public static void bubbleSort(int [] array) {
for(int i = 0; i < array.length -1 ;i++){
boolean isSort = true; // *提高效率,减少不必要的循环
for(int j =0; j< array.length -i -1; j++){
if(array[j] > array[j+1]){
swap(array,j,j+1);
isSort = false; //*
}
}
if(isSort){
return ; //*
}
}
}
c.性能分析
2.3.2快速排序
a.原理
在排序区间选择一个基准值 (pivot)
遍历整个待排序区间,将比基准值大的(可能包含==)放基准值P的左边,比基准值小的(可以包括相等的)放在基准值的右边
采用分治的思想:堆左右的小区间分别采用相同的处理方式, 直到:区间长度1(有序)或者区间长度0(没有数)。
b.实现
public static void quickSort(int[] array) {
quickSortInternal(array, 0, array.length - 1);
}
// [left, right] 为待排序区间
private static void quickSortInternal(int[] array, int left, int right) {
if (left == right) {
return;
}
if (left > right) {
return;
}
// 最简单的选择基准值的方式,选择 array[left] 作为基准值
// pivotIndex 代表基准值最终停留的下标
int pivotIndex = partition(array, left, right);
// [left, pivotIndex - 1] 都是小于等于基准值的
// [pivotIndex + 1, right] 都是大于等于基准值的
quickSortInternal(array, left, pivotIndex - 1);
quickSortInternal(array, pivotIndex + 1, right);
}
private static int partition(int[] array, int left, int right) {
int i = left;
int j = right;
int pivot = array[left];
while (i < j) {
while (i < j && array[j] >= pivot) {
j--;
}
while (i < j && array[i] <= pivot) {
i++;
}
swap(array, i, j);
}
swap(array, i, left);
return i;
}
c.性能分析
稳定性:不稳定
2.4归并排序
a.原理
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide
andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
2.4.1递归排序
b.实现
//合并两个有序数组
private static void merge(int[] array, int low, int mid, int high) {
int i = low;
int j = mid;
int length = high - low;
int[] extra = new int[length];
int k = 0;
// 选择小的放入 extra
while (i < mid && j < high) {
// 加入等于,保证稳定性
if (array[i] <= array[j]) {
extra[k++] = array[i++];
}
else {
extra[k++] = array[j++];
}
}
// 将属于元素放入 extra
while (i < mid) {
extra[k++] = array[i++];
}
while (j < high) {
extra[k++] = array[j++];
}
// 从 extra 搬移回 array
for (int t = 0; t < length; t++) {
// 需要搬移回原位置,从 low 开始
array[low + t] = extra[t];
}
}
//实现
public static void mergeSort(int[] array) {
mergeSortInternal(array, 0, array.length);
}
// 待排序区间为 [low, high)
private static void mergeSortInternal(int[] array, int low, int high) {
if (low >= high - 1) {
return;
}
int mid = (low + high) / 2;
mergeSortInternal(array, low, mid);
mergeSortInternal(array, mid, high);
merge(array, low, mid, high);
}
c.性能分析
2.4.1非递归排序
实现:
> public static void mergeSort(int[] array) {
> for (int i = 1; i < array.length; i = i * 2) {
> for (int j = 0; j < array.length; j = j + 2 * i) {
> int low = j;
> int mid = j + i;
> if (mid >= array.length) {
> continue;
> }
> int high = mid + i;
> if (high > array.length) {
> high = array.length;
> }
> merge(array, low, mid, high);
> }
> } }
3.总结