C语言学习之--数据结构1

数据结构

1. 栈和堆

  1. 定义
    1. 栈(stack)又名堆栈,是一种运算受限的线性表,其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素
  2. 优缺点
    1. 存取速度比堆要快,仅次于直接位于CPU中的寄存器
    2. 但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据在多个线程或者多个栈之间是不可以共享的,但是在栈内部多个值相等的变量是可以指向一个地址的,
  1. 栈和堆的区别理解

    1. 栈和堆是什么时候创建的 ---当线程创建时,os为每一个系统级的线程分配栈,通常情况下操作系统通过调用语言的运行时为应用创建堆
    2. 分别的作用范围: 栈附属与线程,因此当线程结束栈被回收,堆通常是在应用程序启动的时候被分配,应用程序退出的时候被回收
    3. 栈和堆的大小由什么决定:线程创建的时候设置栈的大小,在应用程序启动的时候设置堆的大小,但是在需要的时候扩展,分配器向内存申请
    4. 栈和堆的速度比较: 栈比堆要快,因为她的存取模式使她轻松的分配和重新分配内存(指针/整形只是进行简单的递增或者递减运算),然而堆在分配和释放的时候由更多复杂的bookkeeping参与,另外,在栈上的每个字节频繁的被服用也就意味着它可能映射到处理器缓存中,所以很快;
    5. 较为经典的理解http://blog.jobbole.com/75321/
  1. 数组/链表/队列/栈

    1. 数组 是将元素在内存中连续存放,由于每个元素占用内存相同,可以通过下标迅速访问数组中任何元素。
    2. 链表 链表中的元素在内存中不是顺序存储的,而是通过存在元素中的指针联系到一起,每个结点包括两个部分:一个是存储 数据元素的数据域,另一个是存储下一个结点地址的 指针。
    3. 队列 队列是一种操作受限制的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作
    4. 栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底;
    5. 栈和队列的区别
      1. 栈是限定只能在表的一端进行插入和删除操作的线性表。 队列是限定只能在表的一端进行插入和在另一端进行删除操作的线性表

2.数据结构(Data Structure)是指互相之间存在着一种或多种关系的数据元素的集合。在任何问题中,数据元素之间都不会是孤立的,在它们之间都存在着这样或那样的关系,这种数据元素之间的关系称为结构。根据数据元素间关系的不同特性,通常有下列四类基本的结构:

  1. 集合结构。在集合结构中,数据元素间的关系是“属于同一个集合”。集合是元素 关系极为松散的一种结构。
  2. 线性结构。该结构的数据元素之间存在着一对一的关系。
  3. 树型结构。该结构的数据元素之间存在着一对多的关系。
  4. 图形结构。该结构的数据元素之间存在着多对多的关系,图形结构也称作网状结构。

算法的时间复杂度和空间复杂度

  1. 时间复杂度
    1. 时间频度

      一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n);

    2. 时间复杂度

      在刚才提到的时间频度中,n称为问题的规模,当n不断变化时,时间频度T(n)也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。 一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。

      T (n) = Ο(f (n)) 表示存在一个常数C,使得在当n趋于正无穷时总有 T (n) ≤ C * f(n)。简单来说,就是T(n)在n趋于正无穷时最大也就跟f(n)差不多大。也就是说当n趋于正无穷时T (n)的上界是C * f(n)。其虽然对f(n)没有规定,但是一般都是取尽可能简单的函数。例如,O(2n2+n +1) = O (3n2+n+3) = O (7n2 + n) = O ( n2 ) ,一般都只用O(n2)表示就可以了。注意到大O符号里隐藏着一个常数C,所以f(n)里一般不加系数

      从图中可见,我们应该尽可能选用多项式阶O(nk)的算法,而不希望用指数阶的算法。常见的算法时间复杂度由小到大依次为:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!)一般情况下,对一个问题(或一类算法)只需选择一种基本操作来讨论算法的时间复杂度即可,有时也需要同时考虑几种基本操作,甚至可以对不同的操作赋予不同的权值,以反映执行不同操作所需的相对时间,这种做法便于综合比较解决同一问题的两种完全不同的算法。

      1. 计算

                     将基本语句执行次数的数量级放入大Ο记号中。
                    如果算法中包含嵌套的循环,则基本语句通常是最内层的循环体,如果算法中包含并列的循环,则将并列循环的时间复杂度相加。例如
                             for (i=1; i<=n; i++)  
                                 x++;  
                             for (i=1; i<=n; i++)  
                                 for (j=1; j<=n; j++)  
                                 x++;  
         
                      第一个for循环的时间复杂度为Ο(n),第二个for循环的时间复杂度为Ο(n2),则整个算法的时间复杂度为Ο(n+n2)=Ο(n2)。
                      Ο(1)表示基本语句的执行次数是一个常数,一般来说,只要算法中不存在循环语句,其时间复杂度就是Ο(1)。其中Ο(log2n)、Ο(n)、 Ο(nlog2n)、Ο(n2)和Ο(n3)称为多项式时间,而Ο
                      (2n)和Ο(n!)称为指数时间。
        
      2. 理解

        1. 一个经验规则:其中c是一个常量,如果一个算法的复杂度为c 、 log2n 、n 、 n*log2n ,那么这个算法时间效率比较高 ,如果是2n ,3n ,n!,那么稍微大一些的n就会令这个算法不能动了,居于中间的几个则差强人意。
      3. 时间复杂度评价性能

        有两个算法A1和A2求解同一问题,时间复杂度分别是T1(n)=100n2,T2(n)=5n3。(1)当输入量n<20时,有T1(n)>T2(n),后者花费的时间较少。(2)随着问题规模n的增大,两个算法的时间开销之比5n3/100n2=n/20亦随着增大。即当问题规模较大时,算法A1比算法A2要有效地多。它们的渐近时间复杂度O(n2)和O(n3)从宏观上评价了这两个算法在时间方面的质量。在算法分析时,往往对算法的时间复杂度和渐近时间复杂度不予区分,而经常是将渐近时间复杂度T(n)=O(f(n))简称为时间复杂度,其中的f(n)一般是算法中频度最大的语句频度。

