zoukankan      html  css  js  c++  java
  • 数据结构-链表(LinkedList)

    数据结构-链表(LinkedList)

    三五七言

    不要让数据结构成为天花板

    相关代码地址

    概念

    链表是一个线性结构,同时也是一个天然的递归结构。链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。

    用途

    更快的插入和删除,因为只需要操作插入删除位置相邻元素即可,如果在线性表中,操作中间位置的元素后,后续的元素位置都需要调整。javascript中的应用例如原型链。

    基本属性

    • element当前节点的值
    • next下一个节点

    基本方法

    • insert(item, newitem)在item后面插入一个新元素newitem
      插入一个元素,需要将item元素节点的next指向新元素,新元素的next指向item元素的后继元素。
    • remove(pos)从队头删除一个元素
      删除一个节点时,需要将其前驱节点的next指向其后继节点即可。
    • find(element)查询值为element的节点位置
    • findpre(element)查询值为element的节点的前一个节点
    • display()显示整个链表

    链表的基本特性实现一个LinkedList

    /**
     * 基本属性
     * element当前节点的值
     * next下一个节点
     * 基本方法
     * insert(item, newitem)在item后面插入一个新元素newitem插入一个元素,需要将item元素节点的next指向新元素,新元素的next指向item元素的后继元素。
     * remove(pos)从队头删除一个元素删除一个节点时,需要将其前驱节点的next指向其后继节点即可。
     * find(element)查询值为element的节点位置
     * findpre(element)查询值为element的节点的前一个节点
     * display()显示整个链表
     */
    
    class Node {
      constructor(element) {
        this.element = element
        this.next = null
      }
    }
    
    class LinkedList {
      //定义头节点element属性为'head',next也指向null
      constructor() {
        this.head = new Node('head')
      }
      /**
       * 增加节点
       * 在item节点之后增加newItem
       */
      insert(item, newItem) {
        let itemNode = this.find(item)
        let newNode
        if (itemNode !== null) {
          newNode = new Node(newItem)
          newNode.next = itemNode.next
          itemNode.next = newNode
        }
      }
      //删除元素
      remove(item) {
        let preNode = this.findPre(item)
        if (preNode !== null) {
          preNode.next = preNode.next.next
        }
      }
      //查找item元素的位置
      find(item) {
        let node = this.head
        while (node !== null && node.element !== item) {
          node = node.next
        }
        return node
      }
      //是否为空表
      isEmpty() {
        return this.head.next === null
      }
    
      //寻找元素前一个节点
      findPre(item) {
        let node = this.head
        let preNode = null
        while (node !== null && node.element !== item) {
          preNode = node
          node = node.next
        }
        return preNode
      }
    
      //展示链表
      display() {
        let result = 'head'
        let node = this.head.next
        while (node !== null) {
          result += '-->' + node.element
          node = node.next
        }
        return result
      }
    }
    module.exports = LinkedList
    //测试插入方法
    // let link = new LinkedList();
    // link.insert('head',1);
    // link.insert('head',2);
    // link.insert(2,3);
    // let fresult = link.display();
    // console.log(fresult)
    // // //测试删除方法
    // link.remove(2);
    // link.remove(3);
    // let fresult2 = link.display();
    // console.log(fresult2)
    

    双向链表TwoWayLinkedList

    特征:
    每一个实例会记录前驱节点和后继节点,双向链表比单链表增加了反向遍历的能力,并且由于所查找节点的属性中包含了前驱和后继节点的信息,故插入节点和删除节点时使用同一个搜索方法即可

    /**
     *实现一个双向链表TwoWayLinkedList类。
     */
    
    class Node {
      constructor(element) {
        this.element = element
        this.next = null
        this.pre = null
      }
    }
    
    class TwoWayLinkedList {
      constructor() {
        this.head = new Node('head')
      }
      //是否为空表
      isEmpty() {
        return this.head.next === null
      }
      /**
       * 增加节点
       * 在item节点之后增加newItem
       */
      insert(item, newItem) {
        let itemNode = this.find(item)
        let newNode
        if (itemNode !== null) {
          newNode = new Node(newItem)
          newNode.next = itemNode.next
          //暂时忽略尾节点null的处理
          if (itemNode.next) {
            itemNode.next.pre = newNode
          }
          itemNode.next = newNode
          newNode.pre = itemNode
        }
      }
    
      //查找item元素的位置
      find(item) {
        let node = this.head
        while (node !== null && node.element !== item) {
          node = node.next
        }
        return node
        }
            //删除元素
            remove(item){
                let itemNode = this.find(item);
                if(itemNode !== null){
                    itemNode.pre.next = itemNode.next;
                    itemNode.next.pre = itemNode.pre;
                }
        }
            //展示链表
            display(){
                let result ='head';
                let node = this.head.next;
                while(node !== null){
                    result += '-->' + node.element;
                    node = node.next;
                }
                return result;
            }
    }
    module.exports = TwoWayLinkedList
    
    //测试插入方法
    let link = new TwoWayLinkedList();
    link.insert('head',1);
    link.insert('head',2);
    link.insert('head',3);
    let fresult = link.display();
    console.log(fresult)
    //测试删除方法
    link.remove(2);
    link.remove(3);
    let fresult2 = link.display();
    console.log(fresult2)
    

    循环链表CircularLinkedList

    特征:
    循环链表的特点是尾节点的next指针指向了头节点

    /**
     *以LinkedList类为参考基准,实现一个循环链表CircularLinkedList类 
     循环链表的特点是尾节点的next指针指向了头节点
     * 
     */
    //节点
    class Node{
        constructor(element){
            this.element = element;
            this.next = null;
        }
    }
    
    //循环链表
    class CircularLinkedList{
        //定义头节点element属性为'head',next也指向null
        constructor(){
            this.head = new Node('head');
            this.head.next = this.head;
            this.activeNode = null;
            this.count = 0;
        }
    
        /**
         * 增加节点
         * 在item节点之后增加newItem
         */
        insert(item, newItem){
            let itemNode = this.find(item);
            let newNode;
                newNode = new Node(newItem);
                newNode.next = itemNode.next;
                itemNode.next = newNode;
                this.count++;
        }
    
        //是否为空表
        isEmpty(){
            return this.head.next === this.head;
        }
        
        //查找item元素的位置
        find(item){
            let node = this.head;
            while(node.element !== item){
                node = node.next;
            }
            return node;
        }
    
        //删除元素
        remove(item){
            let preNode = this.findPre(item);
            if(preNode !== null){
                preNode.next = preNode.next.next;
                this.count--;
            }
        }
    
        //寻找元素前一个节点
        findPre(item){
            let node = this.head;
            let preNode = null;
            while(node.element !== item){
                preNode = node;
                node = node.next;
            }
            return preNode;
        }
    
        //展示链表
        display(){
            let result ='head';
            let node = this.head.next;
            while(node.element !== 'head'){
                result += '-->' + node.element;
                node = node.next;
            }
            return result;
        }
    
        //设置激活节点
        setActive(item){
            let itemNode = this.find(item);
            if (itemNode !== null && itemNode !== this.head) {
                 this.activeNode = itemNode;
            } else {
                return '节点不存在';
            }
        }
    
        //测试循环指向
        testCircle(){
            let result ='head';
            let times = 1;
            let node = this.head.next;
            while(times < 3){
                result += '-->' + node.element;
                node = node.next;
                if (node.element  === 'head') {
                    times += 1;
                }
            }
            return result;
        }
    }
    module.exports = CircularLinkedList;
    //测试插入方法
    /*let link = new CircularLinkedList();
    link.insert('head',1);
    link.insert('head',2);
    link.insert('head',3);
    //let fresult = link.display();
    let fresult = link.testCircle();
    console.log(fresult)
    //测试删除方法
    link.remove(2);
    link.remove(3);
    //let fresult2 = link.display();
    let fresult2 = link.testCircle();
    console.log(fresult2)
    */
    

    算法题练习

    传说在公元1世纪犹太战争中,犹太历史学家弗拉维奥·约瑟夫斯和他的40个同胞被罗马士兵包围,犹太士兵决定宁可自杀也不做俘虏,于是商量出一个自杀方案。他们围成一个圈,从一个人开始,数到第三个人事将第三个人杀死,然后再数,直到杀光所有人,约瑟夫和另一个人决定不参加这个疯狂的游戏,他们快速地计算出两个位置,站在那里得以幸存。写一段将n个人围成一圈,并且第m个人会被杀掉,计算一圈中哪两个人最后会存活,使用循环链表解决该问题。

    /**
     * 传说在公元1世纪犹太战争中,犹太历史学家弗拉维奥·约瑟夫斯和他的40个同胞被罗马士兵包围,犹太士兵决定宁可自杀也不做俘虏,于是商量出一个自杀方案。他们围成一个圈,从一个人开始,数到第三个人事将第三个人杀死,然后再数,直到杀光所有人,约瑟夫和另一个人决定不参加这个疯狂的游戏,他们快速地计算出两个位置,站在那里得以幸存。写一段将n个人围成一圈,并且第m个人会被杀掉,计算一圈中哪两个人最后会存货,使用循环链表解决该问题
     */
    const CircularLinkedList = require('./circularlinkedList');
    let link = new CircularLinkedList();
    //装入1-40个号码
    init();
    //开始报数
    start();
    
    
    //初始化
    function init() {
        for(let i = 40;i; i--){
            link.insert('head',i);
        }
    }
    
    function start() {
        //设置1号节点为激活
        link.setActive(1);
        let index = 1;
        while(link.count > 2){
            if(index % 3 === 0){
                link.remove(link.activeNode.element);
                console.log('自杀士兵编号:',link.activeNode.element);
                console.log('当前队列:',link.display());
            }
            link.activeNode = link.activeNode.next === link.head ? link.head.next : link.activeNode.next;
            index++;
        }
    }
    
  • 相关阅读:
    The Future of Middleware and the BizTalk Roadmap
    FW: How to spawn a process that runs under the context of the impersonated user in Microsoft ASP.NET pages
    Strips illegal Xml characters
    luogu P2280 激光炸弹(二维前缀和)
    luogu P2704 炮兵阵地(经典状态压缩DP)
    SP1716 GSS3 Can you answer these queries III (线段树维护最大连续子段和)
    二分图判定、匹配问题
    C++语法综合 | 基于char*设计一个字符串类MyString
    luogu P1044 火车进出栈问题(Catalan数)
    C++设计模式 | 三种设计模式基础
  • 原文地址:https://www.cnblogs.com/zzghk/p/14031722.html
Copyright © 2011-2022 走看看