01-时间复杂度、对数器(python)、冒泡、选择、递归实质、归并、小和问题、逆序对、mid
1、时间复杂度
常数时间的操作:一个操作如果和数据量没有关系,每次都是
固定时间内完成的操作,叫做常数操作。
时间复杂度为一个算法流程中,常数操作数量的指标。常用O
(读作big O)来表示。
具体来说,在常数操作数量的表达式中,
只要高阶项,不要低阶项,也不要高阶项的系数,剩下的部分
如果记为f(N),那么时间复杂度为O(f(N))。
评价一个算法流程的好坏,先看时间复杂度的指标,然后再分
析不同数据样本下的实际运行时间,也就是常数项时间。
例子一:理解时间复杂度
一个简单的理解时间复杂度的例子 一个有序数组A,另一个无序数组B,请打印B中的所有不在A中的数,A数 组长度为N,B数组长度为M。 算法流程1:对于数组B中的每一个数,都在A中通过遍历的方式找一下; 算法流程2:对于数组B中的每一个数,都在A中通过二分的方式找一下; 算法流程3:先把数组B排序,然后用类似外排的方式打印所有不在A中出现 的数; 三个流程,三种时间复杂度的表达... 如何分析好坏?2、冒泡排序
冒泡排序细节的讲解与复杂度分析 时间复杂度O(N^2),额外空间复杂度O(1)
java版本
package basic_class_01; import java.util.Arrays; public class Code_00_BubbleSort { public static void bubbleSort(int[] arr) { if (arr == null || arr.length < 2) { return; } for (int e = arr.length - 1; e > 0; e--) { for (int i = 0; i < e; i++) { if (arr[i] > arr[i + 1]) { swap(arr, i, i + 1); } } } } public static void swap(int[] arr, int i, int j) { arr[i] = arr[i] ^ arr[j]; arr[j] = arr[i] ^ arr[j]; arr[i] = arr[i] ^ arr[j]; } }
python版本
# 冒泡排序 # 时间复杂度O(N^2) # 空间复杂度O(1) def bubble_sort(li): if li is None and len(li) < 2: return for end in range(len(li) - 1, -1, -1): for i in range(end): if li[i] > li[i + 1]: swap(li, i, i + 1) return li # 优化版本 def bubble_sort1(li): if li is None and len(li) < 2: return for end in range(len(li) - 1, -1, -1): exchange = False # 设置哨兵 for i in range(end): if li[i] > li[i + 1]: swap(li, i, i + 1) exchange = True if not exchange: return li return li def swap(data, i, j): temp = data[i] data[i] = data[j] data[j] = temp # for test ret = bubble_sort([9, 2, 7, 8, 3, 2, 1]) print(ret)
3、选择排序
选择排序的细节讲解与复杂度分析
时间复杂度O(N^2),额外空间复杂度O(1)
java版本
package basic_class_01; import java.util.Arrays; public class Code_02_SelectionSort { public static void selectionSort(int[] arr) { if (arr == null || arr.length < 2) { return; } for (int i = 0; i < arr.length - 1; i++) { int minIndex = i; for (int j = i + 1; j < arr.length; j++) { minIndex = arr[j] < arr[minIndex] ? j : minIndex; } swap(arr, i, minIndex); } } public static void swap(int[] arr, int i, int j) { int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } }
python版本
# 选择排序 # 时间复杂度O(N^2) # 空间复杂度O(1) def selection_sort(li): if li is None and len(li) < 2: return for i in range(len(li) - 1): # 当前n-1个确定好了,最后一个就确定ok min_index = i for j in range(i + 1, len(li)): if li[j] < li[min_index]: min_index = j swap(li, i, min_index) return li def swap(data, i, j): data[i] = data[i] ^ data[j] data[j] = data[i] ^ data[j] data[i] = data[i] ^ data[j] # for test ret = selection_sort([9, 2, 7, 8, 3, 2, 1]) print(ret)
4、插入排序
插入排序的细节讲解与复杂度分析
时间复杂度O(N^2),额外空间复杂度O(1)
java版本
public static void insertionSort(int[] arr) { if (arr == null || arr.length < 2) { return; } for (int i = 1; i < arr.length; i++) { for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) { swap(arr, j, j + 1); } } } public static void swap(int[] arr, int i, int j) { arr[i] = arr[i] ^ arr[j]; arr[j] = arr[i] ^ arr[j]; arr[i] = arr[i] ^ arr[j]; }
python版本
# 插入排序 # 时间复杂度O(N^2) # 空间复杂度O(1) def selection_sort(li): if li is None and len(li) < 2: return for i in range(1, len(li)): temp = li[i] j = i - 1 while j >= 0 and temp < li[j]: li[j + 1] = li[j] # 元素往后推一个位置 j -= 1 li[j + 1] = temp # 最小元素放在第一个位置 return li # for test ret = selection_sort([9, 2, 7, 8, 3, 2, 1]) print(ret)
5、对数器
1、用途:验证算法是否正确的一种方式
2、使用 0,有一个你想要测的方法a, 1,实现一个绝对正确但是复杂度不好的方法b, 2,实现一个随机样本产生器 3,实现比对的方法 4,把方法a和方法b比对很多次来验证方法a是否正确。 5,如果有一个样本使得比对出错,打印样本分析是哪个方法出错 6,当样本数量很多时比对测试依然正确,可以确定方法a已经正确。3、实现
1.随机数组发生器
2.准备一个绝对正确的方法
3.大样本测试 500 000
java版本
package basic_class_01; import java.util.Arrays; public class Code_00_BubbleSort { // 需要验证的算法?? public static void bubbleSort(int[] arr) { if (arr == null || arr.length < 2) { return; } for (int e = arr.length - 1; e > 0; e--) { for (int i = 0; i < e; i++) { if (arr[i] > arr[i + 1]) { swap(arr, i, i + 1); } } } } public static void swap(int[] arr, int i, int j) { arr[i] = arr[i] ^ arr[j]; arr[j] = arr[i] ^ arr[j]; arr[i] = arr[i] ^ arr[j]; } // 1.随机数组发生器 // for test public static int[] generateRandomArray(int maxSize, int maxValue) { // Math.random() -> double [0,1) // (int) ((size+1)*Math.random()) -> [0,size] 整数 // size = 6, size+1 = 7 // Math.random() -> [0,1) * 7 -> [0,7) double // double -> int[0,6) -> int // 生成长度随机的数组 int[] arr = new int[(int) ((maxSize + 1) * Math.random())]; for (int i = 0; i < arr.length; i++) { arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random()); } return arr; } // 2.准备一个绝对正确的方法 // for test public static void comparator(int[] arr) { Arrays.sort(arr); } // 3.大样本测试 500 000 // for test public static void main(String[] args) { int testTime = 500000; int maxSize = 100; int maxValue = 100; boolean succeed = true; for (int i = 0; i < testTime; i++) { int[] arr1 = generateRandomArray(maxSize, maxValue); int[] arr2 = copyArray(arr1); bubbleSort(arr1); comparator(arr2); if (!isEqual(arr1, arr2)) { succeed = false; break; } } System.out.println(succeed ? "Nice!" : "Fucking fucked!"); int[] arr = generateRandomArray(maxSize, maxValue); printArray(arr); bubbleSort(arr); printArray(arr); } //3.1 copy // for test public static int[] copyArray(int[] arr) { if (arr == null) { return null; } int[] res = new int[arr.length]; for (int i = 0; i < arr.length; i++) { res[i] = arr[i]; } return res; } // 3.2 验证isEqual,长度一样,每位的数字一样 // for test public static boolean isEqual(int[] arr1, int[] arr2) { if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) { return false; } if (arr1 == null && arr2 == null) { return true; } if (arr1.length != arr2.length) { return false; } for (int i = 0; i < arr1.length; i++) { if (arr1[i] != arr2[i]) { return false; } } return true; } // 3.3 打印结果 // for test public static void printArray(int[] arr) { if (arr == null) { return; } for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); } }
python版本
import random # 我的方法 def bubble_sort(li): if li is None or len(li) < 2: return li for end in range(len(li) - 1, -1, -1): for i in range(end): if li[i] > li[i + 1]: swap(li, i, i + 1) return li # 交换两个数字,与 def swap(data, i, j): data[i] = data[i] ^ data[j] data[j] = data[i] ^ data[j] data[i] = data[i] ^ data[j] '''for test''' # 1.随机数组发生器 def generate_random_array(max_size,max_vaule): arr = [] arr_len = int((max_size+1)*random.random()) # 长度随机,不能大于max_size for i in range(arr_len): arr.append(int((max_vaule + 1) * random.random()) - int(max_vaule * random.random())) # value随机 return arr # 2.准备一个绝对正确的方法 def comparator(arr): return sorted(arr) # 3.大样本测试 500 000 def main(): test_time = 500 max_size = 100 max_value = 100 succeed = True for i in range(test_time): arr1 = generate_random_array(max_size,max_value) arr2 = copy_array(arr1) arr1 = bubble_sort(arr1) # 我的方法 arr2 = comparator(arr2) # 系统绝对正确的方法 if not is_equal(arr1,arr2): succeed = False break print("Nice") if succeed else print("Fucking fucked") arr = generate_random_array(max_size,max_value) print_array(arr) bubble_sort(arr) print_array(arr) # 3.1 copy def copy_array(arr): if arr is None: return None res = [] for i in range(len(arr)): res.append(arr[i]) return res # 3.2 验证isEqual,长度一样,每位的数字一样 def is_equal(arr1,arr2): if (arr1 is None and arr2 is not None) or (arr1 is not None and arr2 is None): return False if arr1 is None and arr2 is None: return True if len(arr1) != len(arr2): return False for i in range(len(arr1)): if arr1[i] != arr2[i]: return False return True # 3.3 打印结果 def print_array(arr): if arr is None: return for i in range(len(arr)): print(arr[i],end=' ') print('\n') if __name__ == '__main__': main()
6、递归
6.1 递归实质
剖析递归行为和递归行为时间复杂度的估算
一个递归行为的例子
def get_max(arr,left,right): if left == right: return arr[left] mid = (left+right)//2 max_left = get_max(arr,left,mid) max_right = get_max(arr,mid+1,right) return max(max_left,max_right) li = [4,3,2,1] ret = get_max(li,0,len(li)-1) print(ret) # 4
还原现场 ,系统栈
6.2 递归行为复杂度通式
master公式的使用
T(N) = a*T(N/b) + O(N^d)
1) log(b,a) > d -> 复杂度为O(N^log(b,a))
2) log(b,a) = d -> 复杂度为O(N^d * logN)
3) log(b,a) < d -> 复杂度为O(N^d)
补充阅读:www.gocalf.com/blog/algorithm-complexity-and-master-
theorem.html
7、归并排序(递归、分治)
归并排序的细节讲解与复杂度分析
时间复杂度O(N*logN),额外空间复杂度O(N)
java版本
public static void mergeSort(int[] arr) { if (arr == null || arr.length < 2) { return; } mergeSort(arr, 0, arr.length - 1); } public static 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 static void merge(int[] arr, int l, int m, int r) { int[] help = new int[r - l + 1]; int i = 0; int p1 = l; int p2 = m + 1; while (p1 <= m && p2 <= r) { help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++]; } while (p1 <= m) { help[i++] = arr[p1++]; } while (p2 <= r) { help[i++] = arr[p2++]; } for (i = 0; i < help.length; i++) { arr[l + i] = help[i]; } }
python版本
def mergeSort(arr): if arr is None or len(arr) < 2: return arr merge_sort(arr,0,len(arr)-1) return arr def merge_sort(arr,left,right): if left == right: return arr mid = left + ((right-left)>>1) # left和right中点的位置 (L+R)//2 # 分解 merge_sort(arr,left,mid) # T(n/2) merge_sort(arr,mid+1,right) # T(n/2) # 合并 merge(arr,left,mid,right) # O(n) # T(n) = 2T(N/2)+ O(N) def merge(arr,left,mid,right): temp = [] i,j = left,mid+1 while i<=mid and j <= right: if arr[i]<arr[j]: temp.append(arr[i]) i += 1 else: temp.append(arr[j]) j +=1 # 两个必且有一个越界 while i<=mid: temp.append(arr[i]) i += 1 while j<=right: temp.append(arr[j]) j += 1 arr[left:right+1] = temp li = [3,1,2,4] ret = mergeSort(li) print(ret)
时间复杂度估算
为什么归并排序比较快
其他排序,有很多无效的比较,浪费比较
8、小和问题
小和问题
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组
的小和。
例子:
[1,3,4,2,5]
1左边比1小的数,没有;
3左边比3小的数,1;
4左边比4小的数,1、3;
2左边比2小的数,1;
5左边比5小的数,1、3、4、2;
所以小和为1+1+3+1+1+3+4+2=16
4*1 + 2*3 + 1*4 + 1*2
java版本
public static int smallSum(int[] arr) { if (arr == null || arr.length < 2) { return 0; } return mergeSort(arr, 0, arr.length - 1); } public static int mergeSort(int[] arr, int l, int r) { if (l == r) { return 0; } int mid = l + ((r - l) >> 1); return mergeSort(arr, l, mid) + mergeSort(arr, mid + 1, r) + merge(arr, l, mid, r); } public static int merge(int[] arr, int l, int m, int r) { int[] help = new int[r - l + 1]; int i = 0; int p1 = l; int p2 = m + 1; int res = 0; while (p1 <= m && p2 <= r) { res += arr[p1] < arr[p2] ? (r - p2 + 1) * arr[p1] : 0; help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++]; } while (p1 <= m) { help[i++] = arr[p1++]; } while (p2 <= r) { help[i++] = arr[p2++]; } for (i = 0; i < help.length; i++) { arr[l + i] = help[i]; } return res; }
python版本
def small_sum(arr): if arr is None or len(arr) < 2: return arr return merge_sort(arr, 0, len(arr) - 1) def merge_sort(arr, l, r): if l == r: # return arr 要进行res相加操作 return 0 m = l + ((r - l) >> 1) # m = (L+R)//2 return merge_sort(arr, l, m) + merge_sort(arr, m + 1, r) + merge(arr, l, m, r) def merge(arr, l, m, r): temp = [] i, j = l, m + 1 res = 0 # 每次merge产生的 小和 while i <= m and j <= r: res += (r - j + 1) * arr[i] if arr[i] < arr[j] else 0 # 小和 if arr[i] < arr[j]: temp.append(arr[i]) i += 1 else: temp.append(arr[j]) j += 1 # 两个必且有一个越界 while i <= m: temp.append(arr[i]) i += 1 while j <= r: temp.append(arr[j]) j += 1 for i in range(len(temp)): arr[l + i] = temp[i] return res arr = [1,3,4,2,5] print(small_sum(arr))
res += (r - j + 1) * arr[i] if arr[i] < arr[j] else 0 # 小和
左神语录
有一个思路要给大家贯彻到底的
所有东西都是技术问题
你写不出来的原因是因为缺乏练习,
而不是你这个人笨
你知道吗,真的是要建立这种信心
因为我就是那样的阶段趟过来的,
这其中的辛苦确实都知道
9、逆序对问题
在一个数组中,左边的数如果比右边的数大,则折两个数构成一个逆序对,请打印所有逆序
对。
10、取mid的几种方法
11、交换两个数的值
方法1:
tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
方法2:
arr[i] = arr[i] ^ arr[j]; arr[j] = arr[i] ^ arr[j]; arr[i] = arr[i] ^ arr[j];
Z、总结
1、时间复杂度
big O 代表常数操作
只要高阶项,不要低阶项,也不要高阶项的系数
2、冒泡
时间复杂度O(N^2)
额外空间复杂度O(1)
3、选择
时间复杂度O(N^2)
额外空间复杂度O(1)
4、插入 时间复杂度O(N^2) 额外空间复杂度O(1) 5、对数器 验证算法是否正确的一种方式 1) 随机数组发生器:长度随机,value随机的数组 2) 一个绝对正确的方法。 3) 我要验证的方法 4) 大样本测试:50000 5) isEqual :长度一样,每一位数字都相同
6、递归实质
还原现场:系统压栈操作
7、递归的master公式
T(N) = a*T(N/b) + O(N^d)
8、归并排序 (重要)
递归、分治 思想
时间复杂度O(N*logN)
额外空间复杂度O(N)
归并排序为什么比其他的快?
其他排序,有很多无效的比较,浪费比较
9、小和问题
例子:
[1,3,4,2,5]
1左边比1小的数,没有;
3左边比3小的数,1;
4左边比4小的数,1、3;
2左边比2小的数,1;
5左边比5小的数,1、3、4、2;
所以小和为1+1+3+1+1+3+4+2=16
4*1 + 2*3 + 1*4 + 1*2
归并合并的时候,每次炸出 小和
炸出 (right-j+1)* arr[i]
炸出 4个 1 的小和
炸出 2个 3 的小和
炸出 1个 4 的小和
炸出 1个 2 的小和
10、逆序对问题
在一个数组中,左边的数如果比右边的数大,
则折两个数构成一个逆序对,请打印所有逆序对
也用归并排序
11、取mid的几种方法
12、交换两个数的值
方法1:
tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
方法2:
arr[i] = arr[i] ^ arr[j]; arr[j] = arr[i] ^ arr[j]; arr[i] = arr[i] ^ arr[j];
左神语录
有一个思路要给大家贯彻到底的
所有东西都是技术问题
你写不出来的原因是因为缺乏练习,
而不是你这个人笨
你知道吗,真的是要建立这种信心
因为我就是那样的阶段趟过来的,
这其中的辛苦确实都知道
准备模板,必要裸奔取考试
1、对数器
2、堆,排序
- 算法初级01——认识时间复杂度、对数器、 master公式计算时间复杂度、小和问题和逆序对问题
- 算法竞赛中的时间复杂度选择——以最大连续和问题为例
- 最坏时间复杂度为N^2排序(冒泡-选择-插入)
- 递归与分治策略-2.9.1线性时间选择(随机划分基准)(第k小问题)
- 线性时间选择问题-第k小(大)问题-递归与分治
- 01-复杂度1 最大子列和问题 (20分) c++ && python
- python 中的一些基础算法:递归/冒泡/选择/插入
- Python--day4--正则表达式/冒泡/时间复杂度
- 递归的实质以及递归时间复杂度的计算
- 终于知道递归解决什么样的问题了,指数级问题(或者说无法简单的用for循环就能完成的问题)。想通了为啥图的优先和广度优先搜索的时间复杂度是o(n+e)了,而且一分不差。
- python内排序算法(冒泡 插入 选择 堆 归并 快排)
- 选择、冒泡、快速、归并、插入排序算法的运行时间比较
- 数据结构(C#)--冒泡、插入、快速、堆、归并、希尔、选择各种排序排序过程比较以及各种排序的所用时间的对比
- 选择问题(线性时间复杂度)
- [置顶] 递归与分治策略-2.9.2线性时间选择(取中位数的中位数基准)(第k小问题)
- 时间空间复杂度 冒泡 选择 快速排序
- 01背包问题python(使用递归和动态规划)
- python数据结构学习笔记-2017-01-08-01-N皇后问题、迷宫问题和跳马问题的递归解决
- 选择问题(线性时间复杂度)
- c语言排序算法总结 选择 插入 冒泡 希尔 快速 堆排序 稳定性 时间复杂度 空间复杂度