漫谈数据结构系列(二)之千里姻缘一“线”牵

前言

前一篇文章我们了解了什么是数据结构以及为什么要学习数据结构。还记得选择数据结构的原则吗?那就是“因地制宜,适合的就是最好的”。今天我们就将正式敲开数据结构的大门。和上一篇文章一样,我们以一句诗作为引子来引出今天的主题,这句诗就是“千里姻缘一线牵”。其中的“线”就是我们今天的主题,说到线我们可能会想到故事发展的时间线,时间就有先后顺序,而且顺序很重要,比如说有时候你会听到这样一句话“并不是你不够优秀,而是她先遇到了他“。也许这个时候你就能体会到顺序的重要。在数据结构中也有一种数据结构非常讲究出场的顺序,那就是线性表。线性表可以说是数据结构世界里的绝世好男人,它不仅讲究顺序,而且还永远都不会出轨。他的感情经历如图所示就像一条线一样,从头到尾不会分叉。
在这里插入图片描述

如同现实社会一样,有暖男就会有渣男,数据结构里的的渣男叫做二叉树,不是劈腿就是在劈腿的路上,不过这又是另一段精彩的故事。不过今天还是专注于专一的线性表。

线性表的表示

我们前面说到线形表的两个特性,一个是讲究顺序,一个是专一不分叉。这是线性表的逻辑结构,逻辑都是抽象的,所以我们可以把逻辑结构看成一个类,两个特性就是类的属性。真正把线形表存储到计算机的结构我们称之为存储结构,我们可以把存储结构看成类的实例,一个类可以有多个不同的实例。我们今天就了解一下最常见的两种线形表的实例顺序表和单链表。

顺序表

想象这样一个场景,一天晚上闲来无事睡不着,你就开始和你下铺的室友开始吹牛b,互相吹嘘自己的情史,说第一任女朋友是刘亦菲,第二任女朋友是杨幂,第三任女友刘诗诗,第四任女朋友是欧阳娜娜。也许你自己都没发现,说着说着,你就发现你的脑子里出现了这样一张表。

在这里插入图片描述

后来你总感觉有点不对劲,直到看到你和女朋友屏保,才发现原来把现女友忘了,赶紧把现女友加上,表就变成了这样。
在这里插入图片描述
后来你想想,你和杨幂也就暧昧了几天,应该不算你女朋友,她在这张表不配拥有姓名,所以你决定把她删了。这是你发现一个问题,虽然你只是删除了杨幂,可杨幂之后的人序号都要改变,她之后的人都受到了影响。
在这里插入图片描述

看着这张表,忽然你的脑海里晃过一个熟悉的身影,那是一个穿着校服扎着马尾的女孩,是她让你知道了喜欢一个人是什么样的感觉,所以你毫不犹豫的加上了她的名字。从回忆中醒来,你发现了和删除同样的问题,当你加入一个人的时候,她后面的人都会受到影响。
在这里插入图片描述

实现

当我们将其用代码实现结果如下,这里采用的是java语言,其他语言实现方式略有不同,但是思想是一样的。

public class SequenceList<T> {
    private final int maxSize = 10;
    private T[] listArray;
    private int length;

    // 创建顺序表
    public SequenceList() {
        this.length = 0;
        this.listArray = (T[]) new Object[maxSize];
    }
    // 创建顺序表
    public SequenceList(int n) {
        if(n < 0 || n > maxSize) {
            System.out.println("Error");
            System.exit(1);
        } else {
            this.length = 0;
            this.listArray = (T[]) new Object[n];
        }
    }

    //表背增加表项
    public void add(T object) {
        listArray[length] = object;
        length++;
    }

    //插入表项
    public boolean insert(T object, int position) {
        if(position < 0 || position > length + 1) {
            System.out.println("error");
            return false;
        } else if(length == listArray.length) {
            T[] p = (T[]) new Object[length * 2];
            for(int i = 0; i < length + 1; i++) {
                p[i] = listArray[i];
            }
            listArray = p;
        }
        for (int i = length + 1 ; position < i; i--) {
            listArray[i - 1] = listArray[i];
            i--;
        }
        listArray[position] = object;
        return true;
    }

    //移除表项
    public boolean remove( int position) {
        if(isEmpty()||position < 0 || position > length) {
            System.out.println("error");
            return false;
        }
        for ( int i = position; i < length + 1; i++) {
            listArray[i] = listArray[i + 1];
        }
        return true;
    }

    //判断顺序表是否为空
    public boolean isEmpty() {
        return length == 0;
    }

    //清空顺序表
    public void  clear() {
        length = 0;
    }

    //更新表项
    public boolean update(T object, int position) {
        if(isEmpty() || position < 0 || position > length) {
            System.out.println("error");
            return false;
        }
        listArray[position] = object;
        return true;
    }

    //遍历表项
    public void nextOrder() {
        for(int i = 0; i < length; i++) {
            System.out.println(listArray[i]);
        }
    }
}

单链表

当你在思考问题的时候,你室友这一边也陷入了深深的回忆,那是2012年的第一场雪,来的没有比以往时候更晚一些,那是一个穿着鹅黄色羽绒服留着齐刘海的女孩,她的名字叫做娜扎(此处略去一万字),娜扎之后是热巴(此处略去两万字),热巴之后又遇到了。。。(此处略去十万字)。

我们注意到顺序表的实现是在数组的基础上做了一些改造,而单链表是建立在节点的基础上,节点的结构如下,其中数据域中存储数据,在我们的例子中就是“娜扎”等等,指针是指向下一个节点,例如我们例子中的“娜扎之后”等等。

