JavaScript实现数据结构与算法(二)线性结构

学习笔记:coderwhy的JavaScript实现数据结构与算法的课程
其他章节笔记链接
1. 重要性
2. 线性结构【本文】
3. 哈希表
4. 树结构
5. 图结构
6. 排序和搜索

(二)线性结构

1. 线性结构

1.1 数组的特点
1.1.1 普通数组

优点:通过下标取元素效率非常高。
缺点:常见语言数组不能存放不同类型的数据,所以一般都先封装一个Object类型

1.1.2 底层数组

扩容方法:新建一个大的数组,把原有数组写进去,再添加新元素。
特点:插入和删除性能(效率)都很低。

1.2 栈(受限的线性结构)

栈的结构示意图

1.2.1 定义

栈(stack)是一种受限的线性表,后进先出(LIFO)

1.2.2 应用举例

函数调用栈
在这里插入图片描述
栈溢出:无限次数递归

1.2.3 栈结构面试题

栈结构面试题
答案:C

1.2.4 栈结构的实现

(1)基于数组

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    // Method:和某一个对象实例有联系

    // Function:
    // 封装栈类
    function Stack() {
        // 栈中属性
        this.items = [];

        // 栈的相关操作
        // 1. 将元素压入栈
        Stack.prototype.push = function (element) {
            this.items.push(element)
        };
        // 2. 从栈中取出元素
        Stack.prototype.pop = function () {
            return this.items.pop()
        };

        // 3. 查看一下栈顶元素
        Stack.prototype.peek = function () {
            return this.items[this.items.length - 1]
        };

        // 4. 判断栈是否为空
        Stack.prototype.isEmpty= function () {
            return this.items.length == 0
        };
        // 5. 获取栈中元素的个数
        Stack.prototype.size = function () {
            return this.items.length
        };
        // 6. toString方法
        Stack.prototype.toString = function () {
            // 20 10 12 8 7
            var resultString = '';
            for (var i = 0; i < this.items.length; i++){
                resultString += this.items[i] + ' '
            }
            return resultString
        }

    };

    // 栈的使用
    var s = new Stack();
    s.push(20);
    s.push(10);
    s.push(100);
    s.push(99);
    alert(s);

    s.pop();
    s.pop();
    alert(s);

    alert(s.peek())
    alert(s.isEmpty())
    alert(s.size())
</script>
</body>
</html>

(2)基于链表–JS暂无

1.2.5 栈的相关操作

在这里插入图片描述
例子:十进制转换成二进制

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    // Method:和某一个对象实例有联系

    // Function:
    // 封装栈类
    function Stack() {
        // 栈中属性
        this.items = [];

        // 栈的相关操作
        // 1. 将元素压入栈
        Stack.prototype.push = function (element) {
            this.items.push(element)
        };
        // 2. 从栈中取出元素
        Stack.prototype.pop = function () {
            return this.items.pop()
        };

        // 3. 查看一下栈顶元素
        Stack.prototype.peek = function () {
            return this.items[this.items.length - 1]
        };

        // 4. 判断栈是否为空
        Stack.prototype.isEmpty= function () {
            return this.items.length == 0
        };
        // 5. 获取栈中元素的个数
        Stack.prototype.size = function () {
            return this.items.length
        };
        // 6. toString方法
        Stack.prototype.toString = function () {
            // 20 10 12 8 7
            var resultString = '';
            for (var i = 0; i < this.items.length; i++){
                resultString += this.items[i] + ' '
            }
            return resultString
        }

    };

    // 函数:将十进制转成二进制
    function dec2bin(decNum) {
        // 1. 定义栈对象
        var stack = new Stack();

        // 2. 循环操作
        while(decNum > 0){
            // 2.1 获取余数,并且放入到栈中
            stack.push(decNum % 2);

            // 2.2 获取整除后的结果,进行下一次运算
            decNum = Math.floor(decNum / 2); // 向下取整

        }
        // 3. 从栈中取出0和1
        var binaryString = '';
        while(!stack.isEmpty()){
            binaryString += stack.pop();
        };
        return binaryString;
    }
    // 测试十进制转成二进制的函数
    alert(dec2bin(8));
</script>
</body>
</html>
1.3 队列(受限的线性结构)
1.3.1 队列的定义

先进先出(Frist)
在这里插入图片描述

1.3.2 队列的应用

在这里插入图片描述

1.3.3 队列的实现

