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;
    }
    
  • 相关阅读:
    unordered_set
    树的所有实现
    各类算法模板
    单链表全部实现(绝对史上最完整 附例题)
    求最长回文子串
    无重复的最长子串
    秋叶集
    1451. 重新排列句子中的单词
    152. 乘积最大子数组
    JVM总结的部分内容
  • 原文地址:https://www.cnblogs.com/A-FM/p/9653292.html
Copyright © 2011-2022 走看看