zoukankan      html  css  js  c++  java
  • 数据结构第一部分-----线性表

    线性表(List)是零个或者多个数据元素的有限序列.

    • 首先它是一个序列.里面的元素是有顺序的,如果有多个元素,除开头和结尾以外的元素都有一个前驱和一个后继.而开头元素只有后继,结尾元素只有前驱.
    • 其次线性表是有限的,也就是里面的元素个数是有限的。

    按存储方式分为 顺序表 和 链表

    顺序表

    1.1顺序表示(顺序表)

    概念:用一组地址连续的存储单元依次存储线性表的数据元素,这种存储结构的线性表称为顺序表。

    特点:逻辑上相邻的数据元素,物理次序也是相邻的。

      只要确定好了存储线性表的起始位置,线性表中任一数据元素都可以随机存取,所以线性表的顺序存储结构是一种随机存取的储存结构,因为高级语言中的数组类型也是有随机存取的特性,所以通常我们都使用数组来描述数据结构中的顺序储存结构,用动态分配的一维数组表示线性表。

    1.2 代码实现

    以最简单的学生信息管理为例:

    首先先创建两个数据结构,如下:

    #define maxsize 100 //定义学生最大数量
    #define OK 1       //正确标志
    #define ERROR 0     //失败标志
    //学生信息的数据结构
    typedef struct
    {
        int id;   //学生id
        char name[30];   //学生姓名
        
    }Student;


    //顺序表数据结构
    typedef struct
    {
        Student *elem;   //储存空间的基地址
        int length;      //数据结构的长度
    }SqList;
    ////////////or 直接:

    1 #define MAXSIZE 20   //存储空间的初始大小
    2 typedef int DataType //类型可根据实际情况而定
    3 typedef struct 
    4 {
    5    DataType data[MAXSIZE]; //数组来存储数据
    6     int length;              //实际长度
    7 }SqlList;


    //定义SqList类型的变量
    SqList L;
    这是一个十分简单的例子,这样我们就可以通过L.elem[i-1]访问序号为i的学生信息了。其实这里我们用到了指针数组。如果你对指针数组还不熟悉的话,可以去我写过的另一篇文章看看:https://blog.csdn.net/qq_38378384/article/details/79951651

    1.初始化

    基本算法:

    //初始化顺序表基本算法
    Status InitList(SqList &L)
    {
    //构造一个空的顺序表L
    L.elem = new ElemType[maxsize]; //分配内存空间
    if(!L.elem) exit(-1);
    L.length = 0;
    return OK;
    }
     

    2.取值

    基本算法:

    //顺序表取值
    Status Get(SqList &L,int i,ElemType &e)
    {
    if(i<1||i>L.length) return ERROR;
    e = L.elem[i-1];
    return OK;

    }
     

    3.查找

    基本算法:

    //顺序表查找
    int Find(SqList L,ElemType e)
    {
    //查找值为e的数据元素,返回其序号
    for(i=0;i<L.length;i++)
    {

    if(L.elem[i]==e) return i+1;
    return ERROR; //查找失败
    }

    }
     

    4.插入

    基本算法:

    //顺序表插入
    Status ListInsert(SqList &L,int i,ElemType e)
    {
    if((i<1)||(i>L.length+1)) return ERROR; //i不合法
    if(L.length == maxsize) return ERROR; //满了
    for(j=L.length-1;j>=i-1;j--)
    L.elem[j+1]=L.elem[j]; //将第n个至i个位置的元素后移
    L.elem[i-1]=e; //将e放进第i个位置
    }
     

    5.删除

    //顺序表删除
    Status ListDelete(SqList &L,int i)
    {
    //删除第i个元素,i的值为[1,L.length]
    if((i<1)||(i>L.length)) return ERROR;
    for(j=i;j<=L.length-1;j++)
    L.elem[j-1]=L.elem[j];
    --L.length; //长度减一
    return OK;
    }
    算法都十分的简单,眼尖的你可能发现了,为啥有的参数用的是引用,有的不是呢?

    这里我就得讲下使用引用作为形参的作用了,主要有三点:

    (1)使用引用作为参数与使用指针作为参数的效果是一样的,形参变化时实参对应也会变化,这个我在上篇文章(我上面给的链接)也有说明,引用只是一个别名。

    (2)引用类型作为形参,在内存中并没有产生实参的副本,而使用一般变量作为形参,,形参和实参会分别占用不同给的存储空间,当数据量较大时,使用变量作为形参可能会浪费时间和空间。

    (3)虽然使用指针也可以达到引用一样的效果,但是在被调函数中需要重复使用"*指针变量名"来访问,很容易产生错误并且使程序的阅读性变差。

    此时你会发现,使用顺序表作为存储时,空间是一次性直接开辟的,所以可能会有空间不足或者浪费空间的情况出现,那么为啥不用一个就分配一个空间呢,再使用一个方式将这些空间串起来不就好了,是时候展现真正的技术了(链表)。

    ////////////////////////////////////////////////////////////////////////////////////////////////////////顺序表说白了用的是数组 正常的

    链表:

         单链表的定义:当一个序列中只含有指向它的后继结点的链接时,就称该链表为单链表。

    1 typedef struct LNode{  
    2     ElemType   data;  
    3     struct LNode  *next;  
    4 }LNode, *LinkList;  

    单链表都有一个头指针,一般以head来表示,存放的是一个地址。链表中的节点分为两类,头结点和一般节点,头结点是没有数据域的(有的话存放链表的长度或者不存)。链表中每个节点都分为两部分,一个数据域,一个是指针域。

    !!!!!!跟以前理解的不一样

                                                                                       

    由定义可知头指针指向链表第一个存储位置,当存在头结点时头指针指向头结点,这时如果删除链表中的节点头指针不会改变(头指针不能删除) 
    当不存在头结点时,头指针指向首结点,如果这时候删除首结点那么头指针发生改变。
                                                              图1图1
    这里有个地方要注意,就是对头指针概念的理解,这个很重要。“链表中第一个结点的存储位置叫做头指针”,如果链表有头结点,那么头指针就是指向头结点数据域的指针。
     
    头指针 头结点
    头指针是指向第一个结点的指针。链表中可以没有头结点,但不能没有头指针。

    以下是头指针与头结点的关系:

    1. //定义结点的结构体  
    2. typedef struct LNode{  
    3.     int data;
    4.     struct LNode *next;  
    5. }LNode,*LinkList;   

     则定义LinkList L;时,L为链表的头指针。

    L=(LinkList) malloc (sizeof(LNode)); //创建一个结点

    此处返回给L的是一个指针,并且赋给了头指针。

    L->next=null; //这里说明我创建了一个头结点,即同时运用了头指针和头结点。

     1 //不带头结点的单链表的初始化  
     2 void LinkedListInit1(LinkedList L)  
     3 {  
     4   L=NULL;  
     5 }  
     6 //带头结点的单链表的初始化  
     7 void LinkedListInit2(LinkedList L)  
     8 {  
     9   L=(LNode *)malloc(sizeof(LNode));  
    10   if(L==NULL)  
    11   {  
    12     printf("申请空间失败!");  
    13     exit(0);  
    14   }  
    15   L->next=NULL;  
    16 }  

    我一直都很混乱这个东西 

    单链表不带头结点为空的判断条件:L==NULL;

    单链表带头结点 为空的判断条件:L->next==NULL;

    L就是个指针,指向第一个节点 ,这个指向,不是L->next  而是L=xxx 你想想c++里的指针指向数组xxx不是直接画等号啊 哪来的什么next->

    再说了L就只是个指针,他没有next这个成员啊

    所以 说白了就是 混为一体了/////or依附于////就是一体

    L是个指针,(没有头结点的时候)链表内容为空,就哪也不指呗,就=null;

    (有头结点了)头指针是肯定指向头结点的 ,L就依附到头结点上了,链表为空,头结点的next指针哪也不指,l->next==NULL呗;

    你再想想

    ANNNNNNNNNNNNNNNNNNNNNNNNNNNNNNND:

    为什么链表初始化和销毁链表的时候 传的是二级指针:

    1 InitList(LinkList *L){
    2 
    3      *L=(LinkList)malloc(sizeof(Node));
    4      (*L)->next=NULL;
    5 }

                             简单来说,修改头指针则必须传递头指针的地址,否则传递头指针值即可(即头指针本身)。

    //后面代码部分转自  大神https://www.cnblogs.com/leaver/p/6718421.html   我害怕你无措

    复制代码
    int createListTail(linkList *L, int n) {
        linkList p, temp;
        temp = (*L);
        int i;
        srand((int)time(0));
        for (i = 0; i < n;i++) {
            p = (linkList)malloc(sizeof(Node));
            p->data = rand() % 100;
            printf("testing:Node[%d]=%d
    ", i + 1, p->data);
            p->next = NULL;
            temp->next = p;
            temp = p;
        }
        printf("链表(尾插法)创建成功
    ");
        return 1;
    }
    复制代码

    (四)获取链表的长度

    复制代码
     1 int getlength(linkList *L) {
     2     linkList p;
     3     int length=0;
     4     p = (*L)->next;//p指向第一个节点;
     5     while (p) {
     6         length++;
     7         p = p->next;
     8     }
     9     return length;
    10 }
    复制代码

    (五)打印整个链表

    复制代码
     1 int printList(linkList *L) {
     2     linkList p;
     3     int i = 0;
     4     p = (*L)->next;//p指向第一个节点;
     5     printf("-----------打印整个链表-----------
    ");
     6     if (p==NULL) {
     7         printf("这是一个空链表.
    ");
     8     }
     9     while (p) {
    10         i++;
    11         printf("第%d个节点的数据data为=%d
    ",i,p->data);
    12         p = p->next;
    13     }
    14     return 1;
    15 }
    复制代码

    (六)获取指定位置处的节点元素;

    复制代码
     1 int getElem(linkList *L, int i, ElemType *getdata) {
     2     linkList p;
     3     p = (*L)->next;
     4     if (p == NULL)
     5     {
     6         printf("链表为空,请创建一个链表
    ");
     7         *getdata = -1;
     8         return 0;
     9     }
    10     if (i < 1)
    11     {
    12         printf("您所查询的节点%d,应该大于0,请重新输入查询
    ",i);
    13         *getdata = -1;
    14         return 0;
    15     }
    16     int j = 1;
    17     while (p&&j<i) {
    18         j++;
    19         p = p->next;
    20     }
    21     if (p == NULL)
    22     {
    23         printf("您所查询的节点%d,已经超出了数组的长度
    ",i);
    24         *getdata = -1;
    25         return 0;
    26     }
    27     *getdata = p->data;
    28     printf("查询成功!
    ", i);
    29     return 1;
    30 }
    复制代码

    (七)插入节点;

    插入节点分为两种,一种是在链表的长度范围内插入节点,第二种是在链表的尾部追加一个节点;

    假设链表的长度为10,第一种可以在1-10位置处插入节点,比如在第10个位置插入一个节点,则原先的第10节点变为了11节点,但是第二种是所有节点位置都不变,在第11个位置追加一个新的节点;

    复制代码
     1 int insertList(linkList *L, int i, ElemType data)
     2 {
     3     linkList p;
     4     linkList insNode;
     5     p = (*L);
     6     int j=0;
     7     // 链表为空,在第1个位置插入一个新的节点;
     8     if (p ->next == NULL) {
     9         printf("链表为空,默认在第一个位置插入一个节点.
    ");
    10         insNode = (linkList)malloc(sizeof(Node));
    11         insNode->data = data;
    12         insNode->next = p->next;
    13         p->next = insNode;
    14         printf("节点插入成功.
    ");
    15         return 1;
    16     }
    17     // 链表非空的情况下,可以在i=1~length的位置插入节点,如果超过了链表的长度,就会提示错误;
    18     // 其实如果在length+1的位置处插入一个新节点,就相当于在尾部追加一个节点,在本函数中会报错,可以单独实现一个函数;
    19     while(p && j<i-1) 
    20     {
    21         j++;
    22         p = p->next;
    23         //printf("j=%d	p->data=%d
    ", j, p->data);
    24     }
    25     if (p->next==NULL) {
    26         printf("您要插入的位置,超过了链表的长度 %d,请重新操作!
    ",j);
    27         return 0;
    28     }
    29     insNode = (linkList)malloc(sizeof(Node));
    30     insNode->data = data;
    31     insNode->next = p->next;
    32     p->next = insNode;
    33     
    34     printf("节点插入成功
    ");
    35     return 1;
    36 }
    复制代码

     追加节点;

    复制代码
     1 int insertListTail(linkList *L, ElemType data)
     2 {
     3     linkList temp;
     4     linkList p=(*L);
     5     while(p) {
     6         temp = p; 
     7         p = p->next;
     8     }
     9     p = (linkList)malloc(sizeof(Node));
    10     p->data = data;
    11     p->next = NULL;
    12     temp->next = p;
    13     printf("节点插入成功
    ");
    14     return 1;
    15 }
    复制代码

    (八)删除节点;

    复制代码
     1 int deleteList(linkList *L, int i, ElemType *data)
     2 {
     3     linkList p,pnext;
     4     int j = 0;
     5     p = (*L);
     6     if (p->next == NULL) {
     7         printf("链表为空,无法删除指定节点.
    ");
     8         *data = -1;
     9         return 0;
    10     }
    11     while (p->next && j<i-1) {
    12         j++;
    13         p = p->next;
    14         //printf("j=%d	 p->data=%d
    ",j,p->data);
    15     }//条件最多定位到最后一个节点;
    16     if ( p->next == NULL) {
    17         printf("您要删除的节点,超过了链表的长度 %d,请重新操作!
    ", j);
    18         *data = -1;
    19         return 0;
    20     }
    21     pnext = p->next;
    22     p->next = pnext->next;
    23     *data = pnext->data;
    24     free(pnext);
    25     printf("节点删除成功
    ");
    26     return 1;
    27 }
    复制代码

    (九)删除这个链表;

    复制代码
     1 int clearList(linkList *L) {
     2     linkList p, temp;
     3     p = (*L)->next;//p指向第一个节点
     4     while (p) {
     5         temp = p;
     6         p = p->next;
     7         free(temp);
     8     }
     9     (*L)->next = NULL;
    10     printf("整个链表已经clear.
    ");
    11     return 1;
    12 }
    复制代码
  • 相关阅读:
    Android开发之Sqlite的使用
    ZOJ 3607 Lazier Salesgirl
    ZOJ 3769 Diablo III
    ZOJ 2856 Happy Life
    Ural 1119 Metro
    Ural 1146 Maximum Sum
    HDU 1003 Max Sum
    HDU 1160 FatMouse's Speed
    Ural 1073 Square Country
    Ural 1260 Nudnik Photographer
  • 原文地址:https://www.cnblogs.com/yundong333/p/11030926.html
Copyright © 2011-2022 走看看