数据结构基础温故-1.线性表(上)

开篇:线性表是最简单也是在编程当中使用最多的一种数据结构。例如,英文字母表(A,B,C,D...,Z)就是一个线性表,表中的每一个英文字母都是一个数据元素;又如,成绩单也是一个线性表,表中的每一行是一个数据元素,每个数据元素又由学号、姓名、成绩等数据项组成。顺序表和链表作为线性表的两种重要的存在形式,它们是堆栈、队列、树、图等数据结构的实现基础。

一、线性表基础

1.1 线性表的基本定义

  线性表:零个或多个数据元素的有限序列。线性表中的元素在位置上是有序的,类似于储户去银行排队取钱,人们依次排着队,排在前面的先取,排在后面的则后取。这种位置上的有序性就是一种线性关系。由此可以看出:线性表的前后两个元素存在一一对应关系

PS:需要注意的是,这种前后关系是逻辑意义上而非物理意义上的,就好比如果银行做了改革,使用排队机进行排队,所有储户分散在银行的各个角落,他们取钱的顺序是根据储户从排队机获取的纸条上的号码来决定的。

1.2 线性表的存储结构

  (1)顺序表

  线性表的顺序存储结构是指【用一块地址连续的存储空间依次存储线性表中的数据元素】。就好像我们刚刚提到的改革之前的银行,需要在业务窗口前排队等候办理。由此可以看出:在顺序表中,逻辑上相邻的元素在物理上也是相邻的

  (2)链表

  相比顺序表需要预先占用一块事先分配好的存储空间,链表就灵活一些。链表中逻辑上相邻的元素在物理上可以不相邻。这就好像改革之后的银行,人们办理业务的顺序是由手上的小纸条的号码来决定。在某些特定场合,链表的使用优先于顺序表。

二、顺序表基础

2.1 静态顺序表之数组

  在日常编程中,在处理一组数据时,最常使用的数据类型就是数组。它是线性表的顺序存储结构在程序语言中最直接的表现形式

  数组是最基础也是存取速度最快的一种集合类型,在.NET中它是引用类型,也就是说它所需的内存空间会在托管堆上分配,一旦数组被创建,其中的所有元素会被初始化为它们的默认值。

PS:另外需要注意的是,当数组元素为值类型时,数组对象存放的是值类型对象本身。而当元素为引用类型时,数组对象存放的则是对象的引用(指针)。

  (1)数组元素为值类型时:

int[] arrInt = new int[5];
arrInt[2] = 5;
arrInt[4] = 3;

  下图展示了上面的数组arrInt在内存(这里如未说明都指在.NET中的内存分配)中的分配形式,可以看到值类型数组在被创建的同时就拥有了默认值0。

  (2)数组元素为值类型时:

// System.Windows.Forms.Control
Control[] arrCtrl = new Control[5];
arrCtrl[0] = new Button();
arrCtrl[3] = new Label();

  下图则展示了上面的数组arrCtrl在内存中的分配,可以看到在托管堆中划分了一块能够存放5个指针的内存区域,并且每个元素都被初始化为null。如果某个元素被赋值,那么会存放一个指向实际对象存储区域的指针。

总结:数组优点很多,缺点也很明显:在实际编程中,无法动态改变集合的大小