(1)基于数组

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>封装队列</title>
</head>
<body>
<script>
    // 封装队列
    function Queue() {
        // 属性:内容
        this.items = [];

        // 方法:操作
        // 1. 将元素加入到队列中
        Queue.prototype.enqueue = function (element) {
            this.items.push(element);
        };

        // 2. 从队列中删除前端元素
        Queue.prototype.dequeue = function () {
            return this.items.shift(); // 删除数组中的第一个元素
        };

        // 3. 查看前端的元素
        Queue.prototype.front = function () {
          return this.items[0];
        };

        // 4. 查看队列是否为空
        Queue.prototype.isEmpty = function () {
          return this.items.length == 0;
        };

        // 5. 查看元素中的个数
        Queue.prototype.size = function () {
            return this.items.length;
        }
        // 6. toString
        Queue.prototype.toString = function () {
            var resultString = '';
            for(var i = 0; i < this.items.length;i++){
                resultString += this.items[i] + " ";
            }
            return resultString;
        }

    };
    // 使用队列
    var queue = new Queue();

    // 将元素加入到队列中
    queue.enqueue("adv");
    queue.enqueue("bfff");
    queue.enqueue("cbaaa");
    queue.enqueue("dbasda");
    alert(queue.toString());

    // 从队列中删除元素
    queue.dequeue()
    alert(queue.toString());
    queue.dequeue()
    alert(queue.toString());

    // front方法
    alert(queue.front());

    // 验证其他方法
    alert(queue.isEmpty());
    alert(queue.size());
</script>
</body>
</html>

(2)基于链表(性能更高)

1.3.4 队列的相关操作

在这里插入图片描述

1.3.5 队列结构面试题–击鼓传花

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>击鼓传花--面试题</title>
</head>
<body>
<script>
    // 封装队列
    function Queue() {
        // 属性:内容
        this.items = [];

        // 方法:操作
        // 1. 将元素加入到队列中
        Queue.prototype.enqueue = function (element) {
            this.items.push(element);
        };

        // 2. 从队列中删除前端元素
        Queue.prototype.dequeue = function () {
            return this.items.shift(); // 删除数组中的第一个元素
        };

        // 3. 查看前端的元素
        Queue.prototype.front = function () {
            return this.items[0];
        };

        // 4. 查看队列是否为空
        Queue.prototype.isEmpty = function () {
            return this.items.length == 0;
        };

        // 5. 查看元素中的个数
        Queue.prototype.size = function () {
            return this.items.length;
        }
        // 6. toString
        Queue.prototype.toString = function () {
            var resultString = '';
            for(var i = 0; i < this.items.length;i++){
                resultString += this.items[i] + " ";
            }
            return resultString;
        }

    };
    // 使用队列
    var queue = new Queue();
    
    // 面试题:击鼓传花
    // 参数:玩家名单,淘汰的数字
    function passGame(nameList,num) {
        // 1. 创建一个队列结构
        var queue = new Queue();

        // 2. 将所有人都依次加入到队列中
        for (var i = 0; i < nameList.length; i++){
            queue.enqueue(nameList[i]);
        };

        // 3. 开始数数字
        // 不是num的时候,重新加入嗷队列的末尾
        // 是num这个数字的时候,将其从队列中删除
        while (queue.size() > 1){
            // 3.1 num数字之前的人重新放入到队列末尾
            for (var i = 0; i < num-1; i++){
                queue.enqueue(queue.dequeue());
            };
            // 3.2 num对应这个人,直接从队列删除
            queue.dequeue();
        };
        // 4. 获取剩下的那个人
        alert(queue.size());
        var endName = queue.front();
        alert("最终剩下的人:"+endName);
        return nameList.indexOf(endName);

        
    }
    // 测试击鼓传花
    names = ['诸葛大力','张伟','胡一菲','曾小贤','关谷神奇'];
    alert(passGame(names,3));

</script>
</body>
</html>
1.3.6 优先级队列

