线性表的链式存储——静态单链表的实现

1,单链表的一个缺陷:

1,触发条件:

1,长时间使用单链表对象频繁增加和删除数据元素;

2,可能的结果:

1,堆空间产生大量的内存碎片,导致系统运行缓慢;

1,增加一个节点,就会在堆空间创建一个结点,但是频繁创建删除就会有大量碎片;

2,解决方案,设计新的线性表:

1,设计思路:

1,在“单链表”的内部增加一片预留的空间,所有的 Node 对象都在这片空间中动态创建和动态销毁;

2,顺序表 + 单链表 = 静态单链表;

3,除了内存分配的不同外,静态单链表和单链表在其他操作上完全一样,只用改写 creat()和destory();

3,静态单链表继承层次结构:

4,静态单链表的实现思路(核心):

1,通过模板定义静态单链表(StaticLinkList);

2,在类中定义固定大小的空间(unsigned char[]);

1,创建 Node 结点;

3,重写 creat() 和 destroy() 函数,改写内存分配和归还方式;

4,在 Node 类中重载 operator new,用于在指定内存上创建对象;

5,StaticLinkList 静态单链表的实现  :

 1 #ifndef STATICLINKLIST_H
 2 #define STATICLINKLIST_H
 3
 4 #include "LinkList.h"
 5
 6 using namespace std;
 7
 8 namespace DTLib
 9 {
10
11 template <typename T, int N>
12 class StaticLinkList : public LinkList<T>
13 {
14 protected:
15    typedef typename LinkList<T>::Node Node;  // 这里的 typename 是因为编译器不知道这里的 Node 是类型还是静态成员变量,所以编译器建议加上 typename,然后又用 typedef 来简化一个类型;
16
17     struct SNode : public Node  // 定义新的类型来重载 new 操作符;
18     {
19         void* operator new(unsigned int size, void* loc)
20         {
21             (void)size;  // 这里是因为编译时候,没有使用 size 参数,然后加入的 C 语言中的暴力的编译方式;
22
23             return loc;
24         };
25    };
26
27     unsigned char m_space[sizeof(SNode) * N];  // 在这片内存里面来分配静态链表的内存;
28    int m_used[N];  // 标记数组,通过相应位置上为 1 可用,为 0 不可用;
29
30     Node* creat()  // 申请空间,调用构造函数
31     {
32         SNode* ret = NULL;
33
34         for(int i=0; i<N; i++)  // 遍历指定内存每个空间;
35         {
36             if( !m_used[i] )  // 如果可以使用,就用;
37             {
38                 ret = reinterpret_cast<SNode*>(m_space) + i;  // 这里只是单纯的分配内存,并没有涉及到 Node 的构造函数调用,内存的分配和初始化是两个不同的步骤,这里需要重新解释内存空间才可以进行指针运算;
39                 ret = new(ret)SNode();   // 内存分配好后,还要在指定的内存上面调用构造函数,调用重载的 new 函数;这里重写后将 new 申请的地址放在了 ret 上面,而不是默认的堆空间上,然后继续像普通的 new 一样调用构造函数来初始化内存;
40                 m_used[i] = 1;  // 标记被分配;
41                 break;   //这里要跳出 for 循环,因为不是依靠 i 来结束的
42             }
43         }
44
45         return ret;  // 这里的返回值运用了赋值兼容性;
46    }
47
48     void destory(Node *pn)  // 归还空间,调用析构函数
49     {
50         SNode* space = reinterpret_cast<SNode*>(m_space);   // 指针运算,所以要转换指针类型
51         SNode* psn = dynamic_cast<SNode*>(pn);
52
53         for(int i=0; i<N; i++)
54         {
55             if( pn == (space + i) )
56             {
57                 m_used[i] = 0; // 标记当前内存单元可用,也就是将其归还给固定空间;
58                 psn->~SNode();  // 调用析构函数,释放对象;
59                 break;  // 释放对象后就直接跳出循环,提高效率;
60             }
61         }
62    }
63
64 public:
65     StaticLinkList()
66     {
67         for(int i=0; i<N; i++)  // 标记每一个内存单元都是可用的
68         {
69             m_used[i] = 0;
70         }
71    }
72
73     int capacity()
74     {
75         return N;
76    }
77
78     ~StaticLinkList()   // 根据资源申请原则,不许用定义这个函数,但前提是这个类是独立的类,但这里是继承的类,所以还要调用父类的析构函数但是父类中析构函数调//用 clear() 不会发生多态(虽然父类和子类中就只有一个这样的函数),然后再调用 destroy()也不会发生多态,只调用父类中的 destroy(),这就造成了 delete 非堆空间上的不稳定性,程序容易发生错误;
79     {
80         this->clear();   // 这里不会发生多态,调用的是自己的 destroy(),因为这里是在析构函数中;
81     }
82 };
83
84 }
85
86 #endif // STATICLINKLIST_H

6,LinkList 中封装 create 和 destroy 函数的意义是什么?

1,为静态单链表(StaticLinkList)的实现做准备。StaticLinkList 与 LinkList 的不同仅在于链表结点内存分配上的不同;因此,将仅有的不同封装于父类和子类的虚函数中。

2,仅重写创建和销毁函数就可以了,其他的直接继承;

3,create() 和 destroy() 调用要用到多态;

7,小结:

1,顺序表与单链表相结合后衍生出静态单链表;

2,静态单链表是 LinkList 的子类,拥有单链表的所有操作;

3,静态单链表在预留的空间中创建结点对象;

