Chapter 3 linear table

Chapter 3 linear table

3.2 Definition of the linear form

Finite sequence of zero or more data elements.

If linear table marked (A1, ..., A I-. 1 , A I , A I +. 1 , ..., A n- ), the table A I. 1- ahead A I , A said I- 1 is a I immediate predecessor elements, a I + 1 is a I directly subsequent element. When i = 1,2, ...,. 1-time n-, A I and only one immediate successor, when l, 2,3 = I, ..., n-, A I and only one direct precursor elements.

Linear table element number n (> = 0) is defined as the length of the linear table, when n = 0, the called empty.

Complex linear table, a data element may consist of a number of data items.

3.2 linear table abstract data types

Linear table abstract data type definitions:

ADT 线性表
Data
    线性表的数据对象集合为{a1,a2,...,an},每个元素的类型均为DataTye。其中,除第一个元素a1外,每一个元素有且只有一个直接前驱元素,除最后一个元素an外,每一个元素有且只有一个直接后继元素。数据元素之间的关系时一对一的关系。
Operation
    InitList(*L):   初始化操作,建立一个空的线性表L。
    ListEmpty(L):   如线性表为空,返回true,否则返回false。
    ClearList(*L):  将线性表清空。
    GetElem(L,i,*e):将线性表L中第i个位置的元素值返回给e。
    LocateElem(L,e):在线性表中查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号表示成功;否则
                    返回0表示失败。
    ListInsert(*L,i,e):在线性表中第i个位置插入元素e。
    ListDelete(*L,i,*e):删除线性表中第i个位置元素,并用e返回其值。
    ListLength(L):  返回线性表L的元素个数。
endADT

Basic operations different for different applications, the linear form, the above-mentioned basic operation, for more complex operations on the linear form of practical problems involved, can use a combination of these basic operations to achieve.

The two elements of the same type linear tables A and B are merged into A:

void unionL(List *La, List b)
{
    int La_len, Lb_len, i;
    ElemType e;                 //声明与La和Lb相同的数据元素e  
    La_len = ListLength(*La);   //求线性表的长度
    Lb_len  = ListLength(Lb);
    for ( i = 1; i <= Lb_len; i++)
    {
        GetElem(Lb, i, &e);     //取Lb中第i个数据元素赋值给e
        if(!LocateElem(*La, e)) //La中不存在和e相同数据元素
            ListInsert(La, ++La_len, e);    //插入
    }
}

3.4 linear form of sequential storage structure

3.4.1 defined sequential storage

Sequential storage structure of linear form, refers to the use of addresses are sequentially contiguous memory locations storing the data elements in the linear form.

3.4.2 sequential storage

One-dimensional array implemented in C language sequential storage structure

#define MAXSIZE 20  //存储空间初始化分配量
typedef int ElemType;   //ElemType类型根据实际情况而定,这里假设为int
typedef struct
{
    ElemType data[MAXSIZE]; //数组存储数据元素,最大值为MAXSIZE
    int length; //线性表当前长度
}SqList;

The difference between the length of the linear array length 3.4.2 Table

Length of the array is a linear memory space to store the length of the table, the memory allocation is generally the same amount.

Table linear element length is the number of data stored in a linear form.

3.4.3 address calculation method

Memory address: a memory in each memory cell has its own number, this number is called address.

C is assumed that each data element occupies storage locations, the storage location of the i + 1 data elements and a storage position of the i-th data element satisfies the following relationship (LOC function retrieves stored position)

LOC (a i + 1 ) = LOC (a i ) + c

LOC (a i ) = LOC (a 1 ) + (i-1) * c

Insertion and deletion 3.5 Sequence structure

