数据结构笔记-线性表

数据结构笔记

线性表

学习问题

  1. 线性表是什么
  2. 物理结构未顺序存储的线性表的优缺点
  3. 物理结构非顺序(链式)存储的线性表的优缺点
  4. 怎样实现相应存储结构上的基本运算

基本概念

线性表

性质相同的有限个(n)数据元素,n>=0。当n>0时,除第1个元素无直接前驱,最后1个元素无直接后继外,其余元素都有唯一的直接前驱和直接后继。

特点

  1. 有限性
  2. 有序性
  3. 同一性

抽象数据类型定义

ADT LinearList{

​ 数据元素:D =

​ 结构关系:

​ 基本操作:

  1. initList(L)

    操作前提:表L未初始化为线性表

    操作结果:将表L初始化为空表

  2. listLength(L)

    操作前提:表L已存在

    操作结果:若是空表返回0,否则返回表中元素个数

  3. getData(L,i)

    操作前提:表L已存在,且i大于等于1且i小于等于listLength(L)

    操作结果:返回表L中第i个位置的元素值

  4. readList(L)

    操作前提:表L已存在

    操作结果:依次读取表中每一个位置的元素值,并输出

  5. insList(L,pos,e)

    操作前提:表L已存在,且插入位置合理

    操作结果:第pos个位置被元素e取代,原第pos个及之后的元素整体后移一位

  6. delList(L,pos,e)

    操作前提:表L已存在,且删除位置合理

    操作结果:第pos+1及之后的元素整体前移一位

  7. updateList(L,pos,e)

    操作前提:表L已存在,且更新位置合理

    操作结果:第pos个位置的元素被替换为e

}

C语言实现-顺序存储

//
//  SeqList.h
//  Data structure
//
//  Created by 樊明杨 on 2020/2/22.
//  Copyright © 2020 樊明杨. All rights reserved.
//

#ifndef SeqList_h

    #define SeqList_h

    #include <stdio.h>
    #define MAX_SIZE 100

    struct SeqList{
            
            int list[MAX_SIZE];
            int last;//The index of the last data element in list.

    };

    void initList(struct SeqList *);

    int listLength(struct SeqList);

    int getData(struct SeqList,int);

    void readList(struct SeqList);

    void insList(struct SeqList *,int,int);

    void delList(struct SeqList *,int);

    void updateList(struct SeqList *,int,int);

#endif /* SeqList_h */

//
//  SeqList.c
//  Data structure
//
//  Created by 樊明杨 on 2020/2/22.
//  Copyright © 2020 樊明杨. All rights reserved.
//

#include "SeqList.h"

void initList(struct SeqList * seqList){
    seqList->last = -1;
    for (int i = 0; i < MAX_SIZE; i++)
    {
        insList(seqList,i + 1 ,0) ;
    }
    seqList->last = -1;
}

int listLength(struct SeqList seqList){
    return seqList.last + 1;
}

int getData(struct SeqList seqlist,int pos){
    if (pos < 1 || pos > seqlist.last + 1) {
        printf("Input error!");
        return -1;
    }
    
    return seqlist.list[pos -1];
}


void readList(struct SeqList seqList){
    printf("seq list content is : \n");
    for (int i = 0; i <= seqList.last; i++)
    {
        printf("%d  ",seqList.list[i]);
    }
    printf("\n");
}

void insList(struct SeqList * seqList,int pos,int e){
    if (seqList->last == (MAX_SIZE - 1) || pos < 1 || pos > seqList->last + 2)
    {
        printf("Input error!    ");
        printf("pos = %d  last = %d \n",pos,seqList->last);
        return;
    }

    for (int i = seqList->last; i >= pos - 1; i--)
    {
        seqList->list[i + 1] = seqList->list[i];
    }
    seqList->list[pos - 1] = e;
    seqList->last++;
    
//    printf("pos = %d  last = %d \n",pos,seqList->last);
}

void delList(struct SeqList * seqList,int pos){
    if (pos <= 0 || pos > seqList->last + 1) {
        printf("Input error!");
        return;
    }
    
    for (int i = pos ; i <= seqList->last ; i++) {
        seqList->list[i-1] = seqList->list[i];
    }
    
    seqList->list[seqList->last] = 0;
    seqList->last = seqList->last - 1;
}