(1)特点
在这里插入图片描述
(2)考虑的问题
在这里插入图片描述
(3) 应用
在这里插入图片描述
(4)实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>优先级队列</title>
</head>
<body>
<script>
    // 封装优先级队列
    function PriorityQueue() {
        // 在PriorityQueue中重新创建了一个类:可以理解为内部类
        function QueueElement(element,priority) {
            this.element = element;
            this.priority = priority;
        };
        // 封装属性
        this.items = [];

        // 实现插入方法
        PriorityQueue.prototype.enqueue = function (element,priority) {
            // 1. 创建QueueElement对象
            var queueElement = new QueueElement(element,priority);

            // 2. 判断队列是否为空
            if (this.items.length == 0){
                this.items.push(queueElement);
            }else{
                var added = false;
                for (var i = 0; i < this.items.length ; i++){
                    if(queueElement.priority<this.items[i].priority){
                        this.items.splice(i,0, queueElement);
                        added = true;
                        break;
                    };
                 };
                if(!added){
                    this.items.push(queueElement);
                }
            }
        };

        // 2. 从队列中删除前端元素
        PriorityQueue.prototype.dequeue = function () {
            return this.items.shift(); // 删除数组中的第一个元素
        };

        // 3. 查看前端的元素
        PriorityQueue.prototype.front = function () {
            return this.items[0];
        };

        // 4. 查看队列是否为空
        PriorityQueue.prototype.isEmpty = function () {
            return this.items.length == 0;
        };

        // 5. 查看元素中的个数
        PriorityQueue.prototype.size = function () {
            return this.items.length;
        }
        // 6. toString
        PriorityQueue.prototype.toString = function () {
            var resultString = '';
            for(var i = 0; i < this.items.length;i++){
                resultString += this.items[i].element + "-" + this.items[i].priority + " ";
            }
            return resultString;
        }
        
    };

    // 测试代码
    var pq = new PriorityQueue();
    pq.enqueue('abc',111);
    pq.enqueue('bweqw',222);
    pq.enqueue('dede',444);
    pq.enqueue('csaq3ew',333);
    alert(pq.toString());
</script>
</body>
</html>
1.4 链表
1.4.1 单向链表

(1)特点
在这里插入图片描述
(2) 优点
在这里插入图片描述
(3)缺点
在这里插入图片描述
(4)单向链表实现代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>单向链表</title>
</head>
<body>
<script>
    // 封装链表类
    function LinkedList(){
        // 内部类:节点类
        function Node(data) {
            this.data = data;
            this.next = null;
        };

        // 属性
        this.head = null;
        this.length = 0; // 记录链表的长度

        // 1. 追加方法
        LinkedList.prototype.append = function (data) {
            // 1.1 创建新的节点
            var newNode = new Node(data);

            // 1.2 判断添加的是不是第一个节点
            if(this.length == 0){ // 1.2.1 是第一个节点
                this.head = newNode;
            }else { // 1.2.2 不是第一个节点
                // 找到最后一个节点
                var current = this.head;
                while (current.next){
                    current = current.next;
                };
                // 最后节点的next指向新的节点
                current.next = newNode;
            };

            // 1.3 length+1
            this.length += 1;
        };
        // 2. toString 方法
        LinkedList.prototype.toString = function () {
            // 2.1 定义变量
            var current = this.head;
            var listString = "";
            // 2.2 循环获取变量
            while(current){
                listString += current.data + " ";
                current = current.next;
            }
            return listString;
        };

        // 3. insert方法
        LinkedList.prototype.insert = function (position,data) {
            // 3.1 对position进行越界判断
            if (position < 0 || position > this.length){
                return false;
            };

            // 3.2 根据data创建newNode
            var newNode = new Node(data);

            // 3.3 判断插入的位置是否是第一个
            if (position == 0){
                newNode.next = this.head;
                this.head = newNode;
            }else{
                var index = 0;
                var current = this.head;
                var previous = null;
                while(index++ < position){
                    previous = current;
                    current = current.next;
                }
                newNode.next = current;
                previous.next = newNode;

            }

            // 3.4 length+1
            this.length += 1;
            return true;
        };

        // 4. get方法
        LinkedList.prototype.get = function (position) {
            // 4.1 对position进行越界判断
            if (position < 0 || position >= this.length){
                return null;
            };

            // 4.2 获取对应的data
            var current = this.head;
            var index = 0;
            while (index++ < position ){
                current = current.next;
            }
            return current.data;
        };

        // 5. indexOf方法
        LinkedList.prototype.indexOf = function (data) {
            // 5.1 定义变量
            var current = this.head;
            var index = 0;
            // 5.2 开始查找
            while(current){
                if(current.data == data){
                    return index;
                }else{
                    index += 1;
                    current = current.next;
                }
            }

            // 5.3 找到最后没有找到,返回-1
            return -1;
        };

        // 6. update方法
        LinkedList.prototype.update = function (position,newData) {
            // 6.1 对position进行越界判断
            if (position < 0 || position >= this.length){
                return false;
            };

            // 6.2 查找正确的节点
            var current = this.head;
            var index = 0;
            while(index++  < position){
                current = current.next;
            }

            // 6.3 将postion位置的node的data,修改成newData
            current.data = newData;
            return true;
        };
        // 7. removeAt方法
        LinkedList.prototype.removeAt = function (position) {
            //7.1 对position进行越界判断
            if (position < 0 || position >= this.length){
                return false;
            };
            // 7.2 判断删除的是否是第一个节点
            if (position == 0){
                this.head = this.head.next;
            }else{
                var index = 0;
                var current = this.head;
                var previous = null;
                while (index++ < position){
                    previous = current;
                    current = current.next;
                };
                previous.next = current.next;
            };
            this.length -= 1;
        };
        // 8. remove方法
        LinkedList.prototype.remove = function (data) {
            // 8.1 获取data在列表中的位置
            var position = this.indexOf(data);

            // 8.2 根据位置信息,删除节点
            this.removeAt(position);
        };

        // 9. isEmpty方法
        LinkedList.prototype.isEmpty = function () {
            return this.length == 0;
        };

        // 10. size方法
        LinkedList.prototype.size = function () {
            return this.length;
        };
    };
    
    // 测试代码
    // 1. 创建LinkedList
    var list = new LinkedList();

    // 2. 测试append方法
    list.append('abc');
    list.append('bsad');
    list.append('csad');
    //alert(list)


    // 3. 测试insert方法
    list.insert(0,'zzzs');
    list.insert(3,'dqewq');
    list.insert(5,'edasd');
    //alert(list);

    // 4. 测试insert方法
    // alert(list.get(2));
    //alert(list.get(1));


    // 5. 测试indexOf方法
    // alert(list.indexOf('zzzs'));
    // alert(list.indexOf('zzzasdsas'));

    // 6. 测试update方法
    // alert(list);
    list.update(2,'xiaozhan')
    alert(list);

    // 7. 测试removeAt方法
    // list.removeAt(1);
    // alert(list);

    // 8. 测试remove方法
    list.remove("xiaozhan");
    alert(list);
    list.remove("abc");
    alert(list);

    // 9. 测试isEmpty方法
    alert(list.isEmpty());

    // 10. 测试size方法
    alert(list.size());

