数据结构之线性表

前言

线性表是指数据之间是一对一的关系,比如数组和链表都属于这一范畴。数组和链表又代表了两种存储方式:顺序存储和链式存储。部分知识已在Java集合源码分析之基础(一):数组与链表中阐述,这里不再赘述,本文是对这些概念的补充,以及给出可参考的Java代码实现。

顺序存储

顺序存储在大多数语言中定义为数组。它的特点就是地址连续,大小固定。下面,我们从增、删、查三个方面说明它的操作性。首先,我们声明了一个数组,并赋值了一些初始值,如下所示:

初始条件
  1. 如果要在空闲位置插入数据,例如位置6,数据插入后无需任何额外操作,此时时间复杂度为O(1),如下所示:
插入12
  1. 如果要在中间某个位置插入数据,例如想把数据6插入到5和7之间,那么7及之后的数据都需要向后移动一位,如下所示:
插入6

此时时间复杂度就不再是O(1)了。我们先用java代码实现它,示例如下:

public static void main(String[] args) {
    int[] arr = {1,2,5,7,9,11,0,0};
    int len = arr.length;
    // 在arr[3]插入6,注意边界,最后的数据会被删除
    for (int i = len-1; i >= 3; i--) {
        arr[i] = arr[i-1];
    }
    arr[3] = 6;

    for (int i = 0; i < len; i++) {
        System.out.print(arr[i]);
        System.out.print(", ");   
    }
}

运行后打印结果如下:

2, 5, 6, 7, 9, 11, 0,

最坏情况下,要把元素插入到位置0,需要执行n-1次操作,所以时间复杂度为O(n)

删除操作正好和插入相反,删除一个数据后,后边的数据都要向前移动一位,所以它的最坏时间复杂度也是O(n)

这里查分两种情况,一种是获取某一位置的数据,一种是查找一个值。

  1. 获取某个位置元素

对一个数组而言,数组的每个位置都可以通过位置0与偏移量计算出来,例如要获取位置6的地址,只需要:

a[6] = a[0] + 6*{数据长度}

所以它对应的时间复杂度为O(1)

  1. 查找值

遗憾的是,数组仅记录下标,却不记录数据本身位置,而且数组的数据也不是有序的(可以插入时排序,但这不是必需),我们要查询一个元素是否存在于数组中,需要对它进行遍历。示例代码如下:

public static void main(String[] args) {
    int[] arr = {1,2,5,7,9,11,0,0};
    int len = arr.length;
    // 查找元素9的位置
    for (int i = 0; i < len; i++) {
        if(9 == arr[i]){
            System.out.println("Index of num 9 is : " + i);
            break;
        }
    }
}

它的最坏时间复杂度也是O(n)

  • 总结

数组是一个中规中矩的数据结构,它在增删查方面的时间复杂度都是O(n),仅在通过Index索引数据时为O(1)。数组也有些高级的用法,比如动态调整大小,这部分知识大家可以看我分析ArrayList的文章:Java集合源码分析之List(二):ArrayList

链式存储

线性存储要求一块完整的内存,在增删数据时也表现一般,而链式存储则正好解决了这些问题。我们依然从增、删、查三个方面来解析。首先,我们先定义使用的Java模型:

class Node{
    Integer data;
    Node next;
}

其中data表示数据,next表示下一个数据的信息。现在我们实现了一个链表,如下所示:

初始条件

要在一个链式存储的结构中插入数据,只需要修改一个节点的指针域即可。比如要把元素7插入到1和5之间,把5挂在7后面,再将1的指针域指向7的位置,示意如下:

插入7

可以看到这个操作对其他数据不会产生影响,所以它的时间复杂度为O(1)

删除与插入类似,其时间复杂度也是O(1),不过其操作顺序和插入时相反。例如要把数据2从原表中删除,需要先把11挂在5的后边,再删除2,示意如下:

删除2

链式存储结构不能像数组一样根据Index获取数据,它的查询也必须要遍历,所以时间复杂度为O(n)

  • 总结

链式存储结构增删数据时时间复杂度是O(1),查询和数组一样,都是O(n),且不支持Index获取。对比数组可以发现,数组适合静态的、不变的数据,而链式适合动态的、变化频繁的数据。

单链表

单链表结构与上述示例类似,每个数据只知道下一个位置的数据,却不知道前一位置的数据。通常,定义一个单链表会设置一个头结点head,它的数据域没有意义,它的指针域指向链表的第一个数据。我们用java代码实现一个简单的单链表。

  1. 声明一个空链表
Node head = new Node(null,null);
  1. 添加一条数据