void updateList(struct SeqList * list,int pos,int e){
    if (pos < 1 || pos > list->last + 1) {
        printf("Input error!");
        return;
    }
    
    list->list[pos - 1] = e;
}

插入算法的时间性能分析

假设表中已存在n个元素,插入不同的位置会得到不同的语句频度。比如:插入到第1个位置,则需要移动n次,即语句频度为n;插入到第n+1个位置,则需要移动0次。

因此,假设插入到各个位置的可能性P相同,即得
P = 1 n + 1 P = \frac{1} { n + 1}
设插入第pos个元素的语句频度为F,即得
F = n p o s + 1 F = n - pos + 1
则平均语句频度E为
E = p o s = 1 n + 1 P F E = \sum_{pos = 1}^{n+1}PF
化简得
E = n 2 E = \frac{n} {2}
故渐进时间复杂度为O(n)。

插入算法的空间性能分析

未占用辅助存储空间,故空间复杂度为S(1)。

删除算法的时间性能分析

假设删除各个位置元素的可能性相同,即得
P = 1 n P = \frac{1} {n}
设删除第pos个元素的语句频度为F,即得
F = n p o s F = n - pos
则平均语句频度E为
E = p o s = 1 n P F E = \sum_{pos = 1}^{n}PF
化简得
E = n 1 2 E = \frac{n -1}{2}
故渐进时间复杂度为O(n)。

删除算法的空间性能分析

未使用辅助空间单元,故空间复杂度为S(1)。

优点
  1. 方便地存取表中的任一元素
  2. 无需为了表示逻辑关系而增加额外的物理存储空间
缺点
  1. 要求占用连续的存储空间。当表长较大时,可能造成一部分空间长期得不到利用。当表长较小时,可能造成输入溢出。
  2. 插入和删除操作效率低。
小结

明显看到,算法的性能不仅受输入规模的影响,与输入的性质也有深刻的关系。

算法的效率是问题规模的函数。

操作 渐进时间复杂度 空间复杂度
插入 O(n) S(1)
删除 O(n) S(1)
更新 O(1) S(1)
查询 O(1) S(1)

C语言实现-动态链式存储

链式存储的物理映像由一个个得结点组成。该结点一部分用来存储研究的数据元素,称为数据域。另一部分存储其他结点的物理存储地址,称为指针域。(备注:要想真正地理解指针,则需要查看其在机器级上地表示)

单(向)链表

如果该结点只有一个指向其直接后驱的指针域,则称为单(向)链表。

//
//  LinkList.h
//  Data structure
//
//  Created by 樊明杨 on 2020/2/22.
//  Copyright © 2020 樊明杨. All rights reserved.
//

#ifndef LinkList_h
    #define LinkList_h

    #include <stdio.h>
    #include <stdlib.h>

    struct Node{
        int data;//把表的长度存储到头结点的数据域
        struct Node * next;
    };
    typedef struct Node Node;

	/*
	* 第一个参数均为头指针,用头指针代表这个表。
	*/
    Node * initList_1(Node *);//
    int listLength_1(Node *);
    int getData_1(Node *,int);
    void readList_1(Node *);
    void insList_1(Node *,int,int);
    void delList_1(Node *,int);
    void updateList_1(Node *,int,int);

#endif /* LinkList_h */

//
//  LinkList.c
//  Data structure
//  单链表的实现
//
//  Created by 樊明杨 on 2020/2/22.
//  Copyright © 2020 樊明杨. All rights reserved.
//

#include "LinkList.h"

Node * initList_1(Node * linkList){
    linkList = (Node *) malloc(sizeof(Node));
    linkList->data = 0;
    linkList->next = NULL;

    return linkList;
}

int listLength_1(Node * linkList){
    if (linkList == NULL) {
        printf("Input list is NULL!");
        return 0;
    }
    
    if (linkList->next == NULL) {
        return 0;
    }else{
        return linkList->data;
    }
}


