时间复杂度

  • 要求:只要高阶项,不要低阶项
  • 常数操作:操作花费的时间和数据量无关,比如数组寻址,直接利用偏移量找到对应元素的位置;
  • 非常数操作:比如list(链表);查找元素需要遍历链表,元素查找时间和元素对应的位置有关;时间复杂度为O(n)
  • 常数时间操作:两个数相加(+)  相减(-) 左移(<<)  右移(>>) 或运算(|) 与运算(&) 异或运算(^)

例子  选择排序  引出时间复杂度

package class01;import java.util.Arrays;public class Code01_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;}// for testpublic static void comparator(int[] arr) {Arrays.sort(arr);}// for testpublic static int[] generateRandomArray(int maxSize, int maxValue) {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;}// for testpublic 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;}// for testpublic 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;}// for testpublic 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();}// for testpublic 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);selectionSort(arr1);comparator(arr2);if (!isEqual(arr1, arr2)) {succeed = false;printArray(arr1);printArray(arr2);break;}}System.out.println(succeed ? "Nice!" : "Fucking fucked!");int[] arr = generateRandomArray(maxSize, maxValue);printArray(arr);selectionSort(arr);printArray(arr);}}
  • 选择排序:先从[0->N-1]范围内找最小值,对于每个位置上元素的操作时间为c;再从[1->N-1]、[2->N-1]、[3->N-1]等,所以一共需要花费时间为((N)+(N-1)+(N-2)+  + (1))*c = (aN^2 + b*N + k)*c = acN^2 + bcN + kc 只要高阶项,不要低阶项,也不要高阶项的系数,时间复杂度为N^2,即O(N^2)
  • 一个操作如果和样本数据量没有关系,每次都是固定时间内完成的操作,叫做常数操作
  • 表达式中,只要高阶项,不要低阶项,也不要高阶项的系数,剩下的部分如果是f(N),那么时间复杂度就是O(f(N))
  • 评价一个算法的好坏,先看时间复杂度指标,然后分析不同数据样本下的实际运行时间,也就是“常数项时间”

冒泡排序

  • 最差情况 每个位置上元素都需要移动
package class01;import java.util.Arrays;public class Code02_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];}// for testpublic static void comparator(int[] arr) {Arrays.sort(arr);}// for testpublic static int[] generateRandomArray(int maxSize, int maxValue) {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;}// for testpublic 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;}// for testpublic 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;}// for testpublic 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();}// for testpublic 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);}}

插入排序

  • 时间复杂度 按照最差情况计算
  • 插入排序 和数据情况有关,因此相较于冒泡排序和选择排序 会好
package class01;import java.util.Arrays;public class Code03_InsertionSort {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];}// for testpublic static void comparator(int[] arr) {Arrays.sort(arr);}// for testpublic static int[] generateRandomArray(int maxSize, int maxValue) {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;}// for testpublic 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;}// for testpublic 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;}// for testpublic 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();}// for testpublic 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);insertionSort(arr1);comparator(arr2);if (!isEqual(arr1, arr2)) {succeed = false;break;}}System.out.println(succeed ? "Nice!" : "Fucking fucked!");int[] arr = generateRandomArray(maxSize, maxValue);printArray(arr);insertionSort(arr);printArray(arr);}}

二分查找

  • 每次都是找中点,比较数据,因此时间复杂度是  O(log2N)

在一个有序数组中,找某个数是否存在?二分查找

package com.example.algorithm.demo.class1;import java.util.Arrays;public class Code04_BSExist {public static boolean exist(int[] sortedArr,int num){if (sortedArr == null || sortedArr.length == 0){return false;}int L = 0;int R = sortedArr.length - 1;int mid = 0;while (L < R){mid = L +((R - L) >> 1);if (sortedArr[mid] == num){return true;} else if (sortedArr[mid] > num){R = mid - 1;} else {L = mid + 1;}}return sortedArr[L] == num;}// for testpublic static void comparator(int[] arr) {Arrays.sort(arr);}// for testpublic static void printArray(int[] arr){for (int i = 0;i < arr.length;i++){System.out.print(arr[i] + " ");}}public static void main(String[] args) {int[] arr = {1,2,5,3,1,6,78,9,234,55,666,76};comparator(arr);printArray(arr);System.out.println();if (exist(arr,234)){System.out.println("存在数据!");} else {System.out.println("不存在数据!");}}
}

