冒泡排序 时间复杂度o(n^2)
定义对数组的一趟排序为,顺序遍历数组,如果数组中a[i]>a[i+1],那么就交换两个数的位置(前提这是升序排序),那么一趟排序就会找到一个最大值,并且将最大值放在数组的最后。
通过对数组的一趟排序,找出一个最大的值,放在数据最后的位置,通过对数组的下一趟排序,找出数组的第二大的数字放在倒数第二个位置,那么对数组进行多趟这样的排序,就能让整个数组编程有序的。如果数组有5个数,那么四趟排序就可以,四趟排序找到四个最大的数,剩下最小的就ok,那么对于数组长度为n数组,排序n-1躺趟就ok
private static void bubbleSort(int[] arr) {
int n=arr.length;
for(int i=0;i<n-1;i++){
for(int j=1;j<n-i;j++){
if(arr[j-1]>arr[j]){
int temp=arr[j];
arr[j]=arr[j-1];
arr[j-1]=temp;
}
}
}
}
选择排序 时间复杂度o(n^2)
每一趟排序选择出来一个最小的放在前面,多趟排序之后序列就会成有序的。同样还是n-1躺排序就可以了
private static void selectSort(int[] arr) {
int n=arr.length;
for(int i=0;i<n-1;i++){
int k=i;//假设每一趟排序的第一个数就是那个最小的
for(int j=i+1;j<n;j++){
if(arr[j]<arr[k]){
k=j;
}
}
if(k>i){
int temp=arr[k];
arr[k]=arr[i];
arr[i]=temp;
}
}
}
堆排序 时间复杂度o(nlogn)
堆是一种重要的数据结构,为一棵完全二叉树, 底层如果用数组存储数据的话,假设某个元素为序号为i(Java数组从0开始,i为0到n-1),如果它有左子树,那么左子树的位置是2i+1,如果有右子树,右子树的位置是2i+2,如果有父节点,父节点的位置是(n-1)/2取整。分为最大堆和最小堆,最大堆的任意子树根节点不小于任意子结点,最小堆的根节点不大于任意子结点。所谓堆排序就是利用堆这种数据结构来对数组排序,我们使用的是最大堆。处理的思想和冒泡排序,选择排序非常的类似,一层层封顶,只是最大元素的选取使用了最大堆。最大堆的最大元素一定在第0位置,构建好堆之后,交换0位置元素与顶即可。堆排序为原位排序(空间小), 且最坏运行时间是O(nlgn),是渐进最优的比较排序算法。
我个人人为堆排序的虽然效率上真的不错,但是这个堆的创建有点啰嗦,并不是很简洁 ,同时快速排序的效率不比堆排序差,但是相比堆排序来说更简洁,用快速排序就ok了
public void myHeapSort(int array[])
{
BuildHeap(array);
for(int i=array.length-1;i>=0;i--)
{ //把堆顶元素移到数组的后面。
int temp=array[0];
array[0]=array[i];
array[i]=temp;
heapify(array,0,i);
}
}
public void BuildHeap(int arr[]){
for(int i=0;i<arr.length;i++)
{//使用插入堆的方法从上往下进行建堆
int parent=(i-1)/2;
while(parent>=0&&arr[i]<arr[parent])
{
//如果父节点的下标大于0、并且当前节点小于父节点交换位置。继续向上比较,否则停止比较。
if(i==0)
break;
int temp=arr[parent];
arr[parent]=arr[i];
arr[i]=temp;
i=parent;
parent=(i-1)/2;
}
}
}
public void heapify(int arr[],int i,int len)
{ //调整堆,因为堆排序的时候我们都是选择堆顶元素,
//所以调整的时候都是从堆顶向下调整,因此此时的i始终是0
int l=2*i+1;
int r=2*i+2;
int lestgest=i;
//用于保存最小节点的下标
while(l<len)
{
if(arr[l]<arr[lestgest])
{
lestgest=l;
}
if(r<len&&arr[r]<arr[lestgest])
{
lestgest=r;
}
if(i==lestgest)
break;
int temp=arr[i];
arr[i]=arr[lestgest];
arr[lestgest]=temp;
i=lestgest;
l=2*i+1;
r=2*i+2;
}
}
快速排序 时间复杂度o(nlogn)
在快速排序中,我们先要选择一个基准,用作一趟排序中交换的标准,在一趟排序中,从左到右要有一个指针start,从右到左还要有一个指针end,从左到右找比基准大的数,然后和end位置进行交换,从右到左找比基准小的数,然后和start位置进行交换。当start和end重合在一起那么这趟排序结束,当前位置就是这个数组的分界点,左边的都是小于当前值,右边的都是大于当前值,然后递归操作两边,直到排序结束。
public void sort(int[] a,int low,int high){
int start = low;
int end = high;
int key = a[low];
while(end>start){
//从后往前比较
while(end>start&&a[end]>=key) //如果没有比关键值小的,比较下一个,直到有比关键值小的交换位置,然后又从前往后比较
end--;
if(a[end]<=key){
int temp = a[end];
a[end] = a[start];
a[start] = temp;
}
//从前往后比较
while(end>start&&a[start]<=key)//如果没有比关键值大的,比较下一个,直到有比关键值大的交换位置
start++;
if(a[start]>=key){
int temp = a[start];
a[start] = a[end];
a[end] = temp;
}
//此时第一次循环比较结束,关键值的位置已经确定了。左边的值都比关键值小,右边的值都比关键值大,但是两边的顺序还有可能是不一样的,进行下面的递归调用
}
//递归
if(start>low) sort(a,low,start-1);//左边序列。第一个索引位置到关键值索引-1
if(end<high) sort(a,end+1,high);//右边序列。从关键值索引+1到最后一个
}
}
归并排序 时间复杂度o(nlogn)
public static int[] sort(int[] a,int low,int high){
int mid = (low+high)/2;
if(low<high){
sort(a,low,mid);
sort(a,mid+1,high);
//左右归并
merge(a,low,mid,high);
}
return a;
}
public static void merge(int[] a, int low, int mid, int high) {
int[] temp = new int[high-low+1];
int i= low;
int j = mid+1;
int k=0;
// 把较小的数先移到新数组中
while(i<=mid && j<=high){
if(a[i]<a[j]){
temp[k++] = a[i++];
}else{
temp[k++] = a[j++];
}
}
// 把左边剩余的数移入数组
while(i<=mid){
temp[k++] = a[i++];
}
// 把右边边剩余的数移入数组
while(j<=high){
temp[k++] = a[j++];
}
// 把新数组中的数覆盖nums数组
for(int x=0;x<temp.length;x++){
a[x+low] = temp[x];
}
}