int getData_1(Node * linkList,int pos){
    if (linkList == NULL) {
        printf("Input list is NULL!");
        return 0;
    }
    
    if (pos < 1 || pos > linkList->data) {
        printf("Position error!\n");
        return 0;
    }
    
    if (linkList->data == 0) {
        printf("Null list!\n");
        return 0;
    }
    
    Node * nowNode = linkList->next;
    for (int i = 1; i < pos; i++) {
        nowNode = nowNode->next;
    }
    
    return nowNode->data;
}

void readList_1(Node * linkList){
    if (linkList == NULL) {
        printf("Input list is NULL!");
        return;
    }
    
    if (linkList->data == 0) {
           printf("The list is empty!");
           return;
       }
    
    printf("The content of link list is :\n");
    Node * nowNode = linkList;
    for (int i = 1; i <= linkList->data; i++) {
        nowNode = nowNode->next;
        printf("%d  ", nowNode->data);
    }
    printf("\n");
}

void insList_1(Node * linkList,int pos,int e){
    if (linkList == NULL) {
        printf("Input list is NULL!");
        return;
    }
    
    if (pos < 1 || pos > linkList->data + 1) {
        printf("Insert position error!\n");
        return ;
    }
    
    Node * newNode = (Node *) malloc(sizeof(Node));
    newNode->data = e;
    newNode->next = NULL;
    
    Node * nowNode = linkList;
    for (int i = 1; i < pos; i++) {
        nowNode = nowNode->next;
    }
    Node * aheadNode = nowNode;
    
    newNode->next = aheadNode->next;
    aheadNode->next = newNode;
    
    linkList->data++;
}

void delList_1(Node * linkList,int pos){
    if (linkList == NULL) {
        printf("Input list is NULL!");
        return;
    }
    
    if (pos <1 || pos > linkList->data) {
        printf("Delete position error!");
        return;
    }
    
    Node * nowNode = linkList;
    for (int i = 1; i < pos; i++) {
        nowNode = nowNode->next;
    }
    
    Node * nextNode = nowNode->next->next;
    free(nowNode->next);
    nowNode->next = nextNode;
    linkList->data--;
}

void updateList_1(Node * linkList,int pos,int e){
    if (linkList == NULL) {
        printf("Input list is NULL!");
        return;
    }
    if (pos < 1 || pos > linkList->data) {
        printf("Update position error!");
        return;
    }
    
    Node * nowNode = linkList;
    for (int i = 1; i <= pos; i++) {
        nowNode = nowNode->next;
    }
    nowNode->data = e;
}


//
//  main.c
//  Data structure
//  测试
//  Created by 樊明杨 on 2020/2/22.
//  Copyright © 2020 樊明杨. All rights reserved.
//

#include "LinearList/LinkList.h"

void testLinkList(void);

int main(void) {
    testLinkList();
    return 0;
}

void testLinkList(void){
    Node * linkList;
    linkList = initList_1(linkList);
    printf("The length is %d\n",linkList->data);
    
    insList_1(linkList, 1, 1);
    
    int e = getData_1(linkList, 1);
    printf("The first element is %d\n",e);
    
    for (int i = 2; i <= 10; i++) {
        insList_1(linkList, i, i);
    }
    printf("The length is %d\n",linkList->data);
    
    readList_1(linkList);
    
    delList_1(linkList, 4);
    readList_1(linkList);
    
    free(linkList);
}

ALL OUTPUT:
The length is 0
The first element is 1
The length is 10
The content of link list is :
1  2  3  4  5  6  7  8  9  10  
The content of link list is :
1  2  3  5  6  7  8  9  10  
Program ended with exit code: 0
语句频度

虽然不用移动大量元素,但是从第一个元素遍历到第pos个元素的情况依然是存在的。由于四种操作均要执行该遍历操作,所以渐进时间复杂全部是O(n)。推到过程如下:

假设获取到各个位置元素的可能性P相同,即得
P = 1 n + 1 ( ) P = 1 n / / P = \frac{1} { n + 1} (插入)或 P = \frac{1}{n}(删除/更新/查询)
设操作第pos个元素的语句频度为F,即得
F = p o s 1 ( / ) F = p o s / , 1 p o s n F = pos - 1 (插入/删除)或 F = pos(更新/查询),1\leq pos \leq n
则平均语句频度E为
E = p o s = 1 n + 1 P F ( ) E = p o s = 1 n P F ( / / ) E = \sum_{pos = 1}^{n+1}PF(插入)或 E = \sum_{pos = 1}^{n}PF(删除/更新/查询)
化简得

操作 语句频度
插入 n 2 \frac{n}{2}
删除 n 1 2 \frac{n-1}{2}
更新 n + 1 2 \frac{n+1}{2}
查询 n + 1 2 \frac{n+1}{2}
优点

缺点

相比顺序表,更新和查询操作效率更低。

小结

下述复杂度的计算皆是基于上述实现。

操作 渐进时间复杂度 空间复杂度
插入 O(n) S(1)
删除 O(n) S(1)
更新 O(n) S(1)
查询 O(n) S(1)

可以看到,相比顺序表,单链表不仅在更新和查询操作上出现耗时增加的情况,插入和删除操作的复杂度也并没有降低。

双(向)链表

如果该结点既有一个指向其直接后驱的指针域,又有一个指向其直接前驱的指针域,则称为双(向)链表。

//
//  DLinkList.h
//  Data structure
//  Double linked list 双向链表
//  Created by 樊明杨 on 2020/2/26.
//  Copyright © 2020 樊明杨. All rights reserved.
//

#ifndef DLinkList_h
    #define DLinkList_h

    #include <stdio.h>
#include <stdlib.h>

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

typedef struct DNode DNode;
/*
第一个参数为头指针,头指针代表这个表
*/
DNode * initList_d(DNode *);//参数为头指针,头指针代表这个表
int listLength_d(DNode *);//参数为头指针,把长度存储到头结点的数据域
int getData_d(DNode *,int);//参数为头指针和位置
void readList_d(DNode *);
void insList_d(DNode *,int,int);
void delList_d(DNode *,int);
void updateList_d(DNode *,int,int);

#endif /* DLinkList_h */

//
//  DLinkList.c
//  Data structure
//  Double linked list 双向链表
//  Created by 樊明杨 on 2020/2/26.
//  Copyright © 2020 樊明杨. All rights reserved.
//

#include "DLinkList.h"

DNode * initList_d(DNode * linkList){
    linkList = (DNode *)malloc(sizeof(DNode));
    linkList->data = 0;
    linkList->prior = linkList;
    linkList->next = linkList;
    return linkList;
}

int listLength_d(DNode * linkList){
    if (!linkList) {
        printf("Input list is NULL!\n");
        return 0;
    }
    
    return linkList->data;
}

int getData_d(DNode * linkList,int pos){
    if (!linkList) {
        printf("Input list is NULL!\n");
        return 0;
    }
    
    if (pos < 1 || pos > linkList->data) {
        printf("Position error!");
        return 0;
    }
    
    DNode * nowNode = linkList;
    for (int i = 1; i <= pos; i++) {
        nowNode = nowNode->next;
    }
    
    return nowNode->data;
}

void readList_d(DNode * linkList){
    if (!linkList) {
       printf("Input list is NULL!\n");
       return ;
    }
    
    DNode * nowNode = linkList;
    for (int pos = 1; pos <= linkList->data; pos++) {
        nowNode = nowNode->next;
        printf("%d  ",nowNode->data);
    }
    printf("\n");
}

void insList_d(DNode * linkList,int pos,int e){
    if (!linkList) {
       printf("Input list is NULL!\n");
       return ;
    }
    
    if (pos < 1 || pos > linkList->data + 1) {
        printf("Position error!\n");
        return ;
    }
    
    DNode * newNode = (DNode *)malloc(sizeof(DNode));
    newNode->data = e;
    
    DNode * nowNode = linkList;
    for (int i = 1; i < pos; i++) {
        nowNode = nowNode->next;
    }
    
    newNode->prior = nowNode;
    newNode->next = nowNode->next;
    nowNode->next = newNode;
    nowNode->next->prior = nowNode;
    linkList->data++;
}

