数据结构与算法(一) 时间复杂度

 在聊时间复杂度之前先对数据结构有个大概的了解(不重要)

什么是数据结构

数据结构是一种具有一定逻辑关系,在计算机中应用某种存储结构,并且封装了相应操作的数据元素集合。它包含三方面的内容,逻辑关系、存储关系及操作。

常见的数据结构

  • 线性表 :另个或多个数据元素的有限序列。
  • 链性表 :链表是一种数据元素按照链式存储结构进行存储的数据结构,这种存储结构在物理上存在非连续的特点。
  • 树 :树是典型的非线性结构,它是包括,2 个结点的有穷集合 K。
  • 图 :图是另一种非线性数据结构。在图结构中,数据结点一般称为顶点,而边是顶点的有序偶对。
  • 堆 :堆是一种特殊的树形数据结构,一般讨论的堆都是二叉堆。

什么是算法

有一个很著名的公式 “程序=数据结构+算法”,就是一套方法,需要的时候拿过来,套用就可以。

-------------------------------------------------------------------------------------------------------------------------------

什么是时间复杂度?

算法复杂度包括空间复杂度(内存问题)和时间复杂度(代码编写问题)。

空间复杂度:这里不讨论。

时间复杂度:理论上,一个算法运行所需要的时间。实际是得不到准确的值的,因为机器设备,配件不同,时间不同。

时间复杂度的计算

例一:
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;
}

你可能感兴趣的:(数据结构,算法)