1、插入排序
插入排序的基本思想:
将待排序表看成左右两个部分,其中左边为有序区,右边为无序区,整个排序过程就是将右边无序区中的元素逐个插入到左边的有序区中,以构成新的有序区
插入排序一般有两种,直接插入排序和希尔排序。
(1)直接插入排序:
稳定性:由于算法搜索插入位置的过程中,遇到相等或比自己小的元素就停止,所以是稳定的排序算法
时间复杂度:最坏:O(n^2);
最好O(n);
public static void directSort(int a[])
{
for(int i=1;i<a.length;i++)
{
int temp=a[i];
int j=i-1;
while(j>=0&&a[j]>temp)
{
a[j+1]=a[j];
j--;
}
a[j+1]=temp;
}
}
(2)希尔排序:
基本思想:将待排序列划分为若干组,在每组内进行直接插入排序,以使整个序列基本有序,然后再对整个序列进行直接插入排序。
分组方法:对给定的一个步长d>0,将下标相差为d的倍数的元素分在一组,这样共得到d组。d依次取值d1=n/2,d2=d1/2,...,dk=1;这样,随着步长逐渐减小,每组规模不断扩大。当取值为1时,执行依次直接插入排序。
一般情况下,时间复杂度O(n^2)。数组基本有序时,为O(n)。因此,将数组变成基本有序时,使用希尔排序,可以提高效率。
public static void shellSort(int a[])
{
int d=a.length/2;
while(d>0)
{
for(int i=d;i<a.length;i++)
{
int temp=a[i];
int j=i-d;
while(j>=0&&a[j]>temp)
{
a[j+d]=a[j];
j=j-d;
}
a[j+d]=temp;
}
d=d/2;
}
}
2、归并排序
时间复杂度:每一趟归并的时间复杂度为O(n),共需进行log2n趟,所以时间复杂度为O(nlog2n)
import java.util.Arrays;
//分而治之的思想
//归并算法基于两个基本操作:划分和归并
//划分操作将1个未排序序列划分为2个更短的子序列;
//归并操作将2个或多个有序子序列合并为1个更长的有序序列
//稳定排序
public class MergeSort {
public static void main(String[] args) {
// TODO 自动生成的方法存根
int a[]= {12,5,4,9,5};
mergeSort(a);
System.out.println(Arrays.toString(a));
}
private static void mergeSort(int[] a) {
int temp[]=new int[a.length];
mergeSort(a,0,a.length-1,temp);
}
private static void mergeSort(int[] a, int left, int right, int[] temp) {
if(left<right)
{
int mid=(left+right)/2;
mergeSort(a, left, mid, temp);//左子区间进行归并排序
mergeSort(a, mid+1, right, temp);//右子区间进行归并排序
merge(a,left,mid,right,temp);//合并左右区间
}
}
//归并
private static void merge(int[] a, int left, int mid, int right, int[] temp) {
int i=left;//左区间指针
int j=mid+1;//右区间指针
int t=0;//临时数组指针
while(i<=mid&&j<=right)
{
if(a[i]<=a[j])//如果左区间元素小于等于右区间元素,把左区间的元素放入临时数组
{
temp[t++]=a[i++];
}
else//否则把右区间元素放入临时数组
{
temp[t++]=a[j++];
}
}
while(i<=mid)//如果左区间有剩余元素,放入临时数组中
{
temp[t++]=a[i++];
}
while(j<=right)//如果右区间有剩余元素,放入临时数组中
{
temp[t++]=a[j++];
}
//把临时数组的元素复制到原数组
t=0;
while(left<=right)
{
a[left++]=temp[t++];
}
}
}
3、交换排序
(1)冒泡排序
基本思想:假设待排序表长为n,从后往前两两比较相邻元素的值,如果为逆序(A[i-1]>A[i]),则交换他们,直到序 列比较完。
时间复杂度:最好情况:初始有序,没有元素交换,为O(n);
最坏情况:初始逆序,为O(n^2);
public class BubbleSort {
//冒泡排序
//基本思想:逐渐比较两个相邻的元素,如果倒序(前大后小),就交换
//改进:如果有一次遍历没有交换元素,说明已经为有序,可以结束遍历;
//可以设置一个交换标志,可以提高算法的性能
//稳定性:稳定
public static void main(String[] args) {
// TODO 自动生成的方法存根
int a[]= {12,5,4,9,5};
bubbleSort(a);
}
public static void bubbleSort(int a[])
{
for(int i=0;i<a.length;i++)
{
for(int j=a.length-1;j>=i+1;j--)
{
if(a[j-1]>a[j])
{
int temp=a[j];
a[j]=a[j-1];
a[j-1]=temp;
for(int k=0;k<a.length;k++)
{
System.out.print(a[k]+" ");
}
System.out.println();
}
}
}
}
}
(2) 快速排序:对冒泡排序的改进
时间复杂度:O(n^2)
//快速排序
//基本思想:分治思想;取一个中间元素作为标志,标志之前放置比标志小的,之后放大的;再分别将左右进行快排;递归算法
//划分思想:
//从后往前找一个比中间元素小的;
//从前往后找一个比中间元素大的;
//稳定性:不稳定
public class QuickSort {
public static void main(String[] args) {
// TODO 自动生成的方法存根
int a[]= {12,5,4,9,5};
quickSort(a,a.length-1,0);
for(int i=0;i<a.length;i++)
{
System.out.print(a[i]+" ");
}
}
public static void quickSort(int a[],int high,int low)
{
if(low<high&&low>=0)
{
int mid=partition(a,low,high);
//System.out.println(mid+" "+low+" "+high);
quickSort(a, mid-1, low);//左区间
quickSort(a, high, mid+1);//右区间
}
}
private static int partition(int[] a, int low, int high) {
int x=a[low];//取第一个元素作为中间元素,把中间元素赋给x,使位置空出
while(low<high)
{
//低位(low)空出
//从后往前寻找比中间元素小的
while(low<high&&a[high]>x)
{
high--;
}
if(low<high)//该条件必须要加,否则会陷入死循环
{
a[low]=a[high];
low++;
}
//高位(high)空出
//从前往后寻找比中间元素大的
while(low<high&&a[low]<x)
{
low++;
}
if(low<high)//
{
a[high]=a[low];
high--;
}
}
a[low]=x;//此时low指向的是中间位置
return low;
}
}
4、选择排序
(1)堆排序
时间复杂度:O(nlog2n)
import java.util.Arrays;
//堆排序(树形选择排序)(完全二叉树)
//(根节点最小)小根堆;(根节点最大)大根堆
//不稳定
public class HeapSort {
public static void main(String[] args) {
// TODO 自动生成的方法存根
int a[]= {12,5,4,9,5};
heapSort(a);
System.out.println(Arrays.toString(a));
}
private static void heapSort(int[] a) {
//第一步:创建大根堆,从最后一个非叶子节点开始,从上到下,从左到右遍历
for(int i=a.length/2-1;i>=0;i--)
{
adjustHeap(a,i,a.length);
}
//第二步: 将堆顶元素与末尾元素进行交换,使末尾元素最大。
//然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。
for(int i=a.length-1;i>=0;i--)
{
swap(a,0,i);//交换堆顶元素和堆尾元素
adjustHeap(a, 0, i);//调整堆结构
}
}
private static void swap(int[] a, int i, int i2) {
int temp=a[i];
a[i]=a[i2];
a[i2]=temp;
}
private static void adjustHeap(int[] a, int i, int length) {
int temp=a[i];//取出当前节点的值
for(int j=i*2+1;j<length;j=j*2+1)//从当前节点的左子节点开始
{
if(j<length-1&&a[j]<a[j+1])//如果左子节点小于右子节点,j指向右子节点
{
j++;
}
if(a[j]>temp)//如果子节点的值大于根节点,把子节点的值赋给根节点,并把子节点位置空出
{
a[i]=a[j];
i=j;
}else
break;
}
a[i]=temp;//空位置放置当前节点的值
}
}
(2)简单选择排序
时间复杂度:O(n^2)
//简单选择排序
//基本思想:通过在待排序子表中完整地比较一遍以确定最大(小)元素,并将该元素放在子表的最前(后)面
//稳定性:不稳定
public class SelectSort {
public static void main(String[] args) {
// TODO 自动生成的方法存根
int a[]= {12,5,4,9,5};
selectSort(a);
for(int i=0;i<a.length;i++)
{
System.out.print(a[i]+" ");
}
}
public static void selectSort(int a[])
{
for(int i=0;i<a.length;i++)
{
int min=i;
for(int j=i+1;j<a.length;j++)
{
if(a[min]>a[j])
min=j;
}
if(min!=i)
{
int t=a[i];
a[i]=a[min];
a[min]=t;
}
}
}
}