3.5.1 Gets the element operation

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;;
/*Status 是函数的类型,其值是函数结果状态码,如OK等。
*初始条件:顺序线性表L已存在,1<=i<=ListLength(L)
*操作结果:用e返回L中第i个数据元素的值。
*/
Status GetEleme(SqList L, int i, ElemType *e)
{
    if (L.length == 0 || i < 1 || i > L.length
        return ERROR;
    *e = L.data[i-1];
    return OK;
}

3.5.2 insert

Insertion algorithm ideas

  • If the insertion position is unreasonable, an exception is thrown

  • If the linear length of the array is greater than the length of the table, an exception is thrown or dynamically increased capacity.

  • The last element from traversing forward to the i-th position, respectively, they will move back one position.

  • The element to be inserted into the insertion position i.

  • Table 1 plus length.

/*
*初始条件:顺序线性表L已经存在,1<=i<=ListLength(L)
*操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1。
*/
Status ListInsert(SqList *L, int i, ElemType e)
{
    int k;
    if (L->length == MAXSIZE)   //顺序线性表已满
        return ERROR;
    if (i < 1 || i > L->length + 1) //i不在范围
        return ERROR;
    if (i <= L->length)
    {
        for ( k = L->length - 1; k >= i - 1; k++)
            L->data[k+1] = L->data[k];
    }
    L->data[i-1] = e;
    L->length++;
    return OK;
}

3.5.3 delete operation

The idea of ​​deletion algorithm

  • If you delete an unreasonable position, throw an exception.
  • Remove the delete data elements.
  • Delete data elements from position to position traversing the last data element, respectively, they will move forward one position.
  • Table length minus 1.

Implementation code:

/*
*初始条件:顺序线性表L已经存在,1<=i<=ListenLength(L)
*操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1
*/
Status ListDelete(SqList *L, int i, ElemType *e)
{
    int k;
     if (0 == L->length) //线性表为空
         return ERROR;
     if (i < 1 || i > L->Length)    //删除位置不正确
         return ERROR;
    *e = L->data[i-1];
    if (i<L->length)
    {
        for (k=i; k<L->length;k++)
            L->data[k-1] = L->data[k];
    }
    L->length--;
    return OK;
}

3.5.4 the advantages and disadvantages of the first sequence table

advantage Shortcoming
No need to express logical relationships between elements in the table and additional storage space. Insert and delete operations need to move a large number of elements.
A table element position may be faster read access to any. When the linear table length vary widely, it is difficult to determine the capacity of the storage space.
Resulting storage space "debris."

3.6 Storage Structure of the linear form

3.6.1 lack of sequential storage structure solution

Xu Shu shortcomings insert and delete storage structures need to move large amounts of data elements, the solution:

All data elements are not considered adjacent to the location, where it is stored in the space where the current data element to know the location of the next data element.

Insert: i position when inserted, the i-2 of the next element to the new element inserted element, the new element points to the location of the original inserted i-1 elements.

Delete: delete when position i, i-1 position of the removing elements, and i-2 to point to the next element's position i the elements.

Storage Structure 3.6.2 linear table defined

In order to represent each data element A I and a subsequent data element is A . 1 I + logical relationship between, on the data element A I , in addition to storing the data elements itself, but also need to store an indication of its direct successor elements information. The data field stores information elements called a data field, the field immediately subsequent storage elements called pointer field. Pointer information stored in the domain called the pointer or chain. Two parts information constituent data elements A I stored images, referred to as the node (Node).

n nodes (ai memory map) as a chain link, namely linear table (A . 1 , A 2 , ..., A n ) chain storage structure, each node in this list because the only It contains a pointer field, so called single chain.

Figure 3-6-2

The first memory location in a node is called a head pointer list.

Finally, a linear list node pointer is "empty."

Figure 3-6-3

In the first linked list node attached to a front node, called the head node

Figure 3-6-4

Similarities and Differences 3.6.3 head pointer to the head node

Head pointer The head node
It refers to the list head pointer a pointer to the first node when the first node if there is a linked list, the first node is a pointer to a pointer. To operate the first node, before the element in the first node, which is generally meaningless data field and facilitate uniform established (the length of the list may also be stored).
It has a head pointer identifies the role, so commonly known as head pointer list of names. With the head node, the operation of the insert and delete nodes the first node before the first element node, its operations and other nodes on unified.
Whether the list is empty, the head pointer is not empty. When necessary elements of the list head pointer. When the head node does not necessarily have to list the elements.

Table Storage Structure 3.6.4 Linear Code Description

Single linked list, the structure can be described by a pointer in the C language.

/*
*线性表的单链表存储结构
*/
typedef struct Node
{
    ElemType data;
    struct Node *next;
} Node;
typedef struct Node *LinkList;

Node data field consists of the data storage element and the successor node storing the address pointer field.

Relationship between the two data elements list below:

Figure 3-6-10

3.7 single reading list

Acquisition algorithm i thought the list of data elements:

  1. Declare a pointer p pointing to a linked list of the first node initializing j from the beginning.
  2. 当j<i时,就遍历链表,让p指针的位置向后移动,不断指向下一个结点,j累加1;
  3. 若链表末尾p为空,则说明第i个结点不存在;
  4. 否则查找成功。
/*
*初始条件:顺序线性表L已经存在,1<=i<=ListLength(L);
*操作结果:用e返回L中第i个数据元素的值。
*/
Status GetElem(LinkList L, int i, ElemType *e)
{
    int j;
    LinkList p;
    p = L->next;
    j = 1;
    while (p && j < 1)
    {
        p = p->next;
        ++j;
    }
    if ( !p || j > 1)
        return ERROR;
    *e = p->data;
    return OK;
}

注:

链表带头结点,不存储任何数据,指向第一个结点。

3.8 单链表的插入与删除

3.8.1 单链表的插入

假设存储元素e的结点为s,将s插入到结点p和p->next的操作:

Figure 3-8-1

s->next = p->next;
p->next = s;

插入后的链表结构

Figure 3-8-3

单链表第i个数据插入结点的算法思路:

  1. 声明一指针p指向链表的头结点,初始化j从1开始。

  2. 当j<i时,遍历链表,让p的指针向后移动,不断指向下一个结点,j累加1。

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

  4. 否则查找成功,在系统生成一个空结点s;

  5. 将数据元素e赋值给s->data;

  6. 单链表插入的标准语句

    s->next = p->next;
    p->next = s;
  7. 返回成功。

实现算法如下:

/*
* 初始条件:顺序线性表L已存在,1<=i<=ListLength(L)
* 操作结果:在L中第i个结点位置之前插入新的数据元素e,L的长度加1。
*/
Status ListInsert(LinkList *L, int i, ElemType e)
{
    int j;
    LinkList p, s;
    p = *L;
    j = 1;
    while (p && j < i)                  //寻找i-1个结点
    {
        p = p->next;
        ++j;
    }
    if(!p || j > i)                     //第i个结点不存在
        return ERROR;
    s = (LinkList)malloc(sizeof(Node)); //生成新的结点(C标准函数)
    s->data = e;
    s->next = p->next;                  //将p的后继结点赋值给s的后继
    p->next = s;                        //将s赋值给p的后继
    return OK;
}

3.8.2 单链表的删除

Figure 3-8-5

删除结点q,需获取到p结点的指针,将前继结点p的后继结点指向q的后继结点,然后删除p。

q = p->next;
p->next = q->next;

单链表删除第i个结点的算法思路:

  1. 声明一指针p指向链表头指针,初始化j从1开始;
  2. 当j<1时,就遍历链表,让p的指针向后移动,不断指向下一个结点,j累加1;
  3. 若到链表末尾p为空,则说明第i个结点不存在;
  4. 否则查找成功,将欲删除的结点p->next赋值给q;
  5. 单链表的删除标准语句p->next=q->next;
  6. 将q结点中的数据赋值给e,作为返回;
  7. 释放q结点;
  8. 返回成功。

实现代码算法如下:

/*
* 初始条件:顺序线性表L已存在,1<=i<=ListLength(L)
* 操作结果:删除L的第i个结点,并用e返回其值,L的长度减1。
*/
Status ListDelete(LinkList *L, int i, ElemType e)
{
    int j;
    LinkList p, q;
    p = *L;
    j = 1;
    while (p->next && j < 1)            //遍历寻找第i个结点
    {
        p = p->next;
        ++j;
    }
    if ( ! (p->next) || j > i)          //第i个结点不存在
        return ERROR;
    q = p->next;
    p->next = q->next;                  //将q的后继赋值给p的后继
    *e = q->data;                       //将q结点的数据赋值给e
    free(q);                            //释放q结点
    return OK;
}

对于插入或删除数据越频繁的操作,单链表的效率越明显。

3.9 单链表的整表创建

单链表整表创建的算法思路:

  1. 声明一指针p和计数器变量i;
  2. 初始化一空链表L;
  3. 让L的头结点的指针指向NULL,即建立一个带头结点的单链表;
  4. 循环:
    • 生成一新结点赋值给p;
    • 随机生成一数字赋值给p的数据域p->data;
    • 将p插入到头结点和与前一新结点之间。

算法代码实现如下:

/*
* 随机产生n个元素的值,建立带表头结点的单链线性表L(头插法)
*/
void CreateListHead(LinkList *L, int n)
{
    LinkList p;
    int i;
    srand(time(0));                         //初始化随机数种子
    *L = (LinkList)malloc(sizeof(Node));
    (*L)->next = NULL;                      //先建立一个带头结点的单链表
    for(i=0; i<n; i++)
    {
        p = (LinkList)malloc(sizeof(Node)); //生成新的结点
        p->data = rand()%100 + 1;           //随机生成100以内的数字
        p->next = (*L)->next;
        (*L)->next = p;                     //插入到表头
    }
}

头插法:始终让新结点在第一的位置。

Figure 3-9-1

尾插法:每次都将新的结点插入到终端结点的后面。

/*
* 随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法)
*/
void CreateListTail(Linst *L, int n)
{
    LinkList p, r;
    int i;
    srand(time(0));                         //初始化随机数种子
    *L = (LinkList)malloc(sizeof(Node));    
    r = *L;                                 //r指向尾部的结点
    for(i=0; i<n; i++)
    {
        p = (Node*)malloc(sizeof(Node));
        p->data = rand()%100 + 1;
        r->next = p;
        r = p;                              //将当前的新结点定义为表尾终端结点
    }
    r->next = NULL;
}

3.10 单链表的整表删除

单链表整表删除的算法思路如下:

  1. 声明一个结点p和q;
  2. 将第一个结点赋值给p;
  3. 循环:
    • 将下一个结点赋值给q;
    • 释放p;
    • 将q赋值给p;

实现代码如下:

/*
* 初始条件:顺序线性表L已存在,操作结果:将L重置为空表
*/
Status ClearList(LinkList *L)
{
    LinkList p, q;
    p = (*L)->next;             //p指向第一个结点
    while(p)                    //没到表尾
    {
        q = p->next;
        free(p);
        p = q;
    }
    (*L)->next = NULL;          //头结点指针域为空
    return OK;
}

3.11 单链表结构与顺序存储结构优缺点

Chain store and order of storing the advantages and disadvantages

3.12 静态链表

静态链表:数组的元素由两个数据域组成data和cur,data存放数据元素,cur存放该数据元素后继数据元素在数组中的下标,cur叫做游标。这种用数组描述的链表叫静态链表。

/*
* 线性表的静态链表存储结构
*/
#define MAXSIZE 1000        //假设链表的最大长度为1000
typedef struct
{
    ElemType data;
    int cur;                //游标,为0时表示无指向
} Component, StaticLinkList[MAXSIZE];

链表第一个和最后一个元素作为特殊元素处理,不存数据。通常把未使用的数据元素称为备用链表。而数组第一个元素,即下标为0的元素cur存放 备用链表的第一个结点的下标。而数组最后一个元素的cur则存放第一个有数值元素的下标。

静态链表存储如下:

Figure 3-12-1

/*
* 将一维数组space中各分量链成一条备用链。
* space[0].cur为头指针,"0"表示空指针
*/

Status InitList(StaticLinkList space)
{
    int i;
    for (i=0; i<MAXSIZE-1; i++)
        space[i].cur = i+1;
    space[MAXSIZE-1].cur = 0;           //目前静态链表为空,最后一个元素的cur为0
    return OK;
}

假设我们已将数据“甲“、”乙“、“丙”、”丁“、”戊“、”己“、庚”数据存入静态链表,则他们的状态,如下图:

Figure 3-12-2

3.12.1 静态链表的插入操作

分配可用结点:

/*
* 若备用空间链表非空,则返回分配的结点下标,否则返回0
*/
int Malloc_SLL(StaticLinkList space)
{
    int i = space[0].cur;   //当前数组第一个元素的cur寸的值,就是要返回的第一个备用空闲的下标
    if (space[0].cur)
        space[0].cur = space[i].cur;//由于要拿出一个分量来使用了,所以就需要把下一个分量做备用。
    return i;
                                        
}

插入结点

/*
*   在L中第i个元素之前插入新的数据元素e
*/
  1 Status LinstInsert(StaticLinkList L, int i, ElemType e)
  2 {
  3     int j, k, i;
  4     k = MAX_SIZE - 1;
  5     if (i < 1 || i > ListLength(L) + 1)
  6         return ERROR;
  7     j = Malloc_SLL(L);          //获得空闲分量下标
  8     if (j)
  9     {
 10         L[j].data = e;          //将数据值赋值给此分量的data
 11         for(l = 1; l <= i - 1; l++)
 12             k = L[k].cur;   
 13         L[j].cur = L[k].cur;//把第i个元素之前的cur赋值给新的元素cur
 14         L[k].cur = j;           //把新元素的下标赋值给第i个元素之前的cur
 15         return OK;
 16     }
 17     return ERROR;
 18 }

以下图静态链表中在"乙"和"丁"之间插入"丙":

Figure 3-12-2

代码解释:

  • 当我们执行插入语句时,我们的目的时在“乙”和"丁"之间插入"丙"。调用代码时i=3。

  • 第4行让k=MAX_SIZE - 1 = 999。

  • 第7行,j = Malloc_SSL(L) = 7。此时下标0的cur也因为7要被占用而改备用链表值为8。

  • 第11~12行,for循环1由1到2,执行2次。

    第一次:k=L[999].cur=1

    第二次:k=L[1].cur=2。

  • 第13行,L[j].cur = L[k].cur;因为j=7,而k=2得到L[7].cur=L[2].cur=3。这就让丙的cur改为3。

  • 第14行,L[k].cur = j;意思就是L[2].cur=7。让乙的cur指向丙的下标7。

实际插入后的效果:

Figure 3-12-3

3.12.2 静态链表的删除操作

删除元素的算法:

/*
* 删除L中第i个元素e
*/
Status ListDelete(StaticLinkList L, int i)
{
    int j, k;
    if (i < 1 || i > ListLength(L))
        return ERROR;
    k = MAX_SIZE - 1;
    for (j = 1; j <= i - 1; j++)
        k = L[k].cur;
    j = L[k].cur;
    L[k].cur = L[j].cur;
    Free_SSL(L,j);
    return OK;
}

释放空闲结点:

/*
* 将下标为k的空闲节点回收到备用链表
*/
void Free_SSL(StaticLinkList space, int k)
{
    space[k].cur = space[0].cur;        //把第一个元素cur值赋给要删除的分量cur
    space[0].cur = k;                   //把要删除的分量下标赋值给第一个元素的cur
}

删除甲之后的静态链表:

Figure 3-12-4

获取静态链表长度

/*
* 初始条件:静态链表L已存在。
* 操作结果:返回L中数据元素的个数。
*/
int ListLength(StaticLinkList L)
{
    int j = 0;
    int i = L[MAXSIZE - 1].cur;
    while(i)
    {
        i = L[i].cur;
        j++;
    }
    return j;
}

3.12.3 静态链表优缺点

优点 缺点
When the insertion and deletion operations, only need to modify the cursor without moving elements, thereby improving the disadvantage of sequential storage structure insertion and deletion operations need to move a large number of elements. Does not solve the problem of continuous memory allocation brings to the table a long difficult to determine.
It lost the characteristics of the random read sequential storage structure.

3.13 circular list

End by a single linked list pointer null pointer to point to the terminating node of the first node, so that the entire list to form a ring, this is called a single linked list to-tail chain single cycle, referred circular list.

3.14 doubly linked list

Doubly linked list (double linked list) is a single list of each node, and then setting a pointer field pointing to its precursor nodes.

/*
* 线性表的双向链表存储结构
*/
typedef struct DulNode
{
    ElemType data;
    struct DulNode *prior;      //指向前驱指针
    struct DulNode *next;       //指向后驱指针
} DulNode, *DuLinkList;

Doubly linked list elements into

Figure 3-14-5

s->prior = p;
s->next = p->next;
p->next->prior = s;
p->next = s;

Delete nodes p:

Figure 3-14-6

p->prior->next = p->next;
p->next->prior = p->prior;
free(p);

Guess you like

Origin www.cnblogs.com/spring-1991/p/11922200.html