在一个有序数组中,找大于等于某个数最左侧的位置?(进一步演化)

  • 比如 1223334444555556666667777777
package com.example.algorithm.demo.class1;import java.util.Arrays;public class Code05_BSNearLeft {//在arr上 找到满足>=value的最左位置public static int nearestIndex(int[] arr,int value){int L = 0;int R = arr.length - 1;int index = -1;while (L < R){int mid = L + ((R - L) >> 1);if (arr[mid] >= value){index = mid;R = mid - 1;} else {L = mid + 1;}}return index;}// for testpublic static void comparator(int[] arr) {Arrays.sort(arr);}// for testpublic static void printArray(int[] arr){for (int i = 0;i < arr.length;i++){System.out.print(arr[i] + " ");}}public static void main(String[] args) {int[] arr = {1,23,4,5,5,5,6,7,7,8,8,8,8,2,1,2,4,5,3,1,6,78,9,234,55,666,76};comparator(arr);printArray(arr);System.out.println();System.out.println("元素7的位置索引是"+ (nearestIndex(arr,7)+1));}
}

额外空间复杂度

  • 使用辅助数组,额外空间就不是O(1)

局部最小

  • 要求:无序数组,相邻不等,返回一个局部最小的位置,怎么整?
  • 具体操作:1,先看0位置的元素,如果0位置小于1位置,那么直接返回1位置的元素;2,上一条件不满足,即0位置元素大于1位置的元素,则看N-1位置的元素是否是局部最小,如果是直接返回。如果不是,则表明N-2位置的元素小于N-1位置的元素。表明0-1曲线下降,N-2  到 N-1 曲线上升,表明在0->(N-1)绝对存在局部最小。数学知识
  • 编码:使用二分搜索,判断局部最小;

原理

  • 【0】<【1】,0是局部最小
  • 【N-1】<【N-2】,N-1是局部最小
  • 【i-1】<【i】<【i+1】,i是局部最小

方法

  • 二分法,在0-N一定存在局部最小
  • 二分不一定只能适用于有序数组,这个和题意有关。比如查找一个局部最小的数值

异或计算(无进位相加)

  • 0异或N=N  N异或N=0
  • 满足交换律和结合律(具体操作和执行的次序无关)

完成数据的交换

  • 引入第三方变量
int a = 1;
int b = 2;
int c = a;
a = b;
b = c;
  • 单独自身计算,不引入第三方变量
int a = 1;
int b = 2;
a = a + b; //a = 3
b = a - b; //b = 3 - 2 = 1
a = a - b; //a = 3 - 1 = 2
  • 使用异或(前提,值是一样的,但是a和b所处的内存区域是不同的,如果相同会出错)相同会导致数据抵消,二者都变成0
int a = 1;
int b = 2;
a = a 异或 b; //a = 1 异或 2,b = 2
b = a 异或 b; //a = 1 异或 2, b = 1
a = a 异或 b; //a = 1 异或 2 异或 1 = 2, b = 1即使a和b的元素都是2,也可以实现数据的交换,但是如果a和b指向相同的地址空间,会消除数据,a和b都变成0
  • 错误使用
  • i和j指向相同的位置,造成数据抹除
int[] arr = {4,5,3}
int i = 0;
int j = 0;arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];for(int c = 0; c < arr.length; c++){System.out.println(arr[c]);
}arr[0]=0;arr[1]=5;arr[2]=3;

1,一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到这一个数

public static void printOddTimesNum1(int[] arr){int eor = 0;for (int cur : arr){eor ^= cur;}System.out.println(eor);
}
  • 只有一个是奇数项,将所有元素都异或,相同元素就会消失,只剩下奇数的元素,利用到了异或的交换律和结合律

2,一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到这两个数 例子

