算法时间复杂度分析方法:
a 为子问题的个数 n/b 为子问题的规模大小 f(n)为子问题的时间复杂度
1. 直接插入排序:
算法思想:
外层循环 1~n来遍历数组的每一个元素;遍历到当前元素时,首先需要设置哨兵来记录当前的值(tmp),然后依次比较前面的所有元素值,比较后进行移动处理。
算法时间复杂度:
设总共有n个元素,则算法会执行n-1趟,最坏条件下当处理到第i个元素时需要和前(0 ~ i-1)个元素都进行比较,复杂度如下图。最坏条件下还需要将前 i 个元素都往后挪动一个单位(同时每轮需要加上哨兵设置复杂度1、找到位置后的插入复杂度1)。
(ps: 图片引用自 https://blog.csdn.net/qq_37623612/article/details/80312121 )
public void InsertSort(int arr[]) {
int i,j,tmp;
for(i=1;i<arr.length;i++) {
//用tmp来记载arr[i]处的元素值(哨兵)
tmp=arr[i];
j=i;
//tmp<arr[j-1]:将比tmp值大的往后移动(求出的是正序排序) | tmp>arr[j-1]:将比tmp值小的往后移动(求出的是逆序排序)
while(j>0 && tmp<arr[j-1]) {
arr[j]=arr[j-1];
j--;
}
arr[j]=tmp;
}
}
2. 快速排序:
算法思想:
每次比较数组头的元素值应该插入的位置,通过与当前数组的头和尾的大小比较来进行元素移动,将比当前元素值大的移动到后面,比当前元素值小的移动到前面,最终得到当前元素值的位置index。最后再递归地去调用排序算法进行从(start ~ index-1) 与 (index+1 ~ end)的排序。
算法时间复杂度:
采用主方法来计算,每次一分为2进行比较,子问题规模为原问题规模的一半,那么a=2 b=2,f(n)=n;那么结果计算为 T[n]= 2T[n/2] + f(n),采用主方法比较得到时间复杂度为 O(nlog n)
public int getIndex(int arr[],int start,int end) {
int temp =arr[start];
while(start<end) {
while(start<end && arr[end]>=temp) { //注意每次都是和temp比较
end--;
}
arr[start]=arr[end];
while(start<end && arr[start]<=temp) {
start++;
}
arr[end]=arr[start];
}
arr[start]=temp;
return start;
}
public void quickSort(int arr[],int start,int end) {
if(start<end) {
int index=getIndex(arr, start, end);
quickSort(arr, start, index-1); //注意这里是start,而不是0,那样会降低效率
quickSort(arr, index+1,end);
}
}
@Test
public void test() {
int arr[]= {2,5,3,1,15,12,42,98,79};
quickSort(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
3. 堆排序:
引用自:https://blog.csdn.net/ns_code/article/details/20227303?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.add_param_isCf&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.add_param_isCf
/*******************************
堆排序
Author:兰亭风雨 Date:2014-02-27
Email:zyb_maodun@163.com
********************************/
#include<stdio.h>
#include<stdlib.h>
/*
arr[start+1...end]满足最大堆的定义,
将arr[start]加入到最大堆arr[start+1...end]中,
调整arr[start]的位置,使arr[start...end]也成为最大堆
注:由于数组从0开始计算序号,也就是二叉堆的根节点序号为0,
因此序号为i的左右子节点的序号分别为2i+1和2i+2
*/
void HeapAdjustDown(int *arr,int start,int end)
{
int temp = arr[start]; //保存当前节点
int i = 2*start+1; //该节点的左孩子在数组中的位置序号
while(i<=end)
{
//找出左右孩子中最大的那个
if(i+1<=end && arr[i+1]>arr[i])
i++;
//如果符合堆的定义,则不用调整位置
if(arr[i]<=temp)
break;
//最大的子节点向上移动,替换掉其父节点
arr[start] = arr[i];
//继续往下遍历
start = i;
i = 2*start+1;
}
arr[start] = temp;
}
/*
堆排序后的顺序为从小到大
因此需要建立最大堆
*/
void Heap_Sort(int *arr,int len)
{
int i;
//把数组建成为最大堆
//第一个非叶子节点的位置序号为len/2-1
for(i=len/2-1;i>=0;i--)
HeapAdjustDown(arr,i,len-1);
//进行堆排序
for(i=len-1;i>0;i--)
{
//堆顶元素和最后一个元素交换位置,
//这样最后的一个位置保存的是最大的数,
//每次循环依次将次大的数值在放进其前面一个位置,
//这样得到的顺序就是从小到大
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
//将arr[0...i-1]重新调整为最大堆
HeapAdjustDown(arr,0,i-1);
}
}
}
4. 归并排序:
import org.junit.Test;
public class MergeSort {
//两路归并算法,两个排好序的子序列合并为一个子序列
public void merge(int []a,int left,int mid,int right){
int []tmp=new int[a.length];//辅助数组
int p1=left,p2=mid+1,k=left;//p1、p2是检测指针,k是存放指针
while(p1<=mid && p2<=right){
if(a[p1]<=a[p2])
tmp[k++]=a[p1++];
else
tmp[k++]=a[p2++];
}
while(p1<=mid) tmp[k++]=a[p1++];//如果第一个序列未检测完,直接将后面所有元素加到合并的序列中
while(p2<=right) tmp[k++]=a[p2++];//同上
//复制回原素组
for (int i = left; i <=right; i++)
a[i]=tmp[i];
}
public void mergeSort(int [] a,int start,int end){
if(start<end){//当子序列中只有一个元素时结束递归
int mid=(start+end)/2;//划分子序列
mergeSort(a, start, mid);//对左侧子序列进行递归排序
mergeSort(a, mid+1, end);//对右侧子序列进行递归排序
merge(a, start, mid, end);//合并
}
}
@Test
public void test(){
int[] a = { 49, 38, 65, 97, 76, 13, 27, 50 };
mergeSort(a, 0, a.length-1);
System.out.println("排好序的数组:");
for (int e : a)
System.out.print(e+" ");
}
}
图片引用自: https://blog.csdn.net/qq_43684985