数据结构之链表

本文介绍了线性表和链表的基本概念,包括顺序存储结构和链式存储结构的特性。详细阐述了单链表、静态链表、循环链表和双向链表,并探讨了在JavaScript中实现单链表增删查改操作的方法。链表的插入和删除操作的时间复杂度分析也进行了讨论。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据结构学习之线性表(JS实现)

知识总结

线性表是一种典型的线性结构。头结点无前驱有一个后继,尾节点无后继有一个前驱。链表只能顺序查找,定位一个元素的时间为O(N),删除一个元素的时间为O(1)

1.线性表的顺序存储结构:把线性表的结点按逻辑顺序依次存放在一组地址连续的存储单元里。用这种方法存储的线性表简称顺序表。是一种随机存取的存储结构。顺序存储指内存地址是一块的,随机存取指访问时可以按下标随机访问,存储和存取是不一样的。如果是存储,则是指按顺序的,如果是存取,则是可以随机的,可以利用元素下标进行。数组比线性表速度更快的是:原地逆序、返回中间节点、选择随机节点。

  • 便于线性表的构造和任意元素的访问
  • 插入:插入新结点,之后结点后移。平均时间复杂度:O(n)
  • 删除:删除节点,之后结点前移。平均时间复杂度:O(n)

2.线性链表:用一组任意的存储单元来依次存放线性表的结点,这组存储单元即可以是连续的,也可以是不连续的,甚至是零散分布在内存中的任意位置上的。因此,链表中结点的逻辑次序和物理次序不一定相同。为了能正确表示结点间的逻辑关系,在存储每个结点值的同时,还必须存储指示其后继结点的地址。data域是数据域,用来存放结点的值。next是指针域(亦称链域),用来存放结点的直接后继的地址(或位置)。不需要事先估计存储空间大小。

3.单链表中每个结点的存储地址是存放在其前趋结点next域中,而开始结点无前趋,故应设头指针head指向开始结点。同时,由于最后一个结点无后继,故结点的指针域为空,即NULL。头插法建表(逆序)、尾插法建表(顺序)。增加头结点的目的是算法实现上的方便,但增大了内存开销。

  • 查找:只能从链表的头指针出发,顺链域next逐个结点往下搜索,直到搜索到第i个结点为止。因此,链表不是随机存取结构
  • 插入:先找到表的第i-1的存储位置,然后插入。新结点先连后继,再连前驱。
  • 删除:首先找到ai-1的存储位置p。然后令p–>next指向ai的直接后继结点,即把ai从链上摘下。最后释放结点ai的空间.r=p->next;p->next=r->next;delete r。
  • 判断一个单向链表中是否存在环的最佳方法是快慢指针

4.静态链表:用一维数组来实现线性链表,这种用一维数组表示的线性链表,称为静态链表。静态:体现在表的容量是一定的。(数组的大小);链表:插入与删除同前面所述的动态链表方法相同。静态链表中指针表示的是下一元素在数组中的位置。
静态链表是用数组实现的,是顺序的存储结构,在物理地址上是连续的,而且需要预先分配大小。动态链表是用申请内存函数(C是malloc,C++是new)动态申请内存的,所以在链表的长度上没有限制。动态链表因为是动态申请内存的,所以每个节点的物理地址不连续,要通过指针来顺序访问。静态链表在插入、删除时也是通过修改指针域来实现的,与动态链表没有什么分别
5.循环链表:是一种头尾相接的链表。其特点是无须增加存储量,仅对表的链接方式稍作改变,即可使得表处理更加方便灵活。
在单链表中,将终端结点的指针域NULL改为指向表头结点的或开始结点,就得到了单链形式的循环链表,并简单称为单循环链表。由于循环链表中没有NULL指针,故涉及遍历操作时,其终止条件就不再像非循环链表那样判断p或p—>next是否为空,而是判断它们是否等于某一指定指针,如头指针或尾指针等。
6.双向链表:在单链表的每个结点里再增加一个指向其直接前趋的指针域prior。这样就形成的链表中有两个方向不同的链。双链表一般由头指针唯一确定的,将头结点和尾结点链接起来构成循环链表,并称之为双向链表。设指针p指向某一结点,则双向链表结构的对称性可用下式描述:p—>prior—>next=p=p—>next—>prior。从两个方向搜索双链表,比从一个方向搜索双链表的方差要小。

  • 插入:先搞定插入节点的前驱和后继,再搞定后结点的前驱,最后搞定前结点的后继
  • 在有序双向链表中定位删除一个元素的平均时间复杂度为O(n)
  • 可以直接删除当前指针所指向的节点。而不需要像单向链表中,删除一个元素必须找到其前驱。因此在插入数据时,单向链表和双向链表操作复杂度相同,而删除数据时,双向链表的性能优于单向链表

JS实现单链表的操作var list = new LinkList();

增加节点

1.在尾节点处添加节点

        function append(element){
        let node = new node(element);    //根据传入的元素定义一个及诶单,该元素作为这个节点的值
        let current;   //定义一个变量表示当前的节点
        if(head == null){   //判断是否含有头节点,如果没有头结点,说明链表中还没有值,将传进来的这个值作为头结点
            current = node
        }else{   //否则对链表进行遍历,找到最后一个节点,将其next属性赋值为新增的节点
            while(current.next){
                current = current.next;
            }
            current.next = node
        }
            length++;   //链表长度+1
        }

2.在任意位置添加节点()

        function insert(position,element){
            let node = new node(element);
            let current = head;
            let temp;   //当前节点的前一个节点,在position处添加节点,就是在temp和current之间添加
            if(position = 0){    
                node.next = head;   
                head = node;
            }else{
                for(let i = 0;i<position; i++){   
                    temp = current;
                    current = current.next;
                }
                temp.next = node;
                node.next = current;
            }
            length++;
            return true;
        }

代码分析:

  • 原理:将这个位置的前一个节点的next属性赋值为这个节点,并将它原先的下一个节点保存下来,赋值给现在这个节点的next属性
  • 检查postion是否越界,若没有越界,则创建一个节点
  • 定义一个变量表示当前的节点,初始化为头节点,表示从头节点开始遍历;一个变量表示当前节点的前一个节点,作用是插入节点时方便找到前一个节点
  • 判断是否在头节点前添加,如果是就将头节点赋给node的next属性,并且头节点改为这个节点;否则,遍历出这个位置的节点,将该节点插入到这个位置的节点前面
  • 链表长度+1

删除节点

1.删除指定节点

        function removed(element){
            let node = new Node(element);
            let temp;
            let nextnode;
            let current = head;
            
            if(head != null){
                while(current != node){
                temp = current;
                current = current.next;
                nextnode = current.next;
                }
            temp.next = nextnode;
            length--;
            return true;
            }else{
                return false;
            }
        }

2.删除指定位置的节点

        function removedloc(position){
            let current = head;
            let temp;
            let nextnode;
            let i = 0;
            
            while(i < position){
                temp = current;
                current = current.next;
                nextnode = current.next;
            }
            
            pervious.next = nextnode;
            length--;
            return true;
        }

查询节点

1.查询某个位置是哪个节点

        function searchElement(element){
            if(head != null){
                let node = new Node(element);
                let current;
                let index = 0;
                if(head == node){
                    return 0;
                }else{
                    current = head;
                    while(current != node){
                        current = current.next;
                        index++;
                    }
                    return index;
                }
            }else{
                return -1;
            }
        }

2.查询某个节点是哪个位置

        function searchPosition(position){
            let i = 0;
            let current = head;
            while(i < position){
                current = current.next;
                i++;
            }
            return current;
        }

更改节点,在查询基础上替换element即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值