在这里插入图片描述

你室友的经历随着一声叹息连成了一张链表如下图所示。
在这里插入图片描述

当你的室友想删去娜扎时,结果图就变成了这样,我们注意到,虽然删去了娜扎,但是好像娜扎后面的节点并没有受到影响。

在这里插入图片描述

当增加一个节点时,结果如下,我们发现,增加一个节点对于后面的节点也没有影响,只是前面一个节点需要把指针指向增加的节点。
在这里插入图片描述

实现

由于没有直接可用的节点的类型,所以我们需要自定义节点类型如下

public class Node<T> {
     T data;
     Node<T> next;

    public Node(Node<T> n) {
        next = n;
    }

    public Node(T obj, Node<T> n) {
        data = obj;
        next = n;
    }

    public T getData() {
        return data;
    }

    public Node<T> getNext() {
        return next;
    }
}

单链表的代码如下

public class LinkList<T> {
    private Node<T> head;
    private int length;

    //创建单链表
    public LinkList() {
        length = 0;
        head = new Node<T>(null);
    };

    //获取头节点
    public Node<T> getHead() {
        return head;
    }

    //在链表尾增加节点
    public void add(T object) {
        Node<T> p, q;
        p = head;
        q = head.next;
        while (q != null) {
            p = q;
            q = q.next;
        }
        p.next = new Node<T>(object, null);
        length++;
    }

    //插入节点
    public boolean insert(T object, int position) {
        if(isEmpty() || position > length) {
            System.out.println("error");
            return false;
        }
        Node<T> p, q;
        int i = 1;
        p = head;
        q = head.next;
        while (i < position) {
            p = q;
            q = q.next;
            i++;
        }
        p.next = new Node<T>(object, q);
        length++;
        return true;
    }
    
    //删除节点
    public boolean remove(int position) {
        if(isEmpty() || position > length) {
            System.out.println("error");
            return false;
        }
        Node<T> p, q;
        p = head;
        q = head.next;
        int i = 1;
        while (i < position) {
            p = q;
            q = q.next;
            i++;
        }
        p.next = q.next;
        length--;
        return true;
    }

    //清除链表
    public void clear() {
        length = 0;
        head.next = null;
    }

    //查找节点
    public T search(int position) {
        if(isEmpty() || position > length) {
            System.out.println("error");
            return null;
        }
        Node<T> p, q;
        int i = 1;
        p = head;
        q = head.next;
        while (i < position) {
            p = q;
            q = q.next;
            i++;
        }
        return  q.data;
    }

    //遍历节点
    public void nextOrder() {
        if(isEmpty()) {
            System.out.println("error");
        }
        Node<T> p, q;
        int i = 1;
        p = head;
        q = head.next;
        while (i < length + 1) {
            p = q;
            System.out.println(p.data);
            q = q.next;
            i++;
        }
    }

    //修改节点
    public void modify(T object, int position) {
        if(isEmpty() || position > length) {
            System.out.println("error");
        }
        Node<T> p, q;
        int i = 1;
        p = head;
        q = head.next;
        while (i < position) {
            p = q;
            q = q.next;
            i++;
        }
        q.data = object;
    }

    //判断节点是否为空
    public boolean isEmpty() {
        return length == 0;
    }
}

总结

从上面的例子我们可以看出,顺序表的增删很麻烦,只要有一个表项的增加或者删除,后面的表项都需要改变,而对于单链表,增删很方便,对于后面的节点不会有影响。但是对于节点查找,单链表很麻烦,必须要从第一个节点开始遍历,直到找到为止,而对于顺序表而言,查找很方便,只需要给定序号,就可以直接找到,如给定5我们就可以直接找到欧阳娜娜。还是回到我们的原则,因地制宜,没有最好的数据结构,只有最合适的数据结构。

彩蛋

唐朝有个文人叫韦固,小时候经常到河边去玩,一天晚上,他见一个慈祥的老人在月光下翻阅书信,一边看,一边用一根红线绳把两块石头系在一起。韦固看见后非常奇怪,随口问道:“老伯伯,你系石头干什么?”老人说:“我在给当婚的人牵线呢!这一对石头,就是世上一对夫妻呀!“韦固好奇地问:”那我的妻子是谁呢?”老人说:“就是村头看菜园子的女孩儿。”

韦固很生气,心想,那丫头又穷又丑,我可不要,不如害死她算了。第二,他路过菜园,看看旁边没有人,拾了一块石头向女孩砸过去,女孩”扑通”一声倒在地上,韦固也吓得逃往外乡。

十几年后,韦固做了大学士,给他提亲的人非常多,但没有一个称心如意的。一天,韦固到张员外家作客,看见张员外的外甥女美貌出众,心里便十分喜欢;姑娘看韦固仪表堂堂,心里也有几分爱意。张员外看在眼里,喜在心上,当下托媒人定了婚事,选了吉期。到了大喜的日子。韦固将小姐娶到府上。洞房花烛夜,韦固细细端详爱妻,发现额角有一块小疤,就问她是怎么回事。小姐说:“小时候家里穷,有一天,我正在菜园里拾菜,不知哪个野小子打了我一石头,因此留下了这个疤。”韦固听后,心里十分吃惊,就把月下老人的话告诉了妻子,他这才相信缘分是拆不散的。

这就是千里姻缘一线牵的由来,也希望单身的各位都能尽快找到属于自己的姻缘。

点赞就是最大的支持,更多学习资料和文章可以关注微信公众号QStack。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值