</script>
</body>
</html>
1.4.2 双向链表

(1)特点
![](https://img-blog.csdnimg.cn/20200210170047717.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzU0NTIyNQ==,size_16,color_FFFFFF,t_70
在这里插入图片描述
在这里插入图片描述
(2)优点
在这里插入图片描述
(3)缺点
在这里插入图片描述
(4)双向链表实现代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>双向链表</title>
</head>
<body>
<script>
     // 封装双向链表
    function DoublyLinkedList() {
        // 属性
        this.head = null;
        this.tail = null;
        this.length = 0;

        // 内部类:节点类
        function Node(data) {
            this.data = data;
            this.prev = null;
            this.next = null;

        };

        // 常见的操作:方法
        // 1. append方法
        DoublyLinkedList.prototype.append = function (data) {
            // 1. 根据data创建节点
            var newNode = new Node(data);

            // 2.判断添加的是否是第一个节点
            if(this.length == 0){
                this.head = newNode;
                this.tail = newNode;
            }else{
                newNode.prev = this.tail;
                this.tail.next = newNode;

                this.tail = newNode;
            };
            // 3. length+1
            this.length += 1;

        };
        // 2. 将链表转化成字符串的形式
        // 2.1 toString方法
        DoublyLinkedList.prototype.toString = function () {
            return this.backwardString();
        };

        // 2.2 forwardString方法
        DoublyLinkedList.prototype.forwardString = function () {
            // 1. 定义变量
            var current = this.tail;
            var resultString = "";

            // 2. 依次向前遍历,获取每一个节点
            while(current){
                resultString += current.data + " " ;
                current = current.prev;
            }
            return resultString;
        };

        // 2.3 backString方法
        DoublyLinkedList.prototype.backwardString = function () {
            // 1. 定义变量
            var current = this.head;
            var resultString = "";

            // 2. 依次向后遍历,获取每一个节点
            while (current) {
                resultString += current.data + " ";
                current = current.next;
            }
            return resultString;
        };

        // 3. insert方法
        DoublyLinkedList.prototype.insert = function (position,data) {
            // 3.1 越界判断
            if (position < 0 || position > this.length){
                return false;
            }

            // 3.2 根据data创建新的节点
            var newNode = new Node(data);

            // 3.3 根据position判断原来的列表是否为空
            if (this.length == 0){
                  this.head = newNode;
                  this.tail = newNode;
            }else{
                // 判断position是不是0
                if(position == 0){
                    this.head.prev = newNode;
                    newNode.next = this.head;
                    this.head = newNode;
                }else if(position == this.length){
                    this.append(data);
                }else{
                    var current = this.head;
                    var index = 0;
                    while(index++<position){
                        current = current.next;
                    };
                    // 修改指针
                    newNode.next = current;
                    newNode.prev = current.prev;
                    current.prev.next = newNode;
                    current.prev = newNode;
                };
            };
            // 3.4 length+1
            this.length += 1;
            return true;

        };
        // 4.get方法
        DoublyLinkedList.prototype.get = function (position) {
            // 4.1 越界判断
            if (position < 0 || position >= this.length){
                return null;
            };
            // 4.2 获取元素
            // this.length / 2 > position: 从头向后遍历
            if (this.length / 2 > position){
                var current = this.head;
                var index = 0;
                while(index++ < position){
                    current = current.next;
                }
            }else{
            // this.length / 2 < position: 从后向头遍历
                var current = this.tail;
                var index = this.length -1 ;
                while(index-- > position){
                    current = current.prev;
                }

            }
            return current.data;
        };

        // 5. indexOf方法
        DoublyLinkedList.prototype.indexOf = function (data) {
            // 5.1 定义变量
            var current = this.head;
            var index = 0;

            // 5.2 查找和data相同的节点
            while (current){
                if(current.data == data){
                    return index;
                }
                index += 1;
                current = current.next;
            };
            return -1;
        };
        // 6. update方法
        DoublyLinkedList.prototype.update = function (position,data) {
            // 6.1 越界判断
            if (position < 0 || position >= this.length){
                return false;
            };
            // 6.2 寻找正确的节点
            var current = this.head;
            var index = 0;
            while (index++ < position){
                 current = current.next;
            };
            // 6.3 正确的节点的data
            current.data = data;
            return true;
        };
        // 7. removeAt方法
        DoublyLinkedList.prototype.removeAt = function (position) {
            // 7.1 越界判断
            if (position < 0 || position >= this.length){
                return null;
            };
            var current = this.head;
            // 7.2 判断是否只有一个节点
            if (this.length == 1){
                this.head = null;
                this.tail = null;
            }else{
                // 判断是否删除的是第一个节点
                if (position == 0){
                    this.head.next.prev = null;
                    this.head = this.head.next;

                }else if(position == this.length - 1 ){// 判断是否删除的是最后一个节点
                    this.tail.prev.next = null;
                    this.tail = this.tail.prev;
                }else{

                    var index = 0;

                    while (index++ < position){
                        current = current.next;
                    };
                    current.prev.next = current.next;
                    current.next.pre = current.prev;
                };
            };

            this.length -= 1;
            return current.data;

            
        };
        // 8.remove方法
        DoublyLinkedList.prototype.remove = function (data) {
            // 1. 根据data获取下标值
            var index = this.indexOf(data);

            // 2. 根据index删除对应位置的节点
            return this.removeAt(index);
        };

        // 9. isEmpty方法
        DoublyLinkedList.prototype.isEmpty = function () {
            if(this.length == 0){
                return true;
            }else{
                return false;
            }
        }

        // 10. size方法
        DoublyLinkedList.prototype.size = function () {
            return this.length;
        };

        // 11. 获取链表的第一个元素
        DoublyLinkedList.prototype.getFrist = function () {
            return this.head.data;
        };
        // 12. 获取链表的最后个元素
        DoublyLinkedList.prototype.getLast = function () {
            return this.tail.data;
        };
    };


    // 测试代码
    var list = new DoublyLinkedList();

    // 1. 测试append方法
    list.append("abc");
    list.append("def");
    list.append("gre");
    // 2. 测试转成字符串的方法
    // alert(list);
    // alert(list.backwardString());
    // alert(list.forwardString());

    // 3. 测试插入insert方法
    list.insert(0,"swing");
    list.insert(4,"bbbbb");
    list.insert(2,"sad");
    alert(list);

    // 4. 测试get方法
    // alert(list.get(0));
    // alert(list.get(2));
    // alert(list.get(5));
    // alert(list.get(6));

    // 5. 测试indexOf方法
    // alert(list.indexOf("swing"));
    // alert(list.indexOf("bbbb"));
    // alert(list.indexOf("bbbbb"));

    // 6. 测试update方法
    list.update(0,"sssssss");
    list.update(5,"zzzzzzz");
    alert(list);

     // 7. 测试removeAt方法
     list.removeAt(0);
     alert(list)

    // 8.测试remove方法
    list.remove("abc");
    alert(list);

    

</script>
</body>

</html>

—————————————————————————————————————————
如果我的文章能帮你节约20秒,就请你为我的文章点个赞吧!

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值