2.2 动态顺序表之ArrayList与List<T>

  如果需要动态地改变数组所占用的内存空间的大小,则需要以数组为基础做进一步的抽象以实现这个功能。在C#中,ArrayList被称为动态数组,它的存储空间可以被动态地改变,同时还有添加、删除元素的功能。

  (1)简单好用但不是类型安全的ArrayList

  ①Add-添加新元素

        // 在数组末尾顺序添加指定元素
        public virtual int Add(object value)
        {
            // 当容量达到最大值时
            if (this._size == this._items.Length)
            {
                // 调整存储空间大小
                this.EnsureCapacity(this._size + 1);
            }

            this._items[this._size] = value;
            return this._size++;
        }    

  可以看到,在添加新元素时会进行数组容量的判断,如果达到最大值则会调用方法动态调整数组大小。

  ②RemoveAt-移除指定元素

        // 移除指定索引的元素
        public virtual void RemoveAt(int index)
        {
            if (index < 0 || index > this._size)
            {
                throw new ArgumentOutOfRangeException("index", "索引超过范围");
            }
            // 插入位置后的元素向前移动一位
            for (int i = index + 1; i < this._size; i++)
            {
                this._items[i - 1] = this._items[i];
            }

            this._size--;
            this._items[this._size] = null; // 最后一位空出的元素置为空
        }

  可以看到,在移除老元素时会进行大量的元素移动操作。这里的ArrayList采用的元素类型是object,所以最后将空出的元素置为null。

  ③EnsureCapacity-动态调整数组大小

        // 动态调整数组空间大小
        private void EnsureCapacity(int min)
        {
            if (this._items.Length < min)
            {
                // 新空间大小=原空间大小*2
                int num = (this._items.Length == 0) ?
                    _defaultCapacity : (this._items.Length * 2);
                if (num < min)
                {
                    num = min;
                }
                // 调整数组空间大小
                this.Capacity = num;
            }
        }

  事实上,内存空间一旦分配是没有办法更改大小的。ArrayList其实使用“搬家”的方法来实现这个功能的,即当房子住不下这么多人的时候,那么换一个更大的新房子就行了。这里,ArrayList需要扩容时,会在内存空间中开辟一块新区域,容量为原来的2倍,并把所有元素都复制到新内存空间中。

  (2).NET2.0出现的泛型版本:List<T>

  由于ArrayList实际存放的是object对象(在.NET中object是万物之宗,即所有类型的父类),在进行存取操作时需要进行大量的装箱和拆箱操作(如果你不知道装箱和拆箱,那么请阅读.NET中六个重要的概念),降低程序性能。于是,从.NET 2.0开始出现了泛型版本的List<T>,它完美取代了ArrayList。

List<int> intNumList = new List<int>();
intNumList.Add(1);
intNumList.Add(2);

int num1 = intNumList[0];
int num2 = intNumList[1];

  可以看到,在集合创建的时候就把元素类型限定为int类型,它是安全的,并且还避免了装箱和拆箱操作。因此,在实际编程中一般都会使用List<T>。

三、.NET中的ArrayList与List<T>

  在.NET中已经为我们提供了两个强有力的顺序表结构类型,我们可以通过Reflector来查看其具体实现。

3.1 ArrayList的实现

  通过查看源码,其关键就在于EnsureCapacity方法动态地调整数组大小。

3.2 List<T>的实现

  通过查看源码,其关键就在于使用了泛型,而其他的方法如Add、Remove以及EnsureCapacity都和ArrayList没有多大区别。

参考资料

(1)程杰,《大话数据结构》