【1011,0110,0110,1011,1000,0001】
1,让所有元素异或运算,得到eor变量,这个利用相同位置上1的个数,如果是奇数个,则为1,同理,偶数个1为0
2,第一步得到的eor=1001,因为eor的末尾元素为1,则将所有末尾为1的元素进行第二次异或,得到eor’,具体是【1011,1011,0001】,则eor'=0001,eor'也为第一个奇数个元素
3,将eor和eor'进行异或,eor=1001,eor'=0001,则二者计算得到1000为第二个奇数个的元素

引出新的问题

  • 上面例子中的eor=1001,我们提取最后面位置上的元素 1,是如何实现的呢?
  • 例子(方法1)
01101000如何操作变成00001000呢?提取到指定位数的1,其余位数全部清零
假设 a = 01101000
1,计算 a-1,目的是打散最末尾的数字1,a-1 = 01100111
2,计算 a同或a-1 = 01100000,目的是清楚原始最右侧的1
3,计算 a异或(a同或a-1)= 00001000
  • 例子(方法2)

a & (~a +1)
a = 01101000, a取反得到10010111,再 +1 得到10011111
a & (~a +1) = 01101000 & 10011111 = 00001000
获取最右边的 1

步骤

  • 先将所有的元素进行异或,使用eor 得到 奇数 a 和 b 的异或,即 eor = a ^ b;
  • 将eor转化为二进制,其中位置为1 的用于区别a 和 b;假设x位置上元素为1,将数组中元素x位置为1的全部进行异或得到eor‘;eor’ = a或者b
  • 再将 eor和 eor‘ 异或得到另外一个元素

代码

