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


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

    什么是数据结构

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

    常见的数据结构

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

    什么是算法

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

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

    什么是时间复杂度?

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

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

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

    时间复杂度的计算

    1. 例一:
    2. void Func1(int n)
    3. {
    4. count = 0;
    5. for(int i = 0; i < n; ++i)
    6. {
    7. for(int j = 0;j < n; ++j)
    8. {
    9. ++count;
    10. }
    11. }
    12. for(int k = 0; k < 2*n; ++k)
    13. {
    14. ++count;
    15. }
    16. for(int m = 0; m < 10; ++m)
    17. {
    18. }
    19. }

    此刻整个代码的运行次数为:F(n)= n^2+2*n+10,

    注:将F(n)变为空间复杂度时,1)整数可以省略掉,2)与n相乘的整数省略

           原因:空间复杂度就是当n为任意值时,找出对F(n)最后结果影响最大的项,其中与n相乘的整数可以省略。

    则例一的空间复杂度为O(n^2+n)-----n也可以换成m,k,j......

    1. 例二:
    2. void Func2(int n,int m)
    3. {
    4. int count = 0;
    5. for(int i = 0; i < n; ++i)
    6. {
    7. ++count;
    8. }
    9. for(int j = 0; j < m; ++j)
    10. {
    11. ++count;
    12. }
    13. }

    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)

    1. void Func3(char *str) //*为取地址符,相当于C中的&
    2. {
    3. while(*str) //*str在函数外已经定义好了,是一串字符
    4. {
    5. if(*str == "#")
    6. return 'ok';
    7. else
    8. ++str; //位置加1
    9. }
    10. }

    同样的,也可以计算字符串。

    那么来进行一个小练习吧!

    当*str = effective ,#=e。时间复杂度为多少----------------------------------------------------O(1)

    当*str = effective,#=f。  时间复杂度为多少-----------------------------------------------------O(1)

    当*ste给出具体的字符或数,#未给出具体的字符或数时,时间复杂度:O(1)

    >>因为*str是具体的,F(n)是有限次的

    当*ste未给出具体的字符或数,#未给出具体的字符或数时,时间复杂度:O(n)

    >>无法确定次数,但最好结果是1,最坏结果是n(最后一个),取最坏结果为时间复杂度。

    冒泡法

    1. void Func4(int *a,int n)
    2. {
    3. for(int end = n; end > 0; --end)
    4. {
    5. int exchange = 0;
    6. for(int i = 0; i < end; ++i)
    7. {
    8. if(a[i-1] > a[i])
    9. {
    10. swap(a[i-1],a[i]);
    11. exchange = 1;
    12. }
    13. }
    14. if(exchang == 1)
    15. break;
    16. }
    17. }

    F(n)=(n-1)+(n-2)+(n-3)......+1=n*(n-1)/2=(n^2-n)/2     时间复杂度为n^2-n

    递归法

    1. //斐波那契序列
    2. void Func5(int n)
    3. {
    4. if(n < 3)
    5. return 1;
    6. return Func4(n-1)+Func4(n-2);
    7. }

    面对递归是每次递归的次数相乘,这里需要用到树形图,之后会讲到。

    f(n)=2^0+2^1+2^2...2^(n-1)-x,   x为一个常数。这里不懂没关系,后面还会讲。

    时间复杂度为O(2^n)

    总结一下:时间复杂度不是只看代码就能得出来的,要通过代码得到一个思路,在应用数学知识在这个思路的基础上,算出最后结果。

    线性表

    链表

    定义

             物理位置随机,也就是随机存储,但有一定的逻辑顺序。逻辑顺序的实现依靠指针。有一个结点叫做单链表,两个结点叫做双链表。

    结点:

            由数据域和指针域组成

    头指针:

            指向第一个结点的指针,没有数据域。有头结点时指向头结点,没有头结点指向第一个结点

    头结点:

            数据域为空,指针域指向第一个节点

    链表与顺序表的区别:

    链表:  随机存储,顺序存取

    顺序表:顺序存储,随机存储

    单链表基本操作

    单链表的定义
    1. typedef struct Lnode{
    2. ElemType data; //定义数据域
    3. struct Lnode *next; //因为指针指向下一个结点,所以指针类型为steuct Lnode
    4. }Lnode,*LinkList;

    头结点一般定义为L,整个链表也称为L;

    定义头结点为Lnode *p;

    定义其它结点LinkList p;

    单链表的初始化:
    1. Status InitList_L(LinkList &L){
    2. L = (LinkList) malloc (sizeof(LNode));
    3. }
    判断链表是否为空:

    (空链表:头指针和头结点存在)

    1. int ListEmpty(LinkList){
    2. if(L->next)
    3. return 0;
    4. else
    5. return 1;
    6. }
     销毁单链表
    1. Status DestroyList_L(LinkList &L){
    2. Lnode *p;
    3. while(L){
    4. p = L;
    5. L = L->next;
    6. delete p;
    7. }
    8. }
    清空链表
    1. Status ClearList(LinkList &L){
    2. Lnode *p,*q;
    3. p = L->next;
    4. while(p){
    5. q = p->next;
    6. delete p;
    7. p = q;
    8. }
    9. L->next = null;
    10. return ok;
    11. }

    清空链表,头结点不动,所以需要q代替L(相对于销毁链表而言)。

    求单链表的长度
    1. int ListLength_L(LInkList L){
    2. LinkList p;
    3. p = L->next;
    4. i = 0;
    5. while(p){
    6. i++;
    7. p = p->next;
    8. }
    9. }
    获取链表中某个元素的内容

    主要是查找

    1. Status GetElem_L(LinkList,int i,ElemType &e){
    2. p = L->next;
    3. j = 1;
    4. while(p && j < i){
    5. p = p->next;
    6. ++j;
    7. }
    8. if(!p || j > i)
    9. return error;
    10. e = p->data;
    11. return ok;
    12. }//GetElem_L
    在链表中插入某个元素

    先查找,在插入

    1. Status ListInsert_L(LinkList &L,int i,ElemType e){
    2. p = L;j = 0
    3. while(p && j < i-1)
    4. {p = p->next;++j} //查找第i-1个元素
    5. if(!p || j > i-1)
    6. return error; //保证查找到的是第i-1个结点,并且存在
    7. s = new Lnode;
    8. s-> = e;
    9. s->next = p->next;
    10. p->next = s; //将s插入
    11. return ok;
    12. }//ListInsert_L

    大家可以尝试一下,删除第i个结点是怎样操作的

    头插法

    1. void CreateList_H(LinkList &L,int n){
    2. L = new Lnode;
    3. L->next = null;
    4. for(i = n; i > 0; i--)
    5. {
    6. p = new Lnode;
    7. cin>>p->next; //获得新元素,在c中表示为scanf("&d",&p->data)
    8. p->next = L->next; //插入新结点
    9. L->next = p;
    10. }

    尾插法

    1. void CreateList_R(LinkList &L,int n){
    2. L = new Lnode;
    3. L->next = null;
    4. r = L;
    5. for(i = n; i < 0;--i){
    6. p = new Lnode;
    7. cin>>p->data;
    8. p->next = null;
    9. r->next = p;
    10. r = p;
    11. }

  • 相关阅读:
    项目全生命周期阶段检查单
    Flume配置1——基础案例
    浅谈 AnalyticDB SQL 优化
    数字IC前端学习笔记:时钟切换电路
    【英语考研词汇训练营】Day 15 —— analyst,general,avoid,surveillance,compared
    代码随想录算法训练营第三十四天| LeetCode1005. K 次取反后最大化的数组和、LeetCode134. 加油站、LeetCode135. 分发糖果
    你真的了解JAVA中对象和类、this、super和static关键字吗
    R语言ggplot2可视化:使用ggpubr包的ggviolin函数可视化小提琴图、设置palette参数自定义不同水平小提琴图的边框颜色
    18. “状态变化”模式 之State模式(状态模式)
    AT&T 格式汇编语言语法
  • 原文地址:https://blog.csdn.net/2301_77288731/article/details/132622321