4,静态单链表适合于频繁增删数据元素的场合(最大元素个数固定,如果确定不了,还是要用单链表);

原文地址:https://www.cnblogs.com/dishengAndziyu/p/10921947.html

时间: 2024-10-13 19:14:08

线性表的链式存储——静态单链表的实现的相关文章

线性表的链式存储之单链表的尾插法

对单链表进行遍历.查找.插入.删除等操作,最终效果如下: 相关C代码如下: /*线性表的链式存储之单链表的尾插法*/ #include <stdio.h> #include <stdlib.h> #include <malloc.h> /*定义变量*/ typedef int DataType; typedef struct node{     //定义链表结点数据结构 DataType data; struct node * pNext; }NODE; typedef

03.线性表(二)链式存储结构.单链表1

链式存储结构.单链表1 1.基本概念 为了表示每个数据元素ai与其直接后继数据元素ai+1之间的逻辑关系,对数据元素ai来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置) (1)数据域:存储线性表数据元素数据信息的域称为数据域: (2)指针域:把存储直接后继位置(下一个数据元素的地址)的域称为指针域,指针域中存储的信息为指针或链: (3)结点(Node):由数据域和指针域两部分信息组成数据元素ai的存储映像,称为结点. (4)头指针:把链表中第一个结点的存储

04.线性表(三)链式存储结构.单链表2

链式存储结构.单链表2 顺序存储结构的创建实质是一个数组的初始化,存储空间连续且其大小和类型已经固定:单链表存储空间不连续,是一种动态结构且它所占用空间的大小和位置是不需要预先分配划定的,可以根据系统的情况和实际的需求即时生成. 一.单链表的整表创建 创建单链表的过程就是一个动态生成链表的过程,即从"空表"的初始化起,依次建立各元素结点,并逐个插入链表. 1.算法思路 (1)声明一个结点p和计数器变量i; (2)初始化一空链表L (3)让链表L的头结点的指针指向NULL,即建立一个带头

数据结构第三篇——线性表的链式存储之单链表

线性表的链式存储结构的特点是用一组任意的存储单元来存储线性表的数据元素,这些单元可以分散在内存中的任意位置上,其在物理上可以是连续的,也可以是不连续的.具有链式存储结构的线性表称为线性链表. 为了表示出每个数据元素与其后继之间的关系,除了存储数据元素本身的信息之外,还需存储指示其直接后继的信息.这可以用一个结点(node)来完整的表示. 将节点中存储数据元素本身信息的域称为数据域:存储其直接后继位置的域称为指针域.指针域中存储的信息称作指针或链. 一般情况下,链表中每个结点可以包含若干个数据域和

《数据结构 - 线性表》链式存储 (单链表)

一:线性表定义 -<数据结构 - 线性表>顺序存储结构 二:为什么要 链式存储 线性表? - 因为在使用 顺序结构 存储方式存储的时候,每次进行 插入/删除 都需要大量移动元素的位置. - 所以设计出一种 存储空间不连续 的存储结构. - 这个线性表可能是这样的(存储位置不固定) -  三:链式存储 定义 -  因为链式存储,不是连续空间,所以需要两个信息 - 一个用于 存储数据元素,也叫做 数据域 - 一个用于 指向 下一个位置 的 指示信息,叫做指针域. - 指针域中存储的信息叫指针/链

线性表的链式存储结构(链表)

链表:基于来链式存储的线性表 链式逻辑存储结构:基于链式存储结构的线性表中,每个节点都包含数据域和指针域(数据域:存储数据元素本身.指针域:存储相邻节点的地址.) 单链表:链表的每个节点只包含后一个元素的地址信息  循环链表:链表的最后一个元素的包含第一个元素的地址信息 双向链表:链表的节点中包含后驱和前驱的地址信息 链表基本概念: 头节点(辅助节点.不包含数据域,指针域指向第0个数据元素)+ 数据节点(数据域+地址域)+  尾节点(指针域为空或某一值,只有数据域) 单链表节点定义: struc

2-5-归并链式存储的单链表-线性表-第2章-《数据结构》课本源码-严蔚敏吴伟民版

课本源码部分 第2章  线性表 - 归并单链表(链式存储) ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑        本源码引入的文件  链接? SinglyLinkedList.c        相关测试数据下载

数据结构之线性表-链式存储之单链表(一)

本人文笔较差,语文从来不及格,基础不好,写此类文章仅供自己学习,理解队列及其他知识,高手大神请略过.参考书籍 <数据结构与算法分析-Java语言描述 1.1 单链表简介 线性表的最大的缺点就是插入和删除操作需要移动大量的元素,这是它在内存中的连续存储结构造成的.为了弥补这2个缺点,就出现了链表,即线性表的链式存储. 链表是由一系列的几点组成,这些节点不必在内存中相连.这就意味着元素可以存在内存未被占用的任意位置.如图 这个存储可以想象成元素在内存中成三维空间存储,他们之间可没有像数组那样的下标标

线性表的链式存储——顺序表和单链表的对比分析

1,线性表两种实现: 1,顺序表: 2,单链表: 2,问题: 1,如何判断某个数据元素是否存在线性表中? 1,遍历线性表: 2,封装这个遍历操作: 3,遗失的操作 - find: 1,可以为线性表(List)增加一个查找操作: 2,int find(const T& e) const; 1,参数: 1,待查找的数据元素: 2,返回值: 1,>= 0:数据元素在线性表中第一次出现的位置: 2,-1:数据元素不存在: 3,遍历中会有相等和不等操作符,当比较对象是类的时候,需要类继承自 Objec