zoukankan      html  css  js  c++  java
  • 数据结构学习笔记01链表

    一.单向链表:

    由于线性表的顺序存储结构(数组),在插入和删除需要移动大量元素以及其他特殊情况下,效率低下,耗时费力,此处引入了链表的概念。

    链表的基本组成单元为结点(node),包括两个域:存储数据元素信息的域->数据域;存储直接后继存储位置的域(指针)->指针域。

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <time.h>
      4 #define OK 1
      5 #define ERROR 0
      6 
      7 
      8 typedef int Status;/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
      9 typedef int ElemType;/* ElemType类型根据实际情况而定,这里假设为int */
     10 
     11 typedef struct Node
     12 {
     13     ElemType data;
     14     struct Node *next;
     15 }Node;
     16 typedef struct Node *LinkList; //定义LinkList
     17 
     18 Status InitList(LinkList *L);
     19 Status Destroy(LinkList L);
     20 Status ClearList(LinkList *L);
     21 bool ListEmpty(LinkList L); 
     22 int ListLength(LinkList L);
     23 Status GetElem(LinkList L,int i,ElemType *e);
     24 int LocateElem(LinkList L,ElemType e);
     25 Status PriorElem(LinkList *L, ElemType cur_e, LinkList *pre_e);
     26 Status NextElem(LinkList *L, ElemType cur_e, LinkList *next_e);
     27 Status ListInsert(LinkList *L,int i,ElemType e);
     28 Status ListDelete(LinkList *L,int i,ElemType *e);
     29 Status ListTraverse(LinkList L);
     30 void CreateListHead(LinkList *L, int n);
     31 void CreateListTail(LinkList *L, int n);
     32 
     33 Status visit(ElemType c)
     34 {
     35     printf("%d ",c);
     36     return OK;
     37 }
     38 
     39 /*初始化顺序线性表: 构造一个空的线性表L*/
     40 Status InitList(LinkList *L) 
     41 { 
     42     *L = (LinkList)malloc(sizeof(Node)); /* 产生头结点,并使L指向此头结点,L为头指针 */
     43     if(!(*L)) /* 存储分配失败 */
     44             return ERROR;
     45     (*L)->next = NULL; /* 指针域为空 */
     46 
     47     return OK;
     48 }
     49 /*初始条件:线性表L已存在。操作结果:销毁线性表L*/
     50 Status Destroy(LinkList L)
     51 {
     52     ClearList(&L);
     53     free(L);
     54     L = NULL;
     55 }
     56 
     57 /* 初始条件:顺序线性表L已存在。操作结果:将L重置为空表 */
     58 Status ClearList(LinkList *L)
     59 { 
     60     LinkList p,q;
     61     p=(*L)->next;           /*  p指向第一个结点 */
     62     while(p)                /*  没到表尾 */
     63     {
     64         q=p->next;
     65         free(p);
     66         p=q;
     67     }
     68     (*L)->next = NULL;        /* 头结点指针域为空 */
     69     return OK;
     70 }
     71 
     72 /* 初始条件:顺序线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */
     73 bool ListEmpty(LinkList L)
     74 { 
     75     if(L->next)
     76         return false;
     77     else
     78         return true;
     79 }
     80 
     81 /* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */
     82 int ListLength(LinkList L)
     83 {
     84     int i = 0;
     85     LinkList p=L->next; /* p指向第一个结点 */
     86     while(p)                        
     87     {
     88         i++;
     89         p=p->next;
     90     }
     91     return i;
     92 }
     93 
     94 /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) 
     95 操作结果:用e返回L中第i个数据元素的值 */
     96 Status GetElem(LinkList L,int i,ElemType *e)
     97 {
     98     int j = 1;             /* j为计数器 */
     99     LinkList p;             /* 声明一结点p */
    100     p = L->next;         /* 让p指向链表L的第一个结点 */          
    101     while (p && j < i) {   /* p不为空或者计数器j还没有等于i时,循环继续 */  
    102         p = p->next;       /* 让p指向下一个结点 */
    103         j++;
    104     }
    105     if ( !p || j>i ) 
    106         return ERROR;       /*  第i个元素不存在 */
    107     *e = p->data;        /*取第i个元素的数据 */
    108     return OK;
    109 }
    110 
    111 /* 初始条件:顺序线性表L已存在 
    112 操作结果:返回L中第1个与e满足关系的数据元素的位序。
    113 若这样的数据元素不存在,则返回值为0 */
    114 int LocateElem(LinkList L,ElemType e)
    115 {
    116     int i=0;
    117     LinkList p=L->next;
    118     while(p)
    119     {
    120         i++;
    121         if(p->data==e) /* 找到这样的数据元素 */
    122                 return i;
    123         p=p->next;
    124     }
    125 
    126     return 0;
    127 }
    128 
    129 /*初始条件:线性表L已存在
    130 操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,
    131 否则操作失败,pre_e无定义*/
    132 Status PriorElem(LinkList *L, ElemType cur_e, LinkList *pre_e)
    133 {
    134     LinkList p;
    135     p = (*L)->next;
    136     if(p->data == cur_e) {        /* 是第一个 */
    137         pre_e = NULL;
    138         return ERROR;
    139     }
    140     while(p->next)
    141     {
    142         if(p->next->data == cur_e) {
    143             *pre_e = p;
    144             return OK;
    145         }
    146         p = p->next;
    147     }
    148     pre_e = NULL;
    149     return ERROR;
    150 }
    151 
    152 /*初始条件:线性表L已存在
    153 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后驱,
    154 否则操作失败,next_e无定义*/
    155 Status NextElem(LinkList *L, ElemType cur_e, LinkList *next_e)
    156 {
    157     LinkList p;
    158     p = (*L);
    159     while(p->next) 
    160     {
    161         if(p->data == cur_e) {
    162             *next_e = p->next;
    163             return OK;
    164         }
    165         p = p->next;
    166     } 
    167     *next_e = NULL;
    168     return ERROR;
    169 }
    170 
    171 /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) + 1,
    172 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
    173 Status ListInsert(LinkList *L,int i,ElemType e)
    174 { 
    175     int j = 1;
    176     LinkList p,s;
    177     p = *L;  
    178     while (p && j < i) {    /* 寻找第i个结点 */
    179         p = p->next;
    180         ++j;
    181     } 
    182     if (!p || j > i) 
    183         return ERROR;   /* 第i个元素不存在 */
    184     s = (LinkList)malloc(sizeof(Node));  /*  生成新结点(C语言标准函数) */
    185     s->data = e;  
    186     s->next = p->next;      /* 将p的后继结点赋值给s的后继  */
    187     p->next = s;          /* 将s赋值给p的后继 */
    188     return OK;
    189 }
    190 
    191 /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L)
    192 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */
    193 Status ListDelete(LinkList *L,int i,ElemType *e) 
    194 { 
    195     int j;
    196     LinkList p,q;
    197     p = *L;
    198     j = 1;
    199     while (p->next && j < i) {    /* 遍历寻找第i个元素 */
    200         p = p->next;
    201         ++j;
    202     }
    203     if (!(p->next) || j > i) 
    204         return ERROR;           /* 第i个元素不存在 */
    205     q = p->next;
    206     p->next = q->next;            /* 将q的后继赋值给p的后继 */
    207     *e = q->data;               /* 将q结点中的数据给e */
    208     free(q);                    /* 让系统回收此结点,释放内存 */
    209     return OK;
    210 }
    211 
    212 /* 初始条件:顺序线性表L已存在
    213 操作结果:依次对L的每个数据元素输出 */
    214 Status ListTraverse(LinkList L)
    215 {
    216     LinkList p=L->next;
    217     while(p)
    218     {
    219         visit(p->data);
    220         p=p->next;
    221     }
    222     printf("
    ");
    223     return OK;
    224 }
    225 
    226 /*  随机产生n个元素的值,建立带表头结点的单链线性表L(头插法) */
    227 void CreateListHead(LinkList *L, int n) 
    228 {
    229     LinkList p;
    230     int i;
    231     srand(time(0));                         /* 初始化随机数种子 */
    232     *L = (LinkList)malloc(sizeof(Node));
    233     (*L)->next = NULL;                      /*  先建立一个带头结点的单链表 */
    234     for (i=0; i<n; i++) 
    235     {
    236         p = (LinkList)malloc(sizeof(Node)); /*  生成新结点 */
    237         p->data = rand()%100+1;             /*  随机生成100以内的数字 */
    238         p->next = (*L)->next;    
    239         (*L)->next = p;                        /*  插入到表头 */
    240     }
    241 }
    242 
    243 /*建立带表头结点的单链线性表L(尾插法) */
    244 void CreateListTail(LinkList *L, int n) 
    245 {
    246     LinkList p,r;
    247     *L = (LinkList)malloc(sizeof(Node));     /* L为整个线性表 */
    248     r = *L;                              /* r为指向尾部的结点 */
    249     for (int i = 0; i < n; i++) {
    250         p = (Node *)malloc(sizeof(Node));      /*  生成新结点 */
    251         scanf("%d",p->data); 
    252         r->next = p;        /* 将表尾终端结点的指针指向新结点 */
    253         r = p;            /* 将当前的新结点定义为表尾终端结点 */
    254     }
    255     r->next = NULL;                       /* 表示当前链表结束 */
    256 }
    257 int main()
    258 {
    259     LinkList L, pre_e,next_e;    //创建一个可以指向结点的指针 
    260     CreateListTail(&L,10);
    261     ListInsert(&L,11,55); 
    262     ListTraverse(L);
    263     PriorElem(&L, 3, &pre_e);
    264     NextElem(&L, 3, &next_e);
    265     printf("前驱%d
    ",(*pre_e).data);
    266     printf("后继%d
    ",(*next_e).data);
    267     Destroy(L);
    268 
    269         
    270     return 0;
    271 } 
    sj1_0

    单向链表的基本结构:

    typedef struct Node

    {

        ElemType data;

        struct Node *next;

    }Node;

    typedef struct Node *LinkList; //定义LinkList

    单向链表的基本操作:/*只需浏览,作了解,详见(代码:sj1_0)*/

    Status InitList(LinkList *L);

           操作结果:构造一个空的线性表。

    Status Destroy(LinkList L);

           初始条件:线性表L已存在。

           操作结果:销毁线性表L

    Status ClearList(LinkList *L);

           初始条件:线性表L已存在。

        操作结果:将L重置为空表

    Status ListEmpty(LinkList L);      

           初始条件:线性表L已存在。

           操作结果:若L为空表,则返回TRUE,否则返回FALSE。

    int ListLength(LinkList L);

        初始条件:线性表L已存在。

        作结果:返回L中数据元素个数。

    Status GetElem(LinkList L, int i, ElemType *e);

           初始条件:顺序线性表L已存在,1≤i≤ListLength(L)。

           操作结果:用e返回L中第i个数据元素的值。

    int LocateElem(LinkList L, ElemType e);

           初始条件:顺序线性表L已存在。

           操作结果:返回L中第1个与e满足关系的数据元素的位序。

    若这样的数据元素不存在,则返回值为0。

    Status PriorElem(LinkList *L, ElemType cur_e, LinkList *pre_e);

           初始条件:线性表L已存在

        操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,

        否则操作失败,pre_e无定义。

    Status NextElem(LinkList *L, ElemType cur_e, LinkList *next_e);

           初始条件:线性表L已存在

        操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后驱,

        否则操作失败,next_e无定义。

    Status ListInsert(LinkList *L, int i, ElemType e);

           初始条件:顺序线性表L已存在,1 ≤i ≤ ListLength(L) + 1。

        操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1。

    Status ListDelete(LinkList *L, int i, ElemType *e);

           初始条件:顺序线性表L已存在,1≤i≤ListLength(L)。

        操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1。

    Status ListTraverse(LinkList L);

           初始条件:顺序线性表L已存在。

        操作结果:依次对L的每个数据元素输出。

    void CreateListHead(LinkList *L, int n);

           操作结果:立带表头结点的单链线性表L(头插法)。

    void CreateListTail(LinkList *L, int n);

           操作结果:建立带表头结点的单链线性表L(尾插法)。

    1.单链表的读取

      算法思路:

      1.声明一个指正p指向链表的第一个结点,初始化j = 1。

      2.当j<i时,遍历链表,p=p->next; j++;

      3.若链表末尾p为空,则说明第i个结点不存在。

      4.否则查找成功,返回结点p的数据。

     1 /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) 
     2 操作结果:用e返回L中第i个数据元素的值 */
     3 Status GetElem(LinkList L,int i,ElemType *e)
     4 {
     5     int j = 1;             /* j为计数器 */
     6     LinkList p;             /* 声明一结点p */
     7     p = L->next;         /* 让p指向链表L的第一个结点 */          
     8     while (p && j < i) {   /* p不为空或者计数器j还没有等于i时,循环继续 */  
     9         p = p->next;       /* 让p指向下一个结点 */
    10         j++;
    11     }
    12     if ( !p || j>i ) 
    13         return ERROR;       /*  第i个元素不存在 */
    14     *e = p->data;        /*取第i个元素的数据 */
    15     return OK;
    16 }
    View Code

    2.单链表的插入

      算法思路:

      1.声明一个指针p指向链表头结点,向后遍历p=p->next,直到满足条件。

      2.新建一个结点s。

      3.s->next = p->next ①

      4.p->next = s       ②③

     1 /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) + 1,
     2 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
     3 Status ListInsert(LinkList *L,int i,ElemType e)
     4 { 
     5     int j = 1;
     6     LinkList p,s;
     7     p = *L;  
     8     while (p && j < i) {    /* 寻找第i个结点 */
     9         p = p->next;
    10         ++j;
    11     } 
    12     if (!p || j > i) 
    13         return ERROR;       /* 第i个元素不存在 */
    14     s = (LinkList)malloc(sizeof(Node));  /*  生成新结点 */
    15     s->data = e;  
    16     s->next = p->next;      /* 将p的后继结点赋值给s的后继  */
    17     p->next = s;            /* 将s赋值给p的后继 */
    18     return OK;
    19 }
    View Code

    3.单链表的删除

      算法思路:

      1. 声明一个指针p指向链表头结点,向后遍历p=p->next,直到满足条件。

      2. q = p->next

      3. p->next = q->next  ①②

      4. free(q)                ③④

     1 /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L)
     2 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */
     3 Status ListDelete(LinkList *L,int i,ElemType *e) 
     4 { 
     5     int j;
     6     LinkList p,q;
     7     p = *L;
     8     j = 1;
     9     while (p->next && j < i) {    /* 遍历寻找第i个元素 */
    10         p = p->next;
    11         ++j;
    12     }
    13     if (!(p->next) || j > i) 
    14         return ERROR;           /* 第i个元素不存在 */
    15     q = p->next;
    16     p->next = q->next;            /* 将q的后继赋值给p的后继 */
    17     *e = q->data;               /* 将q结点中的数据给e */
    18     free(q);                    /* 让系统回收此结点,释放内存 */
    19     return OK;
    20 }
    View Code

    4.单链表的整体创建

      算法思路:

      1.创建指向头结点的头指针L,使r=L

      While(){

        2.新建结点p

        3.r->next = p

        4.r = p

      }如此往复

      5.最后一个结点指针指向NULL

     1 /*建立带表头结点的单链线性表L(尾插法) */
     2 void CreateListTail(LinkList *L, int n) 
     3 {
     4     LinkList p,r;
     5     *L = (LinkList)malloc(sizeof(Node));     /* L为整个线性表 */
     6     r = *L;                              /* r为指向尾部的结点 */
     7     for (int i = 0; i < n; i++) {
     8         p = (Node *)malloc(sizeof(Node));      /*  生成新结点 */
     9         scanf("%d",p->data); 
    10         r->next = p;        /* 将表尾终端结点的指针指向新结点 */
    11         r = p;            /* 将当前的新结点定义为表尾终端结点 */
    12     }
    13     r->next = NULL;                       /* 表示当前链表结束 */
    14 }
    View Code

    总体思路就是如此。理解模型后,其他操作原理相同。

    practice:

    1.已知线性表中的元素以值的递增有序排列,并以单链表做存储结构。试写一高效的算法,删除表中所有大于mink且小于maxk的元素(若表中存在这样德尔元素),同时释放被删结点空间,并分析你的算法复杂度(注意:mink 和 maxk 是给定的两个参变量,它们的值可以和表中元素相同,也可以不同)。代码(代码:sj1_1)

     1 //可以追溯到短学期写的
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 
     5 struct line
     6 {
     7     int num;
     8     struct line *next;
     9 };
    10 typedef struct line *LinkList;
    11 LinkList head;
    12 
    13 struct line *creat();
    14 struct line *cut(int min,int max);
    15 void display();
    16 int main()
    17 {
    18     int mink,maxk;
    19     creat();
    20     display();
    21     printf("Plead input mink & maxk:");
    22     scanf("%d %d",&mink,&maxk);
    23     cut(mink,maxk);
    24     display();
    25     return 0;
    26 } 
    27 
    28 struct line *creat()
    29 {
    30     LinkList p1, p2;
    31     int count;
    32     printf("Please input the number of JieDian:");
    33     scanf("%d",&count);
    34     p1 = p2 =(struct line *)malloc(sizeof(struct line));
    35     scanf("%d",&p1->num);
    36     head = p1;
    37     for(int i = 1;i < count;i++)
    38     {
    39         p2 =(struct line *)malloc(sizeof(struct line));
    40         scanf("%d",&p2->num);
    41         p1->next = p2;
    42         p1 = p2;
    43     }
    44     p2->next = NULL;
    45     return head;
    46 }
    47 struct line *cut(int min,int max)
    48 {
    49     LinkList p, q;
    50     p = head;
    51     while(p->next->num <= min)
    52         p = p->next;
    53     while(p->next->num > min && p->next->num < max)
    54     {
    55         q = p->next;
    56         p->next = q->next ;
    57         free(q);
    58     }
    59     q = NULL;    //    free(q)只是释放了q所指的内存空间。     q = NULL防止发生q为野指针 
    60     return head;
    61 }
    62 
    63 void display()
    64 {
    65     LinkList p;
    66     p = head;
    67     if(p == NULL)
    68         printf("NULL!");
    69     else
    70     {
    71         while(p != NULL)
    72         {
    73             printf("%d ",p->num);
    74             p = p->next; 
    75         }
    76     }
    77     printf("
    ");
    78 }
    sj1_1

    2.试写一算法,实现顺序表的就地置逆(不开辟新空间),即利用原表的存储空间将线性表(a1,a2,…,an)逆置为(an,an-1,…,a1).

    3.试写一算法,对单链表实现就地置逆。(代码:sj1_2)

     1 /*短学期写的。。。 
     2 单链表就地逆置(不开辟新空间) Change()
     3 头结点不存num数据*/
     4 #include <stdio.h>
     5 #include <stdlib.h>
     6 
     7 struct line
     8 {
     9     int num;
    10     struct line* next;
    11 };
    12 struct line *head;
    13 struct line *creat();
    14 void Change();
    15 void display();
    16 int main()
    17 {
    18     creat();
    19     printf("Original data:");
    20     display();
    21     Change();
    22     printf("Changed  data:");
    23     display();
    24 }
    25 
    26 struct line *creat()
    27 {
    28     struct line *p1,*p2;
    29     int count;
    30     printf("Please input the number of JieDian:");
    31     scanf("%d",&count);
    32     p1 = p2 = (struct line *)malloc(sizeof(struct line));
    33     p1->num = 0;
    34     head = p1;
    35     for(int i = 0;i < count;i++)
    36     {
    37         p2 = (struct line *)malloc(sizeof(struct line));
    38         scanf("%d",&p2->num);
    39         p1->next = p2;
    40         p1 = p2;
    41     }
    42     p2->next = NULL;
    43     return head;
    44 }
    45 void Change()
    46 {
    47     struct line *p,*q;
    48     p = head->next;
    49     head->next = NULL;
    50     while(p != NULL)
    51     {
    52         q = p->next;
    53         p->next = head->next;
    54         head->next = p;
    55         p = q;
    56     }
    57 }
    58 void display()
    59 {
    60     struct line *p;
    61     p = head->next;
    62     if(p == NULL)
    63         printf("NULL!");
    64     else
    65     {
    66         while(p != NULL)
    67         {
    68             printf("%d ",p->num);
    69             p = p->next; 
    70         }
    71     }
    72     printf("
    ");
    73 }
    sj1_2

    二.双向链表(做保留)

    三.循环链表(做保留)

  • 相关阅读:
    SpringMVC
    spring-02
    spring-01
    适配器模式
    状态模式
    抽象工厂模式
    观察者模式(发布-订阅模式)
    建造者模式(生成器模式)
    外观模式
    迪米特法则
  • 原文地址:https://www.cnblogs.com/kuotian/p/5302335.html
Copyright © 2011-2022 走看看