文章目录
- 1.排序的基本概念
- 1.1什么是排序
- 1.2稳定性
- 1.3 排序的应用
- 2.插入排序
- 2.1直接插入排序的原理
- 2.2插入排序实现
- 3.希尔排序
- 3.1希尔排序的原理
- 3.2希尔排序的实现
- 4.选择排序
- 4.1选择排序的原理
- 4.2.选择排序的实现
- 5.堆排序
- 5.1基本原理
- 5.2堆排序的实现
1.排序的基本概念
1.1什么是排序
排序:就是一串记录,按照其中某个或某个关键字的大小,递增或递减的排列起来的操作。通常意义上的排序,都是指的是原地排序(在原有的数组的基础上进行排序,而不是建立新的数组)。
1.2稳定性
两个相等的数据,如果经过排序后,排序算法能保证其相对位置不发生变化,则我们称该算法是具备稳定性的排序。
下面的排序具有稳定性:
1.3 排序的应用
排序的用处很多,比如是说淘宝上的商品的价格从高到低,或者说是中国大学的排名。
2.插入排序
2.1直接插入排序的原理
整个区间被分为两部分:待排序区间和已排序区间。然后每次在待排序区间选择一个元素,往有序区间内选择合适的位置插入。
2.2插入排序实现
import java.util.Arrays;
public class MySort {
public static void insertSort(int [] arr){
int bound=1;
//[0,bound)是已排序区间
//[bound,length)是待排序区间
for(;bound<arr.length;bound++){
//里面每次就要执行具体的比较插入过程了
//取出待排序区间的最开始的元素
int v=arr[bound];
int cur=bound-1;
for(;cur>=0;cur--){
//这个循环就是比较插入的细节
//拿着v这个值一次往前比较找到合适的位置
if(arr[cur]>v){
//这就说明v插入到arr[cur]之前
arr[cur+1]=arr[cur];
}else{
//说明已经找到合适的位置了,可以结束循环了
break;
}
}
//这个所谓的合适的位置就是cur的后面,就是cur+1
arr[cur+1]=v;
}
}
public static void main(String[] args) {
int [] arr={4,8,2,9,5,1,0};
insertSort(arr);
System.out.println(Arrays.toString(arr));
}
}
注意: 插入排序是一个稳定排序,但是要注意代码的实现细节,插入排序的时间复杂度为O(N^2),空间复杂度为O(1)。
插入排序的两个特点:
1)如果数组的长度比较短,则排序效率会高;
2)如果数组相对有序,排序效率也很高。
3.希尔排序
希尔排序,是争对插入排序来进行进一步的改进的。
3.1希尔排序的原理
希尔排序的基本思想:
首先选定一个整数gap,然后把所有待排序文件中的所有记录分成组,所有距离为gap的分为同一组,并对每一组的内容进行排序。然后取不同的gap(比前一个gap小),重复上述的分组和排序工作,直到gap为1时,此时所有的记录里排序即可。
gap比较常见的序列选择为:
size/2 ,size/4,size/8…1 称为希尔序列。
3.2希尔排序的实现
public class MySort{
public static void shellSort(int [] arr){
//指定的gap序列,就用希尔序列来指定
int gap=arr.length/2;
while(gap>=1){
_shellSort(arr,gap);
gap=gap/2;
}
}
public static void _shellSort(int[] arr,int gap ){
//进行分组插排,分组的依据就是gap
//gap同时也表示的分的组数
//同组相邻的元素,下标的插值就是gap
int bound=gap;
for(;bound<arr.length;bound++){
int v=arr[bound];
int cur=bound-gap;
for(;cur>=0;cur=cur-gap){
if(arr[cur]>v){
//进行搬运
arr[cur+gap]=arr[cur];
}else{
break;
}
}
arr[cur+gap]=v;
}
}
public static void main(String[] args) {
int [] arr={4,8,2,9,5,1,0};
shellSort(arr);
System.out.println(Arrays.toString(arr));
}
}
希尔排序的时间复杂度仍然为O(N^2),希尔排序的空间复杂度为O(1),稳定性:不稳定排序。
4.选择排序
4.1选择排序的原理
把整个数组分成两个区间:待排序区间和已排序区间,遍历带排序区间,通过打擂台的方式,找出这个区间的最小元素,以待排序区间开始位置作为擂台。以下图为例,刚开始以9作为擂台,和待排序区间的元素比较,刚开始5比9小,然后交换元素的位置,5作为待排序区间的第一个元素,然后5与2比较,2小,然后交换元素的位置,然后2与后面的7 3 6 8按顺序比较,还是2最小,因此2就是排序好了的,以此类推。
4.2.选择排序的实现
public class MySort{
public static void selectSort(int [] arr){
//创建一个变量bound表示已排序区间和待排序区间的边界
//[0,bound)已排序区间
//[bound,length)待排序区间
int bound=0;
for(;bound<arr.length;bound++){
//里层的循环要进行打擂台的过程
//擂台的位置就是bound的下标的位置
for(int cur=bound+1;cur<arr.length;cur++){
if(arr[cur]<arr[bound]){
//如果发现挑战者比擂主小,就交换两个元素
swap(arr,bound,cur);
}
}
}
}
public static void swap(int [] arr,int x,int y){
int tmp=arr[x];
arr[x]=arr[y];
arr[y]=tmp;
}
public static void main(String[] args) {
int [] arr={4,8,2,9,5,1,0};
selectSort(arr);
System.out.println(Arrays.toString(arr));
}
}
选择排序的时间复杂度为O(N^2),空间复杂度为O(1),也是不稳定排序。
5.堆排序
5.1基本原理
堆排序?(升序)我们肯定一开始就想到的是建立一个小堆,每次取堆顶元素,依次取N次,这确实得到了升序结果,但是这种思路,不是原地排序。
为了达到原地排序的效果,就需要建立一个大堆,然后每次删除堆顶元素(拿堆顶元素和堆的最后一个元素交换),此时最大值就跑到最后面了,此时这个最大值虽然从堆上删除了,但是正好来到了数组的最末端,从0号元素向下调整,使前面的元素重新成为堆,再次把0号进行前面的操作。
后面的操作类似于上图。
5.2堆排序的实现
public static void heapSort(int [] arr){
//1.首先建堆
createHeap(arr);
//2.需要循环取出堆顶元素,和最后一个元素交换并初值
//再对0号元素调整
int heapSize=arr.length;
for(int i=0;i<arr.length;i++){
swap(arr,0,heapSize-1);
heapSize--;
shiftDown(arr,heapSize,0);
}
}
public static void createHeap(int [] arr){
for(int i=(arr.length-1-1)/2;i>=0;i--){
shellSort(arr);
}
}
public static void shiftDown(int [] arr,int size,int index){
int parent=index;
int child=2*parent+1;
while(child<size){
//找左右字数比较打大的
if(child+1<size&&arr[child+1]>arr[child]){
child=child+1;
}
if(arr[parent]<arr[child]){
swap(arr,parent,child) ;
}
parent=child;
child=2*parent+1;
}
}
public static void swap(int [] arr,int x,int y){
int tmp=arr[x];
arr[x]=arr[y];
arr[y]=tmp;
}
public static void main(String[] args) {
int [] arr={4,8,2,9,5,1,0};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
}