Node first = new Node(2,null);
Node second = new Node(5,null);
first.next = second;
head.next = first;
  1. 插入一条数据
Node insert = new Node(7,null);
// 注意顺序,①把first也链接在insert后,②再把insert链接在head后
// 因为通常情况下我们只持有head实例的引用
insert.next = head.next;
head.next = insert;
  1. 遍历
Node print = head.next;
while (print!=null) {
    System.out.println(print.data);
    print = print.next;
}
  1. 删除一条数据
// 删除数据2
Node delete = head.next;
Node prev = delete;
while(delete!=null){
    if(delete.data == 2){
        // 也要注意顺序,先把后边的数据,挂载在前一数据后边
        prev.next = delete.next;
        // 再删除数据2
        delete.next = null;
        break;
    }
    // 记录前一数据
    prev = delete;
    delete = delete.next;
}

头结点并非是必需的,只是为了保证数据一致性所添加的辅助结点。

双向链表

单链表的特点就是简单,但是它在遍历时只能从前往后,这在某些情况下是不适用的。比如所有的数据都是有序的,按照从小到大排序,数据依次是{1,3,5,6,8,9,11},现在我们已经找到了数据8的位置,想要找到6,按照单链表的设计只能从前向后遍历。这时候如果链表是双向的,从数据8开始,只需要执行一次操作就可以了。

双向链表是在单链表的基础上实现的,只需要在原有结构上增加一个prev指针即可,如下所示:

class Node{
    Integer data;
    Node prev;
    Node next;
}

它的操作和单链表类似,只是在增删时一定要注意顺序,下面对这两部分做重点说明。

  1. 声明一个空链表

为了数据的一致性,我们增加一个tail结点,作为尾几点:

Node head = new Node(null);
Node tail = new Node(null);
  1. 添加一条数据
Node first = new Node(2);
Node second = new Node(5);
// next和prev成对设置
second.next = tail;
tail.prev = second;
first.next = second;
second.prev = first;
head.next = first;
first.prev = head;
  1. 删除一条数据
// 删除数据2
Node delete = head.next;
Node prev = delete;
while(delete!=null){
    if(delete.data == 2){
        // 要注意顺序,先把后边的数据,挂载在前一数据后边
        prev.next = delete.next;
        delete.next.prev = prev;
        // 再删除数据2
        delete.next = null;
        delete.prev = null;
        break;
    }
    // 记录前一数据
    prev = delete;
    delete = delete.next;
}

只要注意成对地处理prev和next,就不会出错。

循环链表

无论是单链表还是双向链表,从某一数据开始遍历,都不能循环整个链表,除非从head或tail开始遍历。循环链表则可以从任意结点开始,遍历整个链表。它也是单链表的变种。

定义一个循环链表十分简单,只需要把单链表的最后一个元素的next指针,指向head即可。示意图如下所示:

循环链表

它的操作和以上链表都类似,这里不再赘述。

总结

数组和链表是计算机可以存储数据的最基本的结构,其他的数据结构虽然定义不同,但最终还是需要转换成这两种结构才能进行存储,所以我们必须深刻理解。链表也不是仅有以上三种形态,但都是在单链表的基础上进行改造,以适应不同的需求。

在JDK中包装的ArrayListLinkedList,就是数组和链表的应用,这些类包装了数组和链表的常用操作,还增加了动态管理数组大小的技术,在性能上的表现十分优秀,值得我们参考和学习。大家可以查看以下的文章学习相关知识:

Java集合源码分析之List(二):ArrayList

Java集合源码分析之LinkedList

以上涉及代码均已上传至我的github


我是飞机酱,如果您喜欢我的文章,可以关注我~

编程之路,道阻且长。唯,路漫漫其修远兮,吾将上下而求索。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • 转自:http://blog.csdn.net/oreo_go/article/details/52116214 ...
    YYT1992阅读 906评论 0 4
  • 大学的时候不好好学习,老师在讲台上讲课,自己在以为老师看不到的座位看小说,现在用到了老师讲的知识,只能自己看书查资...
    和珏猫阅读 1,385评论 1 3
  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,531评论 0 13
  • 每次看了电影都想写影评,但这次是第一次写。 心灵捕手很早很早就下了的,当时下来看只是因为他是一部心理学的经典作品。...
    橘冬林阅读 405评论 0 0
  • 张开了血盆大口的兽,漆黑深邃的眼瞳拥有着无限魔力,令人迷幻,心甘情愿走进它的口。 只是那一个个无辜的前仆后继的牺牲...
    西风烈_df37阅读 126评论 0 0