深入浅出数据结构C语言版(4)——表与链表
在我们谈论本文具体内容之前,我们首先要说明一些事情。在现实生活中我们所说的“表”往往是二维的,比如课程表,就有行和列,成绩表也是有行和列。但是在数据结构,或者说我们本文讨论的范围内,我们所说的“线性表”是一维的,即所有“元素”都是前后排列的。就我个人而言,这样的“表”用“队列”来形容比较恰当。但是,数据结构中“队列”这个名词是被一种特殊的“线性表”给占用了的,所以我们没法再用“队列”来表示“线性表”,后期我们会对“队列”进行介绍。
在第一段的说明过程中,我们其实已经说出了“线性表”的定义,就是若集合中的元素在逻辑上是一前一后像队伍一样联系起来的,那么这个集合就是一个表(准确地说是线性表)。
显然,我们常用的数组就是表的一种,它符合“元素一前一后联系起来”的要求。线性表是最基础的数据结构(虽然结构体也能存多个数据在内,但其整体还是视为一个对象的),每当我们需要存储的数据不存在特殊的关系(比如以后会说的一对多关系),或者存在的关系为“一前一后”时(比如用户输入多个字符,字符间就存在输入顺序上的“一前一后”关系),我们就可以使用线性表。
那么,线性表的相关知识讲到这儿就算结束了吗?毕竟数组大家应该都会用了。显然不是,我们说了,数组是线性表的一种,但也只是线性表的一种。
接下来我们就要说说,使用数组作为线性表的缺点。首先让我们假设一种情况(这个在(1)中提到过):
我们要写一个程序,这个程序的过程很简单:获取用户输入(这里就需要用到表来存储用户的输入),对用户的输入进行计算或操作,输出结果。
但是这个程序要考虑一个情况:用户有时候只需要输入几个数据,而有时候却需要输入几万个数据(可能有人会说怎么可能输入那么多,人都累死了。。。但是别忘了有一种操作叫文件重定向,虽然我记不得也没用过)。对于C程序来说,这个问题是不得不考虑的,如果你决定使用数组来存储用户的输入,那么数组的大小在创建之时就要确定好。很显然,数组的大小很好确定,比如十万,反正绝对大于用户可能的输入数量就行。但是这会带来一个很严重的问题,就是你的程序在不需要那么多空间的情况下依然会使用那么多空间,比如用户平均输入只有几百个,然而每次运行程序都先占用了十万大小的数组空间。显然这样的程序很不好,一来用户的内存可能还有别的程序需要使用,二来可能别的用户不需要十万个的存储空间而且他的内存也不够。
那么对于这种情况,我们该怎么办呢?
首先我们发现我们依然要用到线性表(假设用户的输入数据间的关系就是一前一后),所以我们先确定下来使用的是线性表类型的数据结构。然后我们希望的关键点其实就是:线性表能够随着需要而改变大小。那么我们该如何实现这样一个线性表呢?现在,不需要你去思考解决方案了,我们已经有了现成的数据结构,那就是链表!
回顾数组,我们发现,其实我们知道的信息有两个:
1.数组第一个元素在哪(数组名)
2.数组中元素都是相邻的,后一个元素就在前一个元素的后面,中间没有“空”
因为我们知道这两个信息,所以我们不需要知道各个元素的具体位置,只要根据某个元素在线性表中的顺序位置n,就可以由第一个元素的位置加上n来得到该元素。
但这也正是数组的不足所在,为了满足第2点,我们必须在创建数组时就指定其大小,只有这样我们才能找到满足2的一整块区域给数组。那么,为了解决这个不足,我们要做的就是不再使用一整块区域,而是令各元素自取所需。即元素不一定是相邻的了,只要添加元素时找一个可以放下该元素的容身之所就行,这一点的实现显然是利用malloc()。
但这又会带来一个问题,由于各元素都是通过malloc()获取的内存,所以各元素都分散开了,那么我们该如何找到各元素呢?实现的办法很简单,就是令每个元素“记住”下一个元素的位置!这样一来,我们就只需要保存第一个元素的位置就可以了,第二个元素的位置在第一个元素中,第三个元素的位置又在第二个元素中,以此类推。
接下来我们要做的,就是将这个想法付诸实现了。相信很快,我们就会发现,实现链表的关键之处在于如何“令元素记住下一个元素的位置”?在这里我们要多嘴一句,正如第一篇博文所说的,数据结构不仅决定如何存储数据,有时候也要决定或者说不得不考虑“存储什么数据”。现在我们就遇上了这样的问题。显然,原始元素本身是不会记住下一个元素在哪的,比如原始元素类型为int,它又如何去记住下一个int在哪?这时候,我们就需要对数据“封装”一下,形成一种新的数据类型,然后这种数据类型要能够记住下一个相同数据类型的位置。
思路已经出来了,就是“封装”出新的数据类型,然后要求其能记住下一个相同数据类型数据的位置。“封装”可以令我们想到结构体(也许你想不到但没关系,现在知道了),而“位置”则会让我们想到指针。因此我们“封装”出来的新数据类型应该是一个类似这样的结构体:
struct Element
{
DataType data; //DataType根据实际元素类型决定
struct Element * next; //next意味着其存储下一个元素的位置
}
然后在我们的程序中,我们只需要知道第一个元素在哪就行了(第一个会告诉我们第二个在哪,以此类推):
struct Element a={data,NULL};
struct Element * List=&a; //这个List就是一个“链表”
现在,对于为什么要使用链表,以及链表该如何实现的基本思想及如何“封装”出链表的元素我们应该都明白了。
但是虽然知道了链表添加新元素时该怎么做已经有了口头上的描述,却还没有给出代码上的实现,并且如何从链表中删除一个元素以及如何找到第n个元素也还没给出实现。
这些可以统称为对链表的操作,也可以说是能对链表这种数据结构使用的算法。关于这些,我们将在下一次博文中介绍。
深入浅出数据结构C语言版(4)——表与链表的更多相关文章
- 深入浅出数据结构C语言版(5)——链表的操作
上一次我们从什么是表一直讲到了链表该怎么实现的想法上:http://www.cnblogs.com/mm93/p/6574912.html 而这一次我们就要实现所说的承诺,即实现链表应有的操作(至于游 ...
- 数据结构C语言版 有向图的十字链表存储表示和实现
/*1wangxiaobo@163.com 数据结构C语言版 有向图的十字链表存储表示和实现 P165 编译环境:Dev-C++ 4.9.9.2 */ #include <stdio.h> ...
- 深入浅出数据结构C语言版(6)——游标数组及其实现
在前两次博文中,我们由表讲到数组,然后又由数组的缺陷提出了指针式链表(即http://www.cnblogs.com/mm93/p/6576765.html中讲解的带有next指针的链表).但是指针式 ...
- 深入浅出数据结构C语言版(7)——特殊的表:队列与栈
从深入浅出数据结构(4)到(6),我们分别讨论了什么是表.什么是链表.为什么用链表以及如何用数组模拟链表(游标数组),而现在,我们要进入到对线性表(特意加了"线性"二字是因为存在多 ...
- 深入浅出数据结构C语言版(1)——什么是数据结构及算法
在很多数据结构相关的书籍,尤其是中文书籍中,常常把数据结构与算法"混合"起来讲,导致很多人初学时对于"数据结构"这个词的意思把握不准,从而降低了学习兴趣和学习信 ...
- 深入浅出数据结构C语言版(8)——后缀表达式、栈与四则运算计算器
在深入浅出数据结构(7)的末尾,我们提到了栈可以用于实现计算器,并且我们给出了存储表达式的数据结构(结构体及该结构体组成的数组),如下: //SIZE用于多个场合,如栈的大小.表达式数组的大小 #de ...
- 深入浅出数据结构C语言版(10)——树的简介
到目前为止,我们一直在谈论的数据结构都是"线性结构",不论是普通链表.栈还是队列,其中的每个元素(除了第一个和最后一个)都只有一个前驱(排在前面的元素)和一个后继(排在后面的元素) ...
- 深入浅出数据结构C语言版(12)——从二分查找到二叉树
在很多有关数据结构和算法的书籍或文章中,作者往往是介绍完了什么是树后就直入主题的谈什么是二叉树balabala的.但我今天决定不按这个套路来.我个人觉得,一个东西或者说一种技术存在总该有一定的道理,不 ...
- 深入浅出数据结构C语言版(14)——散列表
我们知道,由于二叉树的特性(完美情况下每次比较可以排除一半数据),对其进行查找算是比较快的了,时间复杂度为O(logN).但是,是否存在支持时间复杂度为常数级别的查找的数据结构呢?答案是存在,那就是散 ...
随机推荐
- Java豆瓣电影爬虫——减少与数据库交互实现批量插入
节前一个误操作把mysql中record表和movie表都清空了,显然我是没有做什么mysql备份的.所以,索性我把所有的表数据都清空的,一夜回到解放前…… 项目地址:https://github.c ...
- 《InsideUE4》UObject(四)类型系统代码生成
你想要啊?想要你就说出来嘛,你不说我怎么知道你想要呢? 引言 上文讲到了UE的类型系统结构,以及UHT分析源码的一些宏标记设定.在已经进行了类型系统整体的设计之后,本文将开始讨论接下来的步骤.暂时不讨 ...
- css 样式重置
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, ...
- node.js 的事件机制
昨天到今天, 又看了一边node 的事件模块, 觉得很神奇~ 分享一下 - -> 首先, 补充下对node 的理解: nodeJs 是一个单进程单线程应用程序, 但是通过事件和回调支持并发 ...
- ubuntu通过虚拟域名访问不了 502 / 网络错误
##之前把虚拟机的lamp环境搭建好,但是通过自己windows在浏览器访问一直不能正常运行. 简单说明一下我的相关设置: 1.设置windows的ip映射 C:\Windows\System32\d ...
- Ansible之 Inventory 资源清单介绍
一.Inventory 库存清单文件 1.Inventory 作用 Ansible 可以在同一时间针对多个系统设施进行管理工作.它通过选择Ansible 资源清单文件中列出的系统,该清单文件默认是在/ ...
- BZOJ 1337: 最小圆覆盖1336: [Balkan2002]Alien最小圆覆盖(随机增量法)
今天才知道有一种东西叫随机增量法就来学了= = 挺神奇的= = A.令ci为包括前i个点的最小圆,若第i+1个点无法被ci覆盖,则第i+1个点一定在ci+1上 B.令ci为包括前i个点的最小圆且p在边 ...
- jvm系列(七):jvm调优-工具篇
16年的时候花了一些时间整理了一些关于jvm的介绍文章,到现在回顾起来还是一些还没有补充全面,其中就包括如何利用工具来监控调优前后的性能变化.工具做为图形化界面来展示更能直观的发现问题,另一方面一些耗 ...
- 【PHP系列】PHP推荐标准之PSR-3,日志记录器接口
上节聊完了PHP官方的相关代码规范,下面给大家带来了PHP系列的PHP推荐标准的另外两个,PSR-3,PSR-4. 首先,我们先来了解下PSR-3是怎么回事. PHP-FIG发布的第三个推荐规范与前两 ...
- GitHub客户端Desktop的安装和使用总结
前言 这段时间想把我写的东西上传到GitHub上,所以开始收集资料学习,走了很多弯路( msysgit和极慢的FQ网速让我欲仙欲死),最后找到了比较好用的工具GitHub Desktop.在此做出自己 ...