在聊时间复杂度之前先对数据结构有个大概的了解(不重要)
数据结构是一种具有一定逻辑关系,在计算机中应用某种存储结构,并且封装了相应操作的数据元素集合。它包含三方面的内容,逻辑关系、存储关系及操作。
有一个很著名的公式 “程序=数据结构+算法”,就是一套方法,需要的时候拿过来,套用就可以。
-------------------------------------------------------------------------------------------------------------------------------
算法复杂度包括空间复杂度(内存问题)和时间复杂度(代码编写问题)。
空间复杂度:这里不讨论。
时间复杂度:理论上,一个算法运行所需要的时间。实际是得不到准确的值的,因为机器设备,配件不同,时间不同。
- 例一:
- void Func1(int n)
- {
- count = 0;
- for(int i = 0; i < n; ++i)
- {
- for(int j = 0;j < n; ++j)
- {
- ++count;
- }
- }
-
- for(int k = 0; k < 2*n; ++k)
- {
- ++count;
- }
-
- for(int m = 0; m < 10; ++m)
- {
-
- }
-
- }
此刻整个代码的运行次数为:F(n)= n^2+2*n+10,
注:将F(n)变为空间复杂度时,1)整数可以省略掉,2)与n相乘的整数省略
原因:空间复杂度就是当n为任意值时,找出对F(n)最后结果影响最大的项,其中与n相乘的整数可以省略。
则例一的空间复杂度为O(n^2+n)-----n也可以换成m,k,j......
- 例二:
- void Func2(int n,int m)
- {
- int count = 0;
- for(int i = 0; i < n; ++i)
- {
- ++count;
- }
- for(int j = 0; j < m; ++j)
- {
- ++count;
- }
- }
F(n)=n+m
当题目说明n与m相似时O(n)=2n=2m=n=m
n>>m:O(n)
m>>n:O(m)
当题目没有说明:O(m+n)
注:当F(n)=常数时,时间复杂度为O(1)
- void Func3(char *str) //*为取地址符,相当于C中的&
- {
- while(*str) //*str在函数外已经定义好了,是一串字符
- {
- if(*str == "#")
- return 'ok';
- else
- ++str; //位置加1
- }
-
- }
同样的,也可以计算字符串。
那么来进行一个小练习吧!
当*str = effective ,#=e。时间复杂度为多少----------------------------------------------------O(1)
当*str = effective,#=f。 时间复杂度为多少-----------------------------------------------------O(1)
当*ste给出具体的字符或数,#未给出具体的字符或数时,时间复杂度:O(1)
>>因为*str是具体的,F(n)是有限次的
当*ste未给出具体的字符或数,#未给出具体的字符或数时,时间复杂度:O(n)
>>无法确定次数,但最好结果是1,最坏结果是n(最后一个),取最坏结果为时间复杂度。
- void Func4(int *a,int n)
- {
- for(int end = n; end > 0; --end)
- {
- int exchange = 0;
- for(int i = 0; i < end; ++i)
- {
- if(a[i-1] > a[i])
- {
- swap(a[i-1],a[i]);
- exchange = 1;
- }
-
- }
- if(exchang == 1)
- break;
-
- }
-
- }
F(n)=(n-1)+(n-2)+(n-3)......+1=n*(n-1)/2=(n^2-n)/2 时间复杂度为n^2-n
- //斐波那契序列
- void Func5(int n)
- {
- if(n < 3)
- return 1;
- return Func4(n-1)+Func4(n-2);
-
-
- }
面对递归是每次递归的次数相乘,这里需要用到树形图,之后会讲到。
f(n)=2^0+2^1+2^2...2^(n-1)-x, x为一个常数。这里不懂没关系,后面还会讲。
时间复杂度为O(2^n)
总结一下:时间复杂度不是只看代码就能得出来的,要通过代码得到一个思路,在应用数学知识在这个思路的基础上,算出最后结果。
物理位置随机,也就是随机存储,但有一定的逻辑顺序。逻辑顺序的实现依靠指针。有一个结点叫做单链表,两个结点叫做双链表。
由数据域和指针域组成
指向第一个结点的指针,没有数据域。有头结点时指向头结点,没有头结点指向第一个结点
数据域为空,指针域指向第一个节点
链表: 随机存储,顺序存取
顺序表:顺序存储,随机存储
- typedef struct Lnode{
- ElemType data; //定义数据域
- struct Lnode *next; //因为指针指向下一个结点,所以指针类型为steuct Lnode
- }Lnode,*LinkList;
头结点一般定义为L,整个链表也称为L;
定义头结点为Lnode *p;
定义其它结点LinkList p;
- Status InitList_L(LinkList &L){
- L = (LinkList) malloc (sizeof(LNode));
- }
(空链表:头指针和头结点存在)
- int ListEmpty(LinkList){
- if(L->next)
- return 0;
- else
- return 1;
- }
- Status DestroyList_L(LinkList &L){
- Lnode *p;
- while(L){
- p = L;
- L = L->next;
- delete p;
- }
- }
- Status ClearList(LinkList &L){
- Lnode *p,*q;
- p = L->next;
- while(p){
- q = p->next;
- delete p;
- p = q;
- }
- L->next = null;
- return ok;
- }
清空链表,头结点不动,所以需要q代替L(相对于销毁链表而言)。
- int ListLength_L(LInkList L){
- LinkList p;
- p = L->next;
- i = 0;
- while(p){
- i++;
- p = p->next;
- }
- }
主要是查找
- Status GetElem_L(LinkList,int i,ElemType &e){
- p = L->next;
- j = 1;
- while(p && j < i){
- p = p->next;
- ++j;
- }
- if(!p || j > i)
- return error;
- e = p->data;
- return ok;
- }//GetElem_L
先查找,在插入
- Status ListInsert_L(LinkList &L,int i,ElemType e){
- p = L;j = 0
- while(p && j < i-1)
- {p = p->next;++j} //查找第i-1个元素
- if(!p || j > i-1)
- return error; //保证查找到的是第i-1个结点,并且存在
- s = new Lnode;
- s-> = e;
- s->next = p->next;
- p->next = s; //将s插入
- return ok;
- }//ListInsert_L
大家可以尝试一下,删除第i个结点是怎样操作的
- void CreateList_H(LinkList &L,int n){
- L = new Lnode;
- L->next = null;
- for(i = n; i > 0; i--)
- {
- p = new Lnode;
- cin>>p->next; //获得新元素,在c中表示为scanf("&d",&p->data)
- p->next = L->next; //插入新结点
- L->next = p;
- }
- void CreateList_R(LinkList &L,int n){
-
- L = new Lnode;
- L->next = null;
- r = L;
- for(i = n; i < 0;--i){
- p = new Lnode;
- cin>>p->data;
- p->next = null;
- r->next = p;
- r = p;
- }