一、算法的时间复杂度
1.算法的时间频度
一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。
例如使用for(int i = 1;i <= n;i++){},那么在这条for循环中,时间频度就是执行次数,执行次数会随着n的增大而增大,那么它的时间频度就是T(n) = n+1。
而像这种i = n+1;这种语句的执行次数不会随n的增大而增大,始终T(n) = 1
2.时间复杂度
1)
一般情况下,算法中的基本操作语句的重复执行次数是问题规模
n
的某个函数,用
T(n)
表示,若有某个辅助函数
f(n)
,使得当
n
趋近于无穷大时,
T(n) / f(n)
的极限值为不等于零的常数,则称
f(n)
是
T(n)
的同数量级函数。记作
T(n)=
O
( f(n) )
,称O
( f(n) )
为算法的渐进时间复杂度,简称时间复杂度。
●
2)
T(n)
不同,但时间复杂度可能相同。 如:
T(n)=n²+7n+6
与
T(n)=3n²+2n+2
它们的
T(n)
不同,但时间复杂度相同,都为
O(n²)
。
3)
计算时间复杂度的方法:
●
•
用常数
1
代替运行时间中的所有加法常数
T(n)=n²+7n+6 => T(n)=n²+7n+1
•
修改后的运行次数函数中,只保留最高阶项
T(n)=n²+7n+1 => T(n) = n²
•
去除最高阶项的系数
T(n) = n²
=> T(n) = n² => O(n²)
3.常见的时间复杂度
1)
常数阶
O(1)
2)
对数阶
O(
log
2
n
)
3)
线性阶
O(n)
4)
线性对数阶
O(n
log
2
n
)
5)
平方阶
O(n^2)
6)
立方阶
O(n^3)
7)
k
次方阶
O(
n^k
)
8)
指数阶
O(2^n)
说明:
常见的算法时间复杂度由小到大依次为:
Ο(1)
<
Ο(
log
2
n
)
<
Ο(
n)
<
Ο(
nlog
2
n
)
<
Ο(
n
2
)
<
Ο(
n
3
)
<
Ο(
n
k
)
<
Ο(
2
n
)
,随着问题规模
n
的不断增大,上述时间复杂度不断增大,算法的执行效率越低
二、冒泡排序算法
1.冒泡排序实现
思路分析: 1.传入一个数组,进行两次for循环,进行判断 2.第一个for循环决定进行排序的次数,第二个for循环决定每次排序时执行的次数 3.第一轮执行完,那么数组中最大的数将会被放在数组最后一个格子中,依次类推 4.那么,每一轮执行完后,每次排序执行的次数就减少1,因为数组的格子逐渐从后填满,所以在第二个for中的判断条件为int.length-1 5.-1是因为每次当前数组格子与下一个格子数比,如果不减一,那就会取到格子外,数组越界异常
public void maopao(int[] ints){
for(int i = 0;i < ints.length;i++){
for (int j = 0;j < ints.length - i - 1;j++){
if(ints[j] > ints[j + 1]){
int temp = ints[j];
ints[j] = ints[j+1];
ints[j+1] = temp;
}
}
}
}
2.代码优化
思路分析: 1.如果在一轮循环中都没有进行交换,那说明此时已经排序好了,不用浪费时间,直接输出即可 2.创建一个判断标志,只要执行了交换语句,那就继续循环,没执行,直接跳出循环
public void maopaoPlus(int[] ints){
boolean flag = false;
for(int i = 0;i < ints.length;i++){
for (int j = 0;j < ints.length - i - 1;j++){
if(ints[j] > ints[j + 1]){
flag = true;
int temp = ints[j];
ints[j] = ints[j+1];
ints[j+1] = temp;
}
}
if(!flag){//相当于flag == false
break;
}else {
flag = false;
}
}
}
三、选择排序实现
选择排序(select sorting)也是一种简单的排序方法。它的基本思想是:第一次从arr[0]~arr[n-1]中选取最小值,与arr[0]交换,第二次从arr[1]~arr[n-1]中选取最小值,与arr[1]交换,第三次从arr[2]~arr[n-1]中选取最小值,与arr[2]交换,…,第i次从arr[i-1]~arr[n-1]中选取最小值,与arr[i-1]交换,…, 第n-1次从arr[n-2]~arr[n-1]中选取最小值,与arr[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。
思路分析: 1.传入一个数组 2.选择第一个元素,从数组中选择最小的元素,与其交换位置 3.选择第二个元素,从数组.length-1中选择最小的元素,与其交换位置,以此类推
public void choose(int[] ints){
int temp = 0;//记录最小值
int id = 0;//记录最小值索引
for(int i = 0;i < ints.length;i++){
temp = ints[i];
id = i;
for(int j = i + 1;j < ints.length;j++){
if(temp > ints[j]){
temp = ints[j];
id = j;
}
}
//交换最小值与ints[i]值
int a = ints[id];
ints[id] = ints[i];
ints[i] = a;
}
}
四、插入排序的实现
思路分析: 1.插入排序是把传入表中的数据进行插入,完成从小到大或者从大到小排序 2.将表中的第一个数据当做有序表,其他的数据当做无序表 3.从无序表中取依次元素,(从小到大)每次取出一个元素,就在有序表中逆向比较,大的放后面,小的放前面 编程思路: 1.创建一个变量存储当前需要插入的数据,为insertVal,再创建一个变量,用于存储待插入数的插入位置,为insertIndex 2.使用for循环获取每次的待插入数,初始值为ints[1],最后取到ints[ints.length-1] 3.insertIndex初始值为有序表中的最后一个元素位置 4.使用while循环进行判断,待插入值与插入位置值进行比较,如果比他小,继续向前判断,直到到有序表的尽头
public void insert(int[] ints){
int insertVal = 0;
int insertIndex = 0;
for (int i = 1;i < ints.length;i++){
insertVal = ints[i];
insertIndex = i - 1;
while (insertIndex >= 0 && insertVal < ints[insertIndex]){//满足此条件就会继续向下找位置
ints[insertIndex + 1] = ints[insertIndex];//当前对比值向后移动
insertIndex--;//找前面的进行对比
}
ints[insertIndex + 1] = insertVal;
}
}
五、希尔排序的实现
1.交换式
思路分析: 1.希尔排序是通过几次(取决于传入数组的长度)交换后使用简单的大小交换进行的排序 2.第一次交换:每隔i = ints.length/2个数据进行绑定,进行交换 3.往后每次交换为i/2步,直到i == 0
public void hill(int[] ints){
//第一次的i = ints.length/2
int count = ints.length;
while (count != 1) {//循环让每次交换进行,直到交换步数为0
count = count / 2;//每次让步数除2,保留整数
for (int i = count; i < ints.length; i++) {
for (int j = i - count; j >= 0; j -= count) {
if (ints[j] > ints[j + count]) {
int temp = ints[j];
ints[j] = ints[j + count];
ints[j + count] = temp;
}
}
}
}
}
2.移位式
public static void shellSort2(int[] arr) {
// 增量gap, 并逐步的缩小增量
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
// 从第gap个元素,逐个对其所在的组进行直接插入排序
for (int i = gap; i < arr.length; i++) {
int j = i;
int temp = arr[j];
if (arr[j] < arr[j - gap]) {
while (j - gap >= 0 && temp < arr[j - gap]) {
//移动
arr[j] = arr[j-gap];
j -= gap;
}
//当退出while后,就给temp找到插入的位置
arr[j] = temp;
}
}
}
}
六、常用排序复杂度
相关术语解释:
1)
稳定
:如果
a
原本在
b
前面,而
a=b
,排序之后
a
仍然在
b
的前面;
2)
不稳定
:如果
a
原本在
b
的前面,而
a=b
,排序之后
a
可能会出现在
b
的后面;
3)
内排序
:所有排序操作都在内存中完成;
4)
外排序
:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
5)
时间复杂度:
一个算法执行所耗费的时间。
6)
空间复杂度
:运行完一个程序所需内存的大小。
7)
n:
数据规模
8)
k:
“
桶”的个数
9)
In-place:
不占用额外内存
10)
Out-place:
占用额外内存