1.直接插入排序
直接插入排序类似于我们日常生活中的行为,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。直接插入排序的特点是:“越有序,越高效”。
/**
* @name: insertSort
* @description: 直接插入排序,时间负责度O(n^2)-->最好是O(n)
* 空间负责度O(1)
* 稳定性:稳定
* @param array
* @return: void
*
*/
public static void insertSort(int[] array){
for(int i=1;i<array.length;i++){
//保存本次要放置的元素
int tmp=array[i];
int j=0;
for(j=i-1;j>=0;j--){
if(array[j]>tmp){
//注意此处数组的下标应为j相关的,因为j+1不断向后移动
array[j+1]=array[j];
}else{
//有序后,前面的部分一定有序
//array[j+1]=tmp;
break;
}
}
/*走到此处有两种情况:
* 1.j此时小于0;
* 2.array[j]<tmp--->则前面也已经有序
*/
array[j+1]=tmp;
}
}
2.希尔排序(Shell排序)
希尔排序是对插排的优化,因为在部分情况下插排的效率会极低(比如数组完全从大到小),此处插排就完全没有了自己的优势。
希尔排序基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后去重复上述分组和排序的工作。当增量=1时,所有记录在统一组内排好序。
希尔排序特点:“增量分组,组内插排”。经过之前的排序,在增量为1时数组已经大致有序此时进行插排效率就非常高了。
/* *
* @name: shellSort
* @description: 希尔排序(分组排序)
* 直接插排的优化
* 时间复杂度:O(n^1.3 ~ n^1.5)
* 空间复杂度:O(1)
* 稳定性:不稳定
* @param array
* @return: void
*
*/
public static void shellSort(int[] array){
//设置增量数组
int[] drr={5,3,1};
for(int i=0;i<drr.length;i++){
shell(array,drr[i]);
}
}
private static void shell(int[] array, int gap) {
for(int i=gap;i<array.length;i++){
int tmp=array[i];
int j=0;
//此处判断j的减幅应为gap,进行同组直接插排
for(j=i-gap;j>=0;j-=gap){
if(array[j]>tmp) {
array[j+gap]=array[j];
}else{
break;
}
}
array[j+gap]=tmp;
}
}
3.选择排序之直接选择排序
基本思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完
/**
* @name: selectSort
* @description: 选择排序
* 时间复杂度:O(n^2)
* 空间复杂度:O(1)
* 稳定性:不稳定
* @param array
* @return: void
*
*/
public static void selectSort(int[] array){
for(int i=0;i<array.length;i++){
for(int j=i+1;j<array.length;j++){
if(array[j]<array[i]){
int tmp=array[j];
array[j]=array[i];
array[i]=tmp;
}
}
}
}
4.选择排序之堆排序
堆排序是利用堆这种数据结构所涉及的一种排序算法,是选择排序的一种,通过堆来进行选择数据。排升序需要大堆,降序则利用小堆。
在堆排序之前先来回顾一下堆这个数据结构。
堆是用数组实现的完全二叉树,堆根据“堆属性”进行排序,“堆属性”决定了树中节点的位置。“堆属性”分为最大堆和最小堆,两者区别在于节点的排序方式。
最大堆(大顶堆)—父节点的值大于任何一个子节点的值。在最小堆(小顶堆)中,父节点的值比每一个子节点的值都要小。这就是所谓的“堆属性”,并且这个属性对堆中的每一个节点都成立。如下图就是一个典型的最大堆。
根据这一属性,那么最大堆总是将其中的最大值存放在树的根节点。而对于最小堆,根节点中的元素总是树中的最小值。堆属性非常的有用,因为堆常常被当做优先队列使用,因为可以快速的访问到“最重要”的元素。
public class TestHeap{
/*此处堆的实现结构为数组*/
private int[] elem;
//当前使用空间大小
private int usedSize;
//默认数组大小
private static final int DEFAULT_SIZE=10;
public TestHeap(){
this.elem=new int[DEFAULT_SIZE];
this.usedSize=0;
}
}
堆的初始化,此处我们创建一个最大堆
//创建一个(大根)堆
/*
*节点表示(下标)
* 子推父:n-->(n-1)/2
* 父推子:n -->2n+1(左孩子) 2n+2(右孩子)
* 此处需要从子节点进行向上调整
* */
public void initHeap(int[] array) {
for(int i=0;i<array.length;i++){
this.elem[i]=array[i];
this.usedSize++;
}
//从最后一个节点开始
for(int i=(array.length-1)/2;i>=0;i--){
AdjustDown(i,this.usedSize);
}
}
/**
* @name: AdjustDown
* @description: 向下调整
* @param root
* @param len 调整的范围(当前数组的已用大小)
* @return: void
*
*
*/
public void AdjustDown(int root, int len) {
int parent=root;
/*用来指向左右孩子中的最大值进行调整*/
int child=2*parent+1;
while(child<len){
//判断是否存在右孩子
if(child+1<len){
if(this.elem[child]<this.elem[child+1])
child=child+1;
}
//child的存放的是左右孩子的最大值
if(this.elem[parent]<this.elem[child]){
//交换
int tmp=this.elem[parent];
this.elem[parent]=this.elem[child];
this.elem[child]=tmp;
//调整一个可能会影响下面的节点
parent=child;
child=2*parent+1;
}else{
break;
}
}
}
当堆已经调整成为最大堆时才能进行堆排序,将堆顶元素与堆的最后一个元素交换,在进行从堆顶向下的向下调整,调整结束则当前的数组已经有序(升序)。
/**
* @name: HeapSort
* @description: 堆排序(先将堆调整为大根堆)
* 时间复杂度:O(nlogn)
* 空间复杂度:O(1)
* 稳定性:不稳定
* @return: void
*
*/
public void HeapSort() {
//前提条件是当前堆已经为大根堆
int end=this.usedSize-1;
while(end>0){
int tmp=this.elem[0];
this.elem[0]=this.elem[end];
this.elem[end]=tmp;
AdjustDown(0,end);
end--;
}
}
4.冒泡排序
传统的冒泡排序十分简单,效率也不太高,我们今天来写一下冒泡排序的优化。改进后的冒泡排序如果在一趟排序中没有修改过数组则说明当前数组已经有序直接退出循环。
/**
* @name: bubbleSort
* @description: 冒泡排序
* 时间复杂度:O(n^2) 优化之后O(n)
* 空间复杂度:O(1)
* 稳定性:稳定
* @param array
* @return: void
*
*/
public static void bubbleSort(int[] array){
boolean flag=true;
//代表比较的趟数
for(int i=0;i<array.length-1;i++){
for(int j=0;j<array.length-i-1;j++){
if(array[j]>array[j+1]){
int tmp=array[j];
array[j]=array[j+1];
array[j+1]=tmp;
flag=false;
}
}
//如果没被更改过则说明当前数组已经有序,直接break退出for循环
if(flag)
break;
}
}