public static void printOddTimesNum2(int[] arr){int eor = 0;for (int i=0;i<arr.length;i++){eor ^= arr[i];//eor = a ^ b//err != 0//err必然有一个位置上是1int rightOne = eor & (~eor + 1);//提取出最右侧的1int onlyOne = 0;//eor'for (int cur : arr){if((cur & rightOne) == 0){onlyOne ^= cur;}}}System.out.println(onlyOne + " " + (eor ^ onlyOne));
}
package class01;public class Code07_EvenTimesOddTimes {public static void printOddTimesNum1(int[] arr) {int eO = 0;for (int cur : arr) {eO ^= cur;}System.out.println(eO);}public static void printOddTimesNum2(int[] arr) {int eO = 0, eOhasOne = 0;for (int curNum : arr) {eO ^= curNum;}int rightOne = eO & (~eO + 1);for (int cur : arr) {if ((cur & rightOne) != 0) {eOhasOne ^= cur;}}System.out.println(eOhasOne + " " + (eO ^ eOhasOne));}public static void main(String[] args) {int a = 5;int b = 7;a = a ^ b;b = a ^ b;a = a ^ b;System.out.println(a);System.out.println(b);int[] arr1 = { 3, 3, 2, 3, 1, 1, 1, 3, 1, 1, 1 };printOddTimesNum1(arr1);int[] arr2 = { 4, 3, 4, 2, 2, 2, 4, 1, 1, 1, 3, 3, 1, 1, 1, 4, 2, 2 };printOddTimesNum2(arr2);}}

对数器

  • 最简单的方法和优化的方法,使用大量的结果分别测试,如果结果是一致的,说明优化的方法是正确的。

算法入门篇 一 时间复杂度相关推荐

  1. 算法入门篇六 二叉树

    牛客网 算法入门篇 左程云老师 个人复习,如果侵全,设为私密 二叉树遍历(递归) 先序遍历(中,左,右) 中序遍历(左,中,右) 后序遍历(左,右,中) 如上图所示结构,二叉树的遍历本质上都是递归序, ...

  2. 【基础算法Ⅰ】算法入门篇

    目录 进入算法世界 1.输入输出 1.1输入输出 1.2快读 2.位运算 2.1运算符 2.2位运算 3.枚举 3.1枚举的引入 3.2枚举的简单理解 3.3枚举简介 3.4 枚举算法实例 算法复杂度 ...

  3. 算法入门篇九 暴力递归

    牛客网 左程云老师的算法入门课 暴力递归 原则  汉诺塔问题 问题 打印n层汉诺塔从左边移动到最右边的过程 思想 一共六个过程,左到右.左到中,中到左,中到右,右到左,右到中,互相嵌套使用 左到右 将 ...

  4. 算法入门篇八 贪心算法

    牛客网 左程云老师的算法入门课 贪心算法 贪心算法的解题步骤  例子 题目要求  解题策略 按照结束时间早的会议先安排,比如先安排[2,4],当4结束了,所有开始时间小于4的全部淘汰,[1,7].[3 ...

  5. 算法入门篇七 前缀树

    牛客网 左程云老师的算法入门课 找二叉树的节点的后继节点 原则 如果节点有右子树,那么后继节点就是右子树的最左边的第一个节点 如果节点没有右子树,如果节点是父节点的右孩子,就继续往上找,直到找到一个父 ...

  6. 【贪心专题】—— 贪心算法入门篇

    贪心算法入门 一.什么是贪心算法 "贪心算法(greedy algorithm,又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择.也就是说,不从整体最优上加以考虑,算法得到 ...

  7. 牛客网在线编程----算法入门篇

    标题本篇博文主要是记录下自己的在线编程情况,初次练习,有的算法还待改进,大家有需要可以去牛客网上面多练练! 有需戳–>牛客网在线编程 NC65.题目描述 大家都知道斐波那契数列,现在要求输入一个 ...

  8. 通讯录排序 (20分)_算法入门篇:简单的排序算法

    作者:dorseyCh来源:http://www.imooc.com/article/264180 很久之前有过一次面试,被问到一个问题,能不能写一个冒泡排序?说实话,尽管在这之前曾经写过不少比这个更 ...

  9. 算法入门篇三 详解桶排序和整理排序知识 堆的相关操作 补充 不完整

    归并排序不使用递归 使用一个变量,使其按照1.2.4.8递增,控制左右两边1个元素.2个元素.4个元素等元素的合并 完全二叉树 完全二叉树 要不全是满的,要不叶子节点出现在最后一层,只要出现了叶子节点 ...

最新文章

  1. golang go get命令 一键获取代码、编译并安装
  2. 高级性能服务器编程模型【IOCP完成端口】开发实现【二】
  3. 机器学习中样本不平衡处理办法
  4. 初步认识Volatile-CPU层面的内存屏障
  5. rfid射频前端的主要组成部分有_rfid读写器的组成部分包括什么_rfid读写器的主要功能...
  6. 再见!微软宣布终止对旧版 Microsoft Edge 浏览器的支持
  7. 早教机器人刷固件_机器人线刷包_机器人刷机包_机器人固件包_机器人救砖包 - 线刷宝ROM中心...
  8. 【剑指offer - C++/Java】4、重建二叉树
  9. 16.2互联网媒体信息讽刺识别
  10. jupyter notebook怎么画决策树图_状态图怎么画?图文详解快速上手UML图
  11. 基于javaweb的户籍管理系统
  12. android截屏加水印,Android截图以及加水印Demo
  13. Sqlite可视化工具sqliteman安装
  14. WebStorm中TODO的作用
  15. 2021年中国人机界面软件市场趋势报告、技术动态创新及2027年市场预测
  16. CCC与Android交互的注意点
  17. 东北大学oj平台python答案_你觉得东北大学的Python考试怎么样?
  18. 浙江大学软件学院网络自动认证+Padavan路由器挂python脚本
  19. 数论 —— 逆元与同余式定理
  20. Unity框架——MVC框架

热门文章

  1. php获取网页输出,PHP 利用AJAX获取网页并输出的实现代码(Zjmainstay)_PHP教程
  2. 【转】ABP源码分析三十四:ABP.Web.Mvc
  3. 第二十节: 深入理解并发机制以及解决方案(锁机制、EF自有机制、队列模式等)
  4. 【转】IsCallBack属性和IsPostBack属性有什么区别?
  5. 【JS 逆向百例】cnki 学术翻译 AES 加密分析
  6. fmc是fpga直接引出来的吗_家长速看!你还在用“不要和陌生人说话”糊弄孩子的安全教育吗?...
  7. 【LeetCode 986】 区间列表的交集(区间交集)
  8. 【HDU - 5917】Instability(规律,结论,Ramsey定理,知识点,tricks)
  9. Python之Numpy入门实战教程(1):基础篇
  10. Java (jdk win 10)