精华内容
下载资源
问答
  • 线性表链式存储结构.doc 线性表链式存储结构.doc 线性表链式存储结构.doc
  • 2.4 线性表链式存储结构及运算实现;2.4 线性表链式存储结构及运算实现;2.4.1 单链表;以元素(数据元素的映象) + 指针(指示后继元素存储位置) = 结点 (表示数据元素 或 数据元素的映象;2.4.1 单链表; ; ; ; ; ; ; ...
  • 距上一次写线性表的顺序存储结构已经有一段时间了(详戳http://blog.csdn.net/u010429311/article/details/50933932),这次主要接着来实现线性表链式存储结构,即我们通常所说的链表。 链表可以用一组任意的存储...

    距上一次写线性表的顺序存储结构已经有一段时间了(详戳http://blog.csdn.net/u010429311/article/details/50933932),这次主要接着来实现线性表的链式存储结构,即我们通常所说的链表。
    链表可以用一组任意的存储单元存储线性表的数据元素,而存储数据的这组存储单元可以是连续的,也可以是不连续的。具体看一下图解:
    这里写图片描述
    在链表中,我们也可以实现链表的创建、插入、删除等操作。其原理如下图:
    这里写图片描述
    以下是具体的实现代码:
    定义结构体:

    #include <stdio.h>
    #include <malloc.h>
    #define OK 1
    #define ERROR 0
    typedef int ElemType;
    typedef int Status;
    
    typedef struct LNode{
        ElemType data;    //结点的数据
        struct LNode *next;   //指向下一结点的指针
    } LNode, *LinkList;

    创建链表:

    /*
    *逆序往链表里插入n个元素
    */
    Status createList(LinkList &L, int n) {
        LinkList p;   
        L = (LinkList) malloc(sizeof(LNode));  //申请结点空间
        L->next = NULL;   //建立带头结点的链表
        for (; n > 0; n--) {
            p = (LinkList) malloc(sizeof(LNode)); //生成新结点
            scanf("%d", &p->data);  //输入结点的元素
            p->next = L->next;   //插入到表头
            L->next = p; 
        }
        return OK;
    }

    插入元素:

    /*
    *往链表的第i个位置插入元素
    */
    Status listInsert(LinkList &L, int i, ElemType e) {
        LinkList p, s;
        p = L;
        int j = 0;
        while (p && j < i - 1) {  //寻找第i-1个结点
            p = p->next;  
            j++;
        }
        if (!p || j > i - 1) return ERROR;
        s = (LinkList) malloc(sizeof(LNode)); //申请新结点
        s->data = e;  //插入新的结点
        s->next = p->next;
        p->next = s;
        return OK;
    }

    删除元素:

    Status listDelete(LinkList &L, int i, ElemType &e) {
        LinkList p, q;
        p = L;
        int j = 0;
        while (p->next && j < i - 1) {  //寻找第i个结点
            p = p->next;
            j++;
        }
        if (!p->next || j > i - 1) return ERROR;
        q = p->next;  //删除结点
        p->next = q->next;
        e = q->data;
        free(q);  //释放被删除结点的空间
        return OK;
    }

    遍历链表:

    Status loadList(LinkList &L) {
        if (L->next == NULL) {
            return ERROR;
        }
        LinkList p;
        p = L->next;  //注意,头结点没有存放数值,应从L->next开始遍历
        while(p) {    //遍历结点,输出数值
            printf("%d ", p->data);
            p = p->next;
        }
        printf("\n");
        return OK;
    }
    展开全文
  • 数据结构线性表链式存储结构,使用c语言完成。线性表链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素。
  • 线性表链式存储结构线性表链式存储结构单链表头结点和头指针结点和单链表的c语言描述单链表操作的实现单链表获取第i个数据元素插入数据元素删除数据元素重置为一个空表生成包含n个数据元素的链表 线性表链式...

    线性表的链式存储结构

    单链表

    概念: 用一组地址任意的存储单元存放线性表中的数据元素。
    +     = 以元素(数据元素的映象)+ 指针(指示后继元素存储位置) \\ `\\ \ \ \ \ \ = 结点 (表示数据元素 或 数据元素的映象)

    • 以线性表中第一个数据元素a1a_1的存储地址作为线性表的地址,称作线性表的头指针。链表是由头指针唯一确定的,知道了头指针就知道了链表。
    • aia_i的地址被它的直接前驱ai1a_{i-1}存放。
    • 最后一个结点ana_n的指针域为空。

    头结点和头指针

    有的时候为了操作方便,在第一个结点之前虚加一个“头结点”,以指向头结点的指针为链表的头指针。

    在这里插入图片描述
    若线性表为空时,头指针不再为空,头结点的指针域为空。
    一般单链表都是带头结点的。
    总结一下头指针和头结点的异同:

    1. 头指针是链表的必要条件,而头结点不是;
    2. 头指针是链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针;
    3. 头指针具有标识作用,常用头指针冠以链表名;
    4. 头结点使得在第一元素结点前插入结点和删除第一结点不再特殊,和其它结点的操作统一;
    5. 头结点将空表和非空表的操作统一;
    6. 头结点的数据域一般为空(或存放链表长度)。

    结点和单链表的c语言描述

    typedef struct LNode{
    	int data; //数据域
    	struct LNode* next;//指针域
    }LNode,*LinkList;//结点别名 结点指针别名
    
    LinkList L;//单链表的头指针,全局变量不是必须的
    

    单链表操作的实现

    单链表的初始化

    添加头结点,链表的尾结点指针别忘了设成NULL

    int InitList(LinkList& L)
    {
    	//加入头结点
    	LNode* a = new LNode;
    	L = a;
    	L->next=NULL;
    	return 0;
    }
    

    添加元素至链表尾部

    int ListAppend(LinkList& L,int e) {
    	LNode* p = L;
    	while (p->next) { //找到链表尾部
    		p = p->next;
    	}
    	LNode* node = new LNode;
    	node->data = e;
    	node->next = NULL; //结尾别忘了设成NULL
    	p->next = node; //加入新结点
    	return 0;
    }
    

    单链表获取第i个数据元素

    对于单链表来说,无论是按下标获取元素还是按值获取元素都要对链表进行遍历操作,所以时间复杂度都是O(n)。
    按下标获取元素思路:关键是使用一个计数器j记录访问过的结点数,不断用j和要查找的元素下标i进行比较。

    int GetElem(LinkList& L, int i, int& e) {
    	LNode* p=L; int j = 0;
    	while (p&&j<i) { p=p->next; j++; }
    	if (!p || j>i)	return -1;
    	e = p->data;
    	return 0;
    }
    

    插入数据元素

    向位置i插入指定数据元素e。
    思路:让待插入结点的指针*s指向插入位置i的后一结点*ai+1,然后让位置i的结点的指针*ai指向待插入结点*s。
    在链表中插入结点只需要修改指针,时间复杂度O(1)。
    但是,若在第i个结点插入指针,首先要找到第i个结点,这一步需要O(n)。那么,这和顺序表的时间消耗不是一样了吗?
    不是的。虽然都是O(n)的消耗,但顺序表需要移动元素,这是在存储器上操作的;链表只需要比较操作,这是在运算器上操作的。二者不是一个数量级!

    int ElemInsert(LinkList& L, int i, int e) {
    	LNode* p = L;
    	int j = 1; //位置i从1开始计算,如果需要从0开始,令j=0
    	while (p&&j<i) { p=p->next; j++; }
    	if (!p || j>i) return -1;
    	LNode* s = new LNode;
    	s->data = e;
    	s->next=p->next;
    	p->next=s;
    	return 0;
    }
    

    后插与前插

    • 后插结点:p指向链表某结点,s指向待插入的、值为x的新结点,将*s插入到*p的后面。上边的例子就是一个后插结点的操作,直接修改*s让它指向*p的后继结点,再让*p指向*s即可。
    • 前插结点:设p指向链表中某结点,s指向待插入的、值为x的新结点,将*s插入到*p的前面。与后插不同的是:首先要找到*p的前驱*q,然后再在*q之后插入*s,设单链表头指针为L。

    前插伪代码:

    	q=L;
    	while(q->next!=p) q=q++;//找*p的直接前驱
    	s->next=q->next;
    	q->next=s;
    

    显然,前插的时间复杂度是O(n)。

    前插的时间主要消耗在了查找*p的前驱上,*p已知的情况下,能不能用后插实现前插,将时间复杂度降到O(1)呢?
    思路:*s插到*p之后,交换p->dara与s->data。
    代码略。

    删除数据元素

    删除第i个元素ai的操作:
    思路:使待删除结点ai的直接前驱ai-1指向待删除结点的直接后继ai+1

    int ElemDelete(LinkList& L,int i,int& e){
    	LNode* p=L,*q;
    	int j=0;
    	while(p&&j<i-1) {p=p->next;j++;}  //p指向a_{i-1}
    	if(!(p->next)||j>i-1) return -1;
    	q=p->next; //q指向a_{i}
    	p->next=q->next;//a_{i-1}指向a_{i+1}
    	e=q->data;
    	return 0;
    }
    

    时间复杂度O(n)。

    重置为一个空表

    思路:让头指针指向最后一个结点的指针,这个指针是空指针。

    int ClearList(LinkList& L){
    	while(L->next){
    		LNode* p = new LNode;
    		p=L->next;L->next=p->next;//不断的删去当前的第一个结点
    		delete p;
    	}
    	return 0;
    }
    

    时间复杂度O(n)。

    如何从线性表得到单链表(构造单链表)

    链表是一个动态结构,生成链表的过程是逐个结点插入的过程。

    • 第一步:建立空表(带头结点),也就是单链表初始化。
    • 第二步:用元素构建结点,并插入。循环此步骤,直至所有元素都插入完成。

    我们对上边提到的添加元素至链表尾部中的方法不断调用就可以实现第二步,这种方法也叫尾插法

    //传入使用 InitList 初始化之后的L
    int CreatList(LinkList& L,int e[],int n){
    	LNode *r=L;//初始化尾指针
    	for(int i=0;i<n;i++){
    		LNode* p = new LNode;
    		p->data=e[i];
    		r->next=p;//尾部插入新结点
    		r=p;//新结点变为新的尾结点
    	}
    	r->next=NULL;//尾结点的next指向NULL	
    }
    

    此外,还有一种头插法
    头插法: 通过逆位序输入n个元素的值,建立单链表。

    //传入使用 InitList 初始化之后的L
    int CreatList(LinkList& L,int e[],int n){
    	for(int i=0;i<n;i++){
    		LNode* p=new LNode;
    		p->data=e[i];
    		p->next=L->next;
    		L->next=p;
    	}
    	return 0;
    }
    

    循环链表(单向)

    最后一个结点的指针域的指针又指回第一个结点的链表。
    和单链表的差别仅在于,辨别链表中最后一个结点的条件不再是“后继是否为空”,而是“后继是否为头结点”。

    特点:

    1. 对于单链表只能从头结点开始遍历整个链表,而对于单循环链表则可以从表中任意结点开始遍历整个链表。
    2. 有时对链表常做的操作是在表尾、表头进行,此时可以改变一下链表的标识方法,不用头指针而用一个指向尾结点的指针R来标识,可以使得操作效率提高。
    3. 在做链表合并和分裂时,如果不是必须从链表头开始,则可以直接在链表指针处合并,时间复杂度可达O(1)。

    双向链表和双向循环链表

    双向循环链表的空表的结构:头指针指向头结点;头结点的前向指针prior指向自身,后向指针next也指向自身,如图:
    在这里插入图片描述

    插入

    在这里插入图片描述
    s为待插入结点,p为插入位置原来的结点,q为p的直接后继。
    已知项是p的后继是q。
    需要修改的是p的后继,q的前驱,s的后继,s的前驱。
    插入分四步:

    1. 令s的前驱指向p
    2. 令s的后继指向p的后继
    3. 令p的后继(或s的后继)结点的前驱q指向s
    4. 令p的后继指向s
      这四步能不能交换?
      可以交换,但不是无条件的。必须时刻保证能够通过s和p能找到q。
      一个反例:上来就令p的后继指向s,这时我们就无法找到q了。
      规律:第1步的顺序任意,第2步在第4步之前,第3步在第4步之前

    删除

    已知结点p,p的后继结点是s,s的后继结点是q,现在要删除s。
    两步:

    1. p的后继指向p的后继的后继(q)
    2. p的后继(q)的前驱指向p

    双向链表的操作特点:

    • “查询”和单链表相同
    • 插入和删除时需要同时修改两个方向上的指针
    • 访问某个结点的直接前驱和直接后继时间复杂度都是O(1)。
    • 查找第i个结点,向第i给结点插入或删除第i个结点,都要区分是哪个方向
    • 如果是双向循环链表,修改指针要同时考虑前驱环链和后继环链上的修改。
    • 某个结点的直接前驱的直接后继,或它的直接后继的直接前驱,即为该结点本身。

    存储密度:
    分母是结点所占的存储量,分子是数据元素所占的存储量。
    双向链表和双向循环链表的优点是前驱操作更方便
    缺点是存储密度变低了。

    静态链表

    前边的链表结构都属于动态链表,还有一类静态链表。
    借助数组来描述线性表的链式存储结构,结点也有数据域data和指针域next,与动态链表中的指针不同的是,这里的指针是结点的相对地址(数组的下标,整型),称之为静态指针。

    #define MAXSIZE 100
    typedef struct sLNode{
    	int data;
    	int next; //这个“指针”实际上是基址的相对偏移量
    }component,SLinkList[MAXSIZE];
    
    

    静态链表适用于不支持“指针”的高级语言,或者最大元素固定但插入、删除操作频繁的链表应用中。有关基于静态链表上的线性表操作基本与动态链表相同,除了一些描述方法有区别外,算法思路是相同的。

    特点:

    • 所有数据元素均存储在连续的空间段,但是相邻两个元素不一定处在相邻的空间,即物理上相邻,逻辑上不一定相邻
    • 修改指针域即可完成插入和删除操作,不需要移动元素,但是也不能随机访问静态链表中的元素;
    • 一次性分配所有存储空间,插入、删除时无需再向操作系统申请或释放空间,但也限制了最大表长。

    代码可以参考:
    https://www.cnblogs.com/zrj-xjyd/p/8735145.html

    顺序表和链表的比较

    1. 顺序表和链表各有优缺点。
      顺序表优点:
    • 方法简单,各种高级语言都有数组,容易实现。
    • 不用为表示结点间的逻辑关系而增加额外的存储开销。
    • 顺序表具有按元素序号随机访问的特点。

    顺序表缺点:

    • 在顺序表中做插入或删除,平均移动大约表中一半的元素,对n较大的顺序表效率低。
    • 需要预先分配足够大的存储空间,估计过大会导致顺序表后部大量闲置;估计过小会导致溢出。

    链表的优缺点和顺序表相反。
    链表的优点:

    • 插入和删除不需要移动元素,只需要修改指针
    • 动态分配,不需要预先分配,没有溢出和闲置的问题

    缺点:

    • 链表需要指针,不是每一个高级语言都有。
    • 需要借助附加信息,即指针,来描述逻辑关系
    • 只能顺序访问,不能随机访问
    1. 实际中怎样选取存储结构?
    • 基于存储的考虑
      1. 存储规模难以估计时,不宜采用顺序表
      2. 链表的存储密度较低
    • 基于运算的考虑
      1. 经常按序号访问,顺序表更好
      2. 经常做插入、删除,链表更好
    • 基于环境的考虑
      1. 顺序表容易实现,任何高级语言都有数组类型
    展开全文
  • 实现线性表的链式表示结构,要给线性表分配链式存储结构,构造一个空的线性表需要向系统要一个空间,完成简单的插入,删除及输出功能
  • 1.线性表是最简单也是最常用的一种数据结构。线性表的例子不胜枚举,例如,英文字母表就是一个线性表,表中的英文字母是一个数据元素。 2.线性表的定义:线性表是具有相同特性...4.线性表链式存储结构-------链表...

    1.线性表是最简单也是最常用的一种数据结构。线性表的例子不胜枚举,例如,英文字母表就是一个线性表,表中的英文字母是一个数据元素。

    2.线性表的定义:线性表是具有相同特性的数据元素的一个有限序列。

    3.线性表的顺序存储结构-------顺序表

    顺序表是把线性表中的所有元素按照其逻辑顺序依次存储在计算机存储器中指定存储位置开始的一块连续的存储空间中。

    4.线性表的链式存储结构-------链表

    在链式存储中,每个存储点不仅包含元素本身的信息,数据域,还包含元素之间的逻辑关系,即一个节点包含有直接后继节点的地址信息,称之为指针域,如果只设置一个指针域,用以指向其后继的节点,这种构成的链表叫做线性单向链接表,简称单链表,如果设置两个指针域,那就是双链表。

    5.顺序表和链表的特点与区别:

    在顺序表中,逻辑上相邻的元素,其对应的存储位置也相邻,所以,当进行插入或者删除操作的时候,通常要平均移动半个表的数据,这是相当费时的操作。在链表中,逻辑上相邻的元素,其对应的存储位置是通过指针来链接的,因而每个节点的存储位置可以任意安排,不必要求相邻,所以当进行插入删除操作的时候,只修改对应的指针域即可。

    展开全文
  • 线性表线性表链式存储结构 代码 #include <iostream> #include <cstdio> using namespace std; #define OK 1 #define ERROR 0 #define LIST_INIT_SIZE 100 #define LISTINCREMENT 10 typedef int ...

    【线性表】线性表的链式存储结构

    代码

    #include <iostream>
    #include <cstdio>
    using namespace std;
    #define OK 1
    #define ERROR 0
    #define LIST_INIT_SIZE 100
    #define LISTINCREMENT 10
    typedef int status;
    typedef int ElemType;
    //线性表的链式存储结构
    typedef struct LNode{
        ElemType data;
        struct LNode * next;
    }LNode, *LinkList;
        //逆位序建表
    void CreateList_L(LinkList &L, int n){
        L = (LinkList)malloc(sizeof(LNode));
        L->next = NULL;
        for (int i = 0; i < n; i++){
            LinkList p = (LinkList)malloc(sizeof(LNode));
            scanf("%d", &p->data);
            p->next = L->next;
            L->next = p;
        }
    }
        //归并排序
    void MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc){
        LNode * pa = La->next;
        LNode * pb = Lb->next;
        LNode * pc;
        Lc = pc = La;
        while (pa && pb){
            if (pa->data <= pb->data){
                pc->next = pa;
                pc = pa;
                pa = pa->next;
            }
            else {
                pc->next = pb;
                pc = pb;
                pb = pb->next;
            }
        }
        pc->next = pa ? pa : pb;
        free(Lb);
    }
    int main(){
        LinkList La, Lb, Lc;
        int n;
        printf("第一个链表的长度:\n");
        scanf("%d", &n);
        CreateList_L(La, n);
        printf("第二个链表的长度:\n");
        scanf("%d", &n);
        CreateList_L(Lb, n);
        MergeList_L(La, Lb, Lc);
        Lc = Lc->next;
        while (Lc){
            printf("%d ", Lc->data);
            Lc = Lc->next;
        }
        return 0;
    }
    
    展开全文
  • 线性表链式存储结构
  • 前言:为什么会出现线性表链式存储结构呢?因为顺序存储结构是采用一块连续的存储单元存储线性表,在对线性表进行插入删除时非常不方便,为了解决顺序存储结构中的这个缺点,于是人们就想能不能采用一种不连续的...
  • 线性表链式存储结构——单链表 一、线性表链式存储结构定义 基本概念: 线性表链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,这组线性单元可以是连续的,也可以是不连续的。 线性表的顺序存储...
  • 线性表链式存储结构

    千次阅读 2018-12-06 09:30:11
    前言 线性表(顺序存储结构-用数组描述) 为了解决顺序存储不足:用线性表另外一种结构-链式存储。...线性表链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是...
  • 线性表链式存储结构 线性表链式存储结构特点是用一组任意的存储单元存储线性表的数据元素。它除了要存储数据元素信息外,还要存储它的后继元素的存储地址。 线性表链式存储结构中,存储数据元素信息...
  • 文章目录线性表链式存储结构1.基本概念2.设计与实现3.优点和缺点 线性表链式存储结构 1.基本概念 链式存储定义:为了表示每个数据元素与其直接后继元素之间的逻辑关系,每个元素除了存储本身的信息之外,还...
  • 线性表-链式存储结构

    千次阅读 2017-03-12 17:52:10
    3.6 线性表链式存储结构 3.6.1 顺序存储结构不足的解决办法 前面我们讲的线性表的顺序存储结构。它是有缺点的,最大的缺点就是插入和删除时需要移动大量元素,这显然就需要耗费时间。能不能想办法解决呢? 要解决...
  • 线性表链式存储结构 线性表的顺序存储结构要求逻辑关系上相邻的元素在物理位置上也相邻,这样方便了随机存取,但是在插入和删除元素时,需要移动大量元素,而线性表的链式存储则不要求逻辑上相邻的元素在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,275
精华内容 10,110
关键字:

线性表的链式存储结构