算法时间复杂度分析专题一(帮助快速解题)

笔试:

题目告诉数据范围根据题目的数据范围来考虑用什么解法
c++竞赛:一般时限1~2秒

时间范围内指令操作次数<10^8

不同数据范围下,代码时间复杂度和算法该如何选择:

  1. n<=30,指数级别,=> dfs+剪枝,状态压缩dp
  2. n=100,考虑时间复杂度O(n^3),=> floyd,dp
  3. n=1000,考虑时间复杂度O(n^2)或O (n^2longn),=> 枚举或者dp
  4. n=10000,考虑时间复杂度O(n*sqrt(n)),=> 质数,块状链表
  5. n=100000,考虑时间复杂度O(nlogn),=> 各种排序sort,树状数组,set/map平方数,heap堆、图论digkstra+heap、spfa、二分,求凸包,半平面交
  6. n=1000000,考虑时间复杂度O(n)及常熟较小的O(nlogn)=> hash表,双指针扫描,Kmp、AC自动机;
    O(nlogn)的做法:=> sort、树状数组、heap、dijktra、spfa
  7. n=10000000,考虑时间复杂度O(n),=> 双指针扫描、Kmp、AC自动机、线性筛素数
  8. n=10^9,考虑时间复杂度O(sqrt(n))=> 判断质数
  9. n=10^18,考虑时间复杂度O(logn),=> 最大公约数

假设n=100000,对应的
O(n)=100000;
O(n^2) =10^10;
O(nlogn)=20n=2*10^5


  • 1.欧几里得算法:

求两个正整数的最大公约数,时间复杂度O(logn)

代码:

int gcd(int a,int b)
{ 
   return b?gcd(b,a%b):a;
   }
  

  • 2.扩展欧几里得算法

裴蜀定理:若a,b是整数,且(a,b)=d,那么对于任意的整数x,y,ax+by都一定是d的倍数,特别地,一定存在整数x,y,使得ax+by=d成立。

代码:

int exgcd(int a,int b,int &x,int &y)
{
	if(!b){
		x=1;y=0;
		return a;
	}
	int d=exgcd(b,a%b,y,x);
	y-=(a/b)*x;
	return d;
 } 

  • 3.线性筛素数:

可以在O(n)的时间复杂度内求出1~n之间的所有质数

int primes[N],cnt;
bool st[N];

void get_primes(int n)
{
	for(int i=2;i<=n;i++){
		if(!st[i])
		primes[cnt++]=i;
		for(int j=0;j<cnt&&i*primes[j]<=n;j++)
		{
			st[primes[j]*i]=true;
			if(i%primes[j]==0)
			break; 
		}
	}
 } 

  • 4.链表:线性扫描O(n)

题目:
给定一个有序链表,请删除其中的重复元素,使得原链表的元素仅出现一次。

样例1:
输入:1->1->2
输出:1->2

样例2:
输入:1->1->2->3->3
输出:1->2->3

算法:

线性扫描O(n)
从前往后扫描整个链表,如果一个节点和其后继节点相同,则直接删除后继节点,否则指针移动到后继节点。

时间复杂度分析:整个链表只扫描一遍,所以为O(n)
代码:

/*Definition for singly-linked list
struct ListNode{
	int val;
	ListNode *next;
	ListNode(int x);val(x),next(NULL){}
}; 
*/
class Solution{
public :
	ListNode* deleteDuplicates(ListNode* head){
		if(!head)
		return head;
		ListNode* p=head;
		while(p->next)
		{
			if(p->val==p->next->val)
			p->next=p->next->next;
			else p=p->next;
		}
		return head;
	}
};

  • 5.归并排序
    题目:
    给出两个排好序的单向链表,返回合并排序后新的单向链表。
    链表结点的数据结构:
struct ListNode{
	int val;
	ListNode *next;
	ListNode(int x);val(x),next(NULL){}
	}
}; 

样例:
输入:1->2->4,1->3->4
返回:1->1->2->3->4->4

算法:
线性合并O(n)
1.新建头部的保护结点dummy,设置cur指针指向dummy。
2.若当前l1指针指向的结点值val比l2指针指向的结点的值val小,则令cur的next指针指向l2,且l2后移。
3.然后cur指针按照上一部分设置后的位置后移。
4.循环以上步骤直到l1或l2为空。
5.将剩余的l1或l2接到cur指针后面。

时间复杂度:两表各遍历一遍,所以为O(n)
代码:

/*Definition for singly-linked list
struct ListNode{
	int val;
	ListNode *next;
	ListNode(int x);val(x),next(NULL){}
	}
}; 
*/
class Solution{
public :
	ListNode* mergeTwoLists(ListNode* l1,ListNode* l2){
		ListNode*dummy=new ListNode(0); //一开始分别指向两个头结点
		ListNode *cur=dummy;
		while(l1!=NULL&&l2!=NULL){
			if(l1->val<l2->val){
				cur->next=l1;
				l1=l1->next;
			}
			else{
				cur->next=12;
				l2=l2->next;
			}
			cur=cur->next;
		}
		cur->next=(l1!=NULL?l1:l2);
		return dummy->next;
	} 
};

  • 6.深度优先搜索DFS
    题目:
    给定一个字符串,返回由该数字子串所能代表的所有字母的组合。
    每个数字能代表一些字母,和九宫格键盘一样。

样例:
给定子串“23”,返回所有字母组合[“ad”、“ae”、“af”、“bd”、“be”、“bf、”cd"、“ce”、“cf”]。

算法:
深度优先搜索DFS:O(4^l)
1.可以通过手工或者循环的方式预处理每个数字可以代表哪些字母。
2.使用深度优先搜索DFS,每层递归尝试拼接一个新字母。
3.递归到头,将当前字母串加入到答案中。
注意:有可能数字串是空串,需要特盘。
在这里插入图片描述

时间复杂度:使用了递归方式,时间复杂度与答案个数相同,设数字串长度为l,则最坏时间复杂的为O(4^l)
循环数据,根据每一位选字母,依次枚举
2:代表a,b,c
3:代表d,e,f
对应的每个字母枚举一遍,总方案数就是每个字母所能代表的字母数的乘积

代码:

class Solution{
public :
	vector<char> digit[10];
	vector<string> res;
	
	void init(){
		char cur='a';
		for(int i=2;i<10;i++){
			for(int j=0;j<3;j++)
			digit[i].push_back(cur++);
			if(i==7||i==9)
			digit[i].push_back(cur++);
		}
	} 
	
	void dfs(string digits,int d,string cur){
		if(d==digits.length()){
			res.push_back(cur);
			return;
		}
		
		int cur_num=digits[d]-'0';
		
		for(int i=0;i<digit[cur_num].size();i++)
		 dfs(digits,d+1,cur+digit[cur_num][i]);
	}
	
	vector<string> letterCombinations(string digits){
		if(digits=="")
		return res;
		init();
		dfs(digits,0,"");
		return res;
	}
};
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值