在我们谈论本文具体内容之前,我们首先要说明一些事情。在现实生活中我们所说的“表”往往是二维的,比如课程表,就有行和列,成绩表也是有行和列。但是在数据结构,或者说我们本文讨论的范围内,我们所说的“线性表”是一维的,即所有“元素”都是前后排列的。就我个人而言,这样的“表”用“队列”来形容比较恰当。但是,数据结构中“队列”这个名词是被一种特殊的“线性表”给占用了的,所以我们没法再用“队列”来表示“线性表”,后期我们会对“队列”进行介绍。

  

  在第一段的说明过程中,我们其实已经说出了“线性表”的定义,就是若集合中的元素在逻辑上是一前一后像队伍一样联系起来的,那么这个集合就是一个表(准确地说是线性表)。

  显然,我们常用的数组就是表的一种,它符合“元素一前一后联系起来”的要求。线性表是最基础的数据结构(虽然结构体也能存多个数据在内,但其整体还是视为一个对象的),每当我们需要存储的数据不存在特殊的关系(比如以后会说的一对多关系),或者存在的关系为“一前一后”时(比如用户输入多个字符,字符间就存在输入顺序上的“一前一后”关系),我们就可以使用线性表。

  那么,线性表的相关知识讲到这儿就算结束了吗?毕竟数组大家应该都会用了。显然不是,我们说了,数组是线性表的一种,但也只是线性表的一种。

  接下来我们就要说说,使用数组作为线性表的缺点。首先让我们假设一种情况(这个在(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)——表与链表的更多相关文章

  1. 深入浅出数据结构C语言版(5)——链表的操作

    上一次我们从什么是表一直讲到了链表该怎么实现的想法上:http://www.cnblogs.com/mm93/p/6574912.html 而这一次我们就要实现所说的承诺,即实现链表应有的操作(至于游 ...

  2. 数据结构C语言版 有向图的十字链表存储表示和实现

    /*1wangxiaobo@163.com 数据结构C语言版 有向图的十字链表存储表示和实现 P165 编译环境:Dev-C++ 4.9.9.2 */ #include <stdio.h> ...

  3. 深入浅出数据结构C语言版(6)——游标数组及其实现

    在前两次博文中,我们由表讲到数组,然后又由数组的缺陷提出了指针式链表(即http://www.cnblogs.com/mm93/p/6576765.html中讲解的带有next指针的链表).但是指针式 ...

  4. 深入浅出数据结构C语言版(7)——特殊的表:队列与栈

    从深入浅出数据结构(4)到(6),我们分别讨论了什么是表.什么是链表.为什么用链表以及如何用数组模拟链表(游标数组),而现在,我们要进入到对线性表(特意加了"线性"二字是因为存在多 ...

  5. 深入浅出数据结构C语言版(1)——什么是数据结构及算法

    在很多数据结构相关的书籍,尤其是中文书籍中,常常把数据结构与算法"混合"起来讲,导致很多人初学时对于"数据结构"这个词的意思把握不准,从而降低了学习兴趣和学习信 ...

  6. 深入浅出数据结构C语言版(8)——后缀表达式、栈与四则运算计算器

    在深入浅出数据结构(7)的末尾,我们提到了栈可以用于实现计算器,并且我们给出了存储表达式的数据结构(结构体及该结构体组成的数组),如下: //SIZE用于多个场合,如栈的大小.表达式数组的大小 #de ...

  7. 深入浅出数据结构C语言版(10)——树的简介

    到目前为止,我们一直在谈论的数据结构都是"线性结构",不论是普通链表.栈还是队列,其中的每个元素(除了第一个和最后一个)都只有一个前驱(排在前面的元素)和一个后继(排在后面的元素) ...

  8. 深入浅出数据结构C语言版(12)——从二分查找到二叉树

    在很多有关数据结构和算法的书籍或文章中,作者往往是介绍完了什么是树后就直入主题的谈什么是二叉树balabala的.但我今天决定不按这个套路来.我个人觉得,一个东西或者说一种技术存在总该有一定的道理,不 ...

  9. 深入浅出数据结构C语言版(14)——散列表

    我们知道,由于二叉树的特性(完美情况下每次比较可以排除一半数据),对其进行查找算是比较快的了,时间复杂度为O(logN).但是,是否存在支持时间复杂度为常数级别的查找的数据结构呢?答案是存在,那就是散 ...

随机推荐

  1. HDU 2568[前进]模拟

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2568 关键思想:傻傻地模拟 代码如下: #include<iostream> using ...

  2. Android之AppWidget 开发浅析

    什么是AppWidget AppWidget 即桌面小部件,也叫桌面控件,就是能直接显示在Android系统桌面上的小程序,先看图: 图中我用黄色箭头指示的即为AppWidget,一些用户使用比较频繁 ...

  3. Dynamics CRM 2015-Auto Save

    Auto Save,顾名思义,就是不需要明确地点击Save按钮,自动保存.这个功能在创建CRM Organization的时候,默认是开启的. 需要注意的是: 1. Auto Save适用于Main ...

  4. JavaScript———从setTimeout与setInterval到AJAX异步

    setTimeout与setInterval执行 首先我们看一下以下代码打印结果 console.log(1); setTimeout(function() { console.log(2); },1 ...

  5. webapi 发布接口报405错误(angularjs2.0)

    参考链接:http://www.cnblogs.com/shenbin/p/5680976.html web访问接口报405错误,以前的jQuery访问方式访问接口没有问题. 但是换成angularj ...

  6. 安卓UDP通信

    功能: 实现了单次一发一收: import java.net.*; import java.io.*; public class udpRecv { /* * 创建UDP传输的接收端 * 1.建立ud ...

  7. 5个Android开发中比较常见的内存泄漏问题及解决办法

    android中一个对象已经不需要了,但是其他对象还持有他的引用,导致他不能回收,导致这个对象暂存在内存中,这样内存泄漏就出现了.   内存泄漏出现多了,会是应用占用过多的没存,当占用的内存超过了系统 ...

  8. 游戏UI框架设计(一) : 架构设计理论篇

    游戏UI框架设计(一) ---架构设计理论篇 前几天(2017年2月)看到一篇文章,国内王健林.马云等大咖们看好的未来十大最有"钱途"产业中,排名第一的就是"泛娱乐&qu ...

  9. android学习9——Handler简单用法

    Handler用来发消息和处理消息.典型的用法是更新界面.android不允许在子线程里面更新界面,通常是把Handler传到子线程中,在子线程里通过sendEmptyMessage函数发消息.Han ...

  10. 剑指offer编程题Java实现——替换空格

    题目描述 请实现一个函数,将一个字符串中的空格替换成"%20".例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. package ...