(2)陈广,《数据结构(C#语言描述)》

(3)段恩泽,《数据结构(C#语言版)》

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

时间: 2024-08-02 06:55:43

数据结构基础温故-1.线性表(上)的相关文章

数据结构基础温故-1.线性表(中)

在上一篇中,我们学习了线性表最基础的表现形式-顺序表,但是其存在一定缺点:必须占用一整块事先分配好的存储空间,在插入和删除操作上需要移动大量元素(即操作不方便),于是不受固定存储空间限制并且可以进行比较快捷地插入和删除操作的链表横空出世,所以我们就来复习一下链表. 一.单链表基础 1.1 单链表的节点结构 在链表中,每个节点由两部分组成:数据域和指针域. 1.2 单链表的总体结构 链表就是由N个节点链接而成的线性表,如果其中每个节点只包含一个指针域那么就称为单链表,如果含有两个指针域那么就称为双

数据结构基础温故-1.线性表(下)

在上一篇中,我们了解了单链表与双链表,本次将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表(circular linked list). 一.循环链表基础 1.1 循环链表节点结构 循环链表和单链表的主要差异就在于循环的判断条件上,原来是判断p.next是否为空,现在则是p.next不等于头结点,则循环未结束. 1.2 循环链表的O(1)访问时间 在单链表中,有了头结点,我们可以在O(1)时间访问到第一个节点,但如果要访

数据结构基础温故-6.查找(上):基本查找与树表查找

只要你打开电脑,就会涉及到查找技术.如炒股软件中查股票信息.硬盘文件中找照片.在光盘中搜DVD,甚至玩游戏时在内存中查找攻击力.魅力值等数据修改用来作弊等,都要涉及到查找.当然,在互联网上查找信息就更加是家常便饭.查找是计算机应用中最常用的操作之一,也是许多程序中最耗时的一部分,查找方法的优劣对于系统的运行效率影响极大.因此,本篇讨论一些查找方法. 一.顺序查找 1.1 基本思想 顺序查找(Sequential Search)又叫线性查找,是最基本的查找技术,它的查找过程是:从表中第一个(或最后

数据结构基础温故-5.图(上)

前面几篇已经介绍了线性表和树两类数据结构,线性表中的元素是“一对一”的关系,树中的元素是“一对多”的关系,本章所述的图结构中的元素则是“多对多”的关系.图(Graph)是一种复杂的非线性结构,在图结构中,每个元素都可以有零个或多个前驱,也可以有零个或多个后继,也就是说,元素之间的关系是任意的.现实生活中的很多事物都可以抽象为图,例如世界各地接入Internet的计算机通过网线连接在一起,各个城市和城市之间的铁轨等等. 一.图的基本概念 1.1 多对多的复杂关系 现实中人与人之间关系非常复杂,比如

小猪的数据结构辅助教程——2.2 线性表中的单链表

小猪的数据结构辅助教程--2.2 线性表中的单链表 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点: 1.理解顺序表以及单链表各自的有点以及缺点! 2.熟悉单链表的形式,对于头指针,头结点,尾结点,数据域和指针域这些名词要知道是什么! 3.熟悉单链表的结点结构 4.区分头指针与头结点! 5.熟悉创建单链表的两种方式:头插法和尾插法 6.了解单链表12个基本操作的逻辑 7.有趣的算法题:查找单链表的中间结点~ 1.单链表的引入(顺序表与单链表的PK) 2.单链表的结构图以及一些名

小猪的数据结构辅助教程——2.4 线性表中的循环链表

小猪的数据结构辅助教程--2.4 线性表中的循环链表 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点: 1.了解单链表存在怎样的缺点,暴露出来的问题 2.知道什么是循环单链表,掌握单链表的特点以及存储结构 3.掌握循环链表的一些基本操作的实现逻辑,最好能手撕代码 1.循环单链表的引入 2.循环链表的特点以及存储结构 循环链表的特点: 上面也说了,比单链表稍微高比格一点的地方就是: 链表最后一个结点的指针域指向了头结点而已,这样形成所谓的环,就是循环单链表了,呵呵! 特点的话有:

小猪的数据结构辅助教程——2.1 线性表中的顺序表

小猪的数据结构辅助教程--2.1 线性表中的顺序表 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点: 1.抽象数据类型(ADT)的概念,三要素:数据,数据元素间的关系和数据的操作 2.线性表的特点:按照一条线排列的数据集合,1对1,除了首元和尾元,其他元素都有直接前驱和直接后继 3.牢记线性表的存储结构,要理解并熟悉12个基本操作的逻辑,最好能徒手撕出代码 4.求并集,顺序表的经典例子,必须掌握! 1.抽象的数据类型 简单点说: 抽象:有点像我们面向对象语言中的类的思想,将事物

小猪的数据结构辅助教程——2.7 线性表中的双向循环链表

小猪的数据结构辅助教程--2.7 线性表中的双向循环链表 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点: 1.了解引入双向循环链表的原因 2.熟悉双向循环链表的特点以及存储结构 3.掌握双向循环链表的一些基本操作的实现逻辑 4.掌握逆序输出双向循环链表元素逻辑 1.双向循环链表的引入 2.双向循环链表的存储结构 双向循环链表的特点: 上面也说了,空间换时间,比起循环链表只是多了一个指向前驱的指针 特点的话: 判断空表:L ->next = L -> prior = L; 存

【学习总结】《大话数据结构》- 第3章-线性表

[学习总结]<大话数据结构>- 总 启示: 线性表:零个或多个数据元素的有限序列. 目录 3.1 开场白 3.2 线性表的定义 3.3 线性表的抽象数据类型 3.4 线性表的顺序存储结构 3.5 顺序存储结构的插入与删除 3.6 线性表的链式存储结构 3.7 单链表的读取 3.8 单链表的插入与删除 3.9 单链表的整表创建 3.10 单链表的整表删除 3.11 单链表结构与顺序存储结构优缺点 3.12 静态链表 3.13 循环链表 3.14 双向链表 3.15 总结回顾 3.16 结尾语 -