算法复杂度
时间频度:一个算法花费的时间与算法中语句的执行次数成正比,一个算法中执行语句次数为语句频度/时间频度,
记为T(n)
时间复杂度:T(n)/f(n),当n趋向无穷大时,比值为一个不等于0的常数,则称f(n)是T(n)的同量级函数,记作
T(n)=O(f(n)),称O(f(n))为算法的时间复杂度
** 计算方法
** 忽略常数项
** 只保留最高次数项
** 忽略最高次数项的系数
常见的时间复杂度
** 常数阶 O(1) 通常为代码的执行语句不随某个变量的变化而变化
** 对数阶 O(log2(n)) 循环语句i的增加以某个倍数增加
** 线性阶 O(n) 一层循环
** 线性对数阶 O(nlog2(n)) 两层循环,一层的i为倍数增加,另一层自然增长
** 平方阶 O(n^2) 两层循环
** 立方阶 O(n^3) 三层循环
** k次方阶 O(n^k) k层循环
** 指数阶 O(2^n)
从上到下时间复杂度大小依次增长
空间复杂度:一个算法占用的内存与n成正比,缓存技术就是利用空间换时间
排序算法
常见排序算法分类
内部排序(将需要处理的数据加载到内存中进行排序)
** 插入排序
** 直接插入排序
** 希尔排序
** 选择排序
** 简单选择排序
** 堆排序
** 交换排序
** 冒泡排序
** 快速排序
** 归并排序
** 基数排序
外部排序(数据量大,无法全部加载到内存,借助外部存储进行排序)
冒泡排序
基本思想:对待排序序列进行从前往后依次比较相邻元素的值,如果发现二者与规定的规则不符合则交换,这样符合
我们预定规则的元素会逐渐向后移动
代码实现
public class BubblingSort {
public static void main(String[] args) {
int[] arr = {10, 8, 4, 5, 1};
int temp;
boolean flag=false;
System.out.println("排序前"+ Arrays.toString(arr));
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if(arr[j]>arr[j+1]){
flag=true;
temp=arr[j+1];
arr[j+1]=arr[j];
arr[j]=temp;
}
}
System.out.println("第"+(i+1)+"轮交换后排序后"+ Arrays.toString(arr));
if(flag){
flag=false;
}else {
break;
}
}
}
}
时间复杂度为O(n^2) 80000个元素的数组测试平均为13s
选择排序
选择排序属于内部排序法,从欲排序的数据中,按指定的规则选出某一元素,再按照规定交换位置
基本思想:从整个排序的数据中选出最小值与第一个元素进行交换,然后再去掉第一个元素从剩下的元素选出
最小值与第一个元素进行交换,以此类推直到此数据循环结束
代码示例
public class SelectSort {
public static void main(String[] args) {
int[] arr = {10, 8, 4, 5, 1};
System.out.println("排序前 " + Arrays.toString(arr));
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
int min = arr[i];
for (int j = i + 1; j < arr.length; j++) {
if (min > arr[j]) {
minIndex = j;
min = arr[j];
}
}
if (minIndex != i) {
arr[minIndex] = arr[i];
arr[i] = min;
}
System.out.println("第" + (i + 1) + "次排序后 " + Arrays.toString(arr));
}
}
}
时间复杂度为O(n^2) 80000个元素的数组测试平均为4s
插入排序
插入排序也属于内部排序法,是对欲排序元素以插入的方式找到该元素的适当位置
基本思想:将一个n个元素的待排序数据看做一个有序表一个无序表,有序表有一个元素,无序表n-1个元素,排序过
程中每次从无序表取出一个元素,将他与有序表内元素进行比较以找到需要插入的位置
代码实现
public class InsertSort {
public static void main(String[] args) {
int[] arr = {10, 8, 4, 5, 1};
for (int i = 1; i < arr.length; i++) {
int insertIndex = i - 1;
int insertValue = arr[i];
while (insertIndex >= 0 && insertValue < arr[insertIndex]) {
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
if (insertIndex + 1 != i) {
arr[insertIndex + 1] = insertValue;
}
System.out.println("第" + i + "轮插入后 " + Arrays.toString(arr));
}
}
}
时间复杂度为O(n^2) 80000个元素的数组测试平均为1s
希尔排序
以从小到大为例,在插入排序中,如果最小的数在后面,则在移动时,出现的问题就是移动的次数过多
希尔排序就是为了解决此问题的一种改进版插入排序
思想:
把需要排序的数据根据下标进行一定增量分组,对每组使用直接插入排序,增量逐渐减少,直到增量减至为1,
算法终止
交换法代码实现
public class ShellSort {
public static void main(String[] args) {
int[] arr = {8, 9, 5, 6, 0, 1, 3, 2, 4, 7};
int temp;
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
for (int j = i - gap; j >= 0; j -= gap) {
if (arr[j] > arr[j + gap]) {
temp = arr[j + gap];
arr[j + gap] = arr[j];
arr[j] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
}
}
80000个元素的数组测试平均为9s,反而变慢了
移位法
public class ShellInsertSort {
public static void main(String[] args) {
int[] arr = {8, 9, 5, 6, 0, 1, 3, 2, 4, 7};
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
int index = i;
int value = arr[index];
while (index - gap >= 0 && arr[index - gap] > value) {
arr[index] = arr[index - gap];
index -= gap;
}
if (index != i) {
arr[index] = value;
}
}
System.out.println(Arrays.toString(arr));
}
}
}
80000个元素的数组测试平均已经不到1s 8百万个数据时平均2到3s