void delList_d(DNode * linkList,int pos){
    if (!linkList) {
       printf("Input list is NULL!\n");
       return ;
    }
    
    if (pos < 1 || pos > linkList->data) {
        printf("Position error!\n");
        return ;
    }
    
    DNode * nowNode = linkList;
    for (int i = 1; i <= pos; i++) {
        nowNode = nowNode->next;
    }
    
    nowNode->prior->next = nowNode->next;
    nowNode->next->prior = nowNode->prior;
    free(nowNode);
    linkList->data--;
}

void updateList_d(DNode * linkList,int pos,int e){
    if (!linkList) {
       printf("Input list is NULL!\n");
       return ;
    }
    
    if (pos < 1 || pos > linkList->data) {
        printf("Position error!\n");
        return ;
    }
    
    DNode * nowNode = linkList;
    for (int i = 1; i <= pos; i++) {
        nowNode = nowNode->next;
    }
    
    nowNode->data = e;
}

//测试
void testDLinkList(void){
    DNode * list;
    list = initList_d(list);
    printf("length is : %d\n",listLength_d(list));
    
    printf("Test insert \n");
    insList_d(NULL, 1, 1);
    insList_d(list, 1, 1);
    readList_d(list);
    insList_d(list, 3, 3);
    readList_d(list);
    for (int i = 0; i < 9; i++) {
        insList_d(list, i + 2, i + 2);
    }
    readList_d(list);
    insList_d(list, 3, 47);
    readList_d(list);
    
    printf("Test delete \n");
    delList_d(NULL, 1);
    delList_d(list, 1);
    readList_d(list);
    delList_d(list, 2);
    readList_d(list);
    delList_d(list, list->data);
    readList_d(list);
    delList_d(list, list->data + 1);
    readList_d(list);
    
    printf("Test update \n");
    updateList_d(NULL, 1, 2);
    updateList_d(list, 3, 47);
    readList_d(list);
    
}

ALL OUTPUT
length is : 0
Test insert 
Input list is NULL!
1  
Position error!
1  
1  2  3  4  5  6  7  8  9  10  
1  2  47  3  4  5  6  7  8  9  10  
Test delete 
Input list is NULL!
2  47  3  4  5  6  7  8  9  10  
2  3  4  5  6  7  8  9  10  
2  3  4  5  6  7  8  9  
Position error!
2  3  4  5  6  7  8  9  
Test update 
Input list is NULL!
2  3  47  5  6  7  8  9  
Program ended with exit code: 0
语句频度

虽然不用移动大量元素,但是从第一个元素遍历到第pos个元素的情况依然是存在的。由于四种操作均要执行该遍历操作,所以渐进时间复杂全部是O(n)。推导过程如下:

假设获取到各个位置元素的可能性P相同,即得
P = 1 n + 1 ( ) P = 1 n / / P = \frac{1} { n + 1} (插入)或 P = \frac{1}{n}(删除/更新/查询)
设操作第pos个元素的语句频度为F,即得
F = p o s 1 ( / ) F = p o s / , 1 p o s n F = pos - 1 (插入/删除)或 F = pos(更新/查询),1\leq pos \leq n
则平均语句频度E为
E = p o s = 1 n + 1 P F ( ) E = p o s = 1 n P F ( / / ) E = \sum_{pos = 1}^{n+1}PF(插入)或 E = \sum_{pos = 1}^{n}PF(删除/更新/查询)
化简得

操作 语句频度
插入 n 2 \frac{n}{2}
删除 n 1 2 \frac{n-1}{2}
更新 n + 1 2 \frac{n+1}{2}
查询 n + 1 2 \frac{n+1}{2}

下述复杂度的计算皆是基于上述实现。

操作 渐进时间复杂度 空间复杂度
插入 O(n) S(1)
删除 O(n) S(1)
更新 O(n) S(1)
查询 O(n) S(1)

可以看到,相比顺序表,单链表不仅在更新和查询操作上出现耗时增加的情况,插入和删除操作的复杂度也并没有降低。

优点

