数据结构-线性表 linked list

线性表

概念:

线性表是n个数据特性相同元素组成的有限序列,是最基本的也是最常用的一种线性结构(线性表、栈、队列、数组都是线性结构),同时也是其他数据结构的基础。

对于非空的线性表或线性结构,都有:

  • 存在唯一一个被称为‘第一个’的数据元素
  • 存在唯一一个被称为‘最后一个’的数据元素
  • 除第一个外,结构中的每个数据元素均只有一个前驱
  • 除最后一个外,结构中的每个数据元素均只有一个后继

两种存储方式

顺序存储

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

特点:逻辑上相邻的数据元素,存储结构上也相邻。

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

顺序表的C语言代码实现

#define MAXSIZE 10
typedef int ElementType;

typedef struct {
    ElementType data[MAXSIZE];
    int length;
}SeqList;

//初始化
SeqList * initSeqList() {
    SeqList *list;
    list = (SeqList *)malloc(sizeof(SeqList));
    list->length = 0;
    return list;
}

//插入
void insertSeqList(SeqList list,ElementType data,int i) {
    if (i > list.length || i < 1) {
        return;
    }
    for (int j = list.length; j>=i; j--) {
        list.data[j] = list.data[j-1];
    }
    list.data[i-1] = data;
    list.length++;
}

//删除
void deleteSeqList(SeqList list,int i) {
    if (i > list.length || i < 1) {
        return;
    }
    for (int j = i; j<list.length; j++) {
        list.data[j] = list.data[j-1];
    }
    list.length--;
}

//定位 找出表中相等于参数的最小下标值
int locationSeqList(SeqList list,ElementType x) {
    int i = 0;
    while (i<list.length && list.data[i] != x) {
        i++;
    }
    if (i<list.length) {
        return i+1;
    }
    return 0;
}
线性表的链式存储

定义:线性表的链式存储是指存储结构是链式的。常见的链式存储有单链表、循环链表和双向链表。

单链表:单链表是由一个数据域和指针域组成的一个节点集合。data部分称为数据域,用于存放线性表元素;next部分称为指针域,存放一个指针,该指针指向本结点所含数据元素的直接后继结点,所有结点通过指针链接成链表。

大多数情况下链表会有一个头节点head,head的next指针指向链表的第一个结点,称为首节点,最后一个结点称为尾结点,首尾之间的结点称为表结点;

一些基本运算:

结构体定义

typedef struct {
    int age;
    int score;
}DataType;

typedef struct node {
    DataType data;//数据域
    struct node *next;//指针域
}Node,*linkList;

Node head;//头结点

初始化

linkList initLinkList() {
    linkList head;
    head = malloc(sizeof(Node));
    head->next = NULL;
    return head;
}

求表长

int lengthLinkList(linkList head) {
    int count = 0;
    linkList first = head->next;
    while (first->next != NULL) {
        count++;
        first = first->next;
    }
    return count;
}

读表元素 给定序号i 求表head第i个元素

Node * getLinkList(linkList head,int i) {
    Node *p;
    p = head->next;//指向首节点
    int k = 1;
    while (k < i && p != NULL) {
        k++;
        p = p->next;
    }
    if (k == i) {
        return p;
    } else {
        return NULL;
    }
}

定位 给定元素 求index

int locationLinkList(linkList head,DataType x) {
    Node *p = head->next;
    int k = 0;
    while (p != NULL && p->data.num != x.num) {
        k++;
        p = p->next;
    }
    if (p!=NULL) {
        return k+1;
    }
    return 0;
}

插入 单链表的插入是将给定值为x的元素插入到head链表 i 序号之前

void insertLinkList(linkList head,DataType x,int i) {
    Node *insert,*q;
    if (i == 1) {
        q = head;
    } else {
        q = getLinkList(head, i-1);//找到直接前驱
    }
    if (q == NULL) {
        printf("位置不对");
    } else {
        insert = (Node *)malloc(sizeof(Node));
        insert->data = x;
        insert->next = q->next;
        q->next = insert;
    }
}

删除 给定一个值,从链表中删除i序号的元素

void deleteLinklist(linkList head,int i) {
    //先获取被删除元素的直接前驱
    Node *p,*q;
    if (i == 1) {
        q = head;
    } else {
        q = getLinkList(head, i-1);//找到直接前驱
    }
    if (q != NULL && q->next != NULL) {
        p = q->next;
        q->next = p->next;
        free(p);//释放空间
    } else {
        printf("找不到要删除的节点");
    }
}
单链表的三种创建方式
方法一

通过插入算法来实现,依次增大插入位置i 使新的结点链入链表中 时间复杂度为O(n^2)

linkList createLinkList(void) {
    linkList head;
    int i,x;
    head = initLinkList();
    i = 1;
    scanf("%d",&x);
    while (x != 0) {
        insertLinkList(head, x, i);
        i++;
        scanf("%d",&x);
    }
    return head;
}
方法二 尾插法

方法一每次插入元素都要从首结点遍历到尾结点,然后插入元素,所以时间复杂度为O(n^2),可以使用一个指针标明尾结点的位置, 这样每次插入元素就不用遍历整个表 顾时间复杂度为O(n)

linkList createLinkList2() {
    linkList head;
    head = (Node *)malloc(sizeof(Node));
    Node *tail,*q;
    q = head;//头结点
    int x;
    scanf("%d",&x);
    while (x != 0) {
        tail = (Node *)malloc(sizeof(Node));
        tail->data = x;
        q->next = tail;
        q = tail;//指向尾结点
        scanf("%d",&x);
    }
    q->next = NULL;
    return head;
}
方法三 头插法 时间复杂度也为O(n)
linkList createLinkList3() {
    linkList head;
    head = (Node *)malloc(sizeof(Node));
    head->next = NULL;
    Node *p;
    int x;
    scanf("%d",&x);
    while (x != 0) {
        p = malloc(sizeof(Node));
        p->data = x;
        p->next = head->next;//新元素放到第一个,充当首结点
        head->next = p;
        scanf("%d",&x);
    }
    return head;
}
删除重复数据项

在线性表中,有可能有多个元素值相同;它们是重复结点,可以删除重复结点只保留index最小的结点

void deleteRepeatLinkList(linkList head) {
    Node *p,*d,*q;
    q = head->next;
    while (q != NULL) {
        p = q;
        while (p->next != NULL) {
            if (p->next->data == q->data) {
                d = p->next;//被删除结点
                p->next = d->next;
                free(d);
            } else {
                p = p->next;//指针后移
            }
        }
        q = q->next;//指针后移
    }
}

线性表的顺序实现、链接实现比较

顺序实现和链接实现都有各自的优缺点,下面就时间复杂度和空间复杂度展开讨论。

  1. 对于按位置查找,顺序表示随机访问,所以平均时间复杂度为O(1),单链表需要对元素进行扫描,它的平均时间复杂度为O(n);
  2. 对于定位运算,基本操作是比较,需要扫描元素,顺序表和单链表的平均时间复杂度相同O(n);
  3. 对于插入和删除操作,在顺序表和单链表中都需要定位,在顺序表中,其基本操作是元素比较和结点的移动,平均时间复杂度为O(n),在单链表中,由于需要定位,尽管不需要移动结点,平均时间复杂度同样是O(n);
  4. 单链表的每个结点包含数据域和指针域,指针域需要占用额外空间但不需要预分配空间;
  5. 顺序表需要预分配空间,如果预分配过大,造成空间浪费;若分配的小,将发生空间溢出;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355