2. 算法的空间复杂度
    1. 一个算法的空间复杂度(Space Complexity)S(n)定义为该算法所耗费的存储空间,它也是问题规模n的函数。渐近空间复杂度也常常简称为空间复杂度。
    2. 分为三部分 
    -  固定部分。这部分空间的大小与输入/输出的数据的个数多少、数值无关。主要包括指令空间(即代码空间)、数据空间(常量、简单变量)等所占的空间。这部分属于静态空间。
    -  可变空间,这部分空间的主要包括动态分配的空间,以及递归栈所需的空间等。这部分的空间大小与算法有关。一个算法所需的存储空间用f(n)表示。S(n)=O(f(n))  其中n为问题的规模,S(n)表示空间复杂度

代码

单链表的实现

        #include<stdio.h> 
        #include<malloc.h>
        #include<stdlib.h>
        
        //定义链表的节点,由当前的节点值外加指向下个节点的指针
        //NODE等价于struct Node, PNODE等价于struct Node *  
        typedef struct Node{
            int data;
            struct Node *pNext;
        }NODE,*PNODE; 
        
        
        //函数的声明
        PNODE createLinkList(); //定义创建链表的指针函数
        void traverseLinkList(PNODE pHead);//遍历链表的函数
        bool isEmpty(PNODE pHead) ;//判断链表是否为空的函数
        int getLength(PNODE pHead);//获取链表的长度
        bool insertElement(PNODE pHead, int pos, int val);  //向链表中插入元素的函数,三个参数依次为链表头结点、要插入元素的位置和要插入元素的值  
        bool deleteElement(PNODE pHead, int pos, int * pVal); //从链表中删除元素的函数,三个参数依次为链表头结点、要删除的元素的位置和删除的元素的值  
        void sort(PNODE pHead);         //对链表中的元素进行排序的函数(基于冒泡排序) 
        
        
        int main(void){
         
            int val;
            PNODE pHead = NULL;
            pHead = createLinkList();
            traverseLinkList(pHead);
            
            if(isEmpty(pHead)){
                printf("链表为空!\n"); 
            }else{
                printf("链表不为空!\n");
                
            } 
            
            printf("链表的长度为:%d\n",getLength(pHead));
            
            sort(pHead);
            traverseLinkList(pHead);
            
            if(deleteElement(pHead,3,&val))
                printf("删除元素成功!删除的元素是:%d\n",val);
                else
                printf("删除元素失败!\n");
                
            
             traverseLinkList(pHead);  
             system("pause");  
        
            return 0 ;
        } 
        
        //创建一个链表 
        
            PNODE createLinkList(void){
                
                int length;
                int value;
                int i; 
                
                //创建一个不存放有效数据的头节点 
                PNODE pHead = (PNODE)malloc(sizeof(NODE));
                if(pHead==NULL){
                    printf("内存分配失败,程序退出!\n");
                    exit(-1);
                } 
                
                PNODE pTail = pHead;
                
                pTail->pNext=NULL;
                
                printf("请输入您想要创建链表节点的个数: len = ");
                scanf("%d\n",&length);
                
                for( i = 0 ; i < length ; i ++){
                    printf("请输入第%d个节点的值:",i+1);
                    scanf("%d",&value);
                    
                    
                    PNODE pNew= (PNODE)malloc(sizeof(NODE));
                    if(pNew==NULL){
                        printf("内存分配失败,程序退出!\n");
                        exit(-1);
                    } 
                    
                    pNew->data=value;//新节点中放入值 
                    pTail->pNext=pNew;//将尾节点指向新的节点 
                    pNew->pNext=NULL;//将新的节点的指针域清空
                    pTail=pNew;//指针往后移动,将新的节点赋值给pTail,使得pTail始终指向尾节点
                     
                    
                }
                
                return pHead;
            } 
        
        
        //遍历链表 
            void traverseLinkList(PNODE pHead)  {  
                 PNODE p = pHead->pNext;  
                 while(NULL != p)  
                 {  
                  printf("%d  ", p->data);  
                  p = p->pNext;  
                 }  
                 printf("\n");  
                 return;  
        }  
        
        
        bool isEmpty(PNODE pHead)  
        {  
             if(NULL == pHead->pNext)  
              return true;  
             else  
              return false;  
        }   
        
        
        int getLength(PNODE pHead)  
        {  
             PNODE p = pHead->pNext;   //指向首节点  
             int len = 0;     //记录链表长度的变量  
             while(NULL != p)  
             {  
              len++;  
              p = p->pNext;    //p指向下一结点  
             }  
             return len;  
        } 
        
        
        void sort(PNODE pHead)  {  
        
             int len = getLength(pHead);  //获取链表长度     
             int i, j, t;     //用于交换元素值的中间变量  
             PNODE p, q;      //用于比较的两个中间指针变量  
             for(i=0,p=pHead->pNext ; i<len-1 ; i++,p=p->pNext){  
                 for(j=i+1,q=p->pNext;j<len;j++,q=q->pNext){  
                    if(p->data > q->data){  
                        t = p->data;  
                        p->data = q->data;  
                        q->data = t;  
                    }  
                }  
             }  
             
             return;  
        }
        
        
        
        bool insertElement(PNODE pHead, int pos, int val){  
        
             int i = 0;  
             PNODE p = pHead;  
             //判断p是否为空并且使p最终指向pos位置的结点  
             while(NULL!=p && pos-1>i){  
                 p = p->pNext;  
                 i++;  
             }
               
             if(NULL==p || i>pos-1)  
              return false;  
             //创建一个新结点  
             PNODE pNew = (PNODE)malloc(sizeof(NODE));  
             if(NULL == pNew){  
                printf("内存分配失败,程序退出!\n");  
                exit(-1);  
             }  
             
             pNew->data = val;  
             //定义一个临时结点,指向当前p的下一结点  
             PNODE q = p->pNext;  
             //将p指向新结点  
             p->pNext = pNew;  
             //将q指向之前p指向的结点  
             pNew->pNext = q;  
             return true;  
        }  
        
        
        
        bool deleteElement(PNODE pHead, int pos, int * pVal){  
             int i = 0;  
             PNODE p = pHead;  
             //判断p是否为空并且使p最终指向pos结点  
             while(NULL!=p->pNext && i<pos-1){  
                p = p->pNext;  
                 i++;  
             } 
              
             if(NULL==p->pNext || i>pos-1)  
              return false;  
             //保存要删除的结点  
             * pVal = p->pNext->data;  
             //删除p后面的结点  
             PNODE q = p->pNext;  
             p->pNext = p->pNext->pNext;  
             free(q);  
             q = NULL;  
              return true;  
        }  
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容

  • 算法复杂度 时间复杂度 空间复杂度 什么是时间复杂度 算法执行时间需通过依据该算法编制的程序在计算机上运行时所消耗...
    KODIE阅读 3,231评论 0 9
  • 通常,对于一个给定的算法,我们要做 两项分析。第一是从数学上证明算法的正确性,这一步主要用到形式化证明的方法及相关...
    西域小码阅读 1,838评论 0 11
  • 算法的时间复杂度和空间复杂度-总结通常,对于一个给定的算法,我们要做 两项分析。第一是从数学上证明算法的正确性,这...
    Explorer_Mi阅读 1,441评论 0 1
  • 什么是算法的复杂度 算法复杂度,即算法在编写成可执行程序后,运行时所需要的资源,资源包括时间资源和内存资源。 一个...
    儒生阅读 1,708评论 0 2
  • 1 序 2016年6月25日夜,帝都,天下着大雨,拖着行李箱和同学在校门口照了最后一张合照,搬离寝室打车去了提前租...
    RichardJieChen阅读 5,069评论 0 12