认识 时间复杂度
常数时间的操作
一个操作和 样本的数据量没有 关系,每次都是固定时间内完成。
时间复杂度为一个算法流程中,常数操作数量的一个指标; 用Big O来表示
对一个算法流程非常熟悉, 发生多少 常数时间 的操作
表达式中,只要高阶项 。 也不要高阶项的系数
评价一个算法的好坏,先看时间复杂度,然后 分析不同数据样本下 实际 运行时间, 也就是常数项 时间。
选择排序
时间复杂度 O(n^2) 额外空间 O(1)
public void userSort(int[] arr) {
System.out.println("===选择排序===");
if (arr == null || arr.length < 2) {
return;
}
// 选择排序,每次选一个最小的或者最大的,然后和当前 索引交换
// i 从 0 遍历到 length-1, 剩下一个肯定是最大的了
for (int i = 0; i < arr.length - 1; i++) {
int min = i; // 保存 值最小的索引
// 依次遍历后续每个位置
for (int j = i + 1; j < arr.length; j++) {
// 从 i+ 1 开始选择,前面的已经是选择好的
if (arr[j] < arr[min]) {
min = j;
}
}
// 遍历完成之后。min 和 i 交换
swap(arr, i, min);
}
}
python 实现
def user_sort(self, arr):
print("选择排序")
# 选择排序
for i in range(len(arr)):
min_index = i
for j in range(i+1, len(arr)):
if arr[j] < arr[min_index]:
min_index = j
arr[i], arr[min_index] = arr[min_index], arr[i]
冒泡排序
时间复杂度O(n^2) 空间复杂度O(1)
从0开始
public void userSort(int[] arr) {
if(arr == null || arr.length <2){
return;
}
// 冒泡排序,俩俩依次对比
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j < arr.length; j++) {
// 俩俩比较,前面的比后面的大就交换
if (arr[i] > arr[j]) {
swap(arr, i, j);
}
}
}
}
从最后开始
public void userSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
// 冒泡排序,从最后一位开始,谁大,谁冒
for (int i = arr.length - 1; i > 0; i--) {
// 每次冒出一个最大值,索引依次递减
for (int j = 0; j < i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
}
python 实现
def user_sort(self, arr):
print("冒泡排序")
for i in range(len(arr))[::-1]:
for j in range(i):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
插入排序
就是在前面已经排好序的时候,依次和前面的数据比较,如果小,就和当前元素交换,直到不能插入,就退出。
在大部分是已经排好序的时候,效果最好
public void userSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
// 插入排序
// 从索引1开始,0位置就是已经排好的。
for (int i = 1; i < arr.length; i++) {
for (int j = i - 1; j >= 0; j--) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
} else {
break;
}
}
}
}
python 实现
调试的时候,写了个bug,交换 = 写成 ==了。我去。看了半天。
def user_sort(self, arr):
print("插入排序")
for i in range(1, len(arr)):
for j in range(i)[::-1]:
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
else:
break
归并排序
从中间分成左右俩部分,分别排好序,然后合并。
调试的时候,userSort方法忘记调用mergeSort了,看了半天。我了个去。。。
public void userSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
// 归并排序
mergeSort(arr, 0, arr.length - 1);
}
public void mergeSort(int[] arr, int l, int r) {
if (l == r) {
// 递归的结束条件。
return;
}
int mid = l + ((r - l) >> 1);
mergeSort(arr, l, mid); // 排左边, 递归
mergeSort(arr, mid + 1, r); // 排 右边, 递归
merge(arr, l, mid, r); // 合并
}
public void merge(int[] arr, int l, int m, int r) {
// 需要辅助数组,大小是当前r-l + 1
int[] helpArry = new int[r - l + 1];
int helpArryCurrentIndex = 0; // 辅助数组当前索引
int lCurrentIndex = l; // 左边当前索引
int rCurrentIndex = m + 1; // 右边当前索引
// 循环条件,左边当前索引还没有到mid,以及 右边当前索引还没有到r
while (lCurrentIndex <= m && rCurrentIndex <= r) {
// 判断当前左 右俩个数谁进入 辅助数组
if (arr[lCurrentIndex] <= arr[rCurrentIndex]) {
// 左边 数小,进入辅助数组
helpArry[helpArryCurrentIndex] = arr[lCurrentIndex];
// 左边索引加1
lCurrentIndex += 1;
} else {
// 右边 数小, 进入辅助数组
helpArry[helpArryCurrentIndex] = arr[rCurrentIndex];
// 右边索引加1
rCurrentIndex += 1;
}
// 辅助数组索引 必加1
helpArryCurrentIndex += 1;
}
// 上面while循环条件结束,有可能左边还有数,也有可能右边还有数
while (lCurrentIndex <= m) {
// 左边还有数
helpArry[helpArryCurrentIndex++] = arr[lCurrentIndex++];
}
while (rCurrentIndex <= r) {
// 右边还有数
helpArry[helpArryCurrentIndex++] = arr[rCurrentIndex++];
}
// 把辅助数组的数,拷贝到arr数组中
for (int i = 0; i < helpArry.length; i++) {
arr[l + i] = helpArry[i];
}
}
python 实现
注意,一点要检查 传递进来的数组的大小,负责容易死循环,
或者 递归结束条件改为 l>=r
class InsertSort(ArrayUtil):
def user_sort(self, arr):
print("插入排序")
for i in range(1, len(arr)):
for j in range(i)[::-1]:
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
else:
break
class MergeSort(ArrayUtil):
def user_sort(self, arr):
if len(arr) == 0:
return
self.merge_sort(arr, 0, len(arr) - 1)
def merge_sort(self, arr, l, r):
# print(f"l == r {l}, {r}")
if l == r:
return
m = l + ((r-l) >> 1)
# print(f"{l} + {r} ==> {m}")
self.merge_sort(arr, l, m)
self.merge_sort(arr, m + 1, r)
self.merge(arr, l, m, r)
def merge(self, arr, l, m, r):
help_arry = []
l_cur_index = l
r_cur_index = m + 1
while l_cur_index <= m and r_cur_index <= r:
if arr[l_cur_index] <= arr[r_cur_index]:
help_arry.append(arr[l_cur_index])
l_cur_index += 1
else:
help_arry.append(arr[r_cur_index])
r_cur_index += 1
while l_cur_index <= m:
help_arry.append(arr[l_cur_index])
l_cur_index += 1
while r_cur_index <= r:
help_arry.append(arr[r_cur_index])
r_cur_index += 1
for index, item in enumerate(help_arry):
arr[l + index] = item
快速排序1.0
[ <5 5 >5 ]
1:选最后一个元素作为划分值
2:小于这个元素的放在左边,大于这个元素的放在右边
依次对左右俩个子序列操作步骤 1,2
时间复杂度O(N^2)(最坏情况)
123456789
@Override
public void userSort(int[] arr) {
quickSort(arr, 0, arr.length - 1);
}
public static void quickSort(int[] arr, int l, int r) {
// 递归退出条件
if (l >= r) {
return;
}
// 分割
int[] p = pararion(arr, l, r);
quickSort(arr, l, p[0]);
quickSort(arr, p[1], r);
}
public static int[] pararion(int[] arr, int l, int r) {
int less = l - 1; // 小于区域
int more = r; // 大于区域
// 当 l 和大于区域 相撞,停止
// 划分值为最后一个元素 arr[r]
while (l < more) {
if (arr[l] < arr[r]) {
// 当前元素小于 划分值 ,小于区域 和 l 交换
// 小于区域 右扩(less++), 看下一个元素(l++)
less++;
swap(arr, less, l); // 因为 小于区域下一个 元素 可能是划分值,
l++;
} else if (arr[l] > arr[r]) {
// 当前元素 大于 划分值,大于区域左扩(more 减一 )
// 当前元素和 大于区域的下一个元素交换
swap(arr, --more, l);
} else {
// 当前元素等于 划分值, 看下一个元素
l++;
}
}
// 划分完之后,大于区域的左边元素和划分值(最后一个元素)交换
swap(arr, more, r);
// 把小于区域 的 索引 和 大于区域 的 索引返回去
return new int[] { less, more };
}
python 实现
def user_sort(self, arr):
if len(arr) <= 1:
return
self.quick_sort(arr, 0, len(arr) - 1)
def quick_sort(self, arr, l, r):
if l >= r:
return
less, more = self.partition(arr, l, r)
self.quick_sort(arr, l, less)
self.quick_sort(arr, more, r)
def partition(self, arr, l, r):
less = l - 1
more = r
while l < more:
if arr[l] < arr[r]:
less += 1
arr[less], arr[l] = arr[l], arr[less]
l += 1
elif arr[l] > arr[r]:
more -= 1
arr[l], arr[more] = arr[more], arr[l]
else:
l += 1
arr[more], arr[r] = arr[r], arr[more]
return less, more
快速排序2.0
【<5 =5 > 5】
快速排序3.0
划分值 很偏,所以最坏。划分值如果在中间,就很好。
划分值随机选择一个。和最后一个位置交换。
O(NlogN)
空间复杂度O(logN)
swap(arr, l + (int) (Math.random() * (r - l + 1)), r); //随机选择一个位置后最后一个位置交换