zoukankan      html  css  js  c++  java
  • 数据结构-王道-线性表

    线性表-链表

    • 线性表的链式储存又称为链表,他是指通过一组任意的存储单元来存储线性表中的数据元素。

    单链表

    typedf struct LNode
    {
    	ElemType data;
    	struct LNode *next;
    }
    
    • 因为单链表的数据元素是离散的分布在储存空间当中的,所以单链表是非随机存取的存取结构,即不能直接找到表中某个特定的的结点。查找某个特定的结点的时候需要从表头开始遍历,依次查找。

      头结点和头指针的区别:不管带不带头结点,头指针始终指向链表的第一个结点,而头结点是带头结点链表中的第一个结点,结点内通常不储存信息。

    引入头结点之后可以带来两个优点:

    1. 由于开始结点的位置被存放在头结点的指针域中,所以在链表的第一个位置上的操作和在表的其他位置上的操作一致,无需对第一个位置进行特殊的操作。
    2. 无论链表是否为空,其头指针是指向头结点的非空指针(空表中头结点的指针域为空),因此空表和非空表的处理也就统一了。
    typedef struct LNode
    {
        int data;
        struct LNode *next;
    }LNode,*LinkList;
    

    头插法建立单链表

    #ifndef NULL
        #ifdef __cplusplus
            #define NULL    0
        #else  /* __cplusplus */
            #define NULL    ((void *)0)
        #endif  /* __cplusplus */
    #endif  /* NULL */
    typedef struct LNode
    {
        int data;
        struct LNode *next;
    }LNode, *LinkList;
    
    LinkList CreateListHead(LinkList &L)
    {
        LNode *s;
        int x;
        L=(LinkList)malloc(sizeof(LNode));
        L->next=NULL;
        scanf("%d",&x);
        while(x!=9999)
        {
            s=(LinkList)malloc(sizeof(LNode));
            s->data = x;
            s->next = L->next;
            L->next = s;
            scanf("%d",&x);
        }
        return L;
    }
    int main()
    {
        LinkList L;
        CreateListHead(L);
        printf("%d",L->next->data);
    
        return 0;
    }
    

    尾插法建立单链表

    LinkList CreateListHead(LinkList &L)
    {
        LNode *s;
        int x;
        L=(LinkList)malloc(sizeof(LNode));
        L->next=NULL;
        scanf("%d",&x);
        while(x!=9999)
        {
            s=(LNode*)malloc(sizeof(LNode));
            s->data = x;
            s->next = L->next;
            L->next = s;
            scanf("%d",&x);
        }
        return L;
    }
    

    获取第i个元素的值

    LinkList GetElem(LinkList L,int i)//
    {
        int j=0;
        LinkList l=L;
        if(i==0)        // 要第0个就是头结点的地址了。
            return L;
        if(i<0)         // 不存在负结点 好吧。
            return NULL;
        while(l&&j<i)   // l 不为null
        {
            l=l->next;
            j++;
        }
        return l;
    }
    

    双链表

    双链表的定义

    typedef struct DNode
    {
        int data;
        struct DNode *prior,*next;
    }DNode,*DLinkList;
    

    书上没有要求但是还是写一个 双链表的头插法吧。

    DLinkList CreateDListHead(DLinkList &L)
    {
        int x,count=0;
        DLinkList s;
        L = (DLinkList)malloc(sizeof(DNode));
        L->prior=L->next=NULL;
        scanf("%d",&x);
        while(x!=9999)
        {
            if(count==0)
            {
                s=(DLinkList)malloc(sizeof(DNode));
                s->data=x;
                s->prior=L;
                s->next=L->next;
                L->next=s;
                count++;
            }
            else
            {
                s=(DLinkList)malloc(sizeof(DNode));
                s->data=x;          //  头插法
                s->prior=L;         //  新加入的s的头指针 存放L(头结点)的地址
                s->next=L->next;    //  s的尾指针存放L的后继结点。
                L->next->prior=s;   //  L的后继结点的头指针存放s的地址
                L->next=s;          //  ..
            }
            scanf("%d",&x);
        }
        return L;
    }
    int main()
    {
        DLinkList L;
        CreateDListHead(L);
        printf("%d
    ",L->next->next->next->prior->data);
        return 0;
    }
    

    静态链表 提一下好吧。
    静态链表以(next=-1)作为结束标志。静态链表的插入,删除没什么好说的。为什么有这个鬼东西是因为:在一些不支持指针的高级语言(如Basic)中,这是一种巧妙的设计方法。

    #define MaxSize 50
    typedef struct
    {
        int data;
        int next;
    }SLinkList[MaxSize];
    

    顺序表和链表的比较

    1. 存取方式

      顺序表可以顺序存取,也可以随机存取,链表只能从表头顺序取元素。
      欢迎使用马克飞象

    2. 逻辑结构和物理结构
      采用顺序存储时,逻辑上相邻的元素,其对应的物理位置也相邻。而采用链式存储的时候,逻辑上相邻的元素在物理上并不一定相邻,其对应的逻辑关系是通过指针链接来表示的。
      一定要搞清楚存取方式 和 储存方式。

    3. 查找,删除,插入操作。
      对于按值查找来说,当顺序表在无序的情况下,两者的时间复杂度均为(O(n));特别的当顺序表有序的时候可以采用折半查找的方式去查找(O(log_2n))
      对于按序号查找,顺序表支持随机访问,时间复杂度仅为(O(1)),而链表的平均时间复杂度为(O(n))。顺序表的插入,删除操作,平均需要移动半个表长的元素。链表的插入删除操作,只需要修改相关结点的指针即可。因为链表带有指针域所以储存密度较低。

    4. 空间分配
      顺序表在静态储存的情形下,一旦储存空间装满就不能扩充了,如果再加入新元素将出现内存溢出,需要实现分配足够大的空间,但是提前分配的太大了到时候可能又用不上。这个时候就需要动态储存出手了,虽然其储存空间可以扩充,但需要移动大量的元素,导致其操作效率太低,而且如果内存中没有更大块的连续空间的话,这时候扩充可能导致失败。链式储存的结点空间只在需要的时候申请分配,只要内存有空间就可以分配,不像前面的必须要连续,这样灵活而高效。


    @(P38T21)

    时间复杂度(O(n)),一边扫描就得到了想要的结果,这种狗题目容易想的是两边扫描的答案,但是这样的话 最高分十分,下面的可以吃满分也就是十五分。

    int Search_k(LinkList L,int k)
    {
        LinkList l=L;// 存放倒数第k个的值。
        bool flag = false;// 表示暂时没有找到。
        int i=0;
        while(L->next)
        {
            L=L->next;
            i++;
            if(i==k||flag)
            {
                flag=true;
                l=l->next;
            }
        }
        if(flag)
        {
            printf("%d
    ",l->data);
            return 1;
        }
        else
        {
            printf("%d
    ",0);
            return 0;
        }
    }
    

    @(P38T22)

    挺简单的以后这种题目尽量一次遍历就搞定。 时间复杂度(O(1))

    typedef struct LNode
    {
        int data;
        struct LNode *next;
    }LNode, *LinkList;
    
    int ListLen(LinkList L)// 传入L但不加 取地址符号,不改变L原来的内容。
    {
        int len=0;
        while(L->next)
        {
            len++;
            L=L->next;
        }
        return len;
    }
    LinkList FindAddr(LinkList L,LinkList I)
    {
        int lenL,lenI;
        lenI=ListLen(I);
        lenL=ListLen(L);
        if(lenI>lenL)
        {
            while(lenI-lenL)
            {
                lenI--;
                I=I->next;
            }
        }
        if(lenI<lenL)
        {
            while(lenL-lenI)
            {
                lenL--;
                L=L->next;
            }
        }// 因为是找从哪里开始共同的后缀,但是两个链表不一定等长,所以将长的前面的剪掉就行了。
        while(L->next!=NULL&&L->next!=I->next)
        {
            I=I->next;
            L=L->next;
        }
        return L->next;//这个时候的L->next等于I->next。
        
    }
    

    @(p58T23)

    typedef struct LNode
    {
        int data;
        struct LNode *next;
    }LNode, *LinkList;
    
    void DeleSame(LinkList L,int n)
    {
    
        if(L->next==NULL)
            printf("voidmei ¿ÕÁË£¿");
        LinkList l;
        l=L;
        int store[n+1];
        for(int i=0;i<=n;i++)
            store[i] = 0;
        while(l->next!=NULL)
        {
            int temp = l->next->data>0?l->next->data:-l->next->data;
            if(store[temp]==0)      // 如果这个数字(绝对值)没有出现过
            {
                store[temp]++;      // 标记一下出现
                l=l->next;          // 跳过该结点,检查下一个结点。
            }
            else                    // 出现过:让下一个结点的下一个结点作为当前结点的下一个结点。
                l->next=l->next->next; // 删除 该删除的结点。
    
        }
    }
    LinkList CreateListHead(LinkList &L)
    {
        LNode *s;
        int x;
        L=(LinkList)malloc(sizeof(LNode));
        L->next=NULL;
        scanf("%d",&x);
        while(x!=9999)
        {
            s=(LNode*)malloc(sizeof(LNode));
            s->data = x;
            s->next = L->next;
            L->next = s;
            scanf("%d",&x);
        }
        return L;
    }
    int main()
    {
        LinkList L,i;
        CreateListHead(L);
        DeleSame(L,22);
        if(L->next==NULL)
            printf("¿ÕÁË
    ");
        while(L->next!=NULL)
        {
            L=L->next;
            printf("--------%d-----------------
    ",L->data);
        }
        free(L);
        return 0;
    }
    
  • 相关阅读:
    HDU 1009 FatMouse' Trade
    HDU 2602 (简单的01背包) Bone Collector
    LA 3902 Network
    HDU 4513 吉哥系列故事——完美队形II
    LA 4794 Sharing Chocolate
    POJ (Manacher) Palindrome
    HDU 3294 (Manacher) Girls' research
    HDU 3068 (Manacher) 最长回文
    Tyvj 1085 派对
    Tyvj 1030 乳草的入侵
  • 原文地址:https://www.cnblogs.com/A-FM/p/9653292.html
Copyright © 2011-2022 走看看