缺点

相比顺序表,更新和查询操作效率更低。

小结

下述复杂度的计算皆是基于上述实现。

操作 渐进时间复杂度 空间复杂度
插入 O(n) S(1)
删除 O(n) S(1)
更新 O(n) S(1)
查询 O(n) S(1)

可以看到,情况与单链表一样。更糟糕的是,占用了更多的存储空间。

因此,单纯的单向或双向链表并不能使得算法性能比顺序表更优。

循环链表

单链表的最后一个结点指向头结点,形成一个结点环,即为循环单链表。

//
//  CLinkList.h
//  Data structure
//  循环单链表
//  Created by 樊明杨 on 2020/2/24.
//  Copyright © 2020 樊明杨. All rights reserved.
//

#ifndef CLinkList_h
    #define CLinkList_h
    #include "LinkList.h"

    /*
    第一个参数为尾指针,尾指针代表这个表
    */
    Node * initList_c(Node *);//参数为尾指针,头指针代表这个表
    int listLength_c(Node *);//参数为尾指针,把长度存储到头结点的数据域
    int getData_c(Node *,int);//参数为尾指针和位置
    void readList_c(Node *);
    Node * insList_c(Node *,int,int);
    Node * delList_c(Node *,int);
    void updateList_c(Node *,int,int);
    Node * spliceList_c(Node *,Node *);

#endif /* CLinkList_h */

//
//  CLinkList.c
//  Data structure
//  循环单链表
//  Created by 樊明杨 on 2020/2/23.
//  Copyright © 2020 樊明杨. All rights reserved.
//  区别与单向链表,我们用尾指针替代头指针。这样做的好处是末尾元素的插入和删除时间复杂度为O(1),且方便进行多表拼接。

#include "CLinkList.h"

Node * initList_c(Node * linkList){
    linkList = (Node *) malloc(sizeof(Node));
    linkList->data = 0;
    linkList->next = NULL;
    
    return linkList;
}

int listLength_c(Node * linkList){
    if (linkList == NULL) {
        printf("Input list is NULL!");;
        return 0;
    }
    
    if (linkList->next == NULL) {
        return 0;
    }
    
    return linkList->next->data;
}

int getData_c(Node * linkList,int pos){
    if (linkList == NULL) {
        printf("Input list is NULL!\n");
        return 0;
    }
    
    if (linkList->next == NULL) {
        printf("The list is empty!\n");
        return 0;
    }
    
    if (pos == linkList->next->data) {
        return linkList->data;
    }
    
    Node * nowNode = linkList;
    for (int i = -1 ; i < pos; i++) {
        nowNode = nowNode->next;
    }
    return nowNode->data;
}

void readList_c(Node * linkList){
    if (linkList == NULL) {
        printf("Input list is NULL!\n");
        return ;
    }
    
    if (linkList->next == NULL) {
        printf("The list is empty!\n");
        return ;
    }
    
    Node * nowNode = linkList;
    printf("%p  ",linkList);
    for (int i = -1 ; i < linkList->next->data; i++) {
        nowNode = nowNode->next;
        if (i > -1) {
            printf("%d  ",nowNode->data);
        }
    }
    printf("\n");
    
}

Node * insList_c(Node * linkList,int pos,int e){
    if (linkList == NULL) {
        printf("Input list is NULL!");
        return linkList;
    }
        
    if ((linkList->next == NULL && pos != 1) || ((linkList->next != NULL) && (pos < 1 || pos > linkList->next->data + 1))){
        printf("Insert position error!\n");
        return linkList;
    }
    
    Node * newNode = (Node *)malloc(sizeof(Node));
    newNode->data = e;
    newNode->next = NULL;
    
    Node * nowNode = linkList;
    int temp = -1;
    while (nowNode->next != NULL && temp < pos - 1) {
        if (pos == linkList->next->data + 1) {
            break;
        }else if(pos == 1){
            nowNode = nowNode->next;
            break;
        }
        nowNode = nowNode->next;
        temp++;
    }
    
    if (nowNode->next == NULL) {
        linkList->next = newNode;
        newNode->next = linkList;
        linkList = newNode;
        linkList->next->data++;
    }else{
        newNode->next = nowNode->next;
        nowNode->next = newNode;
        if (linkList->next == newNode) {
            linkList = linkList->next;
        }
        linkList->next->data++;
    }
    return linkList;
}

