面试中的时间复杂度分析

到底什么是大O
n表示数据规模。
O(f(n))表示运行算法所需要执行的指令数,和f(n)成正比。
在这里插入图片描述
算法A: O(n)     所需的执行指令数:10000 n
算法B: O(n^2)   所需执行的指令数:10*n ^2
随着数据规模n的增大,两个算法的执行时间为:
在这里插入图片描述
不同算法复杂度的比较在这里插入图片描述
学术界:O(f(n))表示算法执行的上界。
工业界:O表示算法执行的最低上界。

一个字符串数组,将数组中的每一个字符串按照字母序排序;之后再将整个字符串数组按照字典序排序。整个操作的时间复杂度?

假设最长的字符串长度为s;数组中有n个字符串
对每个字符串排序:O(slogs)
将数组中的每一个字符串按照字母序排序:O(n slog(s))
将整个字符串数组按照字典序排序:O(s nlog(n))

O(n slog(s))+O(s nlog(n))=O(nslogs+snlogn)
=O(ns(logs+logn))

对字符串数组进行字典序排序时,进行nlogn次,每次比较需要s次

复杂度分析和用例相关

数据规模的概念
如果要在1s解决问题:
在这里插入图片描述
空间复杂度

在这里插入图片描述
递归调用是有空间代价的,递归的空间复杂度等于递归的深度

常见代码的复杂度

  • O(1)
//两数交换
void swapTwoInts(int &a, int &b){
	int temp = a;
	a = b;
	b = temp;
}
  • O(n)
//求和
int sum (int n){
	int ret = 0;
	for(int i = 0; i <= n; i++)
		ret += i;
	return ret;
}
//翻转字符串
void reverse(string & s){
	int n = s.size();
	for(int i = 0; i < n/2; i++)
		swap(s[i], s[n - 1 - i]);
}
  • O(n^2)
//选择排序
void selectionSort(int arr[], int n){
	for(int i = 0; i < n; i++){
		int minIndex = i;
		for(int j = i + 1; j < n; j++){
			if(arr[j] < arr[minIndex])
				minIndex = j;
		}
		swap(arr[i], arr[minIndex]);
	}
}
  • O(logn)
//二分查找
int binarySearch(int arr[], int n, int target){
	int l = 0, r = n -1 ;
	while(l <= r){
		int mid = l + (r  - l) / 2;
		if(arr[mid] == target)
			return mid;
		if(arr[mid] > target)
			r = mid - 1;
		else
			 l = mid + 1;
	}
	return -1;
}
//将数字整型转换为字符串
string intTostring(int num ){
	string s  ="";
	while(num){
		s += '0' + num % 10;
		num /= 10;
	}
	reverse(s);
	return s;
}
void hello (int n){
	for(int sz = 1; sz < n; sz += sz)
		for(int i = 1; i < n; i++)
			cout << "hello! " << endl;
}
  • O(sqrt(n))
//判断是否是素数
bool isPrime(int n){
	for(int x = 2; x * x <= n; x < n)
		if(n % x == 0)
			return false;
	return true;
}

复杂度试验
如果想要在1s内解决问题:
在这里插入图片描述
实验,观察趋势
每次将数据规模提高两倍,看时间的变化

递归算法的复杂度

1. 递归中进行一次递归调用的复杂度分析

int binarySearch(int arr[], int l, int r, int target){
	if(l > r)
		return -1;
	int mid = l + (r - l)/ 2;
	if(arr[mid] == target)
		return mid;
	else if(arr[mid] > target)
		return binarySearch(arr, l, mid - 1, target);
	else
		return binarySearch(arr, mid +1; r, target);
}

递归深度为 O(logn), 函数处理问题复杂度为O(1),所以时间复杂度为 O(logn)

如果递归函数中,只进行一次递归调用,递归深度为depth,在每个递归函数中,时间复杂度为T;
则总体的时间复杂度为O(T*depth)

例一:

int sum(int n){
	assert(n >= 0);
	if(n == 0)
		return 0;
	return n + sum(n - 1);
}

递归深度为n;
时间复杂度为O(n);

例二:

double pow(double x, int n){
	assert(n >= 0);
	if(n == 0)
		return 1.0;
	double t = pow(x, n / 2);
	if( n % 2)
		return x*t*t;
	return t * t;
}

递归深度为logn;
时间复杂度为O(logn);

2 递归中进行多次递归调用

int f (int n){
	assert(n >= 0);
	if( n == 0)
		return 1;
	return f(n - 1) + f(n -1 );
}

计算调用的次数
在这里插入图片描述
调用次数:1+2+4+8=15;

调用次数:O(2^n)

在这里插入图片描述

递归函数复杂度还可以通过主定理分析

均摊复杂度分析
动态数组(vector)

#include <iostream>
#include<assert.h>
using namespace std;

template <typename T>
class myVector{

private:
	T * data;
	int capacity; //存储数组中可以容纳的最大元素的个数
	int size;     //存储数组中元素的个数

	//O(n)
	void resize(int newCapacity){
		assert(newCapacity >= size);
		T *newData = new T[newCapacity];
		for (int i = 0; i < size; i++){
			newData[i] = data[i];
		}
		delete[] data;
		//data指针指向新开辟的newData
		data = newData;
		capacity = newCapacity;
	}

public:
	myVector(){
		data = new T[10];
		capacity = 10;
		size = 0;
	}
	//析构函数
	~myVector(){
		delete[] data;
	}

	//平均O(1)
	void push_back(T e){
		//assert(size < capacity);
		if (size == capacity)
			resize(2 * capacity);
		data[size++] = e;
	}

	T pop_back(){
		assert(size > 0);
		T ret = data[size - 1];
		size--;
		//防止复杂度震荡
		if (size == capacity / 4)
			resize(capacity / 2);
		return ret;
	}
}; 


#include <cmath>
#include <ctime>
#include "myVector.h"

using namespace std;
int main(){
	for (int i = 10; i <= 26; i++){
		int n = pow(2, i);
		clock_t startTime = clock();
		myVector<int> vec;
		for (int i = 0; i < n; i++){
			vec.push_back(i);
		}
		for (int i = 0; i < n; i++)
			vec.pop_back();
		clock_t endTime = clock();
		cout << 2 * n << " operations : \t";
		cout << double(endTime - startTime) / CLOCKS_PER_SEC  << " s" << endl;
	}
	system("pause");
	return 0;
}

假设当前数组容量为n,
添加时,当数组容量小于n时,时间复杂度为O(1),当容量为n时,调用resize函数,时间复杂度为O(n),前n次添加的时间复杂度为O(1), 一共n次操作,加上最后一次的n,一共需要2n次操作,均摊后,每次操作为O(2),所以动态数组的添加操作时间复杂度为O(1).

实验
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值