Node * delList_c(Node * linkList,int pos){
    if (linkList == NULL) {
        printf("Input list is NULL!\n");
        return linkList;
    }
    
    if (linkList->next == NULL) {
        printf("The list is empty!\n");
        return linkList;
    }
    
    if (pos < 1 || pos > linkList->next->data) {
        printf("Delete position error!");
        return linkList;
    }
    
    Node * nowNode = linkList;
    for (int i = -1 ; i < pos - 1; i++) {
        nowNode = nowNode->next;
    }
    Node * delNode = nowNode->next;
    nowNode->next = nowNode->next->next;
    
    if (pos == linkList->next->data) {
        linkList = nowNode;
    }
    
    if (nowNode == nowNode->next) {
        nowNode->next = NULL;
        nowNode->data--;
    }else{
        linkList->next->data--;
    }
    
    free(delNode);
    return linkList;
}
void updateList_c(Node * linkList,int pos,int e){
    if (linkList == NULL) {
        printf("Input list is NULL!\n");
        return ;
    }
    
    if (linkList->next == NULL) {
        printf("The list is empty!\n");
        return ;
    }
    
    if (pos < 1 || pos > linkList->next->data) {
        printf("Update position error!");
        return ;
    }
    
    Node * nowNode = linkList;
    for (int i = -1 ; i < pos; i++) {
        nowNode = nowNode->next;
    }
    nowNode->data = e;
    
    return ;
}

Node * spliceList_c(Node * aList,Node * bList){
    if (aList == NULL || bList == NULL) {
        printf("Input list is NULL!\n");
        return NULL;
    }
    
    if (aList->next == NULL || bList->next == NULL) {
        printf("Some list is empty!\n");
        return NULL;
    }
    
    aList->next->data = aList->next->data + bList->next->data;
    Node * headNode = aList->next;
    aList->next = bList->next->next;
    bList->next = headNode;
    
    return bList;
}

//测试单向循环链表用例
void testCLinkList(void){
    Node * cLinkList;
    cLinkList = initList_c(cLinkList);
    
    printf("Test insert and read\n");
    for (int i = 1; i <= 3; i++) {
        cLinkList = insList_c(cLinkList, i, i);
        printf("%d  ",getData_c(cLinkList, i));
    }
    printf("\n");
    cLinkList = insList_c(cLinkList, 2, 47);
    readList_c(cLinkList);
    cLinkList = insList_c(cLinkList, 4, 34);
    readList_c(cLinkList);
    cLinkList = insList_c(cLinkList, 3, 11);
    readList_c(cLinkList);
    cLinkList = insList_c(cLinkList, 7, 66);
    readList_c(cLinkList);
    
    printf("Test delete\n");
    cLinkList = delList_c(cLinkList, 3);
    readList_c(cLinkList);
    cLinkList = delList_c(cLinkList, 3);
    readList_c(cLinkList);
    cLinkList = delList_c(cLinkList, 1);
    readList_c(cLinkList);
    cLinkList = delList_c(cLinkList, 4);
    readList_c(cLinkList);
    cLinkList = delList_c(cLinkList, 3);
    readList_c(cLinkList);
    cLinkList = delList_c(cLinkList, 2);
    readList_c(cLinkList);
    cLinkList = delList_c(cLinkList, 1);
    readList_c(cLinkList);
    
    printf("Test update\n");
    for (int pos = 1; pos <= 10; pos++) {
        cLinkList = insList_c(cLinkList, pos, pos);
    }
    readList_c(cLinkList);
    updateList_c(cLinkList, 3, 47);
    readList_c(cLinkList);
    updateList_c(cLinkList, cLinkList->next->data, 99);
    readList_c(cLinkList);
    updateList_c(cLinkList, 1, 0);
    readList_c(cLinkList);
    
    printf("Test splice list\n");
    Node * bList;
    bList = initList_c(bList);
    for (int pos = 1; pos <= 5; pos++) {
        bList = insList_c(bList, pos, pos);
    }
    printf("a list is : \n");
    readList_c(cLinkList);
    printf("b list is : \n");
    readList_c(bList);
    printf("Splicing list result : \n");
    cLinkList = spliceList_c(cLinkList, bList);
    readList_c(cLinkList);
}

ALL OUTPUT
Test insert and read
1  2  3  
0x100541cc0  1  47  2  3  
0x100541cc0  1  47  2  34  3  
0x100541cc0  1  47  11  2  34  3  
0x10053b4a0  1  47  11  2  34  3  66  
Test delete
0x10053b4a0  1  47  2  34  3  66  
0x10053b4a0  1  47  34  3  66  
0x10053b4a0  47  34  3  66  
0x100541cc0  47  34  3  
0x10053b330  47  34  
0x10053c1d0  47  
The list is empty!
Test update
0x1028238c0  1  2  3  4  5  6  7  8  9  10  
0x1028238c0  1  2  47  4  5  6  7  8  9  10  
0x1028238c0  1  2  47  4  5  6  7  8  9  99  
0x1028238c0  0  2  47  4  5  6  7  8  9  99  
Test splice list
a list is : 
0x1028238c0  0  2  47  4  5  6  7  8  9  99  
b list is : 
0x10281b850  1  2  3  4  5  
Splicing list result : 
0x10281b850  0  2  47  4  5  6  7  8  9  99  1  2  3  4  5  
Program ended with exit code: 0
语句频度

同上

优点

同上

缺点

同上

小结

同上

小结

通过平均语句频度得出的渐进时间复杂度说明一般情况下链式存储结构并不优于顺序存储。这与通常的认知"链式存储有利于插入和删除操作,不利于查找和更新操作"相背。这说明不能仅仅考虑"平均"或说"一般"的情况,程序的实际使用场景应该是有所侧重的,也不能不考虑一条语句的实际执行时耗。

基于存储空间考虑

顺序表的存储空间是静态分配的。初始化时,如果估计地较大则可能造成空间浪费,如果估计的较小,可能造成空间溢出。

动态链式表的存储空间是动态分配的。

因此,如果程序运行时表长度变化较大时考虑使用动态链式存储。

从存储密度(数据存储空间占结点存储空间的比例)的角度考虑,顺序表存储密度为1,链式表存储密度必然小于1。

因此,如果提前已知线性表的大小,则为了节约空间,应当使用顺序表。

基于时间的考虑

顺序表在进行插入和删除操作的时候需要移动大量结点,当结点信息量比较大的时候,移动结点的时间开销就比较大。

链式表在进行插入和删除操作的时候虽然也需要不断的移动指针,且通过移动指针的平均语句频度计算出的渐进时间复杂度与顺序表移动结点的相等,但是移动一次指针的指令的时间可能耗费小于移动一个结点的指令。所谓移动指针其实就是不断的改变一个某种数据类型的指针的值,而指针占用的内存空间是一个字长。这种操作从计算机的角度来讲其实就是加载,即从主存复制一个字或字节到寄存器

对比一次指针移动的时间耗费与一次结点移动的时间耗费。假设计算机是64位的且写1个字节需要1微秒,则移动一次指针需要8微秒。一个结点占用存储空间是不同的,假设为s个字节,则一次结点移动的时间耗费为s。因此,得出如下结论:

yes代表更优 s<8 s=8 s>8
顺序表 yes yes no
链式表 no yes yes

并且如果插入和删除操作频繁发生在表的首尾,则优先考虑采用尾指针的循环链表。

对与更新和查询操作,顺序表由于是利用向量实现的随机存取结构,所以对任一结点的存取时间复杂度都是O(1)。而链表需要不断的移动指针,时间复杂度达到O(n)。

因此,如果操作主要是更新和查询,则优先考虑顺序表。

发布了14 篇原创文章 · 获赞 0 · 访问量 727

猜你喜欢

转载自blog.csdn.net/qq_38878217/article/details/104737296
今日推荐